@atproto/lex-builder 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/filter.d.ts +7 -0
  2. package/dist/filter.d.ts.map +1 -0
  3. package/dist/filter.js +30 -0
  4. package/dist/filter.js.map +1 -0
  5. package/dist/filtered-indexer.d.ts +2123 -0
  6. package/dist/filtered-indexer.d.ts.map +1 -0
  7. package/dist/filtered-indexer.js +56 -0
  8. package/dist/filtered-indexer.js.map +1 -0
  9. package/dist/formatter.d.ts +13 -0
  10. package/dist/formatter.d.ts.map +1 -0
  11. package/dist/formatter.js +34 -0
  12. package/dist/formatter.js.map +1 -0
  13. package/dist/index.d.ts +6 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +13 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/lex-builder.d.ts +20 -0
  18. package/dist/lex-builder.d.ts.map +1 -0
  19. package/dist/lex-builder.js +123 -0
  20. package/dist/lex-builder.js.map +1 -0
  21. package/dist/lex-def-builder.d.ts +66 -0
  22. package/dist/lex-def-builder.d.ts.map +1 -0
  23. package/dist/lex-def-builder.js +682 -0
  24. package/dist/lex-def-builder.js.map +1 -0
  25. package/dist/lexicon-directory-indexer.d.ts +11 -0
  26. package/dist/lexicon-directory-indexer.d.ts.map +1 -0
  27. package/dist/lexicon-directory-indexer.js +51 -0
  28. package/dist/lexicon-directory-indexer.js.map +1 -0
  29. package/dist/ref-resolver.d.ts +48 -0
  30. package/dist/ref-resolver.d.ts.map +1 -0
  31. package/dist/ref-resolver.js +246 -0
  32. package/dist/ref-resolver.js.map +1 -0
  33. package/dist/ts-lang.d.ts +3 -0
  34. package/dist/ts-lang.d.ts.map +1 -0
  35. package/dist/ts-lang.js +138 -0
  36. package/dist/ts-lang.js.map +1 -0
  37. package/dist/util.d.ts +11 -0
  38. package/dist/util.d.ts.map +1 -0
  39. package/dist/util.js +67 -0
  40. package/dist/util.js.map +1 -0
  41. package/package.json +49 -0
  42. package/src/filter.ts +41 -0
  43. package/src/filtered-indexer.test.ts +83 -0
  44. package/src/filtered-indexer.ts +60 -0
  45. package/src/formatter.ts +42 -0
  46. package/src/index.ts +17 -0
  47. package/src/lex-builder.ts +149 -0
  48. package/src/lex-def-builder.ts +912 -0
  49. package/src/lexicon-directory-indexer.ts +57 -0
  50. package/src/ref-resolver.ts +301 -0
  51. package/src/ts-lang.ts +134 -0
  52. package/src/util.ts +67 -0
