@gaearon/lex-builder 0.0.13

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