prototypey.org - atproto lexicon typescript toolkit - mirror https://github.com/tylersayshi/prototypey

initial pass at infer

Tyler 6e32af66 45a03e14

+633
+3
deno.json
··· 4 4 }, 5 5 "imports": { 6 6 "@std/assert": "jsr:@std/assert@1" 7 + }, 8 + "compilerOptions": { 9 + "exactOptionalPropertyTypes": true 7 10 } 8 11 }
+103
samples/actor-namespace.json
··· 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
··· 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
··· 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
··· 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
··· 1 + { 2 + "type": "blob", 3 + "accept": [ 4 + "image/png", 5 + "image/jpeg" 6 + ], 7 + "maxSize": 5000000 8 + }
+4
samples/primitives-token.json
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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}`);