An experimental TypeSpec syntax for Lexicon
1/**
2 * Simplified TypeSpec emitter types for Atproto Lexicon
3 *
4 * These types mirror the atproto lexicon structure but use plain TypeScript
5 * instead of Zod schemas. This avoids type conflicts from discriminated unions
6 * while remaining structurally compatible at runtime.
7 */
8
9// Primitives
10export type LexNull = {
11 type: "null";
12 description?: string;
13};
14
15export type LexBoolean = {
16 type: "boolean";
17 description?: string;
18 default?: boolean;
19 const?: boolean;
20};
21
22export type LexInteger = {
23 type: "integer";
24 description?: string;
25 default?: number;
26 minimum?: number;
27 maximum?: number;
28 enum?: number[];
29 const?: number;
30};
31
32export type LexString = {
33 type: "string";
34 format?: string;
35 description?: string;
36 default?: string;
37 minLength?: number;
38 maxLength?: number;
39 minGraphemes?: number;
40 maxGraphemes?: number;
41 enum?: string[];
42 const?: string;
43 knownValues?: string[];
44};
45
46export type LexUnknown = {
47 type: "unknown";
48 description?: string;
49};
50
51export type LexPrimitive = LexNull | LexBoolean | LexInteger | LexString | LexUnknown;
52
53// IPLD types
54export type LexBytes = {
55 type: "bytes";
56 description?: string;
57 maxLength?: number;
58 minLength?: number;
59};
60
61export type LexCidLink = {
62 type: "cid-link";
63 description?: string;
64};
65
66export type LexIpldType = LexBytes | LexCidLink;
67
68// References
69export type LexRef = {
70 type: "ref";
71 description?: string;
72 ref: string;
73};
74
75export type LexRefUnion = {
76 type: "union";
77 description?: string;
78 refs: string[];
79 closed?: boolean;
80};
81
82export type LexRefVariant = LexRef | LexRefUnion;
83
84// Blobs
85export type LexBlob = {
86 type: "blob";
87 description?: string;
88 accept?: string[];
89 maxSize?: number;
90};
91
92// Arrays
93export type LexArrayItem = LexPrimitive | LexIpldType | LexRefVariant | LexBlob;
94
95export type LexArray = {
96 type: "array";
97 description?: string;
98 items: LexArrayItem;
99 minLength?: number;
100 maxLength?: number;
101};
102
103// Objects - use a looser type for properties to avoid discriminated union issues
104export type LexObjectProperty =
105 | LexArray
106 | LexPrimitive
107 | LexIpldType
108 | LexRefVariant
109 | LexBlob
110 | LexObject; // Allow nested objects
111
112export type LexObject = {
113 type: "object";
114 description?: string;
115 required?: string[];
116 nullable?: string[];
117 properties: Record<string, LexObjectProperty>;
118};
119
120// Token
121export type LexToken = {
122 type: "token";
123 description?: string;
124};
125
126// XRPC types
127export type LexPrimitiveArray = {
128 type: "array";
129 description?: string;
130 items: LexPrimitive;
131 minLength?: number;
132 maxLength?: number;
133};
134
135export type LexXrpcParameterProperty = LexPrimitive | LexPrimitiveArray;
136
137export type LexXrpcParameters = {
138 type: "params";
139 description?: string;
140 required?: string[];
141 properties: Record<string, LexXrpcParameterProperty>;
142};
143
144export type LexXrpcBody = {
145 description?: string;
146 encoding: string;
147 schema?: LexRefVariant | LexObject;
148};
149
150export type LexXrpcSubscriptionMessage = {
151 description?: string;
152 schema?: LexRefVariant | LexObject;
153};
154
155export type LexXrpcError = {
156 name: string;
157 description?: string;
158};
159
160export type LexXrpcQuery = {
161 type: "query";
162 description?: string;
163 parameters?: LexXrpcParameters;
164 output?: LexXrpcBody;
165 errors?: LexXrpcError[];
166};
167
168export type LexXrpcProcedure = {
169 type: "procedure";
170 description?: string;
171 parameters?: LexXrpcParameters;
172 input?: LexXrpcBody;
173 output?: LexXrpcBody;
174 errors?: LexXrpcError[];
175};
176
177export type LexXrpcSubscription = {
178 type: "subscription";
179 description?: string;
180 parameters?: LexXrpcParameters;
181 message?: LexXrpcSubscriptionMessage;
182 errors?: LexXrpcError[];
183};
184
185// Records
186export type LexRecord = {
187 type: "record";
188 description?: string;
189 key?: string;
190 record: LexObject;
191};
192
193// Union of all user-definable types
194export type LexUserType =
195 | LexRecord
196 | LexXrpcQuery
197 | LexXrpcProcedure
198 | LexXrpcSubscription
199 | LexBlob
200 | LexArray
201 | LexToken
202 | LexObject
203 | LexPrimitive
204 | LexIpldType;
205
206// Lexicon document
207export type LexiconDoc = {
208 lexicon: 1;
209 id: string;
210 revision?: number;
211 description?: string;
212 defs: Record<string, LexUserType>;
213};
214
215// Helper type for objects that can have descriptions
216export type WithDescription<T> = T & { description?: string };