@atproto/lex-builder 0.0.4 → 0.0.6
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.
- package/CHANGELOG.md +72 -0
- package/dist/filtered-indexer.d.ts +1735 -1750
- package/dist/filtered-indexer.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lex-def-builder.d.ts.map +1 -1
- package/dist/lex-def-builder.js +103 -68
- package/dist/lex-def-builder.js.map +1 -1
- package/dist/lexicon-directory-indexer.d.ts +1 -1
- package/dist/lexicon-directory-indexer.d.ts.map +1 -1
- package/dist/lexicon-directory-indexer.js +1 -1
- package/dist/lexicon-directory-indexer.js.map +1 -1
- package/dist/polyfill.d.ts +1 -0
- package/dist/polyfill.d.ts.map +1 -0
- package/dist/polyfill.js +7 -0
- package/dist/polyfill.js.map +1 -0
- package/package.json +8 -4
- package/src/index.ts +3 -0
- package/src/lex-def-builder.ts +126 -93
- package/src/lexicon-directory-indexer.ts +2 -2
- package/src/polyfill.ts +7 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +7 -0
- package/tsconfig.tests.json +9 -0
package/src/lex-def-builder.ts
CHANGED
|
@@ -132,17 +132,18 @@ export class LexDefBuilder {
|
|
|
132
132
|
|
|
133
133
|
private async addPermissionSet(hash: string, def: LexiconPermissionSet) {
|
|
134
134
|
const permission = def.permissions.map((def) => {
|
|
135
|
-
const options =
|
|
135
|
+
const options = stringifyOptions(def, undefined, ['resource', 'type'])
|
|
136
136
|
return this.pure(
|
|
137
137
|
`l.permission(${JSON.stringify(def.resource)}, ${options})`,
|
|
138
138
|
)
|
|
139
139
|
})
|
|
140
140
|
|
|
141
|
-
const options =
|
|
142
|
-
'
|
|
143
|
-
'
|
|
144
|
-
'
|
|
145
|
-
|
|
141
|
+
const options = stringifyOptions(def, [
|
|
142
|
+
'title',
|
|
143
|
+
'title:lang',
|
|
144
|
+
'detail',
|
|
145
|
+
'detail:lang',
|
|
146
|
+
] satisfies (keyof l.PermissionSetOptions)[])
|
|
146
147
|
|
|
147
148
|
await this.addSchema(hash, def, {
|
|
148
149
|
schema: this.pure(
|
|
@@ -326,11 +327,10 @@ export class LexDefBuilder {
|
|
|
326
327
|
// collisions.
|
|
327
328
|
|
|
328
329
|
const itemSchema = await this.compileContainedSchema(def.items)
|
|
329
|
-
const options =
|
|
330
|
-
'
|
|
331
|
-
'
|
|
332
|
-
|
|
333
|
-
])
|
|
330
|
+
const options = stringifyOptions(def, [
|
|
331
|
+
'minLength',
|
|
332
|
+
'maxLength',
|
|
333
|
+
] satisfies (keyof l.ArraySchemaOptions)[])
|
|
334
334
|
|
|
335
335
|
await this.addSchema(hash, def, {
|
|
336
336
|
type: `(${await this.compileContainedType(def.items)})[]`,
|
|
@@ -425,10 +425,10 @@ export class LexDefBuilder {
|
|
|
425
425
|
if (hash === 'main' && validationUtils) {
|
|
426
426
|
this.addUtils({
|
|
427
427
|
$assert: markPure(`${ref.varName}.assert.bind(${ref.varName})`),
|
|
428
|
-
$
|
|
429
|
-
$
|
|
428
|
+
$ifMatches: markPure(`${ref.varName}.ifMatches.bind(${ref.varName})`),
|
|
429
|
+
$matches: markPure(`${ref.varName}.matches.bind(${ref.varName})`),
|
|
430
430
|
$parse: markPure(`${ref.varName}.parse.bind(${ref.varName})`),
|
|
431
|
-
$
|
|
431
|
+
$safeParse: markPure(`${ref.varName}.safeParse.bind(${ref.varName})`),
|
|
432
432
|
})
|
|
433
433
|
}
|
|
434
434
|
|
|
@@ -459,12 +459,7 @@ export class LexDefBuilder {
|
|
|
459
459
|
if (!def) return this.pure(`l.params({})`)
|
|
460
460
|
|
|
461
461
|
const properties = await this.compilePropertiesSchemas(def)
|
|
462
|
-
|
|
463
|
-
'type',
|
|
464
|
-
'description',
|
|
465
|
-
'properties',
|
|
466
|
-
])
|
|
467
|
-
return this.pure(`l.params({${properties.join(',')}}, ${options})`)
|
|
462
|
+
return this.pure(`l.params({${properties.join(',')}})`)
|
|
468
463
|
}
|
|
469
464
|
|
|
470
465
|
private async compileErrors(defs?: readonly LexiconError[]) {
|
|
@@ -474,29 +469,28 @@ export class LexDefBuilder {
|
|
|
474
469
|
|
|
475
470
|
private async compileObjectSchema(def: LexiconObject): Promise<string> {
|
|
476
471
|
const properties = await this.compilePropertiesSchemas(def)
|
|
477
|
-
|
|
478
|
-
'type',
|
|
479
|
-
'description',
|
|
480
|
-
'properties',
|
|
481
|
-
])
|
|
482
|
-
return this.pure(`l.object({${properties.join(',')}}, ${options})`)
|
|
472
|
+
return this.pure(`l.object({${properties.join(',')}})`)
|
|
483
473
|
}
|
|
484
474
|
|
|
485
475
|
private async compilePropertiesSchemas(options: {
|
|
486
476
|
properties: Record<string, LexiconArray | LexiconArrayItems>
|
|
487
477
|
required?: readonly string[]
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
478
|
+
nullable?: readonly string[]
|
|
479
|
+
}): Promise<string[]> {
|
|
480
|
+
for (const opt of ['required', 'nullable'] as const) {
|
|
481
|
+
if (options[opt]) {
|
|
482
|
+
for (const prop of options[opt]) {
|
|
483
|
+
if (!Object.hasOwn(options.properties, prop)) {
|
|
484
|
+
throw new Error(`No schema found for ${opt} property "${prop}"`)
|
|
485
|
+
}
|
|
486
|
+
}
|
|
492
487
|
}
|
|
493
488
|
}
|
|
494
489
|
|
|
495
490
|
return Promise.all(
|
|
496
|
-
Object.entries(options.properties).map(
|
|
497
|
-
this.compilePropertyEntrySchema,
|
|
498
|
-
|
|
499
|
-
),
|
|
491
|
+
Object.entries(options.properties).map((entry) => {
|
|
492
|
+
return this.compilePropertyEntrySchema(entry, options)
|
|
493
|
+
}),
|
|
500
494
|
)
|
|
501
495
|
}
|
|
502
496
|
|
|
@@ -512,13 +506,27 @@ export class LexDefBuilder {
|
|
|
512
506
|
)
|
|
513
507
|
}
|
|
514
508
|
|
|
515
|
-
private async compilePropertyEntrySchema(
|
|
516
|
-
string,
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
509
|
+
private async compilePropertyEntrySchema(
|
|
510
|
+
[key, def]: [string, LexiconArray | LexiconArrayItems],
|
|
511
|
+
options: {
|
|
512
|
+
required?: readonly string[]
|
|
513
|
+
nullable?: readonly string[]
|
|
514
|
+
},
|
|
515
|
+
) {
|
|
516
|
+
const isNullable = options.nullable?.includes(key)
|
|
517
|
+
const isRequired = options.required?.includes(key)
|
|
518
|
+
|
|
519
|
+
let schema = await this.compileContainedSchema(def)
|
|
520
|
+
|
|
521
|
+
if (isNullable) {
|
|
522
|
+
schema = this.pure(`l.nullable(${schema})`)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (!isRequired) {
|
|
526
|
+
schema = this.pure(`l.optional(${schema})`)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return `${JSON.stringify(key)}:${schema}`
|
|
522
530
|
}
|
|
523
531
|
|
|
524
532
|
private async compilePropertyEntryType(
|
|
@@ -603,11 +611,10 @@ export class LexDefBuilder {
|
|
|
603
611
|
|
|
604
612
|
private async compileArraySchema(def: LexiconArray): Promise<string> {
|
|
605
613
|
const itemSchema = await this.compileContainedSchema(def.items)
|
|
606
|
-
const options =
|
|
607
|
-
'
|
|
608
|
-
'
|
|
609
|
-
|
|
610
|
-
])
|
|
614
|
+
const options = stringifyOptions(def, [
|
|
615
|
+
'minLength',
|
|
616
|
+
'maxLength',
|
|
617
|
+
] satisfies (keyof l.ArraySchemaOptions)[])
|
|
611
618
|
return this.pure(`l.array(${itemSchema}, ${options})`)
|
|
612
619
|
}
|
|
613
620
|
|
|
@@ -626,7 +633,9 @@ export class LexDefBuilder {
|
|
|
626
633
|
private async compileBooleanSchema(def: LexiconBoolean): Promise<string> {
|
|
627
634
|
if (hasConst(def)) return this.compileConstSchema(def)
|
|
628
635
|
|
|
629
|
-
const options =
|
|
636
|
+
const options = stringifyOptions(def, [
|
|
637
|
+
'default',
|
|
638
|
+
] satisfies (keyof l.BooleanSchemaOptions)[])
|
|
630
639
|
return this.pure(`l.boolean(${options})`)
|
|
631
640
|
}
|
|
632
641
|
|
|
@@ -637,24 +646,23 @@ export class LexDefBuilder {
|
|
|
637
646
|
|
|
638
647
|
private async compileIntegerSchema(def: LexiconInteger): Promise<string> {
|
|
639
648
|
if (hasConst(def)) {
|
|
640
|
-
const schema = l.integer(def)
|
|
641
|
-
assert(
|
|
642
|
-
schema.check(def.const),
|
|
643
|
-
`Integer const ${def.const} is out of bounds`,
|
|
644
|
-
)
|
|
649
|
+
const schema: l.IntegerSchema = l.integer(def)
|
|
650
|
+
schema.assert(def.const)
|
|
645
651
|
}
|
|
646
652
|
|
|
647
653
|
if (hasEnum(def)) {
|
|
648
|
-
const schema = l.integer(def)
|
|
649
|
-
for (const val of def.enum)
|
|
650
|
-
assert(schema.check(val), `Integer enum value ${val} is out of bounds`)
|
|
651
|
-
}
|
|
654
|
+
const schema: l.IntegerSchema = l.integer(def)
|
|
655
|
+
for (const val of def.enum) schema.assert(val)
|
|
652
656
|
}
|
|
653
657
|
|
|
654
658
|
if (hasConst(def)) return this.compileConstSchema(def)
|
|
655
659
|
if (hasEnum(def)) return this.compileEnumSchema(def)
|
|
656
660
|
|
|
657
|
-
const options =
|
|
661
|
+
const options = stringifyOptions(def, [
|
|
662
|
+
'default',
|
|
663
|
+
'maximum',
|
|
664
|
+
'minimum',
|
|
665
|
+
] satisfies (keyof l.IntegerSchemaOptions)[])
|
|
658
666
|
return this.pure(`l.integer(${options})`)
|
|
659
667
|
}
|
|
660
668
|
|
|
@@ -667,25 +675,24 @@ export class LexDefBuilder {
|
|
|
667
675
|
|
|
668
676
|
private async compileStringSchema(def: LexiconString): Promise<string> {
|
|
669
677
|
if (hasConst(def)) {
|
|
670
|
-
const schema = l.string(def)
|
|
671
|
-
assert(
|
|
672
|
-
schema.check(def.const),
|
|
673
|
-
`String const "${def.const}" does not match format`,
|
|
674
|
-
)
|
|
678
|
+
const schema: l.StringSchema = l.string(def)
|
|
679
|
+
schema.assert(def.const)
|
|
675
680
|
} else if (hasEnum(def)) {
|
|
676
|
-
const schema = l.string(def)
|
|
677
|
-
for (const val of def.enum)
|
|
678
|
-
assert(
|
|
679
|
-
schema.check(val),
|
|
680
|
-
`String enum value "${val}" does not match format`,
|
|
681
|
-
)
|
|
682
|
-
}
|
|
681
|
+
const schema: l.StringSchema = l.string(def)
|
|
682
|
+
for (const val of def.enum) schema.assert(val)
|
|
683
683
|
}
|
|
684
684
|
|
|
685
685
|
if (hasConst(def)) return this.compileConstSchema(def)
|
|
686
686
|
if (hasEnum(def)) return this.compileEnumSchema(def)
|
|
687
687
|
|
|
688
|
-
const options =
|
|
688
|
+
const options = stringifyOptions(def, [
|
|
689
|
+
'default',
|
|
690
|
+
'format',
|
|
691
|
+
'maxGraphemes',
|
|
692
|
+
'minGraphemes',
|
|
693
|
+
'maxLength',
|
|
694
|
+
'minLength',
|
|
695
|
+
] satisfies (keyof l.StringSchemaOptions)[])
|
|
689
696
|
return this.pure(`l.string(${options})`)
|
|
690
697
|
}
|
|
691
698
|
|
|
@@ -694,20 +701,32 @@ export class LexDefBuilder {
|
|
|
694
701
|
if (hasEnum(def)) return this.compileEnumType(def)
|
|
695
702
|
|
|
696
703
|
switch (def.format) {
|
|
704
|
+
case undefined:
|
|
705
|
+
break
|
|
697
706
|
case 'datetime':
|
|
698
|
-
return 'l.
|
|
707
|
+
return 'l.DatetimeString'
|
|
699
708
|
case 'uri':
|
|
700
|
-
return 'l.
|
|
709
|
+
return 'l.UriString'
|
|
701
710
|
case 'at-uri':
|
|
702
|
-
return 'l.
|
|
711
|
+
return 'l.AtUriString'
|
|
703
712
|
case 'did':
|
|
704
|
-
return 'l.
|
|
713
|
+
return 'l.DidString'
|
|
705
714
|
case 'handle':
|
|
706
|
-
return 'l.
|
|
715
|
+
return 'l.HandleString'
|
|
707
716
|
case 'at-identifier':
|
|
708
|
-
return 'l.
|
|
717
|
+
return 'l.AtIdentifierString'
|
|
709
718
|
case 'nsid':
|
|
710
|
-
return 'l.
|
|
719
|
+
return 'l.NsidString'
|
|
720
|
+
case 'tid':
|
|
721
|
+
return 'l.TidString'
|
|
722
|
+
case 'cid':
|
|
723
|
+
return 'l.CidString'
|
|
724
|
+
case 'language':
|
|
725
|
+
return 'l.LanguageString'
|
|
726
|
+
case 'record-key':
|
|
727
|
+
return 'l.RecordKeyString'
|
|
728
|
+
default:
|
|
729
|
+
throw new Error(`Unknown string format: ${def.format}`)
|
|
711
730
|
}
|
|
712
731
|
|
|
713
732
|
if (def.knownValues?.length) {
|
|
@@ -721,7 +740,10 @@ export class LexDefBuilder {
|
|
|
721
740
|
}
|
|
722
741
|
|
|
723
742
|
private async compileBytesSchema(def: LexiconBytes): Promise<string> {
|
|
724
|
-
const options =
|
|
743
|
+
const options = stringifyOptions(def, [
|
|
744
|
+
'minLength',
|
|
745
|
+
'maxLength',
|
|
746
|
+
] satisfies (keyof l.BytesSchemaOptions)[])
|
|
725
747
|
return this.pure(`l.bytes(${options})`)
|
|
726
748
|
}
|
|
727
749
|
|
|
@@ -730,10 +752,12 @@ export class LexDefBuilder {
|
|
|
730
752
|
}
|
|
731
753
|
|
|
732
754
|
private async compileBlobSchema(def: LexiconBlob): Promise<string> {
|
|
733
|
-
const opts = this.options.allowLegacyBlobs
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
755
|
+
const opts = { ...def, allowLegacy: this.options.allowLegacyBlobs === true }
|
|
756
|
+
const options = stringifyOptions(opts, [
|
|
757
|
+
'maxSize',
|
|
758
|
+
'accept',
|
|
759
|
+
'allowLegacy',
|
|
760
|
+
] satisfies (keyof l.BlobSchemaOptions)[])
|
|
737
761
|
return this.pure(`l.blob(${options})`)
|
|
738
762
|
}
|
|
739
763
|
|
|
@@ -743,13 +767,12 @@ export class LexDefBuilder {
|
|
|
743
767
|
: 'l.BlobRef'
|
|
744
768
|
}
|
|
745
769
|
|
|
746
|
-
private async compileCidLinkSchema(
|
|
747
|
-
|
|
748
|
-
return this.pure(`l.cidLink(${options})`)
|
|
770
|
+
private async compileCidLinkSchema(_def: LexiconCid): Promise<string> {
|
|
771
|
+
return this.pure(`l.cidLink()`)
|
|
749
772
|
}
|
|
750
773
|
|
|
751
774
|
private async compileCidLinkType(_def: LexiconCid): Promise<string> {
|
|
752
|
-
return 'l.
|
|
775
|
+
return 'l.Cid'
|
|
753
776
|
}
|
|
754
777
|
|
|
755
778
|
private async compileRefSchema(def: LexiconRef): Promise<string> {
|
|
@@ -796,12 +819,15 @@ export class LexDefBuilder {
|
|
|
796
819
|
|
|
797
820
|
private async compileConstSchema<
|
|
798
821
|
T extends null | number | string | boolean,
|
|
799
|
-
>(def: { const: T; enum?: readonly T[] }): Promise<string> {
|
|
822
|
+
>(def: { const: T; enum?: readonly T[]; default?: T }): Promise<string> {
|
|
800
823
|
if (hasEnum(def) && !def.enum.includes(def.const)) {
|
|
801
824
|
return this.pure(`l.never()`)
|
|
802
825
|
}
|
|
803
826
|
|
|
804
|
-
|
|
827
|
+
const options = stringifyOptions(def, [
|
|
828
|
+
'default',
|
|
829
|
+
] satisfies (keyof l.LiteralSchemaOptions<any>)[])
|
|
830
|
+
return this.pure(`l.literal(${JSON.stringify(def.const)}, ${options})`)
|
|
805
831
|
}
|
|
806
832
|
|
|
807
833
|
private async compileConstType<
|
|
@@ -815,14 +841,18 @@ export class LexDefBuilder {
|
|
|
815
841
|
|
|
816
842
|
private async compileEnumSchema<T extends null | number | string>(def: {
|
|
817
843
|
enum: readonly T[]
|
|
844
|
+
default?: T
|
|
818
845
|
}): Promise<string> {
|
|
819
846
|
if (def.enum.length === 0) {
|
|
820
847
|
return this.pure(`l.never()`)
|
|
821
848
|
}
|
|
822
|
-
if (def.enum.length === 1) {
|
|
849
|
+
if (def.enum.length === 1 && def.default === undefined) {
|
|
823
850
|
return this.pure(`l.literal(${JSON.stringify(def.enum[0])})`)
|
|
824
851
|
}
|
|
825
|
-
|
|
852
|
+
const options = stringifyOptions(def, [
|
|
853
|
+
'default',
|
|
854
|
+
] satisfies (keyof l.EnumSchemaOptions<any>)[])
|
|
855
|
+
return this.pure(`l.enum(${JSON.stringify(def.enum)}, ${options})`)
|
|
826
856
|
}
|
|
827
857
|
|
|
828
858
|
private async compileEnumType<T extends null | number | string>(def: {
|
|
@@ -888,11 +918,14 @@ function compileJsDoc(description: string) {
|
|
|
888
918
|
}`
|
|
889
919
|
}
|
|
890
920
|
|
|
891
|
-
function
|
|
921
|
+
function stringifyOptions<O extends Record<string, unknown>>(
|
|
892
922
|
obj: O,
|
|
893
|
-
|
|
923
|
+
include?: (keyof O)[],
|
|
924
|
+
exclude?: (keyof O)[],
|
|
894
925
|
) {
|
|
895
|
-
const filtered = Object.entries(obj).filter(
|
|
926
|
+
const filtered = Object.entries(obj).filter(
|
|
927
|
+
([k]) => (!include || include.includes(k)) && !exclude?.includes(k),
|
|
928
|
+
)
|
|
896
929
|
return filtered.length ? JSON.stringify(Object.fromEntries(filtered)) : ''
|
|
897
930
|
}
|
|
898
931
|
|
|
@@ -16,7 +16,7 @@ export class LexiconDirectoryIndexer extends LexiconIterableIndexer {
|
|
|
16
16
|
|
|
17
17
|
type ReadLexiconsOptions = {
|
|
18
18
|
lexicons: string
|
|
19
|
-
|
|
19
|
+
ignoreInvalidLexicons?: boolean
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async function* readLexicons(
|
|
@@ -29,7 +29,7 @@ async function* readLexicons(
|
|
|
29
29
|
yield lexiconDocumentSchema.parse(JSON.parse(data))
|
|
30
30
|
} catch (cause) {
|
|
31
31
|
const message = `Error parsing lexicon document ${filePath}`
|
|
32
|
-
if (options.
|
|
32
|
+
if (options.ignoreInvalidLexicons) console.error(`${message}:`, cause)
|
|
33
33
|
else throw new Error(message, { cause })
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/polyfill.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../../../tsconfig/node.json"],
|
|
3
|
+
"include": ["./src"],
|
|
4
|
+
"exclude": ["**/*.test.ts"],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"noImplicitAny": true,
|
|
7
|
+
"importHelpers": true,
|
|
8
|
+
"target": "ES2023",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"types": ["node"]
|
|
12
|
+
}
|
|
13
|
+
}
|
package/tsconfig.json
ADDED