@@ -0,0 +1,682 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LexDefBuilder = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_assert_1 = tslib_1.__importDefault(require("node:assert"));
6
+ const ts_morph_1 = require("ts-morph");
7
+ const lex_schema_1 = require("@atproto/lex-schema");
8
+ const ref_resolver_js_1 = require("./ref-resolver.js");
9
+ const ts_lang_js_1 = require("./ts-lang.js");
10
+ /**
11
+ * Utility class to build a TypeScript source file from a lexicon document.
12
+ */
13
+ class LexDefBuilder {
14
+ options;
15
+ file;
16
+ doc;
17
+ refResolver;
18
+ constructor(options, file, doc, indexer) {
19
+ this.options = options;
20
+ this.file = file;
21
+ this.doc = doc;
22
+ this.refResolver = new ref_resolver_js_1.RefResolver(doc, file, indexer);
23
+ }
24
+ pure(code) {
25
+ return this.options.pureAnnotations ? markPure(code) : code;
26
+ }
27
+ async build() {
28
+ this.file.addVariableStatement({
29
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
30
+ declarations: [
31
+ { name: '$nsid', initializer: JSON.stringify(this.doc.id) },
32
+ ],
33
+ });
34
+ this.file.addExportDeclaration({
35
+ namedExports: [{ name: '$nsid' }],
36
+ });
37
+ const defs = Object.keys(this.doc.defs);
38
+ if (defs.length) {
39
+ const moduleSpecifier = this.options?.lib ?? '@atproto/lex-schema';
40
+ this.file
41
+ .addImportDeclaration({ moduleSpecifier })
42
+ .addNamedImports([{ name: 'l' }]);
43
+ for (const hash of defs) {
44
+ await this.addDef(hash);
45
+ }
46
+ }
47
+ }
48
+ addUtils(definitions) {
49
+ const entries = Object.entries(definitions).filter((e) => e[1] != null);
50
+ if (entries.length) {
51
+ this.file.addVariableStatement({
52
+ isExported: true,
53
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
54
+ declarations: entries.map(([name, initializer]) => ({
55
+ name,
56
+ initializer,
57
+ })),
58
+ });
59
+ }
60
+ }
61
+ async addDef(hash) {
62
+ const def = Object.hasOwn(this.doc.defs, hash) ? this.doc.defs[hash] : null;
63
+ if (def == null)
64
+ return;
65
+ switch (def.type) {
66
+ case 'permission-set':
67
+ return this.addPermissionSet(hash, def);
68
+ case 'procedure':
69
+ return this.addProcedure(hash, def);
70
+ case 'query':
71
+ return this.addQuery(hash, def);
72
+ case 'subscription':
73
+ return this.addSubscription(hash, def);
74
+ case 'record':
75
+ return this.addRecord(hash, def);
76
+ case 'token':
77
+ return this.addToken(hash, def);
78
+ case 'object':
79
+ return this.addObject(hash, def);
80
+ case 'array':
81
+ return this.addArray(hash, def);
82
+ default:
83
+ await this.addSchema(hash, def, {
84
+ type: await this.compileContainedType(def),
85
+ schema: await this.compileContainedSchema(def),
86
+ validationUtils: true,
87
+ });
88
+ }
89
+ }
90
+ async addPermissionSet(hash, def) {
91
+ const permission = def.permissions.map((def) => {
92
+ const options = stringifyOptionalOptions(def, ['resource', 'type']);
93
+ return this.pure(`l.permission(${JSON.stringify(def.resource)}, ${options})`);
94
+ });
95
+ const options = stringifyOptionalOptions(def, [
96
+ 'type',
97
+ 'description',
98
+ 'permissions',
99
+ ]);
100
+ await this.addSchema(hash, def, {
101
+ schema: this.pure(`l.permissionSet($nsid, [${permission.join(',')}], ${options})`),
102
+ });
103
+ }
104
+ async addProcedure(hash, def) {
105
+ if (hash !== 'main') {
106
+ throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
107
+ }
108
+ // @TODO Build the types instead of using an inferred type.
109
+ const ref = await this.addSchema(hash, def, {
110
+ schema: this.pure(`
111
+ l.procedure(
112
+ $nsid,
113
+ ${await this.compileParamsSchema(def.parameters)},
114
+ ${await this.compilePayload(def.input)},
115
+ ${await this.compilePayload(def.output)},
116
+ ${await this.compileErrors(def.errors)}
117
+ )
118
+ `),
119
+ });
120
+ this.addUtils({
121
+ $params: this.pure(`${ref.varName}.parameters`),
122
+ $input: this.pure(`${ref.varName}.input`),
123
+ $output: this.pure(`${ref.varName}.output`),
124
+ });
125
+ const parametersTypeStmt = this.file.addTypeAlias({
126
+ isExported: true,
127
+ name: 'Params',
128
+ type: `l.InferProcedureParameters<typeof ${ref.varName}>`,
129
+ });
130
+ addJsDoc(parametersTypeStmt, def.parameters);
131
+ const inputTypeStmt = this.file.addTypeAlias({
132
+ isExported: true,
133
+ name: 'Input',
134
+ type: `l.InferProcedureInputBody<typeof ${ref.varName}>`,
135
+ });
136
+ addJsDoc(inputTypeStmt, def.input);
137
+ const outputTypeStmt = this.file.addTypeAlias({
138
+ isExported: true,
139
+ name: 'Output',
140
+ type: `l.InferProcedureOutputBody<typeof ${ref.varName}>`,
141
+ });
142
+ addJsDoc(outputTypeStmt, def.output);
143
+ }
144
+ async addQuery(hash, def) {
145
+ if (hash !== 'main') {
146
+ throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
147
+ }
148
+ // @TODO Build the types instead of using an inferred type.
149
+ const ref = await this.addSchema(hash, def, {
150
+ schema: this.pure(`
151
+ l.query(
152
+ $nsid,
153
+ ${await this.compileParamsSchema(def.parameters)},
154
+ ${await this.compilePayload(def.output)},
155
+ ${await this.compileErrors(def.errors)}
156
+ )
157
+ `),
158
+ });
159
+ this.addUtils({
160
+ $params: `${ref.varName}.parameters`,
161
+ $output: `${ref.varName}.output`,
162
+ });
163
+ this.file.addTypeAlias({
164
+ isExported: true,
165
+ name: 'Params',
166
+ type: `l.InferQueryParameters<typeof ${ref.varName}>`,
167
+ });
168
+ this.file.addTypeAlias({
169
+ isExported: true,
170
+ name: 'Output',
171
+ type: `l.InferQueryOutputBody<typeof ${ref.varName}>`,
172
+ });
173
+ }
174
+ async addSubscription(hash, def) {
175
+ if (hash !== 'main') {
176
+ throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
177
+ }
178
+ // @TODO Build the types instead of using an inferred type.
179
+ const ref = await this.addSchema(hash, def, {
180
+ schema: this.pure(`
181
+ l.subscription(
182
+ $nsid,
183
+ ${await this.compileParamsSchema(def.parameters)},
184
+ ${await this.compileBodySchema(def.message?.schema)},
185
+ ${await this.compileErrors(def.errors)}
186
+ )
187
+ `),
188
+ });
189
+ this.addUtils({
190
+ $params: `${ref.varName}.parameters`,
191
+ $message: `${ref.varName}.message`,
192
+ });
193
+ this.file.addTypeAlias({
194
+ isExported: true,
195
+ name: 'Params',
196
+ type: `l.InferSubscriptionParameters<typeof ${ref.varName}>`,
197
+ });
198
+ this.file.addTypeAlias({
199
+ isExported: true,
200
+ name: 'Message',
201
+ type: `l.InferSubscriptionMessage<typeof ${ref.varName}>`,
202
+ });
203
+ }
204
+ async addRecord(hash, def) {
205
+ if (hash !== 'main') {
206
+ throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
207
+ }
208
+ const key = JSON.stringify(def.key ?? 'any');
209
+ const objectSchema = await this.compileObjectSchema(def.record);
210
+ const properties = await this.compilePropertiesTypes(def.record);
211
+ properties.unshift(`$type: ${JSON.stringify(lex_schema_1.l.$type(this.doc.id, hash))}`);
212
+ await this.addSchema(hash, def, {
213
+ type: `{ ${properties.join(';')} }`,
214
+ schema: (ref) => this.pure(`l.record<${key}, ${ref.typeName}>(${key}, $nsid, ${objectSchema})`),
215
+ objectUtils: true,
216
+ validationUtils: true,
217
+ });
218
+ }
219
+ async addObject(hash, def) {
220
+ const objectSchema = await this.compileObjectSchema(def);
221
+ const properties = await this.compilePropertiesTypes(def);
222
+ properties.unshift(`$type?: ${JSON.stringify(lex_schema_1.l.$type(this.doc.id, hash))}`);
223
+ await this.addSchema(hash, def, {
224
+ type: `{ ${properties.join(';')} }`,
225
+ schema: (ref) => this.pure(`l.typedObject<${ref.typeName}>($nsid, ${JSON.stringify(hash)}, ${objectSchema})`),
226
+ objectUtils: true,
227
+ validationUtils: true,
228
+ });
229
+ }
230
+ async addToken(hash, def) {
231
+ await this.addSchema(hash, def, {
232
+ schema: this.pure(`l.token($nsid, ${JSON.stringify(hash)})`),
233
+ type: JSON.stringify(lex_schema_1.l.$type(this.doc.id, hash)),
234
+ validationUtils: true,
235
+ });
236
+ }
237
+ async addArray(hash, def) {
238
+ // @TODO It could be nice to expose the array item type as a separate type.
239
+ // This was not done (yet) as there is no easy way to name it to avoid
240
+ // collisions.
241
+ const itemSchema = await this.compileContainedSchema(def.items);
242
+ const options = stringifyOptionalOptions(def, [
243
+ 'type',
244
+ 'description',
245
+ 'items',
246
+ ]);
247
+ await this.addSchema(hash, def, {
248
+ type: `(${await this.compileContainedType(def.items)})[]`,
249
+ // @NOTE Not using compileArraySchema to allow specifying the generic
250
+ // parameter to l.array<>.
251
+ schema: (ref) => this.pure(`l.array<${ref.typeName}[number]>(${itemSchema}, ${options})`),
252
+ validationUtils: true,
253
+ });
254
+ }
255
+ async addSchema(hash, def, { type, schema, objectUtils, validationUtils, }) {
256
+ const ref = await this.refResolver.resolveLocal(hash);
257
+ const pub = (0, ref_resolver_js_1.getPublicIdentifiers)(hash);
258
+ // Fool-proofing
259
+ (0, node_assert_1.default)((0, ts_lang_js_1.isSafeIdentifier)(ref.varName), 'Expected safe type identifier');
260
+ (0, node_assert_1.default)((0, ts_lang_js_1.isSafeIdentifier)(ref.typeName), 'Expected safe type identifier');
261
+ (0, node_assert_1.default)((0, ts_lang_js_1.isSafeIdentifier)(pub.typeName), 'Expected safe type identifier');
262
+ if (type) {
263
+ const typeStmt = this.file.addTypeAlias({
264
+ name: ref.typeName,
265
+ type: typeof type === 'function' ? type(ref) : type,
266
+ });
267
+ addJsDoc(typeStmt, def);
268
+ this.file.addExportDeclaration({
269
+ isTypeOnly: true,
270
+ namedExports: [
271
+ {
272
+ name: ref.typeName,
273
+ alias: ref.typeName === pub.typeName ? undefined : pub.typeName,
274
+ },
275
+ ],
276
+ });
277
+ }
278
+ if (schema) {
279
+ const constStmt = this.file.addVariableStatement({
280
+ declarationKind: ts_morph_1.VariableDeclarationKind.Const,
281
+ declarations: [
282
+ {
283
+ name: ref.varName,
284
+ initializer: typeof schema === 'function' ? schema(ref) : schema,
285
+ },
286
+ ],
287
+ });
288
+ addJsDoc(constStmt, def);
289
+ this.file.addExportDeclaration({
290
+ namedExports: [
291
+ {
292
+ name: ref.varName,
293
+ alias: ref.varName === pub.varName
294
+ ? undefined
295
+ : (0, ts_lang_js_1.isSafeIdentifier)(pub.varName)
296
+ ? pub.varName
297
+ : JSON.stringify(pub.varName),
298
+ },
299
+ ],
300
+ });
301
+ }
302
+ if (hash === 'main' && objectUtils) {
303
+ this.addUtils({
304
+ $isTypeOf: markPure(`${ref.varName}.isTypeOf.bind(${ref.varName})`),
305
+ $build: markPure(`${ref.varName}.build.bind(${ref.varName})`),
306
+ $type: markPure(`${ref.varName}.$type`),
307
+ });
308
+ }
309
+ if (hash === 'main' && validationUtils) {
310
+ this.addUtils({
311
+ $assert: markPure(`${ref.varName}.assert.bind(${ref.varName})`),
312
+ $check: markPure(`${ref.varName}.check.bind(${ref.varName})`),
313
+ $maybe: markPure(`${ref.varName}.maybe.bind(${ref.varName})`),
314
+ $parse: markPure(`${ref.varName}.parse.bind(${ref.varName})`),
315
+ $validate: markPure(`${ref.varName}.validate.bind(${ref.varName})`),
316
+ });
317
+ }
318
+ return ref;
319
+ }
320
+ async compilePayload(def) {
321
+ if (!def)
322
+ return this.pure(`l.payload()`);
323
+ const encodedEncoding = JSON.stringify(def.encoding);
324
+ if (def.schema) {
325
+ const bodySchema = await this.compileBodySchema(def.schema);
326
+ return this.pure(`l.payload(${encodedEncoding}, ${bodySchema})`);
327
+ }
328
+ else {
329
+ return this.pure(`l.payload(${encodedEncoding})`);
330
+ }
331
+ }
332
+ async compileBodySchema(def) {
333
+ if (!def)
334
+ return 'undefined';
335
+ if (def.type === 'object')
336
+ return this.compileObjectSchema(def);
337
+ return this.compileContainedSchema(def);
338
+ }
339
+ async compileParamsSchema(def) {
340
+ if (!def)
341
+ return this.pure(`l.params({})`);
342
+ const properties = await this.compilePropertiesSchemas(def);
343
+ const options = stringifyOptionalOptions(def, [
344
+ 'type',
345
+ 'description',
346
+ 'properties',
347
+ ]);
348
+ return this.pure(`l.params({${properties.join(',')}}, ${options})`);
349
+ }
350
+ async compileErrors(defs) {
351
+ if (!defs?.length)
352
+ return '';
353
+ return JSON.stringify(defs.map((d) => d.name));
354
+ }
355
+ async compileObjectSchema(def) {
356
+ const properties = await this.compilePropertiesSchemas(def);
357
+ const options = stringifyOptionalOptions(def, [
358
+ 'type',
359
+ 'description',
360
+ 'properties',
361
+ ]);
362
+ return this.pure(`l.object({${properties.join(',')}}, ${options})`);
363
+ }
364
+ async compilePropertiesSchemas(options) {
365
+ for (const prop of options.required || []) {
366
+ if (!Object.hasOwn(options.properties, prop)) {
367
+ throw new Error(`Required property "${prop}" not found in properties`);
368
+ }
369
+ }
370
+ return Promise.all(Object.entries(options.properties).map(this.compilePropertyEntrySchema, this));
371
+ }
372
+ async compilePropertiesTypes(options) {
373
+ return Promise.all(Object.entries(options.properties).map((entry) => {
374
+ return this.compilePropertyEntryType(entry, options);
375
+ }));
376
+ }
377
+ async compilePropertyEntrySchema([key, def]) {
378
+ const name = JSON.stringify(key);
379
+ const schema = await this.compileContainedSchema(def);
380
+ return `${name}:${schema}`;
381
+ }
382
+ async compilePropertyEntryType([key, def], options) {
383
+ const isNullable = options.nullable?.includes(key);
384
+ const isRequired = options.required?.includes(key);
385
+ const optional = isRequired ? '' : '?';
386
+ const append = isNullable ? ' | null' : '';
387
+ const jsDoc = compileLeadingTrivia(def.description) || '';
388
+ const name = JSON.stringify(key);
389
+ const type = await this.compileContainedType(def);
390
+ return `${jsDoc}${name}${optional}:${type}${append}`;
391
+ }
392
+ async compileContainedSchema(def) {
393
+ switch (def.type) {
394
+ case 'unknown':
395
+ return this.compileUnknownSchema(def);
396
+ case 'boolean':
397
+ return this.compileBooleanSchema(def);
398
+ case 'integer':
399
+ return this.compileIntegerSchema(def);
400
+ case 'string':
401
+ return this.compileStringSchema(def);
402
+ case 'bytes':
403
+ return this.compileBytesSchema(def);
404
+ case 'blob':
405
+ return this.compileBlobSchema(def);
406
+ case 'cid-link':
407
+ return this.compileCidLinkSchema(def);
408
+ case 'ref':
409
+ return this.compileRefSchema(def);
410
+ case 'union':
411
+ return this.compileRefUnionSchema(def);
412
+ case 'array':
413
+ return this.compileArraySchema(def);
414
+ default:
415
+ // @ts-expect-error
416
+ throw new Error(`Unsupported def type: ${def.type}`);
417
+ }
418
+ }
419
+ async compileContainedType(def) {
420
+ switch (def.type) {
421
+ case 'unknown':
422
+ return this.compileUnknownType(def);
423
+ case 'boolean':
424
+ return this.compileBooleanType(def);
425
+ case 'integer':
426
+ return this.compileIntegerType(def);
427
+ case 'string':
428
+ return this.compileStringType(def);
429
+ case 'bytes':
430
+ return this.compileBytesType(def);
431
+ case 'blob':
432
+ return this.compileBlobType(def);
433
+ case 'cid-link':
434
+ return this.compileCidLinkType(def);
435
+ case 'ref':
436
+ return this.compileRefType(def);
437
+ case 'union':
438
+ return this.compileRefUnionType(def);
439
+ case 'array':
440
+ return this.compileArrayType(def);
441
+ default:
442
+ // @ts-expect-error
443
+ throw new Error(`Unsupported def type: ${def.type}`);
444
+ }
445
+ }
446
+ async compileArraySchema(def) {
447
+ const itemSchema = await this.compileContainedSchema(def.items);
448
+ const options = stringifyOptionalOptions(def, [
449
+ 'type',
450
+ 'description',
451
+ 'items',
452
+ ]);
453
+ return this.pure(`l.array(${itemSchema}, ${options})`);
454
+ }
455
+ async compileArrayType(def) {
456
+ return `(${await this.compileContainedType(def.items)})[]`;
457
+ }
458
+ async compileUnknownSchema(_def) {
459
+ return this.pure(`l.unknownObject()`);
460
+ }
461
+ async compileUnknownType(_def) {
462
+ return `l.UnknownObject`;
463
+ }
464
+ async compileBooleanSchema(def) {
465
+ if (hasConst(def))
466
+ return this.compileConstSchema(def);
467
+ const options = stringifyOptionalOptions(def, ['type', 'description']);
468
+ return this.pure(`l.boolean(${options})`);
469
+ }
470
+ async compileBooleanType(def) {
471
+ if (hasConst(def))
472
+ return this.compileConstType(def);
473
+ return 'boolean';
474
+ }
475
+ async compileIntegerSchema(def) {
476
+ if (hasConst(def)) {
477
+ const schema = lex_schema_1.l.integer(def);
478
+ (0, node_assert_1.default)(schema.check(def.const), `Integer const ${def.const} is out of bounds`);
479
+ }
480
+ if (hasEnum(def)) {
481
+ const schema = lex_schema_1.l.integer(def);
482
+ for (const val of def.enum) {
483
+ (0, node_assert_1.default)(schema.check(val), `Integer enum value ${val} is out of bounds`);
484
+ }
485
+ }
486
+ if (hasConst(def))
487
+ return this.compileConstSchema(def);
488
+ if (hasEnum(def))
489
+ return this.compileEnumSchema(def);
490
+ const options = stringifyOptionalOptions(def, ['type', 'description']);
491
+ return this.pure(`l.integer(${options})`);
492
+ }
493
+ async compileIntegerType(def) {
494
+ if (hasConst(def))
495
+ return this.compileConstType(def);
496
+ if (hasEnum(def))
497
+ return this.compileEnumType(def);
498
+ return 'number';
499
+ }
500
+ async compileStringSchema(def) {
501
+ if (hasConst(def)) {
502
+ const schema = lex_schema_1.l.string(def);
503
+ (0, node_assert_1.default)(schema.check(def.const), `String const "${def.const}" does not match format`);
504
+ }
505
+ else if (hasEnum(def)) {
506
+ const schema = lex_schema_1.l.string(def);
507
+ for (const val of def.enum) {
508
+ (0, node_assert_1.default)(schema.check(val), `String enum value "${val}" does not match format`);
509
+ }
510
+ }
511
+ if (hasConst(def))
512
+ return this.compileConstSchema(def);
513
+ if (hasEnum(def))
514
+ return this.compileEnumSchema(def);
515
+ const options = stringifyOptionalOptions(def, ['type', 'description']);
516
+ return this.pure(`l.string(${options})`);
517
+ }
518
+ async compileStringType(def) {
519
+ if (hasConst(def))
520
+ return this.compileConstType(def);
521
+ if (hasEnum(def))
522
+ return this.compileEnumType(def);
523
+ switch (def.format) {
524
+ case 'datetime':
525
+ return 'l.Datetime';
526
+ case 'uri':
527
+ return 'l.Uri';
528
+ case 'at-uri':
529
+ return 'l.AtUri';
530
+ case 'did':
531
+ return 'l.Did';
532
+ case 'handle':
533
+ return 'l.Handle';
534
+ case 'at-identifier':
535
+ return 'l.AtIdentifier';
536
+ case 'nsid':
537
+ return 'l.Nsid';
538
+ }
539
+ if (def.knownValues?.length) {
540
+ return (def.knownValues.map((v) => JSON.stringify(v)).join(' | ') +
541
+ ' | l.UnknownString');
542
+ }
543
+ return 'string';
544
+ }
545
+ async compileBytesSchema(def) {
546
+ const options = stringifyOptionalOptions(def, ['type', 'description']);
547
+ return this.pure(`l.bytes(${options})`);
548
+ }
549
+ async compileBytesType(_def) {
550
+ return 'Uint8Array';
551
+ }
552
+ async compileBlobSchema(def) {
553
+ const opts = this.options.allowLegacyBlobs
554
+ ? { ...def, allowLegacy: true }
555
+ : def;
556
+ const options = stringifyOptionalOptions(opts, ['type', 'description']);
557
+ return this.pure(`l.blob(${options})`);
558
+ }
559
+ async compileBlobType(_def) {
560
+ return this.options.allowLegacyBlobs
561
+ ? 'l.BlobRef | l.LegacyBlobRef'
562
+ : 'l.BlobRef';
563
+ }
564
+ async compileCidLinkSchema(def) {
565
+ const options = stringifyOptionalOptions(def, ['type', 'description']);
566
+ return this.pure(`l.cidLink(${options})`);
567
+ }
568
+ async compileCidLinkType(_def) {
569
+ return 'l.CID';
570
+ }
571
+ async compileRefSchema(def) {
572
+ const { varName, typeName } = await this.refResolver.resolve(def.ref);
573
+ // @NOTE "as any" is needed in schemas with circular refs as TypeScript
574
+ // cannot infer the type of a value that depends on its initializer type
575
+ return this.pure(`l.ref<${typeName}>((() => ${varName}) as any)`);
576
+ }
577
+ async compileRefType(def) {
578
+ const ref = await this.refResolver.resolve(def.ref);
579
+ return ref.typeName;
580
+ }
581
+ async compileRefUnionSchema(def) {
582
+ if (def.refs.length === 0 && def.closed) {
583
+ return this.pure(`l.never()`);
584
+ }
585
+ const refs = await Promise.all(def.refs.map(async (ref) => {
586
+ const { varName, typeName } = await this.refResolver.resolve(ref);
587
+ // @NOTE "as any" is needed in schemas with circular refs as TypeScript
588
+ // cannot infer the type of a value that depends on its initializer type
589
+ return this.pure(`l.typedRef<${typeName}>((() => ${varName}) as any)`);
590
+ }));
591
+ return this.pure(`l.typedUnion([${refs.join(',')}], ${def.closed ?? false})`);
592
+ }
593
+ async compileRefUnionType(def) {
594
+ const types = await Promise.all(def.refs.map(async (ref) => {
595
+ const { typeName } = await this.refResolver.resolve(ref);
596
+ return `l.TypedRef<${typeName}>`;
597
+ }));
598
+ if (!def.closed)
599
+ types.push('l.TypedObject');
600
+ return types.join(' | ') || 'never';
601
+ }
602
+ async compileConstSchema(def) {
603
+ if (hasEnum(def) && !def.enum.includes(def.const)) {
604
+ return this.pure(`l.never()`);
605
+ }
606
+ return this.pure(`l.literal(${JSON.stringify(def.const)})`);
607
+ }
608
+ async compileConstType(def) {
609
+ if (hasEnum(def) && !def.enum.includes(def.const)) {
610
+ return 'never';
611
+ }
612
+ return JSON.stringify(def.const);
613
+ }
614
+ async compileEnumSchema(def) {
615
+ if (def.enum.length === 0) {
616
+ return this.pure(`l.never()`);
617
+ }
618
+ if (def.enum.length === 1) {
619
+ return this.pure(`l.literal(${JSON.stringify(def.enum[0])})`);
620
+ }
621
+ return this.pure(`l.enum(${JSON.stringify(def.enum)})`);
622
+ }
623
+ async compileEnumType(def) {
624
+ return def.enum.map((v) => JSON.stringify(v)).join(' | ') || 'never';
625
+ }
626
+ }
627
+ exports.LexDefBuilder = LexDefBuilder;
628
+ function parseDescription(description) {
629
+ if (/deprecated/i.test(description)) {
630
+ const deprecationMatch = description.match(/(\s*deprecated\s*(?:--?|:)?\s*([^-]*)(?:-+)?)/i);
631
+ if (deprecationMatch) {
632
+ const [, match, deprecationNotice] = deprecationMatch;
633
+ return {
634
+ description: description.replace(match, '').trim(),
635
+ deprecated: deprecationNotice?.trim() || true,
636
+ };
637
+ }
638
+ else {
639
+ return {
640
+ description: description.trim(),
641
+ deprecated: true,
642
+ };
643
+ }
644
+ }
645
+ return {
646
+ description: description.trim(),
647
+ deprecated: false,
648
+ };
649
+ }
650
+ function compileLeadingTrivia(description) {
651
+ if (!description)
652
+ return undefined;
653
+ return `\n\n/**${compileJsDoc(description).replaceAll('\n', '\n * ')}\n */\n`;
654
+ }
655
+ function addJsDoc(declaration, def) {
656
+ if (def?.description) {
657
+ declaration.addJsDoc(compileJsDoc(def.description));
658
+ }
659
+ }
660
+ function compileJsDoc(description) {
661
+ const parsed = parseDescription(description);
662
+ return `\n${parsed.description}${!parsed.deprecated
663
+ ? ''
664
+ : (parsed.description ? '\n\n' : '') +
665
+ (parsed.deprecated === true
666
+ ? '@deprecated'
667
+ : `@deprecated ${parsed.deprecated}`)}`;
668
+ }
669
+ function stringifyOptionalOptions(obj, omit) {
670
+ const filtered = Object.entries(obj).filter(([k]) => !omit?.includes(k));
671
+ return filtered.length ? JSON.stringify(Object.fromEntries(filtered)) : '';
672
+ }
673
+ function hasConst(def) {
674
+ return def.const != null;
675
+ }
676
+ function hasEnum(def) {
677
+ return def.enum != null;
678
+ }
679
+ function markPure(v) {
680
+ return `/*#__PURE__*/ ${v}`;
681
+ }
682
+ //# sourceMappingURL=lex-def-builder.js.map