tangled
alpha
login
or
join now
tylur.dev
/
prototypey
prototypey.org - atproto lexicon typescript toolkit - mirror https://github.com/tylersayshi/prototypey
1
fork
atom
overview
issues
pulls
pipelines
initial pass at infer
Tyler
4 months ago
6e32af66
45a03e14
+633
13 changed files
expand all
collapse all
unified
split
deno.json
samples
actor-namespace.json
generator-view.json
post-view.json
primitives-array.json
primitives-blob.json
primitives-token.json
procedure-create-post.json
profile-namespace.json
profile-view-basic.json
query-search-posts.json
subscription-repos.json
src
infer.ts
+3
deno.json
···
4
},
5
"imports": {
6
"@std/assert": "jsr:@std/assert@1"
0
0
0
7
}
8
}
···
4
},
5
"imports": {
6
"@std/assert": "jsr:@std/assert@1"
7
+
},
8
+
"compilerOptions": {
9
+
"exactOptionalPropertyTypes": true
10
}
11
}
+103
samples/actor-namespace.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "app.bsky.actor.defs",
4
+
"defs": {
5
+
"profileViewBasic": {
6
+
"type": "object",
7
+
"properties": {
8
+
"did": {
9
+
"type": "string",
10
+
"required": true,
11
+
"format": "did"
12
+
},
13
+
"handle": {
14
+
"type": "string",
15
+
"required": true,
16
+
"format": "handle"
17
+
},
18
+
"displayName": {
19
+
"type": "string",
20
+
"maxGraphemes": 64,
21
+
"maxLength": 640
22
+
},
23
+
"pronouns": {
24
+
"type": "string"
25
+
},
26
+
"avatar": {
27
+
"type": "string",
28
+
"format": "uri"
29
+
},
30
+
"associated": {
31
+
"type": "ref",
32
+
"ref": "#profileAssociated"
33
+
},
34
+
"viewer": {
35
+
"type": "ref",
36
+
"ref": "#viewerState"
37
+
},
38
+
"labels": {
39
+
"type": "array",
40
+
"items": {
41
+
"type": "ref",
42
+
"ref": "com.atproto.label.defs#label"
43
+
}
44
+
},
45
+
"createdAt": {
46
+
"type": "string",
47
+
"format": "datetime"
48
+
},
49
+
"verification": {
50
+
"type": "ref",
51
+
"ref": "#verificationState"
52
+
},
53
+
"status": {
54
+
"type": "ref",
55
+
"ref": "#statusView"
56
+
}
57
+
},
58
+
"required": [
59
+
"did",
60
+
"handle"
61
+
]
62
+
},
63
+
"viewerState": {
64
+
"type": "object",
65
+
"properties": {
66
+
"muted": {
67
+
"type": "boolean"
68
+
},
69
+
"mutedByList": {
70
+
"type": "ref",
71
+
"ref": "app.bsky.graph.defs#listViewBasic"
72
+
},
73
+
"blockedBy": {
74
+
"type": "boolean"
75
+
},
76
+
"blocking": {
77
+
"type": "string",
78
+
"format": "at-uri"
79
+
},
80
+
"blockingByList": {
81
+
"type": "ref",
82
+
"ref": "app.bsky.graph.defs#listViewBasic"
83
+
},
84
+
"following": {
85
+
"type": "string",
86
+
"format": "at-uri"
87
+
},
88
+
"followedBy": {
89
+
"type": "string",
90
+
"format": "at-uri"
91
+
},
92
+
"knownFollowers": {
93
+
"type": "ref",
94
+
"ref": "#knownFollowers"
95
+
},
96
+
"activitySubscription": {
97
+
"type": "ref",
98
+
"ref": "app.bsky.notification.defs#activitySubscription"
99
+
}
100
+
}
101
+
}
102
+
}
103
+
}
+83
samples/generator-view.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "object",
3
+
"properties": {
4
+
"uri": {
5
+
"type": "string",
6
+
"required": true,
7
+
"format": "at-uri"
8
+
},
9
+
"cid": {
10
+
"type": "string",
11
+
"required": true,
12
+
"format": "cid"
13
+
},
14
+
"did": {
15
+
"type": "string",
16
+
"required": true,
17
+
"format": "did"
18
+
},
19
+
"creator": {
20
+
"type": "ref",
21
+
"ref": "app.bsky.actor.defs#profileView",
22
+
"required": true
23
+
},
24
+
"displayName": {
25
+
"type": "string",
26
+
"required": true
27
+
},
28
+
"description": {
29
+
"type": "string",
30
+
"maxGraphemes": 300,
31
+
"maxLength": 3000
32
+
},
33
+
"descriptionFacets": {
34
+
"type": "array",
35
+
"items": {
36
+
"type": "ref",
37
+
"ref": "app.bsky.richtext.facet"
38
+
}
39
+
},
40
+
"avatar": {
41
+
"type": "string",
42
+
"format": "uri"
43
+
},
44
+
"likeCount": {
45
+
"type": "integer",
46
+
"minimum": 0
47
+
},
48
+
"acceptsInteractions": {
49
+
"type": "boolean"
50
+
},
51
+
"labels": {
52
+
"type": "array",
53
+
"items": {
54
+
"type": "ref",
55
+
"ref": "com.atproto.label.defs#label"
56
+
}
57
+
},
58
+
"viewer": {
59
+
"type": "ref",
60
+
"ref": "#generatorViewerState"
61
+
},
62
+
"contentMode": {
63
+
"type": "string",
64
+
"knownValues": [
65
+
"app.bsky.feed.defs#contentModeUnspecified",
66
+
"app.bsky.feed.defs#contentModeVideo"
67
+
]
68
+
},
69
+
"indexedAt": {
70
+
"type": "string",
71
+
"required": true,
72
+
"format": "datetime"
73
+
}
74
+
},
75
+
"required": [
76
+
"uri",
77
+
"cid",
78
+
"did",
79
+
"creator",
80
+
"displayName",
81
+
"indexedAt"
82
+
]
83
+
}
+76
samples/post-view.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "object",
3
+
"properties": {
4
+
"uri": {
5
+
"type": "string",
6
+
"required": true,
7
+
"format": "at-uri"
8
+
},
9
+
"cid": {
10
+
"type": "string",
11
+
"required": true,
12
+
"format": "cid"
13
+
},
14
+
"author": {
15
+
"type": "ref",
16
+
"ref": "app.bsky.actor.defs#profileViewBasic",
17
+
"required": true
18
+
},
19
+
"record": {
20
+
"type": "unknown",
21
+
"required": true
22
+
},
23
+
"embed": {
24
+
"type": "union",
25
+
"refs": [
26
+
"app.bsky.embed.images#view",
27
+
"app.bsky.embed.video#view",
28
+
"app.bsky.embed.external#view",
29
+
"app.bsky.embed.record#view",
30
+
"app.bsky.embed.recordWithMedia#view"
31
+
]
32
+
},
33
+
"bookmarkCount": {
34
+
"type": "integer"
35
+
},
36
+
"replyCount": {
37
+
"type": "integer"
38
+
},
39
+
"repostCount": {
40
+
"type": "integer"
41
+
},
42
+
"likeCount": {
43
+
"type": "integer"
44
+
},
45
+
"quoteCount": {
46
+
"type": "integer"
47
+
},
48
+
"indexedAt": {
49
+
"type": "string",
50
+
"required": true,
51
+
"format": "datetime"
52
+
},
53
+
"viewer": {
54
+
"type": "ref",
55
+
"ref": "#viewerState"
56
+
},
57
+
"labels": {
58
+
"type": "array",
59
+
"items": {
60
+
"type": "ref",
61
+
"ref": "com.atproto.label.defs#label"
62
+
}
63
+
},
64
+
"threadgate": {
65
+
"type": "ref",
66
+
"ref": "#threadgateView"
67
+
}
68
+
},
69
+
"required": [
70
+
"uri",
71
+
"cid",
72
+
"author",
73
+
"record",
74
+
"indexedAt"
75
+
]
76
+
}
+9
samples/primitives-array.json
···
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "array",
3
+
"items": {
4
+
"type": "string"
5
+
},
6
+
"minLength": 1,
7
+
"maxLength": 10,
8
+
"required": true
9
+
}
+8
samples/primitives-blob.json
···
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "blob",
3
+
"accept": [
4
+
"image/png",
5
+
"image/jpeg"
6
+
],
7
+
"maxSize": 5000000
8
+
}
+4
samples/primitives-token.json
···
0
0
0
0
···
1
+
{
2
+
"type": "token",
3
+
"description": "Request that less content like the given feed item be shown in the feed"
4
+
}
+61
samples/procedure-create-post.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "procedure",
3
+
"description": "Create a post",
4
+
"input": {
5
+
"encoding": "application/json",
6
+
"schema": {
7
+
"type": "object",
8
+
"properties": {
9
+
"repo": {
10
+
"type": "string",
11
+
"required": true
12
+
},
13
+
"collection": {
14
+
"type": "string",
15
+
"required": true
16
+
},
17
+
"record": {
18
+
"type": "unknown",
19
+
"required": true
20
+
},
21
+
"validate": {
22
+
"type": "boolean",
23
+
"default": true
24
+
}
25
+
},
26
+
"required": [
27
+
"repo",
28
+
"collection",
29
+
"record"
30
+
]
31
+
}
32
+
},
33
+
"output": {
34
+
"encoding": "application/json",
35
+
"schema": {
36
+
"type": "object",
37
+
"properties": {
38
+
"uri": {
39
+
"type": "string",
40
+
"required": true
41
+
},
42
+
"cid": {
43
+
"type": "string",
44
+
"required": true
45
+
}
46
+
},
47
+
"required": [
48
+
"uri",
49
+
"cid"
50
+
]
51
+
}
52
+
},
53
+
"errors": [
54
+
{
55
+
"name": "InvalidSwap"
56
+
},
57
+
{
58
+
"name": "InvalidRecord"
59
+
}
60
+
]
61
+
}
+25
samples/profile-namespace.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"lexicon": 1,
3
+
"id": "app.bsky.actor.profile",
4
+
"defs": {
5
+
"main": {
6
+
"type": "record",
7
+
"key": "self",
8
+
"record": {
9
+
"type": "object",
10
+
"properties": {
11
+
"displayName": {
12
+
"type": "string",
13
+
"maxLength": 64,
14
+
"maxGraphemes": 64
15
+
},
16
+
"description": {
17
+
"type": "string",
18
+
"maxLength": 256,
19
+
"maxGraphemes": 256
20
+
}
21
+
}
22
+
}
23
+
}
24
+
}
25
+
}
+58
samples/profile-view-basic.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "object",
3
+
"properties": {
4
+
"did": {
5
+
"type": "string",
6
+
"required": true,
7
+
"format": "did"
8
+
},
9
+
"handle": {
10
+
"type": "string",
11
+
"required": true,
12
+
"format": "handle"
13
+
},
14
+
"displayName": {
15
+
"type": "string",
16
+
"maxGraphemes": 64,
17
+
"maxLength": 640
18
+
},
19
+
"pronouns": {
20
+
"type": "string"
21
+
},
22
+
"avatar": {
23
+
"type": "string",
24
+
"format": "uri"
25
+
},
26
+
"associated": {
27
+
"type": "ref",
28
+
"ref": "#profileAssociated"
29
+
},
30
+
"viewer": {
31
+
"type": "ref",
32
+
"ref": "#viewerState"
33
+
},
34
+
"labels": {
35
+
"type": "array",
36
+
"items": {
37
+
"type": "ref",
38
+
"ref": "com.atproto.label.defs#label"
39
+
}
40
+
},
41
+
"createdAt": {
42
+
"type": "string",
43
+
"format": "datetime"
44
+
},
45
+
"verification": {
46
+
"type": "ref",
47
+
"ref": "#verificationState"
48
+
},
49
+
"status": {
50
+
"type": "ref",
51
+
"ref": "#statusView"
52
+
}
53
+
},
54
+
"required": [
55
+
"did",
56
+
"handle"
57
+
]
58
+
}
+63
samples/query-search-posts.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "query",
3
+
"description": "Find posts matching search criteria",
4
+
"parameters": {
5
+
"type": "params",
6
+
"properties": {
7
+
"q": {
8
+
"type": "string",
9
+
"required": true
10
+
},
11
+
"sort": {
12
+
"type": "string",
13
+
"enum": [
14
+
"top",
15
+
"latest"
16
+
],
17
+
"default": "latest"
18
+
},
19
+
"limit": {
20
+
"type": "integer",
21
+
"minimum": 1,
22
+
"maximum": 100,
23
+
"default": 25
24
+
},
25
+
"cursor": {
26
+
"type": "string"
27
+
}
28
+
},
29
+
"required": [
30
+
"q"
31
+
]
32
+
},
33
+
"output": {
34
+
"encoding": "application/json",
35
+
"schema": {
36
+
"type": "object",
37
+
"properties": {
38
+
"cursor": {
39
+
"type": "string"
40
+
},
41
+
"hitsTotal": {
42
+
"type": "integer"
43
+
},
44
+
"posts": {
45
+
"type": "array",
46
+
"items": {
47
+
"type": "ref",
48
+
"ref": "app.bsky.feed.defs#postView"
49
+
},
50
+
"required": true
51
+
}
52
+
},
53
+
"required": [
54
+
"posts"
55
+
]
56
+
}
57
+
},
58
+
"errors": [
59
+
{
60
+
"name": "BadQueryString"
61
+
}
62
+
]
63
+
}
+35
samples/subscription-repos.json
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
{
2
+
"type": "subscription",
3
+
"description": "Repository event stream, aka Firehose endpoint",
4
+
"parameters": {
5
+
"type": "params",
6
+
"properties": {
7
+
"cursor": {
8
+
"type": "integer"
9
+
}
10
+
}
11
+
},
12
+
"message": {
13
+
"description": "Represents an update of repository state",
14
+
"schema": {
15
+
"type": "union",
16
+
"refs": [
17
+
"#commit",
18
+
"#identity",
19
+
"#account",
20
+
"#handle",
21
+
"#migrate",
22
+
"#tombstone",
23
+
"#info"
24
+
]
25
+
}
26
+
},
27
+
"errors": [
28
+
{
29
+
"name": "FutureCursor"
30
+
},
31
+
{
32
+
"name": "ConsumerTooSlow"
33
+
}
34
+
]
35
+
}
+105
src/infer.ts
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
type InferType<T> = T extends { type: "record" } ? InferRecord<T>
2
+
: T extends { type: "object" } ? InferObject<T>
3
+
: T extends { type: "array" } ? InferArray<T>
4
+
: T extends { type: "params" } ? InferParams<T>
5
+
: T extends { type: "union" } ? InferUnion<T>
6
+
: T extends { type: "token" } ? InferToken<T>
7
+
: T extends { type: "ref" } ? InferRef<T>
8
+
: T extends { type: "unknown" } ? unknown
9
+
: T extends { type: "null" } ? null
10
+
: T extends { type: "boolean" } ? boolean
11
+
: T extends { type: "integer" } ? number
12
+
: T extends { type: "string" } ? string
13
+
: T extends { type: "bytes" } ? Uint8Array
14
+
: T extends { type: "cid-link" } ? string
15
+
: T extends { type: "blob" } ? Blob
16
+
: never;
17
+
18
+
type InferToken<T> = T extends { enum: readonly (infer U)[] } ? U : string;
19
+
20
+
type InferObject<T> = T extends { properties: infer P } ?
21
+
& {
22
+
-readonly [K in keyof P as P[K] extends { type: string } ? K : never]?:
23
+
InferType<
24
+
P[K]
25
+
>;
26
+
}
27
+
& (T extends { required?: readonly (infer R)[] } ? {
28
+
-readonly [K in R extends string ? R : never]-?: InferType<
29
+
P[K & keyof P]
30
+
>;
31
+
}
32
+
: never)
33
+
: never;
34
+
35
+
type InferArray<T> = T extends { items: infer Items } ? InferType<Items>[]
36
+
: never[];
37
+
38
+
type InferUnion<T> = T extends { refs: readonly (infer R)[] }
39
+
? R extends string ? { $type: R; [key: string]: unknown } : never
40
+
: never;
41
+
42
+
type InferRef<T> = T extends { ref: infer R }
43
+
? R extends string ? { $type: R; [key: string]: unknown } : unknown
44
+
: unknown;
45
+
46
+
type InferParams<T> = T extends { properties: infer P } ? InferObject<T>
47
+
: never;
48
+
49
+
type InferRecord<T> = T extends { record: infer R }
50
+
? R extends { type: "object" } ? InferObject<R>
51
+
: R extends { type: "union" } ? InferUnion<R>
52
+
: unknown
53
+
: unknown;
54
+
55
+
type Prettify<T> =
56
+
& {
57
+
[K in keyof T]: T[K];
58
+
}
59
+
// deno-lint-ignore ban-types
60
+
& {};
61
+
62
+
export type InferDefs<T extends Record<string, unknown>> = Prettify<
63
+
{
64
+
-readonly [K in keyof T]: InferType<T[K]>;
65
+
}
66
+
>;
67
+
68
+
type InferNS<T extends { id: string; defs: Record<string, unknown> }> =
69
+
InferDefs<T["defs"]>;
70
+
71
+
const schema = {
72
+
"lexicon": 1,
73
+
"id": "app.bsky.actor.profile",
74
+
"defs": {
75
+
"main": {
76
+
"type": "record",
77
+
"key": "self",
78
+
"record": {
79
+
"type": "object",
80
+
"properties": {
81
+
"displayName": {
82
+
"type": "string",
83
+
"maxLength": 64,
84
+
"maxGraphemes": 64,
85
+
},
86
+
"description": {
87
+
"type": "string",
88
+
"maxLength": 256,
89
+
"maxGraphemes": 256,
90
+
},
91
+
},
92
+
"nullable": ["displayName"],
93
+
},
94
+
},
95
+
},
96
+
} as const;
97
+
98
+
type Schema = InferNS<typeof schema>;
99
+
100
+
const main: Schema["main"] = {
101
+
displayName: "blob",
102
+
description: "I am a blob dude",
103
+
};
104
+
105
+
console.log(`hi ${main.displayName}`);