@atproto/lex-builder 0.1.0 → 0.1.2

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.
@@ -22,9 +22,6 @@ export class LexDefBuilder {
22
22
  this.doc = doc;
23
23
  this.refResolver = new RefResolver(doc, file, indexer, options);
24
24
  }
25
- pure(code) {
26
- return this.options.pureAnnotations ? markPure(code) : code;
27
- }
28
25
  async build() {
29
26
  this.file.addVariableStatement({
30
27
  declarationKind: VariableDeclarationKind.Const,
@@ -47,15 +44,13 @@ export class LexDefBuilder {
47
44
  }
48
45
  }
49
46
  addUtils(definitions) {
50
- const entries = Object.entries(definitions).filter((e) => e[1] != null);
51
- if (entries.length) {
47
+ for (const [name, initializer] of Object.entries(definitions)) {
48
+ if (initializer == null)
49
+ continue;
52
50
  this.file.addVariableStatement({
53
51
  isExported: true,
54
52
  declarationKind: VariableDeclarationKind.Const,
55
- declarations: entries.map(([name, initializer]) => ({
56
- name,
57
- initializer,
58
- })),
53
+ declarations: [{ name, initializer }],
59
54
  });
60
55
  }
61
56
  }
@@ -91,7 +86,7 @@ export class LexDefBuilder {
91
86
  async addPermissionSet(hash, def) {
92
87
  const permission = def.permissions.map((def) => {
93
88
  const options = stringifyOptions(def, undefined, ['resource', 'type']);
94
- return this.pure(`l.permission(${JSON.stringify(def.resource)}, ${options})`);
89
+ return markPure(`l.permission(${JSON.stringify(def.resource)}, ${options})`);
95
90
  });
96
91
  const options = stringifyOptions(def, [
97
92
  'title',
@@ -100,121 +95,135 @@ export class LexDefBuilder {
100
95
  'detail:lang',
101
96
  ]);
102
97
  await this.addSchema(hash, def, {
103
- schema: this.pure(`l.permissionSet($nsid, [${permission.join(',')}], ${options})`),
98
+ schema: markPure(`l.permissionSet($nsid, [${permission.join(',')}], ${options})`),
99
+ });
100
+ }
101
+ async addParameters(parameters) {
102
+ const varName = '$params';
103
+ this.addUtils({
104
+ [varName]: await this.compileParamsSchema(parameters),
105
+ });
106
+ // @TODO Build the types instead of using an inferred type.
107
+ this.file.addTypeAlias({
108
+ isExported: true,
109
+ name: '$Params',
110
+ type: `l.InferOutput<typeof ${varName}>`,
111
+ docs: compileDocs(parameters?.description),
112
+ });
113
+ return varName;
114
+ }
115
+ async addInput(input) {
116
+ const varName = '$input';
117
+ this.addUtils({
118
+ [varName]: await this.compilePayload(input),
119
+ });
120
+ // @TODO Build the types instead of using an inferred type.
121
+ this.file.addTypeAlias({
122
+ isExported: true,
123
+ name: '$Input<B = l.BinaryData>',
124
+ type: `l.InferPayload<typeof ${varName}, B>`,
125
+ docs: compileDocs(input?.description),
126
+ });
127
+ this.file.addTypeAlias({
128
+ isExported: true,
129
+ name: '$InputBody<B = l.BinaryData>',
130
+ type: `l.InferPayloadBody<typeof ${varName}, B>`,
131
+ docs: compileDocs(input?.description),
132
+ });
133
+ return varName;
134
+ }
135
+ async addOutput(output) {
136
+ const varName = '$output';
137
+ this.addUtils({
138
+ [varName]: await this.compilePayload(output),
139
+ });
140
+ // @TODO Build the types instead of using an inferred type.
141
+ this.file.addTypeAlias({
142
+ isExported: true,
143
+ name: '$Output<B = l.BinaryData>',
144
+ type: `l.InferPayload<typeof ${varName}, B>`,
145
+ docs: compileDocs(output?.description),
146
+ });
147
+ this.file.addTypeAlias({
148
+ isExported: true,
149
+ name: '$OutputBody<B = l.BinaryData>',
150
+ type: `l.InferPayloadBody<typeof ${varName}, B>`,
151
+ docs: compileDocs(output?.description),
152
+ });
153
+ return varName;
154
+ }
155
+ async addMessage(message) {
156
+ const varName = '$message';
157
+ this.addUtils({
158
+ [varName]: await this.compileBodySchema(message?.schema),
159
+ });
160
+ // @TODO Build the types instead of using an inferred type.
161
+ this.file.addTypeAlias({
162
+ isExported: true,
163
+ name: '$Message',
164
+ type: `l.InferOutput<typeof ${varName}>`,
165
+ docs: compileDocs(message?.description),
104
166
  });
167
+ return varName;
105
168
  }
106
169
  async addProcedure(hash, def) {
107
170
  if (hash !== 'main') {
108
171
  throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
109
172
  }
110
- // @TODO Build the types instead of using an inferred type.
111
- const ref = await this.addSchema(hash, def, {
112
- schema: this.pure(`
113
- l.procedure(
114
- $nsid,
115
- ${await this.compileParamsSchema(def.parameters)},
116
- ${await this.compilePayload(def.input)},
117
- ${await this.compilePayload(def.output)},
118
- ${await this.compileErrors(def.errors)}
119
- )
120
- `),
173
+ // Declare each piece of the method as its own top-level exported const
174
+ // *before* `main`. This allows to export those pieces individually instead
175
+ // of "extracting" them from the "main" definition as below, which is bad
176
+ // for tree-shaking.
177
+ //
178
+ // export const $params = main.params`
179
+ const paramsVar = await this.addParameters(def.parameters);
180
+ const inputVar = await this.addInput(def.input);
181
+ const outputVar = await this.addOutput(def.output);
182
+ await this.addSchema(hash, def, {
183
+ schema: markPure(`l.procedure($nsid, ${paramsVar}, ${inputVar}, ${outputVar}${formatErrorsArg(await this.compileErrors(def.errors))})`),
121
184
  });
122
- this.addMethodTypeUtils(ref, def);
123
185
  this.addUtils({
124
- $lxm: this.pure(`${ref.varName}.nsid`),
125
- $params: this.pure(`${ref.varName}.parameters`),
126
- $input: this.pure(`${ref.varName}.input`),
127
- $output: this.pure(`${ref.varName}.output`),
186
+ $lxm: '$nsid',
128
187
  });
129
188
  }
130
189
  async addQuery(hash, def) {
131
190
  if (hash !== 'main') {
132
191
  throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
133
192
  }
134
- // @TODO Build the types instead of using an inferred type.
135
- const ref = await this.addSchema(hash, def, {
136
- schema: this.pure(`
137
- l.query(
138
- $nsid,
139
- ${await this.compileParamsSchema(def.parameters)},
140
- ${await this.compilePayload(def.output)},
141
- ${await this.compileErrors(def.errors)}
142
- )
143
- `),
193
+ // Declare each piece of the method as its own top-level exported const
194
+ // *before* `main`. This allows to export those pieces individually instead
195
+ // of "extracting" them from the "main" definition as below, which is bad
196
+ // for tree-shaking:
197
+ //
198
+ // export const $params = main.params
199
+ const paramsVar = await this.addParameters(def.parameters);
200
+ const outputVar = await this.addOutput(def.output);
201
+ await this.addSchema(hash, def, {
202
+ schema: markPure(`l.query($nsid, ${paramsVar}, ${outputVar}${formatErrorsArg(await this.compileErrors(def.errors))})`),
144
203
  });
145
- this.addMethodTypeUtils(ref, def);
146
204
  this.addUtils({
147
- $lxm: this.pure(`${ref.varName}.nsid`),
148
- $params: `${ref.varName}.parameters`,
149
- $output: `${ref.varName}.output`,
205
+ $lxm: '$nsid',
150
206
  });
151
207
  }
152
208
  async addSubscription(hash, def) {
153
209
  if (hash !== 'main') {
154
210
  throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
155
211
  }
156
- // @TODO Build the types instead of using an inferred type.
157
- const ref = await this.addSchema(hash, def, {
158
- schema: this.pure(`
159
- l.subscription(
160
- $nsid,
161
- ${await this.compileParamsSchema(def.parameters)},
162
- ${await this.compileBodySchema(def.message?.schema)},
163
- ${await this.compileErrors(def.errors)}
164
- )
165
- `),
212
+ // Declare each piece of the method as its own top-level exported const
213
+ // *before* `main`. This allows to export those pieces individually instead
214
+ // of "extracting" them from the "main" definition as below, which is bad
215
+ // for tree-shaking.
216
+ //
217
+ // export const $params = main.params`
218
+ const paramsVar = await this.addParameters(def.parameters);
219
+ const messageVar = await this.addMessage(def.message);
220
+ await this.addSchema(hash, def, {
221
+ schema: markPure(`l.subscription($nsid, ${paramsVar}, ${messageVar}${formatErrorsArg(await this.compileErrors(def.errors))})`),
166
222
  });
167
- this.addMethodTypeUtils(ref, def);
168
223
  this.addUtils({
169
- $lxm: this.pure(`${ref.varName}.nsid`),
170
- $params: `${ref.varName}.parameters`,
171
- $message: `${ref.varName}.message`,
224
+ $lxm: '$nsid',
172
225
  });
173
226
  }
174
- addMethodTypeUtils(ref, def) {
175
- this.file.addTypeAlias({
176
- isExported: true,
177
- name: '$Params',
178
- type: `l.InferMethodParams<typeof ${ref.varName}>`,
179
- docs: compileDocs(def.parameters?.description),
180
- });
181
- if (def.type === 'procedure') {
182
- this.file.addTypeAlias({
183
- isExported: true,
184
- name: '$Input<B = l.BinaryData>',
185
- type: `l.InferMethodInput<typeof ${ref.varName}, B>`,
186
- docs: compileDocs(def.input?.description),
187
- });
188
- this.file.addTypeAlias({
189
- isExported: true,
190
- name: '$InputBody<B = l.BinaryData>',
191
- type: `l.InferMethodInputBody<typeof ${ref.varName}, B>`,
192
- docs: compileDocs(def.input?.description),
193
- });
194
- }
195
- if (def.type === 'procedure' || def.type === 'query') {
196
- this.file.addTypeAlias({
197
- isExported: true,
198
- name: '$Output<B = l.BinaryData>',
199
- type: `l.InferMethodOutput<typeof ${ref.varName}, B>`,
200
- docs: compileDocs(def.output?.description),
201
- });
202
- this.file.addTypeAlias({
203
- isExported: true,
204
- name: '$OutputBody<B = l.BinaryData>',
205
- type: `l.InferMethodOutputBody<typeof ${ref.varName}, B>`,
206
- docs: compileDocs(def.output?.description),
207
- });
208
- }
209
- if (def.type === 'subscription') {
210
- this.file.addTypeAlias({
211
- isExported: true,
212
- name: '$Message',
213
- type: `l.InferSubscriptionMessage<typeof ${ref.varName}>`,
214
- docs: compileDocs(def.message?.description),
215
- });
216
- }
217
- }
218
227
  async addRecord(hash, def) {
219
228
  if (hash !== 'main') {
220
229
  throw new Error(`Definition ${hash} cannot be of type ${def.type}`);
@@ -225,7 +234,7 @@ export class LexDefBuilder {
225
234
  properties.unshift(`$type: ${JSON.stringify(l.$type(this.doc.id, hash))}`);
226
235
  await this.addSchema(hash, def, {
227
236
  type: `{ ${properties.join(';')} }`,
228
- schema: (ref) => this.pure(`l.record<${key}, ${ref.typeName}>(${key}, $nsid, ${objectSchema})`),
237
+ schema: (ref) => markPure(`l.record<${key}, ${ref.typeName}>(${key}, $nsid, ${objectSchema})`),
229
238
  objectUtils: true,
230
239
  validationUtils: true,
231
240
  });
@@ -236,14 +245,14 @@ export class LexDefBuilder {
236
245
  properties.unshift(`$type?: ${JSON.stringify(l.$type(this.doc.id, hash))}`);
237
246
  await this.addSchema(hash, def, {
238
247
  type: `{ ${properties.join(';')} }`,
239
- schema: (ref) => this.pure(`l.typedObject<${ref.typeName}>($nsid, ${JSON.stringify(hash)}, ${objectSchema})`),
248
+ schema: (ref) => markPure(`l.typedObject<${ref.typeName}>($nsid, ${JSON.stringify(hash)}, ${objectSchema})`),
240
249
  objectUtils: true,
241
250
  validationUtils: true,
242
251
  });
243
252
  }
244
253
  async addToken(hash, def) {
245
254
  await this.addSchema(hash, def, {
246
- schema: this.pure(`l.token($nsid, ${JSON.stringify(hash)})`),
255
+ schema: markPure(`l.token($nsid, ${JSON.stringify(hash)})`),
247
256
  type: JSON.stringify(l.$type(this.doc.id, hash)),
248
257
  validationUtils: true,
249
258
  });
@@ -261,7 +270,7 @@ export class LexDefBuilder {
261
270
  type: `(${await this.compileContainedType(def.items)})[]`,
262
271
  // @NOTE Not using compileArraySchema to allow specifying the generic
263
272
  // parameter to l.array<>.
264
- schema: (ref) => this.pure(`l.array<${ref.typeName}[number]>(${itemSchema}, ${options})`),
273
+ schema: (ref) => markPure(`l.array<${ref.typeName}[number]>(${itemSchema}, ${options})`),
265
274
  validationUtils: true,
266
275
  });
267
276
  }
@@ -310,9 +319,9 @@ export class LexDefBuilder {
310
319
  }
311
320
  if (hash === 'main' && objectUtils) {
312
321
  this.addUtils({
322
+ $type: `$nsid`,
313
323
  $isTypeOf: markPure(`${ref.varName}.isTypeOf.bind(${ref.varName})`),
314
324
  $build: markPure(`${ref.varName}.build.bind(${ref.varName})`),
315
- $type: markPure(`${ref.varName}.$type`),
316
325
  });
317
326
  }
318
327
  if (hash === 'main' && validationUtils) {
@@ -332,19 +341,19 @@ export class LexDefBuilder {
332
341
  }
333
342
  async compilePayload(def) {
334
343
  if (!def)
335
- return this.pure(`l.payload()`);
344
+ return markPure(`l.payload()`);
336
345
  // Special case for JSON object payloads
337
346
  if (def.encoding === 'application/json' && def.schema?.type === 'object') {
338
347
  const properties = await this.compilePropertiesSchemas(def.schema);
339
- return this.pure(`l.jsonPayload({${properties.join(',')}})`);
348
+ return markPure(`l.jsonPayload({${properties.join(',')}})`);
340
349
  }
341
350
  const encodedEncoding = JSON.stringify(def.encoding);
342
351
  if (def.schema) {
343
352
  const bodySchema = await this.compileBodySchema(def.schema);
344
- return this.pure(`l.payload(${encodedEncoding}, ${bodySchema})`);
353
+ return markPure(`l.payload(${encodedEncoding}, ${bodySchema})`);
345
354
  }
346
355
  else {
347
- return this.pure(`l.payload(${encodedEncoding})`);
356
+ return markPure(`l.payload(${encodedEncoding})`);
348
357
  }
349
358
  }
350
359
  async compileBodySchema(def) {
@@ -356,9 +365,9 @@ export class LexDefBuilder {
356
365
  }
357
366
  async compileParamsSchema(def) {
358
367
  if (!def)
359
- return this.pure(`l.params()`);
368
+ return markPure(`l.params()`);
360
369
  const properties = await this.compilePropertiesSchemas(def);
361
- return this.pure(properties.length === 0
370
+ return markPure(properties.length === 0
362
371
  ? `l.params()`
363
372
  : `l.params({${properties.join(',')}})`);
364
373
  }
@@ -369,7 +378,7 @@ export class LexDefBuilder {
369
378
  }
370
379
  async compileObjectSchema(def) {
371
380
  const properties = await this.compilePropertiesSchemas(def);
372
- return this.pure(`l.object({${properties.join(',')}})`);
381
+ return markPure(`l.object({${properties.join(',')}})`);
373
382
  }
374
383
  async compilePropertiesSchemas(options) {
375
384
  for (const opt of ['required', 'nullable']) {
@@ -395,10 +404,10 @@ export class LexDefBuilder {
395
404
  const isRequired = options.required?.includes(key);
396
405
  let schema = await this.compileContainedSchema(def);
397
406
  if (isNullable) {
398
- schema = this.pure(`l.nullable(${schema})`);
407
+ schema = markPure(`l.nullable(${schema})`);
399
408
  }
400
409
  if (!isRequired) {
401
- schema = this.pure(`l.optional(${schema})`);
410
+ schema = markPure(`l.optional(${schema})`);
402
411
  }
403
412
  return `${JSON.stringify(key)}:${schema}`;
404
413
  }
@@ -472,13 +481,13 @@ export class LexDefBuilder {
472
481
  'minLength',
473
482
  'maxLength',
474
483
  ]);
475
- return this.pure(`l.array(${itemSchema}, ${options})`);
484
+ return markPure(`l.array(${itemSchema}, ${options})`);
476
485
  }
477
486
  async compileArrayType(def) {
478
487
  return `(${await this.compileContainedType(def.items)})[]`;
479
488
  }
480
489
  async compileUnknownSchema(_def) {
481
- return this.pure(`l.lexMap()`);
490
+ return markPure(`l.lexMap()`);
482
491
  }
483
492
  async compileUnknownType(_def) {
484
493
  return `l.LexMap`;
@@ -486,7 +495,7 @@ export class LexDefBuilder {
486
495
  withDefault(schema, defaultValue) {
487
496
  if (defaultValue === undefined)
488
497
  return schema;
489
- return this.pure(`l.withDefault(${schema}, ${JSON.stringify(defaultValue)})`);
498
+ return markPure(`l.withDefault(${schema}, ${JSON.stringify(defaultValue)})`);
490
499
  }
491
500
  async compileBooleanSchema(def) {
492
501
  const schema = l.boolean();
@@ -495,7 +504,7 @@ export class LexDefBuilder {
495
504
  }
496
505
  if (hasConst(def))
497
506
  return this.compileConstSchema(def);
498
- return this.withDefault(this.pure(`l.boolean()`), def.default);
507
+ return this.withDefault(markPure(`l.boolean()`), def.default);
499
508
  }
500
509
  async compileBooleanType(def) {
501
510
  if (hasConst(def))
@@ -522,7 +531,7 @@ export class LexDefBuilder {
522
531
  'maximum',
523
532
  'minimum',
524
533
  ]);
525
- return this.withDefault(this.pure(`l.integer(${options})`), def.default);
534
+ return this.withDefault(markPure(`l.integer(${options})`), def.default);
526
535
  }
527
536
  async compileIntegerType(def) {
528
537
  if (hasConst(def))
@@ -567,7 +576,7 @@ export class LexDefBuilder {
567
576
  'knownValues',
568
577
  ])
569
578
  : undefined;
570
- return this.withDefault(this.pure(`l.string${generic ? `<${generic}>` : ''}(${options})`), def.default);
579
+ return this.withDefault(markPure(`l.string${generic ? `<${generic}>` : ''}(${options})`), def.default);
571
580
  }
572
581
  async compileStringType(def) {
573
582
  if (hasConst(def))
@@ -613,7 +622,7 @@ export class LexDefBuilder {
613
622
  'minLength',
614
623
  'maxLength',
615
624
  ]);
616
- return this.pure(`l.bytes(${options})`);
625
+ return markPure(`l.bytes(${options})`);
617
626
  }
618
627
  async compileBytesType(_def) {
619
628
  return 'Uint8Array';
@@ -623,13 +632,13 @@ export class LexDefBuilder {
623
632
  'maxSize',
624
633
  'accept',
625
634
  ]);
626
- return this.pure(`l.blob(${options})`);
635
+ return markPure(`l.blob(${options})`);
627
636
  }
628
637
  async compileBlobType(_def) {
629
638
  return 'l.BlobRef';
630
639
  }
631
640
  async compileCidLinkSchema(_def) {
632
- return this.pure(`l.cid()`);
641
+ return markPure(`l.cid()`);
633
642
  }
634
643
  async compileCidLinkType(_def) {
635
644
  return 'l.Cid';
@@ -638,7 +647,7 @@ export class LexDefBuilder {
638
647
  const { varName, typeName } = await this.refResolver.resolve(def.ref);
639
648
  // @NOTE "as any" is needed in schemas with circular refs as TypeScript
640
649
  // cannot infer the type of a value that depends on its initializer type
641
- return this.pure(`l.ref<${typeName}>((() => ${varName}) as any)`);
650
+ return markPure(`l.ref<${typeName}>((() => ${varName}) as any)`);
642
651
  }
643
652
  async compileRefType(def) {
644
653
  const ref = await this.refResolver.resolve(def.ref);
@@ -646,15 +655,15 @@ export class LexDefBuilder {
646
655
  }
647
656
  async compileRefUnionSchema(def) {
648
657
  if (def.refs.length === 0 && def.closed) {
649
- return this.pure(`l.never()`);
658
+ return markPure(`l.never()`);
650
659
  }
651
660
  const refs = await Promise.all(def.refs.map(async (ref) => {
652
661
  const { varName, typeName } = await this.refResolver.resolve(ref);
653
662
  // @NOTE "as any" is needed in schemas with circular refs as TypeScript
654
663
  // cannot infer the type of a value that depends on its initializer type
655
- return this.pure(`l.typedRef<${typeName}>((() => ${varName}) as any)`);
664
+ return markPure(`l.typedRef<${typeName}>((() => ${varName}) as any)`);
656
665
  }));
657
- return this.pure(`l.typedUnion([${refs.join(',')}], ${def.closed ?? false})`);
666
+ return markPure(`l.typedUnion([${refs.join(',')}], ${def.closed ?? false})`);
658
667
  }
659
668
  async compileRefUnionType(def) {
660
669
  const types = await Promise.all(def.refs.map(async (ref) => {
@@ -667,9 +676,9 @@ export class LexDefBuilder {
667
676
  }
668
677
  async compileConstSchema(def) {
669
678
  if (hasEnum(def) && !def.enum.includes(def.const)) {
670
- return this.pure(`l.never()`);
679
+ return markPure(`l.never()`);
671
680
  }
672
- const result = this.pure(`l.literal(${JSON.stringify(def.const)})`);
681
+ const result = markPure(`l.literal(${JSON.stringify(def.const)})`);
673
682
  return this.withDefault(result, def.default);
674
683
  }
675
684
  async compileConstType(def) {
@@ -680,11 +689,11 @@ export class LexDefBuilder {
680
689
  }
681
690
  async compileEnumSchema(def) {
682
691
  if (def.enum.length === 0) {
683
- return this.pure(`l.never()`);
692
+ return markPure(`l.never()`);
684
693
  }
685
694
  const result = def.enum.length === 1
686
- ? this.pure(`l.literal(${JSON.stringify(def.enum[0])})`)
687
- : this.pure(`l.enum(${JSON.stringify(def.enum)})`);
695
+ ? markPure(`l.literal(${JSON.stringify(def.enum[0])})`)
696
+ : markPure(`l.enum(${JSON.stringify(def.enum)})`);
688
697
  return this.withDefault(result, def.default);
689
698
  }
690
699
  async compileEnumType(def) {
@@ -742,4 +751,7 @@ function hasEnum(def) {
742
751
  function markPure(v) {
743
752
  return `/*#__PURE__*/ ${v}`;
744
753
  }
754
+ function formatErrorsArg(errors) {
755
+ return errors ? `, ${errors}` : '';
756
+ }
745
757
  //# sourceMappingURL=lex-def-builder.js.map