wellcrafted
Version:
Delightful TypeScript patterns for elegant, type-safe applications
344 lines (339 loc) • 10.1 kB
JavaScript
//#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