@atproto/lex-schema 0.0.14 → 0.0.15
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 +21 -0
- package/dist/core/schema.d.ts +4 -3
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +6 -1
- package/dist/core/schema.js.map +1 -1
- package/dist/core/standard-schema.d.ts +14 -0
- package/dist/core/standard-schema.d.ts.map +1 -0
- package/dist/core/standard-schema.js +27 -0
- package/dist/core/standard-schema.js.map +1 -0
- package/dist/core/string-format.d.ts +4 -15
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +17 -14
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/validation-error.d.ts +10 -2
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +10 -0
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validation-issue.d.ts +15 -15
- package/dist/core/validation-issue.d.ts.map +1 -1
- package/dist/core/validation-issue.js +33 -29
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +16 -13
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +3 -2
- package/dist/core/validator.js.map +1 -1
- package/dist/core.d.ts +0 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +0 -1
- package/dist/core.js.map +1 -1
- package/dist/schema/custom.d.ts +1 -1
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/never.d.ts +1 -1
- package/dist/schema/nullable.d.ts +1 -1
- package/dist/schema/record.d.ts +1 -1
- package/dist/schema/ref.d.ts +1 -1
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +1 -1
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +1 -1
- package/dist/schema/typed-union.d.ts +1 -1
- package/dist/schema/union.d.ts +2 -2
- package/dist/schema/union.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/core/schema.ts +11 -10
- package/src/core/standard-schema.test.ts +124 -0
- package/src/core/standard-schema.ts +31 -0
- package/src/core/string-format.ts +14 -18
- package/src/core/validation-error.ts +16 -1
- package/src/core/validation-issue.ts +32 -32
- package/src/core/validator.ts +9 -5
- package/src/core.ts +0 -1
- package/src/schema/array.test.ts +2 -2
- package/src/schema/custom.ts +1 -7
- package/src/schema/params.test.ts +2 -2
- package/src/schema/ref.ts +1 -5
- package/src/schema/refine.ts +0 -1
- package/dist/core/property-key.d.ts +0 -2
- package/dist/core/property-key.d.ts.map +0 -1
- package/dist/core/property-key.js +0 -3
- package/dist/core/property-key.js.map +0 -1
- package/src/core/property-key.ts +0 -1
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,qBAAqB,CAAA"}
|
package/dist/core.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
tslib_1.__exportStar(require("./core/$type.js"), exports);
|
|
5
|
-
tslib_1.__exportStar(require("./core/property-key.js"), exports);
|
|
6
5
|
tslib_1.__exportStar(require("./core/record-key.js"), exports);
|
|
7
6
|
tslib_1.__exportStar(require("./core/result.js"), exports);
|
|
8
7
|
tslib_1.__exportStar(require("./core/schema.js"), exports);
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;;AAAA,0DAA+B;AAC/B
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;;AAAA,0DAA+B;AAC/B,+DAAoC;AACpC,2DAAgC;AAChC,2DAAgC;AAChC,kEAAuC;AACvC,0DAA+B;AAC/B,qEAA0C;AAC1C,qEAA0C;AAC1C,8DAAmC","sourcesContent":["export * from './core/$type.js'\nexport * from './core/record-key.js'\nexport * from './core/result.js'\nexport * from './core/schema.js'\nexport * from './core/string-format.js'\nexport * from './core/types.js'\nexport * from './core/validation-error.js'\nexport * from './core/validation-issue.js'\nexport * from './core/validator.js'\n"]}
|
package/dist/schema/custom.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/schema/custom.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/schema/custom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAe,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAC7B,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,MAAM,IAAI,CACpC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,sBAAsB,KACxB,KAAK,IAAI,MAAM,CAAA;AAEpB;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAY,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAE,SAAQ,MAAM,CAAC,MAAM,CAAC;IAIlE,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IALxB,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;gBAGd,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC,EAClC,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,GAAE,WAAW,GAAG,SAAS,WAAW,EAAE,aAAA;IAK9D,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAOzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,wBAAgB,MAAM,CAAC,MAAM,EAC3B,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC,EAClC,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,wBAG5C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/schema/custom.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/schema/custom.ts"],"names":[],"mappings":";;;AAmGA,wBAMC;AAzGD,wCAA0E;AAwB1E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,YAAmC,SAAQ,gBAAc;IAIjD;IACA;IACA;IALV,IAAI,GAAG,QAAiB,CAAA;IAEjC,YACmB,SAAkC,EAClC,OAAe,EACf,IAA2C;QAE5D,KAAK,EAAE,CAAA;QAJU,cAAS,GAAT,SAAS,CAAyB;QAClC,YAAO,GAAP,OAAO,CAAQ;QACf,SAAI,GAAJ,IAAI,CAAuC;IAG9D,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACtC,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,qBAAW,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;QAC9D,CAAC;QACD,OAAO,GAAG,CAAC,OAAO,CAAC,KAAe,CAAC,CAAA;IACrC,CAAC;CACF;AAlBD,oCAkBC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAwB;AACxB,SAAgB,MAAM,CACpB,SAAkC,EAClC,OAAe,EACf,IAA2C;IAE3C,OAAO,IAAI,YAAY,CAAS,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;AAC3D,CAAC","sourcesContent":["import { Issue, IssueCustom, Schema, ValidationContext } from '../core.js'\n\n/**\n * Context object provided to custom assertion functions.\n *\n * @property path - Current validation path as an array of property keys\n * @property addIssue - Function to add additional validation issues\n */\nexport type CustomAssertionContext = {\n path: PropertyKey[]\n addIssue(issue: Issue): void\n}\n\n/**\n * Type guard function for custom schema validation.\n *\n * @template TValue - The type to validate/narrow to\n */\nexport type CustomAssertion<TValue> = (\n this: null,\n input: unknown,\n ctx: CustomAssertionContext,\n) => input is TValue\n\n/**\n * Schema with a custom validation function.\n *\n * Allows defining completely custom validation logic using a type guard\n * assertion function. The function receives the input and validation context,\n * and must return whether the input is valid.\n *\n * @template TValue - The validated output type\n *\n * @example\n * ```ts\n * const schema = new CustomSchema(\n * (input): input is Date => input instanceof Date,\n * 'Expected a Date instance'\n * )\n * ```\n */\nexport class CustomSchema<out TValue = unknown> extends Schema<TValue> {\n readonly type = 'custom' as const\n\n constructor(\n private readonly assertion: CustomAssertion<TValue>,\n private readonly message: string,\n private readonly path?: PropertyKey | readonly PropertyKey[],\n ) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (!this.assertion.call(null, input, ctx)) {\n const path = ctx.concatPath(this.path)\n return ctx.issue(new IssueCustom(path, input, this.message))\n }\n return ctx.success(input as TValue)\n }\n}\n\n/**\n * Creates a custom schema with a user-defined validation function.\n *\n * Use this when the built-in schemas don't cover your validation needs.\n * The assertion function must be a type guard that narrows the input type.\n *\n * @param assertion - Type guard function that validates the input\n * @param message - Error message when validation fails\n * @param path - Optional path to associate with validation errors\n * @returns A new {@link CustomSchema} instance\n *\n * @example\n * ```ts\n * // Validate Date instances\n * const dateSchema = l.custom(\n * (input): input is Date => input instanceof Date && !isNaN(input.getTime()),\n * 'Expected a valid Date'\n * )\n *\n * // Validate specific object shape\n * const pointSchema = l.custom(\n * (input): input is { x: number; y: number } =>\n * typeof input === 'object' &&\n * input !== null &&\n * typeof (input as any).x === 'number' &&\n * typeof (input as any).y === 'number',\n * 'Expected a point with x and y coordinates'\n * )\n *\n * // With custom path\n * const validConfig = l.custom(\n * (input): input is Config => validateConfig(input),\n * 'Invalid configuration',\n * ['config']\n * )\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function custom<TValue>(\n assertion: CustomAssertion<TValue>,\n message: string,\n path?: PropertyKey | readonly PropertyKey[],\n) {\n return new CustomSchema<TValue>(assertion, message, path)\n}\n"]}
|
package/dist/schema/never.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { Schema, ValidationContext } from '../core.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export declare class NeverSchema extends Schema<never> {
|
|
15
15
|
readonly type: "never";
|
|
16
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
16
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Creates a never schema that always fails validation.
|
|
@@ -18,7 +18,7 @@ export declare class NullableSchema<const TValidator extends Validator> extends
|
|
|
18
18
|
readonly validator: TValidator;
|
|
19
19
|
readonly type: "nullable";
|
|
20
20
|
constructor(validator: TValidator);
|
|
21
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
21
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError | import("../core.js").ValidationSuccess<null> | import("../core.js").ValidationSuccess<InferInput<TValidator>>;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Creates a nullable schema that accepts null in addition to the wrapped type.
|
package/dist/schema/record.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export declare class RecordSchema<const TKey extends LexiconRecordKey = any, con
|
|
|
41
41
|
readonly type: "record";
|
|
42
42
|
keySchema: RecordKeySchema<TKey>;
|
|
43
43
|
constructor(key: TKey, $type: TType, schema: TShape);
|
|
44
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
44
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError | import("../core.js").ValidationSuccess<InferInput<TShape>>;
|
|
45
45
|
build(input: Omit<InferInput<this>, '$type'>): $Typed<InferOutput<this>, TType>;
|
|
46
46
|
isTypeOf<TValue extends {
|
|
47
47
|
$type?: unknown;
|
package/dist/schema/ref.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type RefSchemaGetter<out TValidator extends Validator> = () => TValidator
|
|
|
22
22
|
* })
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
|
-
export declare class RefSchema<const TValidator extends Validator> extends Schema<InferInput<TValidator>, InferOutput<TValidator
|
|
25
|
+
export declare class RefSchema<const TValidator extends Validator> extends Schema<InferInput<TValidator>, InferOutput<TValidator>> implements WrappedValidator<TValidator> {
|
|
26
26
|
#private;
|
|
27
27
|
readonly type: "ref";
|
|
28
28
|
constructor(getter: RefSchemaGetter<TValidator>);
|
package/dist/schema/ref.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,EACX,MAAM,EACN,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAA;AAEnB;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,GAAG,CAAC,UAAU,SAAS,SAAS,IAAI,MAAM,UAAU,CAAA;AAEhF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAS,CAAC,KAAK,CAAC,UAAU,SAAS,SAAS,CACvD,SAAQ,MAAM,
|
|
1
|
+
{"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,EACX,MAAM,EACN,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAA;AAEnB;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,GAAG,CAAC,UAAU,SAAS,SAAS,IAAI,MAAM,UAAU,CAAA;AAEhF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAS,CAAC,KAAK,CAAC,UAAU,SAAS,SAAS,CACvD,SAAQ,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAC9D,YAAW,gBAAgB,CAAC,UAAU,CAAC;;IAEvC,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAS;gBAIlB,MAAM,EAAE,eAAe,CAAC,UAAU,CAAC;IAS/C,IAAI,SAAS,IAAI,UAAU,CAE1B;IAED,MAAM,IAAI,UAAU;IAIpB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAGzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,wBAAgB,GAAG,CAAC,KAAK,CAAC,UAAU,SAAS,SAAS,EACpD,GAAG,EAAE,eAAe,CAAC,UAAU,CAAC,GAC/B,SAAS,CAAC,UAAU,CAAC,CAAA;AACxB,wBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,SAAS,MAAM,GAAG,MAAM,EACzD,GAAG,EAAE,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC/C,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA"}
|
package/dist/schema/ref.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":";;;AAkGA,kBAIC;AAtGD,wCAOmB;AASnB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,SACX,SAAQ,gBAAuD;IAGtD,IAAI,GAAG,KAAc,CAAA;IAE9B,OAAO,CAA6B;IAEpC,YAAY,MAAmC;QAC7C,uEAAuE;QACvE,sEAAsE;QAEtE,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;IACvB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IAC5C,CAAC;CACF;AA5BD,8BA4BC;AAqCD,SAAgB,GAAG,CACjB,GAAgC;IAEhC,OAAO,IAAI,SAAS,CAAa,GAAG,CAAC,CAAA;AACvC,CAAC","sourcesContent":["import {\n InferInput,\n InferOutput,\n Schema,\n ValidationContext,\n Validator,\n WrappedValidator,\n} from '../core.js'\n\n/**\n * Function type that returns a validator, used for lazy schema resolution.\n *\n * @template TValidator - The validator type that will be returned\n */\nexport type RefSchemaGetter<out TValidator extends Validator> = () => TValidator\n\n/**\n * Schema for creating references to other schemas with lazy resolution.\n *\n * Useful for handling circular references or breaking module dependency cycles.\n * The referenced schema is resolved lazily when first needed for validation.\n *\n * @template TValidator - The referenced validator type\n *\n * @example\n * ```ts\n * // Self-referential schema for tree structure\n * const nodeSchema = l.object({\n * value: l.string(),\n * children: l.array(l.ref(() => nodeSchema)),\n * })\n * ```\n */\nexport class RefSchema<const TValidator extends Validator>\n extends Schema<InferInput<TValidator>, InferOutput<TValidator>>\n implements WrappedValidator<TValidator>\n{\n readonly type = 'ref' as const\n\n #getter: RefSchemaGetter<TValidator>\n\n constructor(getter: RefSchemaGetter<TValidator>) {\n // @NOTE In order to avoid circular dependency issues, we don't resolve\n // the schema here. Instead, we resolve it lazily when first accessed.\n\n super()\n\n this.#getter = getter\n }\n\n get validator(): TValidator {\n return this.#getter.call(null)\n }\n\n unwrap(): TValidator {\n return this.validator\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n return ctx.validate(input, this.validator)\n }\n}\n\n/**\n * Creates a reference schema with lazy resolution.\n *\n * Allows referencing schemas that may not be defined yet, enabling\n * circular references and breaking dependency cycles. The getter function\n * is called lazily when validation is first performed.\n *\n * @param get - Function that returns the referenced validator\n * @returns A new {@link RefSchema} instance\n *\n * @example\n * ```ts\n * // Circular reference - tree node that contains children of the same type\n * const treeNodeSchema = l.object({\n * name: l.string(),\n * children: l.optional(l.array(l.ref(() => treeNodeSchema))),\n * })\n *\n * // Cross-module reference\n * const commentSchema = l.object({\n * text: l.string(),\n * author: l.ref(() => userSchema), // userSchema defined elsewhere\n * })\n *\n * // Explicitly typed reference\n * const itemSchema = l.ref<Item>(() => complexItemSchema)\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n): RefSchema<TValidator>\nexport function ref<TInput, TOutput extends TInput = TInput>(\n get: RefSchemaGetter<Validator<TInput, TOutput>>,\n): RefSchema<Validator<TInput, TOutput>>\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n) {\n return new RefSchema<TValidator>(get)\n}\n"]}
|
package/dist/schema/refine.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refine.d.ts","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,
|
|
1
|
+
{"version":3,"file":"refine.d.ts","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAIV,SAAS,EACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAEpD;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAA;IACzD,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;CAC5C,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI;IAClD,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,sBAAsB,KAAK,KAAK,IAAI,GAAG,CAAA;IAC1E,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;CAC5C,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAC3B,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,CAAC,GAC9B,CAAC,GACD,CAAC,SAAS,mBAAmB,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GACzC,CAAC,GACD,KAAK,CAAA;AAEb;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,IAC7C,eAAe,CAAC,CAAC,CAAC,GAClB,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,MAAM,CACpB,KAAK,CAAC,UAAU,SAAS,SAAS,EAClC,MAAM,SAAS,UAAU,CAAC,UAAU,CAAC,EAErC,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,mBAAmB,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,GAC9D,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;AACjC,wBAAgB,MAAM,CAAC,KAAK,CAAC,UAAU,SAAS,SAAS,EACvD,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,GAClD,UAAU,CAAA;AACb,wBAAgB,MAAM,CACpB,WAAW,SAAS,UAAU,EAC9B,KAAK,CAAC,UAAU,SAAS,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,EAChE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,GAAG,UAAU,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refine.js","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"refine.js","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":";;AAkHA,wBAiBC;AAnID,wCAMmB;AA2GnB,wBAAwB;AACxB,SAAgB,MAAM,CACpB,MAAkB,EAClB,UAA+B;IAE/B,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,6BAA6B;IAC7B,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;QAC3B,iBAAiB,EAAE;YACjB,8DAA8D;YAC9D,KAAK,EAAE,wBAAwB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC5D,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;SACnB;KACF,CAAC,CAAA;AACJ,CAAC;AAED,wBAAwB;AACxB,SAAS,wBAAwB,CAK/B,KAAc,EACd,GAAsB;IAEtB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAA;IAElC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IACvE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACjD,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,qBAAW,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import {\n InferInput,\n IssueCustom,\n ValidationContext,\n ValidationResult,\n Validator,\n} from '../core.js'\nimport { CustomAssertionContext } from './custom.js'\n\n/**\n * Configuration for a refinement check that validates a condition.\n *\n * @template T - The type being validated\n * @property check - Function that returns true if the value passes the check\n * @property message - Error message when the check fails\n * @property path - Optional path to associate with the error\n */\nexport type RefinementCheck<T> = {\n check: (value: T, ctx: CustomAssertionContext) => boolean\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\n/**\n * Configuration for a refinement assertion that narrows the type.\n *\n * @template T - The input type being validated\n * @template Out - The narrowed output type\n * @property check - Type guard function that narrows the type\n * @property message - Error message when the assertion fails\n * @property path - Optional path to associate with the error\n */\nexport type RefinementAssertion<T, Out extends T> = {\n check: (this: null, value: T, ctx: CustomAssertionContext) => value is Out\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\n/**\n * Infers the input type from a refinement configuration.\n *\n * @template R - The refinement type\n */\nexport type InferRefinement<R> =\n R extends RefinementCheck<infer T>\n ? T\n : R extends RefinementAssertion<infer T, any>\n ? T\n : never\n\n/**\n * Union type of refinement check or assertion.\n *\n * @template T - The input type being validated\n * @template Out - The output type (same as T for checks, narrowed for assertions)\n */\nexport type Refinement<T = any, Out extends T = T> =\n | RefinementCheck<T>\n | RefinementAssertion<T, Out>\n\n/**\n * Creates a refined schema by adding additional validation constraints.\n *\n * Wraps an existing schema with an additional check function. The base schema\n * is validated first, then the refinement check is applied to the result.\n *\n * @param schema - The base schema to refine\n * @param refinement - The refinement check or assertion to apply\n * @returns A new schema that includes the refinement\n *\n * @example\n * ```ts\n * // Simple check refinement\n * const positiveInt = l.refine(l.integer(), {\n * check: (value) => value > 0,\n * message: 'Value must be positive',\n * })\n *\n * positiveInt.parse(5) // 5\n * positiveInt.parse(-1) // throws\n *\n * // Type-narrowing assertion\n * const nonEmptyString = l.refine(l.string(), {\n * check: (value): value is string & { length: number } => value.length > 0,\n * message: 'String must not be empty',\n * })\n *\n * // With custom path for nested errors\n * const validDateRange = l.refine(\n * l.object({ start: l.string(), end: l.string() }),\n * {\n * check: (v) => new Date(v.start) < new Date(v.end),\n * message: 'Start date must be before end date',\n * path: ['end'],\n * }\n * )\n * ```\n */\nexport function refine<\n const TValidator extends Validator,\n TInput extends InferInput<TValidator>,\n>(\n schema: TValidator,\n refinement: RefinementAssertion<InferInput<TValidator>, TInput>,\n): TValidator & Validator<TInput>\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: RefinementCheck<InferInput<TValidator>>,\n): TValidator\nexport function refine<\n TRefinement extends Refinement,\n const TValidator extends Validator<InferRefinement<TRefinement>>,\n>(schema: TValidator, refinement: TRefinement): TValidator\n/*@__NO_SIDE_EFFECTS__*/\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: Refinement<unknown>,\n): TValidator {\n // This is basically the same as monkey patching the \"validateInContext\"\n // method to the schema, but done in a way that does not mutate the original\n // schema. This is safe to do because Validators don't update their internal\n // state over their lifetime.\n return Object.create(schema, {\n validateInContext: {\n // We do not use an arrow function to avoid creating a closure\n value: validateInContextUnbound.bind({ schema, refinement }),\n enumerable: false,\n writable: false,\n configurable: true,\n },\n })\n}\n\n/*@__NO_SIDE_EFFECTS__*/\nfunction validateInContextUnbound<S extends Validator>(\n this: {\n schema: S\n refinement: Refinement<InferInput<S>>\n },\n input: unknown,\n ctx: ValidationContext,\n): ValidationResult<InferInput<S>> {\n const result = ctx.validate(input, this.schema)\n if (!result.success) return result\n\n const checkResult = this.refinement.check.call(null, result.value, ctx)\n if (!checkResult) {\n const path = ctx.concatPath(this.refinement.path)\n return ctx.issue(new IssueCustom(path, input, this.refinement.message))\n }\n\n return result\n}\n"]}
|
|
@@ -41,7 +41,7 @@ export declare class TypedRefSchema<const TValidator extends TypedObjectValidato
|
|
|
41
41
|
constructor(getter: TypedRefGetter<TValidator>);
|
|
42
42
|
get validator(): TValidator;
|
|
43
43
|
get $type(): TValidator['$type'];
|
|
44
|
-
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").
|
|
44
|
+
validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError | import("../core.js").ValidationSuccess<InferInput<TValidator>>;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Creates a reference to a typed object schema for use in typed unions.
|
|
@@ -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").LexValidationError | import("../core.js").ValidationSuccess<Record<string, unknown>> | import("../core.js").ValidationSuccess<InferInput<TValidators[number]>>;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Creates a typed union schema for Lexicon unions.
|
package/dist/schema/union.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InferInput, InferOutput, Schema, ValidationContext,
|
|
1
|
+
import { InferInput, InferOutput, LexValidationError, Schema, ValidationContext, Validator } from '../core.js';
|
|
2
2
|
/**
|
|
3
3
|
* Type representing a non-empty tuple of validators for union schemas.
|
|
4
4
|
*
|
|
@@ -25,7 +25,7 @@ export declare class UnionSchema<const TValidators extends UnionSchemaValidators
|
|
|
25
25
|
protected readonly validators: TValidators;
|
|
26
26
|
readonly type: "union";
|
|
27
27
|
constructor(validators: TValidators);
|
|
28
|
-
validateInContext(input: unknown, ctx: ValidationContext):
|
|
28
|
+
validateInContext(input: unknown, ctx: ValidationContext): LexValidationError | import("../core.js").ValidationSuccess<unknown>;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* Creates a union schema that accepts values matching any of the provided schemas.
|
|
@@ -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,EACX,kBAAkB,EAClB,MAAM,EACN,iBAAiB,EAEjB,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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex-schema",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Lexicon schema system for AT Lexicons",
|
|
6
6
|
"keywords": [
|
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"@standard-schema/spec": "^1.1.0",
|
|
38
39
|
"tslib": "^2.8.1",
|
|
39
|
-
"@atproto/syntax": "^0.5.
|
|
40
|
+
"@atproto/syntax": "^0.5.1",
|
|
40
41
|
"@atproto/lex-data": "^0.0.13"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
package/src/core/schema.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from '@standard-schema/spec'
|
|
1
2
|
import { lazyProperty } from '../util/lazy-property.js'
|
|
3
|
+
import { StandardSchemaAdapter } from './standard-schema.js'
|
|
2
4
|
import {
|
|
3
5
|
InferInput,
|
|
4
6
|
InferOutput,
|
|
@@ -52,7 +54,6 @@ export interface SchemaInternals<out TInput = unknown, out TOutput = TInput> {
|
|
|
52
54
|
*
|
|
53
55
|
* @typeParam TInput - The type accepted as valid input during validation
|
|
54
56
|
* @typeParam TOutput - The type returned after parsing (may include transformations)
|
|
55
|
-
* @typeParam TInternals - Internal type structure for type inference
|
|
56
57
|
*
|
|
57
58
|
* @example
|
|
58
59
|
* ```typescript
|
|
@@ -72,14 +73,8 @@ export interface SchemaInternals<out TInput = unknown, out TOutput = TInput> {
|
|
|
72
73
|
* schema.matches(123) // false
|
|
73
74
|
* ```
|
|
74
75
|
*/
|
|
75
|
-
export abstract class Schema<
|
|
76
|
-
|
|
77
|
-
out TOutput = TInput,
|
|
78
|
-
out TInternals extends SchemaInternals<TInput, TOutput> = SchemaInternals<
|
|
79
|
-
TInput,
|
|
80
|
-
TOutput
|
|
81
|
-
>,
|
|
82
|
-
> implements Validator<TInternals['input'], TInternals['output']>
|
|
76
|
+
export abstract class Schema<out TInput = unknown, out TOutput = TInput>
|
|
77
|
+
implements Validator<TInput, TOutput>, StandardSchemaV1<TInput, TOutput>
|
|
83
78
|
{
|
|
84
79
|
/**
|
|
85
80
|
* Internal phantom property for type inference.
|
|
@@ -87,7 +82,13 @@ export abstract class Schema<
|
|
|
87
82
|
*
|
|
88
83
|
* @internal
|
|
89
84
|
*/
|
|
90
|
-
declare readonly ['__lex']:
|
|
85
|
+
declare readonly ['__lex']: SchemaInternals<TInput, TOutput>
|
|
86
|
+
|
|
87
|
+
get '~standard'(): StandardSchemaV1.Props<TInput, TOutput> {
|
|
88
|
+
// Lazily create, and cache, the Standard Schema adapter for this schema
|
|
89
|
+
// instance.
|
|
90
|
+
return lazyProperty(this, '~standard', new StandardSchemaAdapter(this))
|
|
91
|
+
}
|
|
91
92
|
|
|
92
93
|
// Needed to discriminate multiple schema types when used in unions. Without
|
|
93
94
|
// this, Typescript could allow an EnumSchema<"foo" | "bar"> to be used where
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { assert, describe, expect, it } from 'vitest'
|
|
2
|
+
import { array } from '../schema/array.js'
|
|
3
|
+
import { integer } from '../schema/integer.js'
|
|
4
|
+
import { object } from '../schema/object.js'
|
|
5
|
+
import { optional } from '../schema/optional.js'
|
|
6
|
+
import { string } from '../schema/string.js'
|
|
7
|
+
import { withDefault } from '../schema/with-default.js'
|
|
8
|
+
import { LexValidationError } from './validation-error.js'
|
|
9
|
+
|
|
10
|
+
describe('StandardSchemaAdapter', () => {
|
|
11
|
+
describe('metadata', () => {
|
|
12
|
+
const schema = integer()
|
|
13
|
+
|
|
14
|
+
it('has version 1', () => {
|
|
15
|
+
expect(schema['~standard'].version).toBe(1)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('has vendor @atproto/lex-schema', () => {
|
|
19
|
+
expect(schema['~standard'].vendor).toBe('@atproto/lex-schema')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('lazy caching', () => {
|
|
24
|
+
it('returns the same adapter instance on repeated accesses', () => {
|
|
25
|
+
const schema = integer()
|
|
26
|
+
const first = schema['~standard']
|
|
27
|
+
const second = schema['~standard']
|
|
28
|
+
expect(first).toBe(second)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe('validate() result shape on success', () => {
|
|
33
|
+
it('returns a value property for a valid integer', () => {
|
|
34
|
+
const result = integer()['~standard'].validate(42)
|
|
35
|
+
expect(result).toMatchObject({ value: 42 })
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('returns a value property for a valid string', () => {
|
|
39
|
+
const result = string()['~standard'].validate('hello')
|
|
40
|
+
expect(result).toMatchObject({ value: 'hello' })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('does not include an issues property on success', () => {
|
|
44
|
+
const result = integer()['~standard'].validate(1)
|
|
45
|
+
expect(result).not.toHaveProperty('issues')
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('validate() result shape on failure', () => {
|
|
50
|
+
it('returns a LexValidationError with issues for an invalid value', () => {
|
|
51
|
+
const result = integer()['~standard'].validate('not-a-number')
|
|
52
|
+
assert(result instanceof LexValidationError)
|
|
53
|
+
expect(Array.isArray(result.issues)).toBe(true)
|
|
54
|
+
expect(result.issues.length).toBeGreaterThan(0)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('does not include a value property on failure', () => {
|
|
58
|
+
const result = integer()['~standard'].validate('not-a-number')
|
|
59
|
+
expect(result).not.toHaveProperty('value')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('issues[].message', () => {
|
|
63
|
+
it('is a non-empty string', () => {
|
|
64
|
+
const result = integer()['~standard'].validate('not-a-number')
|
|
65
|
+
assert(result instanceof LexValidationError)
|
|
66
|
+
for (const issue of result.issues) {
|
|
67
|
+
expect(typeof issue.message).toBe('string')
|
|
68
|
+
expect(issue.message.length).toBeGreaterThan(0)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('describes the type mismatch', () => {
|
|
73
|
+
const result = integer()['~standard'].validate('not-a-number')
|
|
74
|
+
assert(result instanceof LexValidationError)
|
|
75
|
+
expect(result.issues[0].message).toContain('integer')
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe('issues[].path', () => {
|
|
80
|
+
it('is an empty array for a root-level failure', () => {
|
|
81
|
+
const result = integer()['~standard'].validate('not-a-number')
|
|
82
|
+
assert(result instanceof LexValidationError)
|
|
83
|
+
expect(result.issues[0].path).toEqual([])
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('reflects the property key for a nested object failure', () => {
|
|
87
|
+
const schema = object({ age: integer() })
|
|
88
|
+
const result = schema['~standard'].validate({ age: 'not-a-number' })
|
|
89
|
+
assert(result instanceof LexValidationError)
|
|
90
|
+
expect(result.issues[0].path).toContain('age')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('reflects the index for an array element failure', () => {
|
|
94
|
+
const schema = array(integer())
|
|
95
|
+
const result = schema['~standard'].validate([1, 'bad', 3])
|
|
96
|
+
assert(result instanceof LexValidationError)
|
|
97
|
+
expect(result.issues[0].path).toContain(1)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('parse mode (default value application)', () => {
|
|
103
|
+
it('applies default values when input is undefined', () => {
|
|
104
|
+
const schema = withDefault(integer(), 10)
|
|
105
|
+
const result = schema['~standard'].validate(undefined)
|
|
106
|
+
expect(result).toMatchObject({ value: 10 })
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('uses the provided value instead of default when input is present', () => {
|
|
110
|
+
const schema = withDefault(integer(), 10)
|
|
111
|
+
const result = schema['~standard'].validate(42)
|
|
112
|
+
expect(result).toMatchObject({ value: 42 })
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('applies defaults for optional object properties in parse mode', () => {
|
|
116
|
+
const schema = object({
|
|
117
|
+
name: string(),
|
|
118
|
+
count: optional(withDefault(integer(), 0)),
|
|
119
|
+
})
|
|
120
|
+
const result = schema['~standard'].validate({ name: 'Alice' })
|
|
121
|
+
expect(result).toMatchObject({ value: { name: 'Alice', count: 0 } })
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from '@standard-schema/spec'
|
|
2
|
+
import { ValidationContext, Validator } from './validator.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The Standard Schema adapter for {@link Validator} instances.
|
|
6
|
+
*/
|
|
7
|
+
export class StandardSchemaAdapter<TInput, TOutput>
|
|
8
|
+
implements StandardSchemaV1.Props<TInput, TOutput>
|
|
9
|
+
{
|
|
10
|
+
readonly version = 1
|
|
11
|
+
|
|
12
|
+
readonly vendor = '@atproto/lex-schema'
|
|
13
|
+
|
|
14
|
+
declare readonly types: StandardSchemaV1.Types<TInput, TOutput>
|
|
15
|
+
|
|
16
|
+
constructor(private readonly validator: Validator<TInput, TOutput>) {}
|
|
17
|
+
|
|
18
|
+
validate(
|
|
19
|
+
value: unknown,
|
|
20
|
+
options?: StandardSchemaV1.Options,
|
|
21
|
+
): StandardSchemaV1.Result<TOutput> {
|
|
22
|
+
// Perform validation in "parse" mode to ensure transformations (defaults,
|
|
23
|
+
// coercions, etc.) are applied. Also ensures that the output type is
|
|
24
|
+
// returned. Note that ValidationResult is compatible with
|
|
25
|
+
// StandardSchemaV1.Result :-)
|
|
26
|
+
return ValidationContext.validate(value, this.validator, {
|
|
27
|
+
...options?.libraryOptions,
|
|
28
|
+
mode: 'parse',
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
RecordKeyString,
|
|
10
10
|
TidString,
|
|
11
11
|
UriString,
|
|
12
|
+
isAtIdentifierString,
|
|
12
13
|
isDatetimeString,
|
|
13
|
-
isValidAtIdentifier as isValidAtId,
|
|
14
14
|
isValidAtUri,
|
|
15
15
|
isValidDid,
|
|
16
16
|
isValidHandle,
|
|
@@ -30,30 +30,26 @@ import { CheckFn } from '../util/assertion-util.js'
|
|
|
30
30
|
// documentation for types and utilities that are already well-defined there.
|
|
31
31
|
// @TODO rework other string formats in @atproto/syntax to follow this pattern
|
|
32
32
|
// and re-export here, e.g. language tags, NSIDs, record keys, etc.
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
type AtIdentifierString,
|
|
36
|
+
asAtIdentifierString,
|
|
37
|
+
ifAtIdentifierString,
|
|
38
|
+
isAtIdentifierString,
|
|
39
|
+
} from '@atproto/syntax'
|
|
40
|
+
|
|
41
|
+
// AtIdentifierString utilities
|
|
42
|
+
export { isDidIdentifier, isHandleIdentifier } from '@atproto/syntax'
|
|
43
|
+
|
|
33
44
|
export {
|
|
34
45
|
type DatetimeString,
|
|
35
46
|
asDatetimeString,
|
|
36
|
-
currentDatetimeString,
|
|
37
47
|
ifDatetimeString,
|
|
38
48
|
isDatetimeString,
|
|
39
|
-
toDatetimeString,
|
|
40
49
|
} from '@atproto/syntax'
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
* @param value - The value to check
|
|
46
|
-
* @returns `true` if the value is a valid AT identifier
|
|
47
|
-
*/
|
|
48
|
-
export const isAtIdentifierString: CheckFn<AtIdentifierString> = isValidAtId
|
|
49
|
-
export type {
|
|
50
|
-
/**
|
|
51
|
-
* An AT identifier string - either a DID or a handle.
|
|
52
|
-
*
|
|
53
|
-
* @example `"did:plc:1234..."` or `"alice.bsky.social"`
|
|
54
|
-
*/
|
|
55
|
-
AtIdentifierString,
|
|
56
|
-
}
|
|
51
|
+
// DatetimeString utilities
|
|
52
|
+
export { currentDatetimeString, toDatetimeString } from '@atproto/syntax'
|
|
57
53
|
|
|
58
54
|
/**
|
|
59
55
|
* Type guard that checks if a value is a valid AT URI.
|
|
@@ -29,8 +29,15 @@ import {
|
|
|
29
29
|
* console.log(error.toJSON())
|
|
30
30
|
* // { error: 'InvalidRequest', message: '...', issues: [...] }
|
|
31
31
|
* ```
|
|
32
|
+
*
|
|
33
|
+
* @note this class implements {@link ResultFailure} to allow it to be used
|
|
34
|
+
* directly as a failure reason in validation results, avoiding the need for
|
|
35
|
+
* wrapping it in an additional object.
|
|
32
36
|
*/
|
|
33
|
-
export class LexValidationError
|
|
37
|
+
export class LexValidationError
|
|
38
|
+
extends LexError<'InvalidRequest'>
|
|
39
|
+
implements ResultFailure<LexValidationError>
|
|
40
|
+
{
|
|
34
41
|
name = 'LexValidationError'
|
|
35
42
|
|
|
36
43
|
/**
|
|
@@ -56,6 +63,14 @@ export class LexValidationError extends LexError<'InvalidRequest'> {
|
|
|
56
63
|
this.issues = issuesAgg
|
|
57
64
|
}
|
|
58
65
|
|
|
66
|
+
/** @see {ResultFailure.success} */
|
|
67
|
+
readonly success = false as const
|
|
68
|
+
|
|
69
|
+
/** @see {ResultFailure.reason} */
|
|
70
|
+
get reason() {
|
|
71
|
+
return this
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
/**
|
|
60
75
|
* Converts the error to a JSON-serializable object.
|
|
61
76
|
*
|