@atproto/lex-schema 0.0.13 → 0.0.14
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 +18 -0
- package/dist/core/schema.d.ts +23 -32
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +67 -53
- package/dist/core/schema.js.map +1 -1
- package/dist/core/string-format.d.ts +1 -14
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +12 -9
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-error.d.ts +5 -5
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +8 -8
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validator.d.ts +6 -6
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +3 -3
- package/dist/core/validator.js.map +1 -1
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +4 -1
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/record.d.ts +12 -6
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +21 -12
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/typed-object.d.ts +12 -4
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +21 -12
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +1 -1
- package/dist/schema/union.js.map +1 -1
- package/package.json +3 -3
- package/src/core/schema.ts +67 -59
- package/src/core/string-format.ts +14 -17
- package/src/core/validation-error.ts +10 -10
- package/src/core/validator.ts +9 -9
- package/src/schema/params.test.ts +16 -0
- package/src/schema/params.ts +5 -2
- package/src/schema/record.ts +27 -22
- package/src/schema/typed-object.test.ts +38 -0
- package/src/schema/typed-object.ts +29 -24
- package/src/schema/union.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n $Typed,\n $typed,\n InferInput,\n InferOutput,\n LexiconRecordKey,\n NsidString,\n Schema,\n TidString,\n Unknown$TypedObject,\n ValidationContext,\n Validator,\n} from '../core.js'\nimport { lazyProperty } from '../util/lazy-property.js'\nimport { literal } from './literal.js'\nimport { string } from './string.js'\n\n/**\n * Infers the record key type from a RecordSchema.\n *\n * @template R - The RecordSchema type\n */\nexport type InferRecordKey<R extends RecordSchema> =\n R extends RecordSchema<infer TKey> ? RecordKeySchemaOutput<TKey> : never\n\nexport type TypedRecord<\n TType extends NsidString,\n TValue extends { $type?: unknown } = { $type?: unknown },\n> = TValue extends { $type: TType }\n ? TValue\n : $Typed<Exclude<TValue, Unknown$TypedObject>, TType>\n\n/**\n * Schema for AT Protocol records with a type identifier and key constraints.\n *\n * Records are the primary data unit in AT Protocol. Each record has a `$type`\n * field identifying its Lexicon schema, and is stored at a specific key\n * (TID, NSID, or other format) in a repository.\n *\n * @template TKey - The record key type ('tid', 'nsid', 'any', or 'literal:...')\n * @template TType - The NSID string identifying this record type\n * @template TShape - The validator type for the record's data shape\n *\n * @example\n * ```ts\n * const postSchema = new RecordSchema(\n * 'tid',\n * 'app.bsky.feed.post',\n * l.object({ text: l.string(), createdAt: l.string() })\n * )\n * ```\n */\nexport class RecordSchema<\n const TKey extends LexiconRecordKey = any,\n const TType extends NsidString = any,\n const TShape extends Validator<{ [k: string]: unknown }> = any,\n> extends Schema<\n $Typed<InferInput<TShape>, TType>,\n $Typed<InferOutput<TShape>, TType>\n> {\n readonly type = 'record' as const\n\n keySchema: RecordKeySchema<TKey>\n\n constructor(\n readonly key: TKey,\n readonly $type: TType,\n readonly schema: TShape,\n ) {\n super()\n this.keySchema = recordKey(key)\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const result = ctx.validate(input, this.schema)\n\n if (!result.success) {\n return result\n }\n\n if (result.value.$type !== this.$type) {\n return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])\n }\n\n return result\n }\n\n build(\n input: Omit<InferInput<this>, '$type'>,\n ): $Typed<InferOutput<this>, TType> {\n return this.parse($typed(input, this.$type))\n }\n\n isTypeOf<TValue extends { $type?: unknown }>(\n value: TValue,\n ): value is TypedRecord<TType, TValue> {\n return value.$type === this.$type\n }\n\n /**\n * Bound alias for {@link build} for compatibility with generated utilities.\n * @see {@link build}\n */\n get $build(): typeof this.build {\n return lazyProperty(this, '$build', this.build.bind(this))\n }\n\n /**\n * Bound alias for {@link isTypeOf} for compatibility with generated utilities.\n * @see {@link isTypeOf}\n */\n get $isTypeOf(): typeof this.isTypeOf {\n return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))\n }\n}\n\nexport type RecordKeySchemaOutput<Key extends LexiconRecordKey> =\n Key extends 'any'\n ? string\n : Key extends 'tid'\n ? TidString\n : Key extends 'nsid'\n ? NsidString\n : Key extends `literal:${infer L extends string}`\n ? L\n : never\n\nexport type RecordKeySchema<Key extends LexiconRecordKey> = Schema<\n RecordKeySchemaOutput<Key>\n>\n\nconst keySchema = string({ minLength: 1 })\nconst tidSchema = string({ format: 'tid' })\nconst nsidSchema = string({ format: 'nsid' })\nconst selfLiteralSchema = literal('self')\n\nfunction recordKey<Key extends LexiconRecordKey>(\n key: Key,\n): RecordKeySchema<Key> {\n // @NOTE Use cached instances for common schemas\n if (key === 'any') return keySchema as any\n if (key === 'tid') return tidSchema as any\n if (key === 'nsid') return nsidSchema as any\n if (key.startsWith('literal:')) {\n const value = key.slice(8) as RecordKeySchemaOutput<Key>\n if (value === 'self') return selfLiteralSchema as any\n return literal(value)\n }\n\n throw new Error(`Unsupported record key type: ${key}`)\n}\n\n/**\n * Ensures that a `$type` used in a record is a valid NSID (i.e. no fragment).\n */\ntype AsNsid<T> = T extends `${string}#${string}` ? never : T\n\n/**\n * Creates a record schema for AT Protocol records.\n *\n * Records are the primary data unit in AT Protocol repositories. They have\n * a `$type` field identifying their Lexicon schema, and are stored at keys\n * following a specific format (TID, NSID, etc.).\n *\n * This function offers two overloads:\n * - One that infers the output type from the provided arguments (does not\n * support circular references)\n * - One with an explicitly defined interface for use with codegen and\n * circular references\n *\n * @param key - The record key type: 'tid', 'nsid', 'any', or 'literal:value'\n * @param type - The NSID identifying this record type (e.g., 'app.bsky.feed.post')\n * @param validator - Schema validator for the record's properties\n * @returns A new {@link RecordSchema} instance\n *\n * @example\n * ```ts\n * // Post record with TID key\n * const postSchema = l.record('tid', 'app.bsky.feed.post', l.object({\n * text: l.string({ maxGraphemes: 300 }),\n * createdAt: l.string({ format: 'datetime' }),\n * reply: l.optional(l.object({\n * root: l.ref(() => strongRefSchema),\n * parent: l.ref(() => strongRefSchema),\n * })),\n * }))\n *\n * // Profile record with literal 'self' key\n * const profileSchema = l.record('literal:self', 'app.bsky.actor.profile', l.object({\n * displayName: l.optional(l.string({ maxGraphemes: 64 })),\n * description: l.optional(l.string({ maxGraphemes: 256 })),\n * avatar: l.optional(l.blob({ accept: ['image/*'] })),\n * }))\n *\n * // Build a record with automatic $type injection\n * const post = postSchema.build({ text: 'Hello!', createdAt: new Date().toISOString() })\n * ```\n */\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<{ [k: string]: unknown }>,\n>(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>\nexport function record<\n const K extends LexiconRecordKey,\n const V extends { $type: NsidString },\n>(\n key: K,\n type: AsNsid<V['$type']>,\n validator: Validator<Omit<V, '$type'>>,\n): RecordSchema<K, V['$type'], Validator<Omit<V, '$type'>>>\n/*@__NO_SIDE_EFFECTS__*/\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<{ [k: string]: unknown }>,\n>(key: K, type: T, validator: S) {\n return new RecordSchema<K, T, S>(key, type, validator)\n}\n"]}
|
|
@@ -31,11 +31,19 @@ export declare class TypedObjectSchema<const TType extends $Type = $Type, const
|
|
|
31
31
|
readonly schema: TShape;
|
|
32
32
|
readonly type: "typedObject";
|
|
33
33
|
constructor($type: TType, schema: TShape);
|
|
34
|
-
isTypeOf<TValue extends Record<string, unknown>>(value: TValue): value is MaybeTypedObject<TType, TValue>;
|
|
35
|
-
build(input: Omit<InferInput<this>, '$type'>): $Typed<InferOutput<this>, TType>;
|
|
36
|
-
$isTypeOf<TValue extends Record<string, unknown>>(value: TValue): value is MaybeTypedObject<TType, TValue>;
|
|
37
|
-
$build(input: Omit<InferInput<this>, '$type'>): $Typed<InferOutput<this>, TType>;
|
|
38
34
|
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<InferInput<TShape>>;
|
|
35
|
+
build(input: Omit<InferInput<this>, '$type'>): $Typed<InferOutput<this>, TType>;
|
|
36
|
+
isTypeOf<TValue extends Record<string, unknown>>(value: TValue): value is MaybeTypedObject<TType, TValue>;
|
|
37
|
+
/**
|
|
38
|
+
* Bound alias for {@link build} for compatibility with generated utilities.
|
|
39
|
+
* @see {@link build}
|
|
40
|
+
*/
|
|
41
|
+
get $build(): typeof this.build;
|
|
42
|
+
/**
|
|
43
|
+
* Bound alias for {@link isTypeOf} for compatibility with generated utilities.
|
|
44
|
+
* @see {@link isTypeOf}
|
|
45
|
+
*/
|
|
46
|
+
get $isTypeOf(): typeof this.isTypeOf;
|
|
39
47
|
}
|
|
40
48
|
/**
|
|
41
49
|
* Creates a typed object schema for use in Lexicon unions.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-object.d.ts","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,EACL,OAAO,EACP,MAAM,EACN,WAAW,EAGX,UAAU,EACV,WAAW,EACX,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACV,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"typed-object.d.ts","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,EACL,OAAO,EACP,MAAM,EACN,WAAW,EAGX,UAAU,EACV,WAAW,EACX,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACV,MAAM,YAAY,CAAA;AAGnB,MAAM,MAAM,gBAAgB,CAC1B,KAAK,SAAS,KAAK,EACnB,MAAM,SAAS;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,IACtD,MAAM,SAAS;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAChC,MAAM,GACN,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAAE,KAAK,CAAC,CAAA;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAiB,CAC5B,KAAK,CAAC,KAAK,SAAS,KAAK,GAAG,KAAK,EACjC,KAAK,CAAC,MAAM,SAAS,SAAS,CAAC;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,GAAG,GAAG,CAC9D,SAAQ,MAAM,CACd,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EACtC,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CACxC;IAIG,QAAQ,CAAC,KAAK,EAAE,KAAK;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,QAAQ,CAAC,IAAI,EAAG,aAAa,CAAS;gBAG3B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM;IAKzB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;IAgBxD,KAAK,CACH,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,GACrC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;IAOnC,QAAQ,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7C,KAAK,EAAE,MAAM,GACZ,KAAK,IAAI,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC;IAI3C;;;OAGG;IACH,IAAI,MAAM,IAAI,OAAO,IAAI,CAAC,KAAK,CAE9B;IAED;;;OAGG;IACH,IAAI,SAAS,IAAI,OAAO,IAAI,CAAC,QAAQ,CAEpC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,WAAW,CACzB,KAAK,CAAC,CAAC,SAAS,UAAU,EAC1B,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,KAAK,CAAC,CAAC,SAAS,SAAS,CAAC;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,EACnD,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACpE,wBAAgB,WAAW,CAAC,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,EACrD,IAAI,EAAE,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAC9C,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,MAAM,EAAE,GAC9B,CAAC,GACD,CAAC,GACH,KAAK,EACT,IAAI,EAAE,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAC9C,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,CAAC,EAAE,GAC9B,CAAC,GACD,MAAM,GACR,KAAK,EACT,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,GACrC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -4,6 +4,7 @@ exports.TypedObjectSchema = void 0;
|
|
|
4
4
|
exports.typedObject = typedObject;
|
|
5
5
|
const lex_data_1 = require("@atproto/lex-data");
|
|
6
6
|
const core_js_1 = require("../core.js");
|
|
7
|
+
const lazy_property_js_1 = require("../util/lazy-property.js");
|
|
7
8
|
/**
|
|
8
9
|
* Schema for typed objects in Lexicon unions.
|
|
9
10
|
*
|
|
@@ -31,18 +32,6 @@ class TypedObjectSchema extends core_js_1.Schema {
|
|
|
31
32
|
this.$type = $type;
|
|
32
33
|
this.schema = schema;
|
|
33
34
|
}
|
|
34
|
-
isTypeOf(value) {
|
|
35
|
-
return value.$type === undefined || value.$type === this.$type;
|
|
36
|
-
}
|
|
37
|
-
build(input) {
|
|
38
|
-
return this.parse((0, core_js_1.$typed)(input, this.$type));
|
|
39
|
-
}
|
|
40
|
-
$isTypeOf(value) {
|
|
41
|
-
return this.isTypeOf(value);
|
|
42
|
-
}
|
|
43
|
-
$build(input) {
|
|
44
|
-
return this.build(input);
|
|
45
|
-
}
|
|
46
35
|
validateInContext(input, ctx) {
|
|
47
36
|
if (!(0, lex_data_1.isPlainObject)(input)) {
|
|
48
37
|
return ctx.issueUnexpectedType(input, 'object');
|
|
@@ -54,6 +43,26 @@ class TypedObjectSchema extends core_js_1.Schema {
|
|
|
54
43
|
}
|
|
55
44
|
return ctx.validate(input, this.schema);
|
|
56
45
|
}
|
|
46
|
+
build(input) {
|
|
47
|
+
return this.parse((0, core_js_1.$typed)(input, this.$type));
|
|
48
|
+
}
|
|
49
|
+
isTypeOf(value) {
|
|
50
|
+
return value.$type === undefined || value.$type === this.$type;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Bound alias for {@link build} for compatibility with generated utilities.
|
|
54
|
+
* @see {@link build}
|
|
55
|
+
*/
|
|
56
|
+
get $build() {
|
|
57
|
+
return (0, lazy_property_js_1.lazyProperty)(this, '$build', this.build.bind(this));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Bound alias for {@link isTypeOf} for compatibility with generated utilities.
|
|
61
|
+
* @see {@link isTypeOf}
|
|
62
|
+
*/
|
|
63
|
+
get $isTypeOf() {
|
|
64
|
+
return (0, lazy_property_js_1.lazyProperty)(this, '$isTypeOf', this.isTypeOf.bind(this));
|
|
65
|
+
}
|
|
57
66
|
}
|
|
58
67
|
exports.TypedObjectSchema = TypedObjectSchema;
|
|
59
68
|
/*@__NO_SIDE_EFFECTS__*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-object.js","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"typed-object.js","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":";;;AA6KA,kCAMC;AAnLD,gDAAiD;AACjD,wCAcmB;AACnB,+DAAuD;AASvD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,iBAGX,SAAQ,gBAGT;IAIY;IACA;IAJF,IAAI,GAAG,aAAsB,CAAA;IAEtC,YACW,KAAY,EACZ,MAAc;QAEvB,KAAK,EAAE,CAAA;QAHE,UAAK,GAAL,KAAK,CAAO;QACZ,WAAM,GAAN,MAAM,CAAQ;IAGzB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,CAAC,IAAA,wBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjD,CAAC;QAED,IACE,OAAO,IAAI,KAAK;YAChB,KAAK,CAAC,KAAK,KAAK,SAAS;YACzB,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAC1B,CAAC;YACD,OAAO,GAAG,CAAC,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QACpE,CAAC;QAED,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IAED,KAAK,CACH,KAAsC;QAEtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,gBAAM,EAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAG1C,CAAA;IACH,CAAC;IAED,QAAQ,CACN,KAAa;QAEb,OAAO,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAA;IAChE,CAAC;IAED;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5D,CAAC;IAED;;;OAGG;IACH,IAAI,SAAS;QACX,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAClE,CAAC;CACF;AA9DD,8CA8DC;AAmED,wBAAwB;AACxB,SAAgB,WAAW,CAIzB,IAAO,EAAE,IAAO,EAAE,SAAY;IAC9B,OAAO,IAAI,iBAAiB,CAAiB,IAAA,eAAK,EAAC,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,CAAA;AAC5E,CAAC","sourcesContent":["import { isPlainObject } from '@atproto/lex-data'\nimport {\n $Type,\n $TypeOf,\n $Typed,\n $TypedMaybe,\n $type,\n $typed,\n InferInput,\n InferOutput,\n NsidString,\n Schema,\n Unknown$TypedObject,\n ValidationContext,\n Validator,\n} from '../core.js'\nimport { lazyProperty } from '../util/lazy-property.js'\n\nexport type MaybeTypedObject<\n TType extends $Type,\n TValue extends { $type?: unknown } = { $type?: unknown },\n> = TValue extends { $type?: TType }\n ? TValue\n : $TypedMaybe<Exclude<TValue, Unknown$TypedObject>, TType>\n\n/**\n * Schema for typed objects in Lexicon unions.\n *\n * Typed objects have a `$type` field that identifies which variant they are\n * in a union. The `$type` can be omitted in input (it's implicit), but if\n * present, it must match the expected value.\n *\n * @template TType - The $type string literal type\n * @template TShape - The validator type for the object's shape\n *\n * @example\n * ```ts\n * const schema = new TypedObjectSchema(\n * 'app.bsky.embed.images#view',\n * l.object({ images: l.array(imageSchema) })\n * )\n * ```\n */\nexport class TypedObjectSchema<\n const TType extends $Type = $Type,\n const TShape extends Validator<{ [k: string]: unknown }> = any,\n> extends Schema<\n $TypedMaybe<InferInput<TShape>, TType>,\n $TypedMaybe<InferOutput<TShape>, TType>\n> {\n readonly type = 'typedObject' as const\n\n constructor(\n readonly $type: TType,\n readonly schema: TShape,\n ) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (!isPlainObject(input)) {\n return ctx.issueUnexpectedType(input, 'object')\n }\n\n if (\n '$type' in input &&\n input.$type !== undefined &&\n input.$type !== this.$type\n ) {\n return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])\n }\n\n return ctx.validate(input, this.schema)\n }\n\n build(\n input: Omit<InferInput<this>, '$type'>,\n ): $Typed<InferOutput<this>, TType> {\n return this.parse($typed(input, this.$type)) as $Typed<\n InferOutput<this>,\n TType\n >\n }\n\n isTypeOf<TValue extends Record<string, unknown>>(\n value: TValue,\n ): value is MaybeTypedObject<TType, TValue> {\n return value.$type === undefined || value.$type === this.$type\n }\n\n /**\n * Bound alias for {@link build} for compatibility with generated utilities.\n * @see {@link build}\n */\n get $build(): typeof this.build {\n return lazyProperty(this, '$build', this.build.bind(this))\n }\n\n /**\n * Bound alias for {@link isTypeOf} for compatibility with generated utilities.\n * @see {@link isTypeOf}\n */\n get $isTypeOf(): typeof this.isTypeOf {\n return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))\n }\n}\n\n/**\n * Creates a typed object schema for use in Lexicon unions.\n *\n * Typed objects are identified by their `$type` field, which combines an NSID\n * and a hash (e.g., 'app.bsky.embed.images#view'). Used for union variants.\n *\n * This function offers two overloads:\n * - One that infers the type from arguments (no circular reference support)\n * - One with explicit interface for codegen with circular references\n *\n * @param nsid - The NSID part of the type (e.g., 'app.bsky.embed.images')\n * @param hash - The hash part of the type (e.g., 'view'), defaults to 'main'\n * @param validator - Schema for validating the object properties\n * @returns A new {@link TypedObjectSchema} instance\n *\n * @example\n * ```ts\n * // Image embed view\n * const imageViewSchema = l.typedObject(\n * 'app.bsky.embed.images',\n * 'view',\n * l.object({\n * images: l.array(l.object({\n * thumb: l.string(),\n * fullsize: l.string(),\n * alt: l.string(),\n * })),\n * })\n * )\n *\n * // Main type (hash defaults to 'main')\n * const postViewSchema = l.typedObject(\n * 'app.bsky.feed.defs',\n * 'postView',\n * l.object({ uri: l.string(), cid: l.string(), author: authorSchema })\n * )\n *\n * // Use $isTypeOf to narrow union types\n * if (imageViewSchema.$isTypeOf(embed)) {\n * // embed is narrowed to image view type\n * }\n *\n * // Use $build to construct typed objects\n * const view = imageViewSchema.$build({ images: [...] })\n * // view.$type === 'app.bsky.embed.images#view'\n * ```\n */\nexport function typedObject<\n const N extends NsidString,\n const H extends string,\n const S extends Validator<{ [k: string]: unknown }>,\n>(nsid: N, hash: H, validator: S): TypedObjectSchema<$Type<N, H>, S>\nexport function typedObject<V extends { $type?: $Type }>(\n nsid: V extends { $type?: infer T extends string }\n ? T extends `${infer N}#${string}`\n ? N\n : T // (T is a \"main\" type, so already an NSID)\n : never,\n hash: V extends { $type?: infer T extends string }\n ? T extends `${string}#${infer H}`\n ? H\n : 'main'\n : never,\n validator: Validator<Omit<V, '$type'>>,\n): TypedObjectSchema<$TypeOf<V>, Validator<V>>\n/*@__NO_SIDE_EFFECTS__*/\nexport function typedObject<\n const N extends NsidString,\n const H extends string,\n const S extends Validator<{ [k: string]: unknown }>,\n>(nsid: N, hash: H, validator: S) {\n return new TypedObjectSchema<$Type<N, H>, S>($type(nsid, hash), validator)\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"union.d.ts","sourceRoot":"","sources":["../../src/schema/union.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,
|
|
1
|
+
{"version":3,"file":"union.d.ts","sourceRoot":"","sources":["../../src/schema/union.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,EAEX,MAAM,EACN,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACV,MAAM,YAAY,CAAA;AAEnB;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAAC,SAAS,EAAE,GAAG,SAAS,EAAE,CAAC,CAAA;AAExE;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,WAAW,CACtB,KAAK,CAAC,WAAW,SAAS,qBAAqB,GAAG,GAAG,CACrD,SAAQ,MAAM,CACd,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAC/B,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CACjC;IAGa,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,WAAW;IAFtD,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAS;gBAED,UAAU,EAAE,WAAW;IAItD,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAYzD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,wBAAgB,KAAK,CAAC,KAAK,CAAC,WAAW,SAAS,qBAAqB,EACnE,UAAU,EAAE,WAAW,4BAGxB"}
|
package/dist/schema/union.js
CHANGED
|
@@ -34,7 +34,7 @@ class UnionSchema extends core_js_1.Schema {
|
|
|
34
34
|
return result;
|
|
35
35
|
failures.push(result);
|
|
36
36
|
}
|
|
37
|
-
return ctx.failure(core_js_1.
|
|
37
|
+
return ctx.failure(core_js_1.LexValidationError.fromFailures(failures));
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
exports.UnionSchema = UnionSchema;
|
package/dist/schema/union.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"union.js","sourceRoot":"","sources":["../../src/schema/union.ts"],"names":[],"mappings":";;;AAoFA,sBAIC;AAxFD,wCAQmB;AASnB;;;;;;;;;;;;;;;GAeG;AACH,MAAa,WAEX,SAAQ,gBAGT;IAGgC;IAFtB,IAAI,GAAG,OAAgB,CAAA;IAEhC,YAA+B,UAAuB;QACpD,KAAK,EAAE,CAAA;QADsB,eAAU,GAAV,UAAU,CAAa;IAEtD,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,MAAM,QAAQ,GAAwB,EAAE,CAAA;QAExC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;YAC7C,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,MAAM,CAAA;YAEjC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACvB,CAAC;QAED,OAAO,GAAG,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"union.js","sourceRoot":"","sources":["../../src/schema/union.ts"],"names":[],"mappings":";;;AAoFA,sBAIC;AAxFD,wCAQmB;AASnB;;;;;;;;;;;;;;;GAeG;AACH,MAAa,WAEX,SAAQ,gBAGT;IAGgC;IAFtB,IAAI,GAAG,OAAgB,CAAA;IAEhC,YAA+B,UAAuB;QACpD,KAAK,EAAE,CAAA;QADsB,eAAU,GAAV,UAAU,CAAa;IAEtD,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,MAAM,QAAQ,GAAwB,EAAE,CAAA;QAExC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;YAC7C,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,MAAM,CAAA;YAEjC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACvB,CAAC;QAED,OAAO,GAAG,CAAC,OAAO,CAAC,4BAAkB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC/D,CAAC;CACF;AAxBD,kCAwBC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAwB;AACxB,SAAgB,KAAK,CACnB,UAAuB;IAEvB,OAAO,IAAI,WAAW,CAAc,UAAU,CAAC,CAAA;AACjD,CAAC","sourcesContent":["import {\n InferInput,\n InferOutput,\n LexValidationError,\n Schema,\n ValidationContext,\n ValidationFailure,\n Validator,\n} from '../core.js'\n\n/**\n * Type representing a non-empty tuple of validators for union schemas.\n *\n * Requires at least one validator in the tuple.\n */\nexport type UnionSchemaValidators = readonly [Validator, ...Validator[]]\n\n/**\n * Schema for validating values that match one of several possible schemas.\n *\n * Tries each validator in order until one succeeds. If all validators fail,\n * returns a combined error from all attempts.\n *\n * @template TValidators - Tuple type of the validators in the union\n *\n * @example\n * ```ts\n * const schema = new UnionSchema([l.string(), l.integer()])\n * schema.validate('hello') // success\n * schema.validate(42) // success\n * schema.validate(true) // fails\n * ```\n */\nexport class UnionSchema<\n const TValidators extends UnionSchemaValidators = any,\n> extends Schema<\n InferInput<TValidators[number]>,\n InferOutput<TValidators[number]>\n> {\n readonly type = 'union' as const\n\n constructor(protected readonly validators: TValidators) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const failures: ValidationFailure[] = []\n\n for (const validator of this.validators) {\n const result = ctx.validate(input, validator)\n if (result.success) return result\n\n failures.push(result)\n }\n\n return ctx.failure(LexValidationError.fromFailures(failures))\n }\n}\n\n/**\n * Creates a union schema that accepts values matching any of the provided schemas.\n *\n * Validators are tried in order. Use `discriminatedUnion()` for better\n * performance when discriminating on a known property.\n *\n * @param validators - Non-empty array of validators to try\n * @returns A new {@link UnionSchema} instance\n *\n * @example\n * ```ts\n * // String or number\n * const stringOrNumber = l.union([l.string(), l.integer()])\n *\n * // Nullable value\n * const nullableString = l.union([l.string(), l.null()])\n *\n * // Multiple object types\n * const mediaSchema = l.union([\n * l.object({ type: l.literal('image'), url: l.string() }),\n * l.object({ type: l.literal('video'), url: l.string(), duration: l.integer() }),\n * ])\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function union<const TValidators extends UnionSchemaValidators>(\n validators: TValidators,\n) {\n return new UnionSchema<TValidators>(validators)\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex-schema",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Lexicon schema system for AT Lexicons",
|
|
6
6
|
"keywords": [
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"tslib": "^2.8.1",
|
|
39
|
-
"@atproto/syntax": "^0.
|
|
40
|
-
"@atproto/lex-data": "^0.0.
|
|
39
|
+
"@atproto/syntax": "^0.5.0",
|
|
40
|
+
"@atproto/lex-data": "^0.0.13"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"vitest": "^4.0.16"
|
package/src/core/schema.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { lazyProperty } from '../util/lazy-property.js'
|
|
1
2
|
import {
|
|
2
3
|
InferInput,
|
|
3
4
|
InferOutput,
|
|
@@ -66,7 +67,7 @@ export interface SchemaInternals<out TInput = unknown, out TOutput = TInput> {
|
|
|
66
67
|
*
|
|
67
68
|
* const schema = new MySchema()
|
|
68
69
|
* schema.assert('hello') // OK
|
|
69
|
-
* schema.assert(123) // Throws
|
|
70
|
+
* schema.assert(123) // Throws LexValidationError
|
|
70
71
|
* schema.matches('hello') // true
|
|
71
72
|
* schema.matches(123) // false
|
|
72
73
|
* ```
|
|
@@ -190,12 +191,12 @@ export abstract class Schema<
|
|
|
190
191
|
*
|
|
191
192
|
* Unlike {@link validate}, this method allows the schema to transform
|
|
192
193
|
* the input value (e.g., applying default values, type coercion).
|
|
193
|
-
* Throws a {@link
|
|
194
|
+
* Throws a {@link LexValidationError} if the input is invalid.
|
|
194
195
|
*
|
|
195
196
|
* @param input - The value to parse
|
|
196
197
|
* @param options - Optional parsing configuration
|
|
197
198
|
* @returns The parsed and potentially transformed value
|
|
198
|
-
* @throws {
|
|
199
|
+
* @throws {LexValidationError} If the input fails validation
|
|
199
200
|
*
|
|
200
201
|
* @example
|
|
201
202
|
* ```typescript
|
|
@@ -244,13 +245,13 @@ export abstract class Schema<
|
|
|
244
245
|
*
|
|
245
246
|
* Unlike {@link parse}, this method requires the input to exactly match
|
|
246
247
|
* the schema without any transformations (no defaults applied, no coercion).
|
|
247
|
-
* Throws a {@link
|
|
248
|
+
* Throws a {@link LexValidationError} if the input is invalid or would require transformation.
|
|
248
249
|
*
|
|
249
250
|
* @typeParam I - The input type (preserved in the return type)
|
|
250
251
|
* @param input - The value to validate
|
|
251
252
|
* @param options - Optional validation configuration
|
|
252
253
|
* @returns The validated input with narrowed type
|
|
253
|
-
* @throws {
|
|
254
|
+
* @throws {LexValidationError} If the input fails validation or requires transformation
|
|
254
255
|
*
|
|
255
256
|
* @example
|
|
256
257
|
* ```typescript
|
|
@@ -297,104 +298,111 @@ export abstract class Schema<
|
|
|
297
298
|
|
|
298
299
|
// @NOTE Dollar-prefixed aliases
|
|
299
300
|
//
|
|
300
|
-
// The
|
|
301
|
-
// the schema's methods without the need to specify ".main."
|
|
302
|
-
// namespace. This
|
|
303
|
-
// like "app.bsky.feed.post.<utility>()" instead of
|
|
304
|
-
// "app.bsky.feed.post.main.<utility>()".
|
|
305
|
-
// conflict with other schemas (e.g. if there is a lexicon definition at
|
|
306
|
-
// "#<utility>"), those exported utilities will be prefixed with "$". In order
|
|
307
|
-
// to be able to consistently call the utilities, when using the "main" and
|
|
308
|
-
// non "main" definitions, we also expose the same methods with a "$" prefix.
|
|
309
|
-
// Thanks to this, both of the following call will be possible:
|
|
301
|
+
// The `lex-builder` lib generates namespaced utility functions that allow
|
|
302
|
+
// accessing the schema's methods without the need to specify the ".main."
|
|
303
|
+
// part of the namespace. This allows utilities for a particular record type
|
|
304
|
+
// to be called like "app.bsky.feed.post.<utility>()" instead of
|
|
305
|
+
// "app.bsky.feed.post.main.<utility>()".
|
|
310
306
|
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
307
|
+
// Because those utilities could conflict with other schemas (e.g. if there is
|
|
308
|
+
// a lexicon definition with the same name as the "<utility>"), those exported
|
|
309
|
+
// utilities will be prefixed with "$".
|
|
310
|
+
//
|
|
311
|
+
// Similarly, since those utilities are defined as simple "const", they are
|
|
312
|
+
// also bound (using JS's .bind) to the schema instance, so that they can be
|
|
313
|
+
// used without worrying about the context (e.g. "app.bsky.feed.post.$parse()"
|
|
314
|
+
// will work regardless of how it is imported or called).
|
|
315
|
+
//
|
|
316
|
+
// In order to provide the same functionalities for non-main definitions, we
|
|
317
|
+
// also define those aliases directly on the schema instance, so that they can
|
|
318
|
+
// be used in the same way as the utilities generated by "lex-builder". For
|
|
319
|
+
// example, if there is a non-main definition "app.bsky.feed.defs.postView",
|
|
320
|
+
// it will also be possible to call "app.bsky.feed.defs.postView.$parse()".
|
|
321
|
+
//
|
|
322
|
+
// These methods are also "bound" to the instance so that they can be used
|
|
323
|
+
// exactly like the utilities generated by "lex-builder", without worrying
|
|
324
|
+
// about the context.
|
|
325
|
+
//
|
|
326
|
+
// There are two ways we could "bind" those methods to the instance:
|
|
327
|
+
// 1. Define them as getters that return the bound method (e.g. get $parse() {
|
|
328
|
+
// return this.parse.bind(this) })
|
|
329
|
+
// 2. Define them as properties that are initialized in the constructor (e.g.
|
|
330
|
+
// this.$parse = this.parse.bind(this))
|
|
331
|
+
//
|
|
332
|
+
// Since a **lot** of those methods would end-up being created in systems that
|
|
333
|
+
// contains many schemas (e.g. the appview), we choose the first approach
|
|
334
|
+
// (getters) in order to avoid the overhead of creating all those bound
|
|
335
|
+
// functions upfront when instantiating the schemas.
|
|
313
336
|
|
|
314
337
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
338
|
+
* Bound alias for {@link assert} for compatibility with generated utilities.
|
|
317
339
|
* @see {@link assert}
|
|
318
340
|
*/
|
|
319
|
-
$assert(
|
|
320
|
-
return this.assert(
|
|
341
|
+
get $assert(): typeof this.assert {
|
|
342
|
+
return lazyProperty(this, '$assert', this.assert.bind(this))
|
|
321
343
|
}
|
|
322
344
|
|
|
323
345
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
346
|
+
* Bound alias for {@link check} for compatibility with generated utilities.
|
|
326
347
|
* @see {@link check}
|
|
327
348
|
*/
|
|
328
|
-
$check(
|
|
329
|
-
return this.check(
|
|
349
|
+
get $check(): typeof this.check {
|
|
350
|
+
return lazyProperty(this, '$check', this.check.bind(this))
|
|
330
351
|
}
|
|
331
352
|
|
|
332
353
|
/**
|
|
333
|
-
*
|
|
334
|
-
*
|
|
354
|
+
* Bound alias for {@link cast} for compatibility with generated utilities.
|
|
335
355
|
* @see {@link cast}
|
|
336
356
|
*/
|
|
337
|
-
$cast
|
|
338
|
-
return this.cast(
|
|
357
|
+
get $cast(): typeof this.cast {
|
|
358
|
+
return lazyProperty(this, '$cast', this.cast.bind(this))
|
|
339
359
|
}
|
|
340
360
|
|
|
341
361
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
362
|
+
* Bound alias for {@link matches} for compatibility with generated utilities.
|
|
344
363
|
* @see {@link matches}
|
|
345
364
|
*/
|
|
346
|
-
$matches(
|
|
347
|
-
return this.matches(
|
|
365
|
+
get $matches(): typeof this.matches {
|
|
366
|
+
return lazyProperty(this, '$matches', this.matches.bind(this))
|
|
348
367
|
}
|
|
349
368
|
|
|
350
369
|
/**
|
|
351
|
-
*
|
|
352
|
-
*
|
|
370
|
+
* Bound alias for {@link ifMatches} for compatibility with generated utilities.
|
|
353
371
|
* @see {@link ifMatches}
|
|
354
372
|
*/
|
|
355
|
-
$ifMatches
|
|
356
|
-
return this.ifMatches(
|
|
373
|
+
get $ifMatches(): typeof this.ifMatches {
|
|
374
|
+
return lazyProperty(this, '$ifMatches', this.ifMatches.bind(this))
|
|
357
375
|
}
|
|
358
376
|
|
|
359
377
|
/**
|
|
360
|
-
*
|
|
361
|
-
*
|
|
378
|
+
* Bound alias for {@link parse} for compatibility with generated utilities.
|
|
362
379
|
* @see {@link parse}
|
|
363
380
|
*/
|
|
364
|
-
$parse(
|
|
365
|
-
return this.parse(
|
|
381
|
+
get $parse(): typeof this.parse {
|
|
382
|
+
return lazyProperty(this, '$parse', this.parse.bind(this))
|
|
366
383
|
}
|
|
367
384
|
|
|
368
385
|
/**
|
|
369
|
-
*
|
|
370
|
-
*
|
|
386
|
+
* Bound alias for {@link safeParse} for compatibility with generated utilities.
|
|
371
387
|
* @see {@link safeParse}
|
|
372
388
|
*/
|
|
373
|
-
$safeParse(
|
|
374
|
-
|
|
375
|
-
options?: ValidateOptions,
|
|
376
|
-
): ValidationResult<InferOutput<this>> {
|
|
377
|
-
return this.safeParse(input, options)
|
|
389
|
+
get $safeParse(): typeof this.safeParse {
|
|
390
|
+
return lazyProperty(this, '$safeParse', this.safeParse.bind(this))
|
|
378
391
|
}
|
|
379
392
|
|
|
380
393
|
/**
|
|
381
|
-
*
|
|
382
|
-
*
|
|
394
|
+
* Bound alias for {@link validate} for compatibility with generated utilities.
|
|
383
395
|
* @see {@link validate}
|
|
384
396
|
*/
|
|
385
|
-
$validate
|
|
386
|
-
return this.validate(
|
|
397
|
+
get $validate(): typeof this.validate {
|
|
398
|
+
return lazyProperty(this, '$validate', this.validate.bind(this))
|
|
387
399
|
}
|
|
388
400
|
|
|
389
401
|
/**
|
|
390
|
-
*
|
|
391
|
-
*
|
|
402
|
+
* Bound alias for {@link safeValidate} for compatibility with generated utilities.
|
|
392
403
|
* @see {@link safeValidate}
|
|
393
404
|
*/
|
|
394
|
-
$safeValidate
|
|
395
|
-
|
|
396
|
-
options?: ValidateOptions,
|
|
397
|
-
): ValidationResult<I & InferInput<this>> {
|
|
398
|
-
return this.safeValidate(input, options)
|
|
405
|
+
get $safeValidate(): typeof this.safeValidate {
|
|
406
|
+
return lazyProperty(this, '$safeValidate', this.safeValidate.bind(this))
|
|
399
407
|
}
|
|
400
408
|
}
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
RecordKeyString,
|
|
10
10
|
TidString,
|
|
11
11
|
UriString,
|
|
12
|
+
isDatetimeString,
|
|
12
13
|
isValidAtIdentifier as isValidAtId,
|
|
13
14
|
isValidAtUri,
|
|
14
|
-
isValidDatetime,
|
|
15
15
|
isValidDid,
|
|
16
16
|
isValidHandle,
|
|
17
17
|
isValidLanguage,
|
|
@@ -26,6 +26,19 @@ import { CheckFn } from '../util/assertion-util.js'
|
|
|
26
26
|
// Individual string format types and type guards
|
|
27
27
|
// -----------------------------------------------------------------------------
|
|
28
28
|
|
|
29
|
+
// Re-exporting from @atproto/syntax without modification to preserve types and
|
|
30
|
+
// documentation for types and utilities that are already well-defined there.
|
|
31
|
+
// @TODO rework other string formats in @atproto/syntax to follow this pattern
|
|
32
|
+
// and re-export here, e.g. language tags, NSIDs, record keys, etc.
|
|
33
|
+
export {
|
|
34
|
+
type DatetimeString,
|
|
35
|
+
asDatetimeString,
|
|
36
|
+
currentDatetimeString,
|
|
37
|
+
ifDatetimeString,
|
|
38
|
+
isDatetimeString,
|
|
39
|
+
toDatetimeString,
|
|
40
|
+
} from '@atproto/syntax'
|
|
41
|
+
|
|
29
42
|
/**
|
|
30
43
|
* Type guard that checks if a value is a valid AT identifier (DID or handle).
|
|
31
44
|
*
|
|
@@ -74,22 +87,6 @@ export const isCidString = ((v) => validateCidString(v)) as CheckFn<CidString>
|
|
|
74
87
|
*/
|
|
75
88
|
export type CidString = string
|
|
76
89
|
|
|
77
|
-
/**
|
|
78
|
-
* Type guard that checks if a value is a valid datetime string.
|
|
79
|
-
*
|
|
80
|
-
* @param value - The value to check
|
|
81
|
-
* @returns `true` if the value is a valid datetime string
|
|
82
|
-
*/
|
|
83
|
-
export const isDatetimeString: CheckFn<DatetimeString> = isValidDatetime
|
|
84
|
-
export type {
|
|
85
|
-
/**
|
|
86
|
-
* An ISO 8601 datetime string.
|
|
87
|
-
*
|
|
88
|
-
* @example `"2024-01-15T12:30:00.000Z"`
|
|
89
|
-
*/
|
|
90
|
-
DatetimeString,
|
|
91
|
-
}
|
|
92
|
-
|
|
93
90
|
/**
|
|
94
91
|
* Type guard that checks if a value is a valid DID string.
|
|
95
92
|
*
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* ```typescript
|
|
22
|
-
* const error = new
|
|
22
|
+
* const error = new LexValidationError([
|
|
23
23
|
* new IssueInvalidType(['user', 'age'], 'hello', ['number'])
|
|
24
24
|
* ])
|
|
25
25
|
* console.log(error.message)
|
|
@@ -30,8 +30,8 @@ import {
|
|
|
30
30
|
* // { error: 'InvalidRequest', message: '...', issues: [...] }
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
|
-
export class
|
|
34
|
-
name = '
|
|
33
|
+
export class LexValidationError extends LexError<'InvalidRequest'> {
|
|
34
|
+
name = 'LexValidationError'
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* The list of validation issues that caused this error.
|
|
@@ -59,9 +59,9 @@ export class ValidationError extends LexError {
|
|
|
59
59
|
/**
|
|
60
60
|
* Converts the error to a JSON-serializable object.
|
|
61
61
|
*
|
|
62
|
-
* @returns An object containing the error details and
|
|
62
|
+
* @returns An object containing the error details and issues details
|
|
63
63
|
*/
|
|
64
|
-
toJSON() {
|
|
64
|
+
override toJSON() {
|
|
65
65
|
return {
|
|
66
66
|
...super.toJSON(),
|
|
67
67
|
issues: this.issues.map((issue) => issue.toJSON()),
|
|
@@ -81,23 +81,23 @@ export class ValidationError extends LexError {
|
|
|
81
81
|
* ```typescript
|
|
82
82
|
* const failures = schemas.map(s => s.safeValidate(data)).filter(r => !r.success)
|
|
83
83
|
* if (failures.length === schemas.length) {
|
|
84
|
-
* throw
|
|
84
|
+
* throw LexValidationError.fromFailures(failures)
|
|
85
85
|
* }
|
|
86
86
|
* ```
|
|
87
87
|
*/
|
|
88
88
|
static fromFailures(
|
|
89
|
-
failures: ResultFailure<
|
|
90
|
-
):
|
|
89
|
+
failures: readonly ResultFailure<LexValidationError>[],
|
|
90
|
+
): LexValidationError {
|
|
91
91
|
if (failures.length === 1) return failureReason(failures[0])
|
|
92
92
|
const issues = failures.flatMap(extractFailureIssues)
|
|
93
|
-
return new
|
|
93
|
+
return new LexValidationError(issues, {
|
|
94
94
|
// Keep the original errors as the cause chain
|
|
95
95
|
cause: failures.map(failureReason),
|
|
96
96
|
})
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function extractFailureIssues(result: ResultFailure<
|
|
100
|
+
function extractFailureIssues(result: ResultFailure<LexValidationError>) {
|
|
101
101
|
return result.reason.issues
|
|
102
102
|
}
|
|
103
103
|
|