UNPKG

wellcrafted

Version:

Delightful TypeScript patterns for elegant, type-safe applications

344 lines (339 loc) 10.1 kB
//#region src/standard-schema/failures.ts const FAILURES = { EXPECTED_OBJECT: { issues: [{ message: "Expected object" }] }, EXPECTED_DATA_ERROR_PROPS: { issues: [{ message: "Expected object with 'data' and 'error' properties" }] }, EXPECTED_ERROR_NULL: { issues: [{ message: "Expected 'error' to be null for Ok variant", path: ["error"] }] }, EXPECTED_ERROR_NOT_NULL: { issues: [{ message: "Expected 'error' to be non-null for Err variant", path: ["error"] }] } }; //#endregion //#region src/standard-schema/types.ts /** * Checks if a schema has validation capability. */ function hasValidate(schema) { return "validate" in schema["~standard"] && typeof schema["~standard"].validate === "function"; } /** * Checks if a schema has JSON Schema generation capability. */ function hasJsonSchema(schema) { return "jsonSchema" in schema["~standard"] && typeof schema["~standard"].jsonSchema === "object" && schema["~standard"].jsonSchema !== null; } //#endregion //#region src/standard-schema/err.ts function createErrValidate(innerSchema) { return (value) => { if (typeof value !== "object" || value === null) return FAILURES.EXPECTED_OBJECT; if (!("data" in value) || !("error" in value)) return FAILURES.EXPECTED_DATA_ERROR_PROPS; const obj = value; if (obj.error === null) return FAILURES.EXPECTED_ERROR_NOT_NULL; const innerResult = innerSchema["~standard"].validate(obj.error); if (innerResult instanceof Promise) return innerResult.then((r) => { if (r.issues) return { issues: r.issues.map((issue) => ({ ...issue, path: ["error", ...issue.path || []] })) }; return { value: { data: null, error: r.value } }; }); if (innerResult.issues) return { issues: innerResult.issues.map((issue) => ({ ...issue, path: ["error", ...issue.path || []] })) }; return { value: { data: null, error: innerResult.value } }; }; } function createErrJsonSchema(innerSchema) { return { input(options) { return { type: "object", properties: { data: { type: "null" }, error: innerSchema["~standard"].jsonSchema.input(options) }, required: ["data", "error"], additionalProperties: false }; }, output(options) { return { type: "object", properties: { data: { type: "null" }, error: innerSchema["~standard"].jsonSchema.output(options) }, required: ["data", "error"], additionalProperties: false }; } }; } /** * Wraps a Standard Schema into an Err variant schema. * * Takes a schema for type E and returns a schema for `{ data: null, error: E }`. * Preserves the capabilities of the input schema (validate, jsonSchema, or both). * * @example * ```typescript * import { z } from "zod"; * import { ErrSchema } from "wellcrafted/standard-schema"; * * const errorSchema = z.object({ code: z.string(), message: z.string() }); * const errResultSchema = ErrSchema(errorSchema); * * // Validates: { data: null, error: { code: "NOT_FOUND", message: "User not found" } } * const result = errResultSchema["~standard"].validate({ * data: null, * error: { code: "NOT_FOUND", message: "User not found" }, * }); * ``` */ function ErrSchema(innerSchema) { const base = { "~standard": { version: 1, vendor: "wellcrafted", types: { input: void 0, output: void 0 } } }; if (hasValidate(innerSchema)) base["~standard"].validate = createErrValidate(innerSchema); if (hasJsonSchema(innerSchema)) base["~standard"].jsonSchema = createErrJsonSchema(innerSchema); return base; } //#endregion //#region src/standard-schema/ok.ts function createOkValidate(innerSchema) { return (value) => { if (typeof value !== "object" || value === null) return FAILURES.EXPECTED_OBJECT; if (!("data" in value) || !("error" in value)) return FAILURES.EXPECTED_DATA_ERROR_PROPS; const obj = value; if (obj.error !== null) return FAILURES.EXPECTED_ERROR_NULL; const innerResult = innerSchema["~standard"].validate(obj.data); if (innerResult instanceof Promise) return innerResult.then((r) => { if (r.issues) return { issues: r.issues.map((issue) => ({ ...issue, path: ["data", ...issue.path || []] })) }; return { value: { data: r.value, error: null } }; }); if (innerResult.issues) return { issues: innerResult.issues.map((issue) => ({ ...issue, path: ["data", ...issue.path || []] })) }; return { value: { data: innerResult.value, error: null } }; }; } function createOkJsonSchema(innerSchema) { return { input(options) { return { type: "object", properties: { data: innerSchema["~standard"].jsonSchema.input(options), error: { type: "null" } }, required: ["data", "error"], additionalProperties: false }; }, output(options) { return { type: "object", properties: { data: innerSchema["~standard"].jsonSchema.output(options), error: { type: "null" } }, required: ["data", "error"], additionalProperties: false }; } }; } /** * Wraps a Standard Schema into an Ok variant schema. * * Takes a schema for type T and returns a schema for `{ data: T, error: null }`. * Preserves the capabilities of the input schema (validate, jsonSchema, or both). * * @example * ```typescript * import { z } from "zod"; * import { OkSchema } from "wellcrafted/standard-schema"; * * const userSchema = z.object({ name: z.string() }); * const okUserSchema = OkSchema(userSchema); * * // Validates: { data: { name: "Alice" }, error: null } * const result = okUserSchema["~standard"].validate({ * data: { name: "Alice" }, * error: null, * }); * ``` */ function OkSchema(innerSchema) { const base = { "~standard": { version: 1, vendor: "wellcrafted", types: { input: void 0, output: void 0 } } }; if (hasValidate(innerSchema)) base["~standard"].validate = createOkValidate(innerSchema); if (hasJsonSchema(innerSchema)) base["~standard"].jsonSchema = createOkJsonSchema(innerSchema); return base; } //#endregion //#region src/standard-schema/result.ts function createResultValidate(dataSchema, errorSchema) { return (value) => { if (typeof value !== "object" || value === null) return FAILURES.EXPECTED_OBJECT; if (!("data" in value) || !("error" in value)) return FAILURES.EXPECTED_DATA_ERROR_PROPS; const obj = value; const isOk = obj.error === null; if (isOk) { const innerResult$1 = dataSchema["~standard"].validate(obj.data); if (innerResult$1 instanceof Promise) return innerResult$1.then((r) => { if (r.issues) return { issues: r.issues.map((issue) => ({ ...issue, path: ["data", ...issue.path || []] })) }; return { value: { data: r.value, error: null } }; }); if (innerResult$1.issues) return { issues: innerResult$1.issues.map((issue) => ({ ...issue, path: ["data", ...issue.path || []] })) }; return { value: { data: innerResult$1.value, error: null } }; } const innerResult = errorSchema["~standard"].validate(obj.error); if (innerResult instanceof Promise) return innerResult.then((r) => { if (r.issues) return { issues: r.issues.map((issue) => ({ ...issue, path: ["error", ...issue.path || []] })) }; return { value: { data: null, error: r.value } }; }); if (innerResult.issues) return { issues: innerResult.issues.map((issue) => ({ ...issue, path: ["error", ...issue.path || []] })) }; return { value: { data: null, error: innerResult.value } }; }; } function createResultJsonSchema(dataSchema, errorSchema) { return { input(options) { return { oneOf: [{ type: "object", properties: { data: dataSchema["~standard"].jsonSchema.input(options), error: { type: "null" } }, required: ["data", "error"], additionalProperties: false }, { type: "object", properties: { data: { type: "null" }, error: errorSchema["~standard"].jsonSchema.input(options) }, required: ["data", "error"], additionalProperties: false }] }; }, output(options) { return { oneOf: [{ type: "object", properties: { data: dataSchema["~standard"].jsonSchema.output(options), error: { type: "null" } }, required: ["data", "error"], additionalProperties: false }, { type: "object", properties: { data: { type: "null" }, error: errorSchema["~standard"].jsonSchema.output(options) }, required: ["data", "error"], additionalProperties: false }] }; } }; } /** * Combines two Standard Schemas into a Result discriminated union schema. * * Takes a data schema for type T and an error schema for type E, returning a schema * for `{ data: T, error: null } | { data: null, error: E }`. * * Preserves the capabilities of the input schemas - if both have validate, output * has validate; if both have jsonSchema, output has jsonSchema. * * @example * ```typescript * import { z } from "zod"; * import { ResultSchema } from "wellcrafted/standard-schema"; * * const userSchema = z.object({ id: z.string(), name: z.string() }); * const errorSchema = z.object({ code: z.string(), message: z.string() }); * const resultSchema = ResultSchema(userSchema, errorSchema); * * // Validates Ok variant: { data: { id: "1", name: "Alice" }, error: null } * // Validates Err variant: { data: null, error: { code: "NOT_FOUND", message: "..." } } * const result = resultSchema["~standard"].validate({ * data: { id: "1", name: "Alice" }, * error: null, * }); * ``` */ function ResultSchema(dataSchema, errorSchema) { const base = { "~standard": { version: 1, vendor: "wellcrafted", types: { input: void 0, output: void 0 } } }; if (hasValidate(dataSchema) && hasValidate(errorSchema)) base["~standard"].validate = createResultValidate(dataSchema, errorSchema); if (hasJsonSchema(dataSchema) && hasJsonSchema(errorSchema)) base["~standard"].jsonSchema = createResultJsonSchema(dataSchema, errorSchema); return base; } //#endregion export { ErrSchema, FAILURES, OkSchema, ResultSchema, hasJsonSchema, hasValidate }; //# sourceMappingURL=index.js.map