@atproto/lex-schema 0.1.1 → 0.1.3
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 +12 -0
- package/dist/core/record-key.d.ts +10 -0
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/result.d.ts +1 -125
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +1 -128
- package/dist/core/result.js.map +1 -1
- package/dist/core/validation-error.d.ts +0 -18
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +0 -30
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validator.d.ts +8 -15
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +3 -14
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +35 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +33 -0
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +1 -1
- package/dist/schema/blob.d.ts +1 -1
- package/dist/schema/boolean.d.ts +1 -1
- package/dist/schema/bytes.d.ts +1 -1
- package/dist/schema/cid.d.ts +1 -1
- package/dist/schema/custom.d.ts +1 -1
- package/dist/schema/dict.d.ts +1 -1
- package/dist/schema/enum.d.ts +1 -1
- package/dist/schema/integer.d.ts +1 -1
- package/dist/schema/intersection.d.ts +1 -1
- package/dist/schema/lex-map.d.ts +1 -1
- package/dist/schema/lex-value.d.ts +1 -1
- package/dist/schema/literal.d.ts +1 -1
- package/dist/schema/null.d.ts +1 -1
- package/dist/schema/nullable.d.ts +1 -1
- package/dist/schema/object.d.ts +1 -1
- package/dist/schema/optional.d.ts +1 -1
- package/dist/schema/params.d.ts +2 -2
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/record.d.ts +4 -4
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +4 -3
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/regexp.d.ts +1 -1
- package/dist/schema/string.d.ts +1 -1
- package/dist/schema/token.d.ts +2 -1
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +6 -1
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-union.d.ts +1 -1
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +3 -3
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown.d.ts +1 -1
- package/package.json +2 -2
- package/src/core/record-key.ts +20 -1
- package/src/core/result.ts +8 -155
- package/src/core/validation-error.ts +1 -33
- package/src/core/validator.ts +10 -21
- package/src/helpers.test.ts +126 -1
- package/src/helpers.ts +108 -1
- package/src/schema/record.test.ts +9 -30
- package/src/schema/record.ts +9 -16
- package/src/schema/token.ts +9 -1
- package/src/schema/union.ts +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,MAAM,
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexMap } from '@atproto/lex-data'\nimport {\n $Typed,\n $typed,\n InferInput,\n InferOutput,\n LexiconRecordKey,\n NsidString,\n RecordKeyValue,\n Schema,\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'\nimport { withDefault } from './with-default.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> ? RecordKeyValue<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 = LexiconRecordKey,\n const TType extends NsidString = NsidString,\n const TShape extends Validator<LexMap> = Validator<LexMap>,\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<InferOutput<TShape>, '$type'>,\n ): $Typed<InferOutput<TShape>, TType>\n build(\n input: Omit<InferInput<TShape>, '$type'>,\n ): $Typed<InferInput<TShape>, TType>\n build(input: Record<string, unknown>) {\n return $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 RecordKeyValue<Key>\n\nexport type RecordKeySchema<Key extends LexiconRecordKey> = Schema<\n RecordKeyValue<Key>\n>\n\nconst keySchema = string({ format: 'record-key' })\nconst tidSchema = string({ format: 'tid' })\nconst nsidSchema = string({ format: 'nsid' })\nconst selfLiteralSchema = withDefault(literal('self'), '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 RecordKeyValue<Key>\n if (value === 'self') return selfLiteralSchema as any\n return withDefault(literal(value), 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<LexMap>,\n>(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>\nexport function record<\n const K extends LexiconRecordKey,\n const V extends LexMap & { $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<LexMap>,\n>(key: K, type: T, validator: S) {\n return new RecordSchema<K, T, S>(key, type, validator)\n}\n"]}
|
package/dist/schema/regexp.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export declare class RegexpSchema<TValue extends string = string> extends Schema
|
|
|
19
19
|
readonly message?: string | undefined;
|
|
20
20
|
readonly type: "regexp";
|
|
21
21
|
constructor(pattern: RegExp, message?: string | undefined);
|
|
22
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
22
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError | import("../core.js").ValidationSuccess<TValue>;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Creates a regexp schema that validates strings against a pattern.
|
package/dist/schema/string.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export declare class StringSchema<const TOptions extends StringSchemaOptions = S
|
|
|
40
40
|
readonly type: "string";
|
|
41
41
|
readonly options: StringSchemaOptions;
|
|
42
42
|
constructor(options: TOptions);
|
|
43
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
43
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError | import("../core.js").ValidationSuccess<string>;
|
|
44
44
|
}
|
|
45
45
|
export declare function coerceToString(input: unknown): string | null;
|
|
46
46
|
declare function _string(): StringSchema<NonNullable<unknown>>;
|
package/dist/schema/token.d.ts
CHANGED
|
@@ -18,7 +18,8 @@ export declare class TokenSchema<const TValue extends string = string> extends S
|
|
|
18
18
|
readonly value: TValue;
|
|
19
19
|
readonly type: "token";
|
|
20
20
|
constructor(value: TValue);
|
|
21
|
-
|
|
21
|
+
get $token(): TValue;
|
|
22
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError | import("../core.js").ValidationSuccess<TValue>;
|
|
22
23
|
toJSON(): string;
|
|
23
24
|
toString(): string;
|
|
24
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/schema/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEzE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,WAAW,CACtB,KAAK,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CACpC,SAAQ,MAAM,CAAC,MAAM,CAAC;IAGV,QAAQ,CAAC,KAAK,EAAE,MAAM;IAFlC,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAS;gBAEX,KAAK,EAAE,MAAM;IAIlC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/schema/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEzE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,WAAW,CACtB,KAAK,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CACpC,SAAQ,MAAM,CAAC,MAAM,CAAC;IAGV,QAAQ,CAAC,KAAK,EAAE,MAAM;IAFlC,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAS;gBAEX,KAAK,EAAE,MAAM;IAIlC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;IAyBxD,MAAM,IAAI,MAAM;IAIhB,QAAQ,IAAI,MAAM;CAGnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,wBAAgB,KAAK,CACnB,KAAK,CAAC,CAAC,SAAS,UAAU,EAC1B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC/B,IAAI,EAAE,CAAC,EAAE,IAAI,GAAE,CAAe,iDAE/B"}
|
package/dist/schema/token.js
CHANGED
|
@@ -20,13 +20,18 @@ export class TokenSchema extends Schema {
|
|
|
20
20
|
this.value = value;
|
|
21
21
|
this.type = 'token';
|
|
22
22
|
}
|
|
23
|
+
get $token() {
|
|
24
|
+
return this.value;
|
|
25
|
+
}
|
|
23
26
|
validateInContext(input, ctx) {
|
|
24
27
|
if (input === this.value) {
|
|
25
28
|
return ctx.success(this.value);
|
|
26
29
|
}
|
|
27
30
|
// @NOTE: allow using the token instance itself (but convert to the actual
|
|
28
31
|
// token value)
|
|
29
|
-
if (
|
|
32
|
+
if (ctx.options.mode === 'parse' &&
|
|
33
|
+
input instanceof TokenSchema &&
|
|
34
|
+
input.value === this.value) {
|
|
30
35
|
return ctx.success(this.value);
|
|
31
36
|
}
|
|
32
37
|
if (typeof input !== 'string') {
|
package/dist/schema/token.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/schema/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAc,MAAM,EAAqB,MAAM,YAAY,CAAA;AAEzE;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,WAEX,SAAQ,MAAc;IAGtB,YAAqB,KAAa;QAChC,KAAK,EAAE,CAAA;QADY,UAAK,GAAL,KAAK,CAAQ;QAFzB,SAAI,GAAG,OAAgB,CAAA;IAIhC,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;QAED,0EAA0E;QAC1E,eAAe;QACf,IAAI,KAAK,YAAY,WAAW,
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/schema/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAc,MAAM,EAAqB,MAAM,YAAY,CAAA;AAEzE;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,WAEX,SAAQ,MAAc;IAGtB,YAAqB,KAAa;QAChC,KAAK,EAAE,CAAA;QADY,UAAK,GAAL,KAAK,CAAQ;QAFzB,SAAI,GAAG,OAAgB,CAAA;IAIhC,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;QAED,0EAA0E;QAC1E,eAAe;QACf,IACE,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO;YAC5B,KAAK,YAAY,WAAW;YAC5B,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAC1B,CAAC;YACD,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAChD,CAAC;QAED,OAAO,GAAG,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACnD,CAAC;IAED,yEAAyE;IACzE,cAAc;IAEd,MAAM;QACJ,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAwB;AACxB,MAAM,UAAU,KAAK,CAGnB,IAAO,EAAE,OAAU,MAAW;IAC9B,OAAO,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAC3C,CAAC","sourcesContent":["import { $type, NsidString, Schema, ValidationContext } from '../core.js'\n\n/**\n * Schema for Lexicon token values.\n *\n * Tokens are named constants in Lexicon, identified by their NSID and hash.\n * They validate to their string value (e.g., 'app.bsky.feed.defs#requestLess').\n * TokenSchema instances can also be used as values themselves.\n *\n * @template TValue - The token string literal type\n *\n * @example\n * ```ts\n * const schema = new TokenSchema('app.bsky.feed.defs#requestLess')\n * schema.validate('app.bsky.feed.defs#requestLess') // success\n * ```\n */\nexport class TokenSchema<\n const TValue extends string = string,\n> extends Schema<TValue> {\n readonly type = 'token' as const\n\n constructor(readonly value: TValue) {\n super()\n }\n\n get $token(): TValue {\n return this.value\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (input === this.value) {\n return ctx.success(this.value)\n }\n\n // @NOTE: allow using the token instance itself (but convert to the actual\n // token value)\n if (\n ctx.options.mode === 'parse' &&\n input instanceof TokenSchema &&\n input.value === this.value\n ) {\n return ctx.success(this.value)\n }\n\n if (typeof input !== 'string') {\n return ctx.issueUnexpectedType(input, 'token')\n }\n\n return ctx.issueInvalidValue(input, [this.value])\n }\n\n // When using the TokenSchema instance as data, let's serialize it to the\n // token value\n\n toJSON(): string {\n return this.value\n }\n\n toString(): string {\n return this.value\n }\n}\n\n/**\n * Creates a token schema for Lexicon named constants.\n *\n * Tokens are used in Lexicon as named constants or enum-like values.\n * The token instance can be used both as a schema validator and as\n * the token value itself (it serializes to its string value).\n *\n * @param nsid - The NSID part of the token\n * @param hash - The hash part of the token (defaults to 'main')\n * @returns A new {@link TokenSchema} instance\n *\n * @example\n * ```ts\n * // Define tokens\n * const requestLess = l.token('app.bsky.feed.defs', 'requestLess')\n * const requestMore = l.token('app.bsky.feed.defs', 'requestMore')\n *\n * // Use as a value\n * console.log(requestLess.toString()) // 'app.bsky.feed.defs#requestLess'\n *\n * // Use in union for validation\n * const feedbackSchema = l.union([requestLess, requestMore])\n *\n * // Validate\n * feedbackSchema.parse('app.bsky.feed.defs#requestLess') // success\n *\n * // Token instances can be used as values in other schemas\n * const feedbackRequest = l.object({\n * feedback: requestLess, // Accepts the token value\n * })\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function token<\n const N extends NsidString,\n const H extends string = 'main',\n>(nsid: N, hash: H = 'main' as H) {\n return new TokenSchema($type(nsid, hash))\n}\n"]}
|
|
@@ -26,7 +26,7 @@ export declare class TypedUnionSchema<const TValidators extends readonly (TypedR
|
|
|
26
26
|
constructor(validators: TValidators, closed: TClosed);
|
|
27
27
|
get validatorsMap(): Map<unknown, TValidators[number]>;
|
|
28
28
|
get $types(): unknown[];
|
|
29
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
29
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationSuccess<Record<string, unknown>> | import("../core.js").ValidationResult<InferInput<TValidators[number]>>;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Creates a typed union schema for Lexicon unions.
|
|
@@ -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,kBAAkB,EAClB,MAAM,EACN,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
|
@@ -22,14 +22,14 @@ export class UnionSchema extends Schema {
|
|
|
22
22
|
this.type = 'union';
|
|
23
23
|
}
|
|
24
24
|
validateInContext(input, ctx) {
|
|
25
|
-
const
|
|
25
|
+
const issues = [];
|
|
26
26
|
for (const validator of this.validators) {
|
|
27
27
|
const result = ctx.validate(input, validator);
|
|
28
28
|
if (result.success)
|
|
29
29
|
return result;
|
|
30
|
-
|
|
30
|
+
issues.push(...result.issues);
|
|
31
31
|
}
|
|
32
|
-
return
|
|
32
|
+
return new LexValidationError(issues);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
/**
|
package/dist/schema/union.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"union.js","sourceRoot":"","sources":["../../src/schema/union.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"union.js","sourceRoot":"","sources":["../../src/schema/union.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,kBAAkB,EAClB,MAAM,GAGP,MAAM,YAAY,CAAA;AASnB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,WAEX,SAAQ,MAGT;IAGC,YAA+B,UAAuB;QACpD,KAAK,EAAE,CAAA;QADsB,eAAU,GAAV,UAAU,CAAa;QAF7C,SAAI,GAAG,OAAgB,CAAA;IAIhC,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,MAAM,MAAM,GAAY,EAAE,CAAA;QAE1B,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,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;QAC/B,CAAC;QAED,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACvC,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAwB;AACxB,MAAM,UAAU,KAAK,CACnB,UAAuB;IAEvB,OAAO,IAAI,WAAW,CAAc,UAAU,CAAC,CAAA;AACjD,CAAC","sourcesContent":["import {\n InferInput,\n InferOutput,\n Issue,\n LexValidationError,\n Schema,\n ValidationContext,\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 issues: Issue[] = []\n\n for (const validator of this.validators) {\n const result = ctx.validate(input, validator)\n if (result.success) return result\n\n issues.push(...result.issues)\n }\n\n return new LexValidationError(issues)\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/dist/schema/unknown.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { Schema, ValidationContext } from '../core.js';
|
|
|
14
14
|
*/
|
|
15
15
|
export declare class UnknownSchema extends Schema<unknown> {
|
|
16
16
|
readonly type: "unknown";
|
|
17
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
17
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationSuccess<unknown>;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Creates an unknown schema that accepts any value.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex-schema",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22"
|
|
6
6
|
},
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@standard-schema/spec": "^1.1.0",
|
|
38
38
|
"tslib": "^2.8.1",
|
|
39
39
|
"@atproto/syntax": "^0.6.1",
|
|
40
|
-
"@atproto/lex-data": "^0.1.
|
|
40
|
+
"@atproto/lex-data": "^0.1.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"vitest": "^4.0.16"
|
package/src/core/record-key.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isValidRecordKey } from '@atproto/syntax'
|
|
1
|
+
import { NsidString, TidString, isValidRecordKey } from '@atproto/syntax'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The valid record key constraint types in a lexicon definition.
|
|
@@ -64,3 +64,22 @@ export function asLexiconRecordKey(key: unknown): LexiconRecordKey {
|
|
|
64
64
|
if (isLexiconRecordKey(key)) return key
|
|
65
65
|
throw new Error(`Invalid record key: ${String(key)}`)
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Maps a lexicon record key definition to its corresponding string subtype.
|
|
70
|
+
*
|
|
71
|
+
* - `'any'` maps to `string`
|
|
72
|
+
* - `'nsid'` maps to `NsidString`
|
|
73
|
+
* - `'tid'` maps to `TidString`
|
|
74
|
+
* - `'literal:...'` maps to the literal string value
|
|
75
|
+
*/
|
|
76
|
+
export type RecordKeyValue<Key extends LexiconRecordKey = LexiconRecordKey> =
|
|
77
|
+
Key extends 'any'
|
|
78
|
+
? string
|
|
79
|
+
: Key extends 'tid'
|
|
80
|
+
? TidString
|
|
81
|
+
: Key extends 'nsid'
|
|
82
|
+
? NsidString
|
|
83
|
+
: Key extends `literal:${infer L extends string}`
|
|
84
|
+
? L
|
|
85
|
+
: never
|
package/src/core/result.ts
CHANGED
|
@@ -1,162 +1,15 @@
|
|
|
1
|
-
export type ResultSuccess<V = any> = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* @typeParam E - The type of the error reason
|
|
7
|
-
*/
|
|
8
|
-
export type ResultFailure<E = Error> = { success: false; reason: E }
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A discriminated union type representing either a success or failure outcome.
|
|
12
|
-
*
|
|
13
|
-
* Check the `success` property to determine the outcome and access the
|
|
14
|
-
* appropriate property (`value` for success, `reason` for failure).
|
|
15
|
-
*
|
|
16
|
-
* @typeParam V - The type of the success value
|
|
17
|
-
* @typeParam E - The type of the error reason
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* function parseJson(text: string): Result<unknown, SyntaxError> {
|
|
22
|
-
* try {
|
|
23
|
-
* return success(JSON.parse(text))
|
|
24
|
-
* } catch (e) {
|
|
25
|
-
* return failure(e as SyntaxError)
|
|
26
|
-
* }
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export type Result<V = any, E = Error> = ResultSuccess<V> | ResultFailure<E>
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Creates a successful result wrapping the given value.
|
|
34
|
-
*
|
|
35
|
-
* @typeParam V - The type of the value
|
|
36
|
-
* @param value - The success value to wrap
|
|
37
|
-
* @returns {ResultSuccess} A success result containing the value
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```typescript
|
|
41
|
-
* const result = success(42)
|
|
42
|
-
* console.log(result.success) // true
|
|
43
|
-
* console.log(result.value) // 42
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
47
|
-
export function success<V>(value: V): ResultSuccess<V> {
|
|
48
|
-
return { success: true, value }
|
|
1
|
+
export type ResultSuccess<V = any> = {
|
|
2
|
+
success: true
|
|
3
|
+
value: V
|
|
4
|
+
reason?: undefined
|
|
49
5
|
}
|
|
50
6
|
|
|
51
7
|
/**
|
|
52
|
-
*
|
|
8
|
+
* Represents a failed result containing an error reason.
|
|
53
9
|
*
|
|
54
10
|
* @typeParam E - The type of the error reason
|
|
55
|
-
* @param reason - The error reason to wrap
|
|
56
|
-
* @returns {ResultFailure} A failure result containing the error
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* const result = failure(new Error('Something went wrong'))
|
|
61
|
-
* console.log(result.success) // false
|
|
62
|
-
* console.log(result.reason.message) // "Something went wrong"
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
66
|
-
export function failure<E>(reason: E): ResultFailure<E> {
|
|
67
|
-
return { success: false, reason }
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Extracts the error reason from a failure result.
|
|
72
|
-
*
|
|
73
|
-
* @typeParam T - The type of the error reason
|
|
74
|
-
* @param result - A failure result
|
|
75
|
-
* @returns {T} The error reason
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```typescript
|
|
79
|
-
* const result = failure(new Error('oops'))
|
|
80
|
-
* const error = failureReason(result)
|
|
81
|
-
* console.log(error.message) // "oops"
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
85
|
-
export function failureReason<T>(result: ResultFailure<T>): T {
|
|
86
|
-
return result.reason
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Extracts the value from a success result.
|
|
91
|
-
*
|
|
92
|
-
* @typeParam T - The type of the success value
|
|
93
|
-
* @param result - A success result
|
|
94
|
-
* @returns {T} The success value
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```typescript
|
|
98
|
-
* const result = success(42)
|
|
99
|
-
* const value = successValue(result)
|
|
100
|
-
* console.log(value) // 42
|
|
101
|
-
* ```
|
|
102
|
-
*/
|
|
103
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
104
|
-
export function successValue<T>(result: ResultSuccess<T>): T {
|
|
105
|
-
return result.value
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Catches any error and wraps it in a {@link ResultFailure<Error>}.
|
|
110
|
-
*
|
|
111
|
-
* @param err - The error to catch.
|
|
112
|
-
* @returns {ResultFailure} A failure result containing the error.
|
|
113
|
-
* @example
|
|
114
|
-
*
|
|
115
|
-
* ```ts
|
|
116
|
-
* declare function someFunction(): Promise<string>
|
|
117
|
-
*
|
|
118
|
-
* const result = await someFunction().then(success, catchall)
|
|
119
|
-
* if (result.success) {
|
|
120
|
-
* console.log(result.value) // string
|
|
121
|
-
* } else {
|
|
122
|
-
* console.error(result.reason instanceof Error) // true
|
|
123
|
-
* console.error(result.reason.message) // string
|
|
124
|
-
* }
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
128
|
-
export function catchall(err: unknown): ResultFailure<Error> {
|
|
129
|
-
if (err instanceof Error) return failure(err)
|
|
130
|
-
return failure(new Error('Unknown error', { cause: err }))
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Creates a catcher function for the given constructor that wraps caught errors
|
|
135
|
-
* in a {@link ResultFailure}.
|
|
136
|
-
*
|
|
137
|
-
* @example
|
|
138
|
-
*
|
|
139
|
-
* ```ts
|
|
140
|
-
* class FooError extends Error {}
|
|
141
|
-
* class BarError extends Error {}
|
|
142
|
-
*
|
|
143
|
-
* declare function someFunction(): Promise<string>
|
|
144
|
-
*
|
|
145
|
-
* const result = await someFunction()
|
|
146
|
-
* .then(success)
|
|
147
|
-
* .catch(createCatcher(FooError))
|
|
148
|
-
* .catch(createCatcher(BarError))
|
|
149
|
-
*
|
|
150
|
-
* if (result.success) {
|
|
151
|
-
* console.log(result.value) // string
|
|
152
|
-
* } else {
|
|
153
|
-
* console.error(result.reason) // FooError | BarError
|
|
154
|
-
* }
|
|
155
11
|
*/
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (err instanceof Ctor) return failure(err)
|
|
160
|
-
throw err
|
|
161
|
-
}
|
|
12
|
+
export type ResultFailure<E = Error> = {
|
|
13
|
+
success: false
|
|
14
|
+
reason: E
|
|
162
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LexError } from '@atproto/lex-data'
|
|
2
2
|
import { arrayAgg } from '../util/array-agg.js'
|
|
3
|
-
import { ResultFailure
|
|
3
|
+
import { ResultFailure } from './result.js'
|
|
4
4
|
import {
|
|
5
5
|
Issue,
|
|
6
6
|
IssueInvalidType,
|
|
@@ -82,38 +82,6 @@ export class LexValidationError
|
|
|
82
82
|
issues: this.issues.map((issue) => issue.toJSON()),
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Creates a validation error by combining multiple validation failures.
|
|
88
|
-
*
|
|
89
|
-
* This is useful when validating against multiple possible schemas (e.g., unions)
|
|
90
|
-
* and all branches fail. The resulting error contains issues from all failures.
|
|
91
|
-
*
|
|
92
|
-
* @param failures - The validation failures to combine
|
|
93
|
-
* @returns A single validation error containing all issues from the failures
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* ```typescript
|
|
97
|
-
* const failures = schemas.map(s => s.safeValidate(data)).filter(r => !r.success)
|
|
98
|
-
* if (failures.length === schemas.length) {
|
|
99
|
-
* throw LexValidationError.fromFailures(failures)
|
|
100
|
-
* }
|
|
101
|
-
* ```
|
|
102
|
-
*/
|
|
103
|
-
static fromFailures(
|
|
104
|
-
failures: readonly ResultFailure<LexValidationError>[],
|
|
105
|
-
): LexValidationError {
|
|
106
|
-
if (failures.length === 1) return failureReason(failures[0])
|
|
107
|
-
const issues = failures.flatMap(extractFailureIssues)
|
|
108
|
-
return new LexValidationError(issues, {
|
|
109
|
-
// Keep the original errors as the cause chain
|
|
110
|
-
cause: failures.map(failureReason),
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function extractFailureIssues(result: ResultFailure<LexValidationError>) {
|
|
116
|
-
return result.reason.issues
|
|
117
85
|
}
|
|
118
86
|
|
|
119
87
|
function aggregateIssues(issues: Issue[]): Issue[] {
|
package/src/core/validator.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { ResultFailure, ResultSuccess, success } from './result.js'
|
|
1
|
+
import type * as Result from './result.js'
|
|
3
2
|
import { LexValidationError } from './validation-error.js'
|
|
4
3
|
import {
|
|
5
4
|
Issue,
|
|
@@ -15,16 +14,16 @@ import {
|
|
|
15
14
|
/**
|
|
16
15
|
* Represents a successful validation result.
|
|
17
16
|
*
|
|
18
|
-
* @typeParam
|
|
17
|
+
* @typeParam TValue - The type of the validated value
|
|
18
|
+
* @extends Result.ResultSuccess<TValue>
|
|
19
19
|
*/
|
|
20
|
-
export type ValidationSuccess<
|
|
20
|
+
export type ValidationSuccess<TValue = unknown> = Result.ResultSuccess<TValue>
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Represents a failed validation result containing a {@link LexValidationError}.
|
|
24
24
|
*
|
|
25
|
-
* @extends ResultFailure<LexValidationError>
|
|
26
|
-
* @see {@link ResultFailure}
|
|
27
25
|
* @see {@link LexValidationError}
|
|
26
|
+
* @extends Result.ResultFailure<LexValidationError>
|
|
28
27
|
*/
|
|
29
28
|
export type ValidationFailure = LexValidationError
|
|
30
29
|
|
|
@@ -429,24 +428,14 @@ export class ValidationContext {
|
|
|
429
428
|
}
|
|
430
429
|
|
|
431
430
|
/**
|
|
432
|
-
*
|
|
431
|
+
* Helper method to create a successful validation result.
|
|
433
432
|
*
|
|
434
433
|
* @typeParam V - The value type
|
|
435
434
|
* @param value - The validated value
|
|
436
435
|
* @returns A successful validation result
|
|
437
436
|
*/
|
|
438
|
-
success<V>(value: V):
|
|
439
|
-
return success
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Creates a failed validation result with the given error.
|
|
444
|
-
*
|
|
445
|
-
* @param reason - The validation error
|
|
446
|
-
* @returns A failed validation result
|
|
447
|
-
*/
|
|
448
|
-
failure(reason: LexValidationError): ValidationFailure {
|
|
449
|
-
return reason
|
|
437
|
+
success<V>(value: V): ValidationSuccess<V> {
|
|
438
|
+
return { success: true, value }
|
|
450
439
|
}
|
|
451
440
|
|
|
452
441
|
/**
|
|
@@ -457,8 +446,8 @@ export class ValidationContext {
|
|
|
457
446
|
* @param issue - The validation issue that caused the failure
|
|
458
447
|
* @returns A failed validation result
|
|
459
448
|
*/
|
|
460
|
-
issue(issue: Issue) {
|
|
461
|
-
return
|
|
449
|
+
issue(issue: Issue): ValidationFailure {
|
|
450
|
+
return new LexValidationError([...this.issues, issue])
|
|
462
451
|
}
|
|
463
452
|
|
|
464
453
|
/**
|