@oh-my-pi/omptype 17.2.6 → 17.2.8
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 +40 -2
- package/README.md +83 -14
- package/dist/js/ark.js +16 -0
- package/dist/js/compile.js +942 -0
- package/dist/js/errors.js +394 -0
- package/dist/js/from-json-schema.js +234 -0
- package/dist/js/index.js +13 -0
- package/dist/js/infer.js +2 -0
- package/dist/js/interp.js +858 -0
- package/dist/js/ir.js +1700 -0
- package/dist/js/json-schema.js +312 -0
- package/dist/js/keywords.js +340 -0
- package/dist/js/type.js +3062 -0
- package/dist/js/typebox.js +355 -0
- package/dist/js/zod.js +269 -0
- package/dist/types/ark.d.ts +3 -11
- package/dist/types/compile.d.ts +2 -0
- package/dist/types/errors.d.ts +53 -16
- package/dist/types/from-json-schema.d.ts +9 -0
- package/dist/types/index.d.ts +4 -4
- package/dist/types/infer.d.ts +147 -31
- package/dist/types/interp.d.ts +14 -5
- package/dist/types/ir.d.ts +129 -9
- package/dist/types/json-schema.d.ts +15 -1
- package/dist/types/keywords.d.ts +7 -0
- package/dist/types/type.d.ts +525 -60
- package/dist/types/typebox.d.ts +78 -50
- package/dist/types/zod.d.ts +1 -1
- package/package.json +24 -8
- package/src/ark.ts +4 -14
- package/src/compile.ts +887 -154
- package/src/errors.ts +347 -37
- package/src/from-json-schema.ts +231 -0
- package/src/index.ts +4 -4
- package/src/infer.ts +363 -81
- package/src/interp.ts +585 -102
- package/src/ir.ts +1444 -128
- package/src/json-schema.ts +162 -19
- package/src/keywords.ts +380 -0
- package/src/type.ts +3842 -198
- package/src/typebox.ts +142 -99
- package/src/zod.ts +41 -28
package/dist/types/errors.d.ts
CHANGED
|
@@ -7,45 +7,81 @@
|
|
|
7
7
|
* construction stores only the path, the expectation, and the offending value.
|
|
8
8
|
* All human-readable strings are built lazily on property access.
|
|
9
9
|
*/
|
|
10
|
+
/** Context supplied to configurable error formatters. */
|
|
11
|
+
export interface ErrorContext {
|
|
12
|
+
readonly code: string;
|
|
13
|
+
readonly path: readonly PropertyKey[];
|
|
14
|
+
readonly propString: string;
|
|
15
|
+
readonly data: unknown;
|
|
16
|
+
readonly expected: string;
|
|
17
|
+
readonly actual: string;
|
|
18
|
+
readonly problem: string;
|
|
19
|
+
readonly description: string;
|
|
20
|
+
readonly rule?: unknown;
|
|
21
|
+
}
|
|
22
|
+
/** Per-schema overrides for validation error text. */
|
|
23
|
+
export interface ErrorConfig {
|
|
24
|
+
readonly expected?: string | ((context: ErrorContext) => string);
|
|
25
|
+
readonly actual?: string | ((data: unknown) => string);
|
|
26
|
+
readonly problem?: string | ((context: ErrorContext) => string);
|
|
27
|
+
readonly message?: string | ((context: ErrorContext) => string);
|
|
28
|
+
/** Internal: custom predicate expectations display the offending value rather than its domain. */
|
|
29
|
+
readonly preserveActual?: boolean;
|
|
30
|
+
}
|
|
10
31
|
/** A single validation failure at one path. */
|
|
11
32
|
export declare class OmpError {
|
|
33
|
+
#private;
|
|
12
34
|
/** Property path from the root to the failing value (empty at root). */
|
|
13
35
|
readonly path: PropertyKey[];
|
|
14
|
-
/** Human-readable expectation, e.g. `"a string"` or `"at most 3600"`. */
|
|
15
|
-
readonly expected: string;
|
|
16
36
|
/** The value that failed validation. */
|
|
17
37
|
readonly data: unknown;
|
|
18
38
|
constructor(
|
|
19
39
|
/** Property path from the root to the failing value (empty at root). */
|
|
20
|
-
path: PropertyKey[],
|
|
21
|
-
/** Human-readable expectation, e.g. `"a string"` or `"at most 3600"`. */
|
|
22
|
-
expected: string,
|
|
40
|
+
path: PropertyKey[], expected: string,
|
|
23
41
|
/** The value that failed validation. */
|
|
24
|
-
data: unknown);
|
|
42
|
+
data: unknown, config?: ErrorConfig);
|
|
43
|
+
/** Prefix this failure when a nested schema delegates validation. */
|
|
44
|
+
prefix(key: PropertyKey): this;
|
|
45
|
+
/** Apply schema-local formatting to this failure. */
|
|
46
|
+
configure(config: ErrorConfig): this;
|
|
47
|
+
/** Stable category for programmatic error handling. */
|
|
48
|
+
get code(): string;
|
|
49
|
+
/** Human-readable expectation, including a configured override. */
|
|
50
|
+
get expected(): string;
|
|
25
51
|
/** Short description of the received value, e.g. `"a number"` or `"missing"`. */
|
|
26
52
|
get actual(): string;
|
|
27
53
|
/** Path-less problem statement: `must be <expected> (was <actual>)`. */
|
|
28
54
|
get problem(): string;
|
|
29
|
-
/** Full message including the path prefix. */
|
|
30
55
|
get message(): string;
|
|
31
56
|
toString(): string;
|
|
32
57
|
}
|
|
33
58
|
/** Sentinel for a required key that was absent (distinguishes from `undefined`). */
|
|
34
59
|
export declare const MISSING: unique symbol;
|
|
35
60
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
61
|
+
* Validation failure result. The common single-error case remains lazy;
|
|
62
|
+
* traversal only materializes an entry array when a second error is appended.
|
|
38
63
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
64
|
+
type StoredPath = PropertyKey[] | PropertyKey | undefined;
|
|
65
|
+
export declare class OmpErrors implements Iterable<OmpError> {
|
|
66
|
+
#private;
|
|
67
|
+
constructor(path: StoredPath, expected: string, data: unknown, config?: ErrorConfig);
|
|
68
|
+
get length(): number;
|
|
69
|
+
get 0(): OmpError;
|
|
70
|
+
static single(path: PropertyKey[], expected: string, data: unknown, config?: ErrorConfig): OmpErrors;
|
|
71
|
+
/** Append all failures from `other`, preserving traversal order. */
|
|
72
|
+
append(other: OmpErrors): this;
|
|
73
|
+
/** Prefix every failure path with `key` when nesting sub-schemas. */
|
|
44
74
|
prefix(key: PropertyKey): this;
|
|
45
|
-
/**
|
|
75
|
+
/** Apply schema-local message formatting without rebuilding failures. */
|
|
76
|
+
configure(config: ErrorConfig): this;
|
|
77
|
+
get byPath(): Readonly<Record<string, OmpError>>;
|
|
78
|
+
map<result>(fn: (error: OmpError, index: number, errors: OmpErrors) => result): result[];
|
|
79
|
+
filter(fn: (error: OmpError, index: number, errors: OmpErrors) => unknown): OmpError[];
|
|
80
|
+
[Symbol.iterator](): IterableIterator<OmpError>;
|
|
81
|
+
/** @internal Render multiple branch failures as alternatives rather than independent failures. */
|
|
82
|
+
asAlternatives(): this;
|
|
46
83
|
get summary(): string;
|
|
47
84
|
toString(): string;
|
|
48
|
-
/** Throw a `TraversalError` carrying the summary. */
|
|
49
85
|
throw(): never;
|
|
50
86
|
}
|
|
51
87
|
/** Error thrown by `Type.assert` on invalid input. */
|
|
@@ -61,3 +97,4 @@ export declare class TraversalError extends Error {
|
|
|
61
97
|
export declare class OmpTypeError extends Error {
|
|
62
98
|
constructor(message: string);
|
|
63
99
|
}
|
|
100
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type BaseType } from "./type.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a callable omptype schema from a JSON Schema document.
|
|
4
|
+
*
|
|
5
|
+
* # Errors
|
|
6
|
+
* Throws {@link OmpTypeError} on malformed nodes, unresolvable `$ref`s, or
|
|
7
|
+
* types omptype cannot represent.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fromJsonSchema(schema: unknown): BaseType;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* omptype — ArkType-compatible schema validation with a lazy JIT runtime.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* plus `typeof schema.infer` static inference.
|
|
4
|
+
* ArkType-compatible `type()`/`Type`, keyword modules, recursive scopes,
|
|
5
|
+
* composition and morph APIs, structured errors, input/output inference, and
|
|
6
|
+
* JSON Schema emission.
|
|
8
7
|
*/
|
|
9
8
|
export * from "./errors.js";
|
|
9
|
+
export * from "./from-json-schema.js";
|
|
10
10
|
export * from "./infer.js";
|
|
11
11
|
export * from "./ir.js";
|
|
12
12
|
export * from "./json-schema.js";
|
package/dist/types/infer.d.ts
CHANGED
|
@@ -1,49 +1,165 @@
|
|
|
1
|
-
/** Type-level output inference for
|
|
1
|
+
/** Type-level input and output inference for definitions accepted by omptype. */
|
|
2
2
|
type Whitespace = " " | "\n" | "\r" | "\t";
|
|
3
3
|
type TrimLeft<s extends string> = s extends `${Whitespace}${infer rest}` ? TrimLeft<rest> : s;
|
|
4
4
|
type TrimRight<s extends string> = s extends `${infer rest}${Whitespace}` ? TrimRight<rest> : s;
|
|
5
5
|
type Trim<s extends string> = TrimLeft<TrimRight<s>>;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Flat keyword lookup. An indexed access is one instantiation level, unlike a
|
|
8
|
+
* nested conditional chain — this sits under every string-DSL property, so its
|
|
9
|
+
* depth is multiplied by every layer of object nesting above it.
|
|
10
|
+
* `never` is intentionally absent: the parser rejects it and the fallback in
|
|
11
|
+
* `InferMember` treats a missing entry as "not a primitive".
|
|
12
|
+
*/
|
|
13
|
+
type ArkAny = ReturnType<typeof JSON.parse>;
|
|
14
|
+
interface PrimitiveMap {
|
|
15
|
+
string: string;
|
|
16
|
+
"string.url": string;
|
|
17
|
+
number: number;
|
|
18
|
+
"number.integer": number;
|
|
19
|
+
"number.epoch": number;
|
|
20
|
+
"number.safe": number;
|
|
21
|
+
"number.NaN": number;
|
|
22
|
+
"number.Infinity": number;
|
|
23
|
+
"number.NegativeInfinity": number;
|
|
24
|
+
boolean: boolean;
|
|
25
|
+
null: null;
|
|
26
|
+
undefined: undefined;
|
|
27
|
+
unknown: unknown;
|
|
28
|
+
"unknown.any": ArkAny;
|
|
29
|
+
any: unknown;
|
|
30
|
+
object: object;
|
|
31
|
+
bigint: bigint;
|
|
32
|
+
symbol: symbol;
|
|
33
|
+
Key: PropertyKey;
|
|
34
|
+
Date: Date;
|
|
35
|
+
Array: unknown[];
|
|
36
|
+
Function: Function;
|
|
37
|
+
RegExp: RegExp;
|
|
38
|
+
File: File;
|
|
39
|
+
Error: Error;
|
|
40
|
+
Set: Set<unknown>;
|
|
41
|
+
Map: Map<unknown, unknown>;
|
|
42
|
+
WeakSet: WeakSet<WeakKey>;
|
|
43
|
+
WeakMap: WeakMap<WeakKey, unknown>;
|
|
44
|
+
Promise: Promise<unknown>;
|
|
45
|
+
FormData: FormData;
|
|
46
|
+
"object.json": unknown;
|
|
47
|
+
true: true;
|
|
48
|
+
false: false;
|
|
49
|
+
}
|
|
50
|
+
type Merge<left, right> = left extends object ? right extends object ? Omit<left, keyof right> & right : never : never;
|
|
51
|
+
type InferUtility<s extends string> = s extends `Record<${string},${infer value}>` ? Record<string, InferString<value>> : s extends `Array<${infer element}>` | `Array.liftFrom<${infer element}>` ? InferString<element>[] : s extends `Partial<${infer value}>` ? Partial<InferString<value>> : s extends `Required<${infer value}>` ? Required<InferString<value>> : s extends `Pick<${infer value},${infer keys}>` ? Pick<InferString<value>, Extract<InferString<keys>, keyof InferString<value>>> : s extends `Omit<${infer value},${infer keys}>` ? Omit<InferString<value>, Extract<InferString<keys>, keyof InferString<value>>> : s extends `Merge<${infer left},${infer right}>` ? Merge<InferString<left>, InferString<right>> : never;
|
|
52
|
+
/** Output types of morph (`.parse`) keywords; `never` when `s` is not one. */
|
|
53
|
+
type InferParse<s extends string> = s extends "string.numeric.parse" | "string.integer.parse" | "parse.number" | "parse.integer" ? number : s extends "string.date.parse" | "string.date.iso.parse" | "string.date.epoch.parse" | "parse.date" ? Date : s extends "string.url.parse" | "parse.url" ? URL : s extends "string.json.parse" | "parse.json" ? unknown : s extends "object.json.stringify" ? string : s extends "FormData.parse" ? Record<string, Bun.FormDataEntryValue | Bun.FormDataEntryValue[]> : s extends "parse.boolean" ? boolean : s extends "parse.bigint" ? bigint : never;
|
|
54
|
+
/**
|
|
55
|
+
* Member inference as a flat false-branch chain: TypeScript tail-evaluates
|
|
56
|
+
* chained conditionals in the false position, so this stays at constant
|
|
57
|
+
* instantiation depth where the previous `extends infer` ladder nested every
|
|
58
|
+
* fallback inside a true branch and accumulated depth per step.
|
|
59
|
+
*/
|
|
60
|
+
type InferMember<member extends string> = Trim<member> extends infer s extends string ? InferMemberTrimmed<s> : unknown;
|
|
61
|
+
type InferMemberTrimmed<s extends string> = s extends `(${infer inner})` ? InferString<inner> : s extends `${infer element}[]` ? InferMember<element>[] : s extends `'${infer literal}'` | `"${infer literal}"` ? literal : s extends `d'${string}'` | `d"${string}"` ? Date : s extends `\`${string}\`` ? string : s extends `/${string}/${string}` | `/${string}/` ? string : s extends `${infer literal extends number}` ? literal : s extends keyof PrimitiveMap ? PrimitiveMap[s] : [InferParse<s>] extends [never] ? InferUtility<s> extends infer utility ? [utility] extends [never] ? InferMemberFallback<s> : utility : unknown : InferParse<s>;
|
|
62
|
+
type InferMemberFallback<s extends string> = s extends `string.${string}` ? string : s extends `${string}Date${string}` ? Date : s extends `${string}string${string}` ? string : s extends `${string}number${string}` ? number : unknown;
|
|
63
|
+
/** Split unions without distributing over the accumulated members. */
|
|
9
64
|
type InferUnion<s extends string, result = never> = s extends `${infer head}|${infer tail}` ? InferUnion<tail, result | InferMember<head>> : result | InferMember<s>;
|
|
65
|
+
/** Input side of one union member: morph keywords accept their source type. */
|
|
66
|
+
type InferMemberIn<member extends string> = Trim<member> extends infer s extends string ? s extends `${string}.parse` | `parse.${string}` ? string : InferMemberTrimmed<s> : unknown;
|
|
67
|
+
/** Split unions on the input side without distributing over accumulated members. */
|
|
68
|
+
type InferUnionIn<s extends string, result = never> = s extends `${infer head}|${infer tail}` ? InferUnionIn<tail, result | InferMemberIn<head>> : result | InferMemberIn<s>;
|
|
10
69
|
type HasInlineDefault<s extends string> = s extends `${string}=${string}` ? s extends `${string}<${string}` | `${string}>${string}` ? false : true : false;
|
|
11
70
|
type WithoutInlineDefault<s extends string> = HasInlineDefault<s> extends true ? (s extends `${infer base}=${string}` ? Trim<base> : s) : s;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type
|
|
71
|
+
type InferStringOutput<s extends string> = InferUnion<s>;
|
|
72
|
+
/** String-DSL output inference. */
|
|
73
|
+
export type InferString<s extends string> = WithoutInlineDefault<Trim<s>> extends infer trimmed extends string ? trimmed extends `${infer base}?` ? InferString<base> : trimmed extends `(${infer inner})[]` ? InferUnion<inner>[] : InferStringOutput<trimmed> : unknown;
|
|
74
|
+
/** String-DSL input inference, preserving the source side of morph keywords. */
|
|
75
|
+
export type InferStringIn<s extends string> = WithoutInlineDefault<Trim<s>> extends infer trimmed extends string ? trimmed extends `${infer base}?` ? InferStringIn<base> : trimmed extends `(${infer inner})[]` ? InferUnionIn<inner>[] : InferUnionIn<trimmed> : unknown;
|
|
76
|
+
type HasDefault<def> = def extends string ? HasInlineDefault<def> : def extends readonly [unknown, "=", unknown] ? true : def extends {
|
|
15
77
|
readonly hasDefault: true;
|
|
16
78
|
} ? true : false;
|
|
17
|
-
type DefinitionKeys<def extends object> = Exclude<keyof def, "+" | "[string]">;
|
|
18
|
-
|
|
19
|
-
type IsOptionalProp<key, def> = key extends `${string}?` ? true : def extends string ? Trim<def> extends `${string}?` ? true : false : false;
|
|
79
|
+
type DefinitionKeys<def extends object> = Exclude<keyof def, "+" | "[string]" | "...">;
|
|
80
|
+
type IsOptionalProp<key, def> = key extends `${string}?` ? true : def extends string ? Trim<def> extends `${string}?` ? true : false : def extends readonly [unknown, "?"] ? true : false;
|
|
20
81
|
type PropName<key extends PropertyKey> = key extends `${infer name}?` ? name : key;
|
|
21
|
-
type
|
|
22
|
-
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? HasDefault<def[key]> extends true ? PropName<key> : never : PropName<key>]-?: InferDef<def[key]>;
|
|
23
|
-
};
|
|
24
|
-
type OptionalProperties<def extends object> = {
|
|
25
|
-
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? HasDefault<def[key]> extends true ? never : PropName<key> : never]?: InferDef<def[key]>;
|
|
26
|
-
};
|
|
82
|
+
type UnwrapProperty<def> = def extends readonly [infer value, "?" | "=", ...unknown[]] ? value : def;
|
|
27
83
|
type Simplify<t> = {
|
|
28
84
|
[key in keyof t]: t[key];
|
|
29
85
|
};
|
|
30
|
-
type
|
|
31
|
-
|
|
32
|
-
|
|
86
|
+
type OutputRequired<def extends object> = {
|
|
87
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? HasDefault<def[key]> extends true ? PropName<key> : never : PropName<key>]-?: InferDef<UnwrapProperty<def[key]>>;
|
|
88
|
+
};
|
|
89
|
+
type OutputOptional<def extends object> = {
|
|
90
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? HasDefault<def[key]> extends true ? never : PropName<key> : never]?: InferDef<UnwrapProperty<def[key]>>;
|
|
91
|
+
};
|
|
92
|
+
type InputRequired<def extends object> = {
|
|
93
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? never : HasDefault<def[key]> extends true ? never : PropName<key>]-?: InferDefIn<UnwrapProperty<def[key]>>;
|
|
94
|
+
};
|
|
95
|
+
type InputOptional<def extends object> = {
|
|
96
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? PropName<key> : HasDefault<def[key]> extends true ? PropName<key> : never]?: InferDefIn<UnwrapProperty<def[key]>>;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* CYCLE SAFETY: when a fluent generic method is called on a schema whose def
|
|
100
|
+
* embeds other schemas, TypeScript instantiates `InferDef<def>` while `def`
|
|
101
|
+
* is still generic. `"..." extends keyof def` resolves TRUE under permissive
|
|
102
|
+
* instantiation (`keyof any` contains every literal), so spread/index members
|
|
103
|
+
* cannot be deferred by wrapper conditionals — a bare `InferDef<def["..."]>`
|
|
104
|
+
* member re-enters this expansion and instantiates without bound (TS2589).
|
|
105
|
+
* Interface members only instantiate when resolved, so routing the recursive
|
|
106
|
+
* reference through `DefBox` keeps generic instantiation shallow: it stops at
|
|
107
|
+
* a type reference plus an indexed access instead of expanding `InferDef`.
|
|
108
|
+
*/
|
|
109
|
+
interface DefBox<def> {
|
|
110
|
+
readonly out: InferDef<def>;
|
|
111
|
+
readonly in: InferDefIn<def>;
|
|
112
|
+
}
|
|
113
|
+
type OutputSpread<def extends object> = "..." extends keyof def ? DefBox<def["..."]>["out"] : unknown;
|
|
114
|
+
type InputSpread<def extends object> = "..." extends keyof def ? DefBox<def["..."]>["in"] : unknown;
|
|
115
|
+
type OutputIndex<def extends object> = "[string]" extends keyof def ? Record<string, DefBox<def["[string]"]>["out"]> : unknown;
|
|
116
|
+
type InputIndex<def extends object> = "[string]" extends keyof def ? Record<string, DefBox<def["[string]"]>["in"]> : unknown;
|
|
117
|
+
type InferObject<def extends object> = "[string]" extends keyof def ? [DefinitionKeys<def>] extends [never] ? Record<string, DefBox<def["[string]"]>["out"]> : Simplify<OutputRequired<def> & OutputOptional<def> & OutputSpread<def>> & OutputIndex<def> : Simplify<OutputRequired<def> & OutputOptional<def> & OutputSpread<def>>;
|
|
118
|
+
type InferObjectIn<def extends object> = "[string]" extends keyof def ? [DefinitionKeys<def>] extends [never] ? Record<string, DefBox<def["[string]"]>["in"]> : Simplify<InputRequired<def> & InputOptional<def> & InputSpread<def>> & InputIndex<def> : Simplify<InputRequired<def> & InputOptional<def> & InputSpread<def>>;
|
|
119
|
+
/** Object-literal inference used by fluent composition overloads. */
|
|
33
120
|
export type InferObjectDef<def extends object> = InferObject<def>;
|
|
34
|
-
type InferLiteralDef<def> = def extends
|
|
35
|
-
|
|
36
|
-
|
|
121
|
+
type InferLiteralDef<def> = def extends {
|
|
122
|
+
readonly infer: infer output;
|
|
123
|
+
} ? output : def extends string ? InferString<def> : def extends object ? InferObjectLiteral<def> : unknown;
|
|
124
|
+
type InferLiteralDefIn<def> = def extends {
|
|
125
|
+
readonly inferIn: infer input;
|
|
126
|
+
} ? input : def extends string ? InferStringIn<def> : def extends object ? InferObjectLiteralIn<def> : unknown;
|
|
127
|
+
type LiteralRequired<def extends object> = {
|
|
128
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? HasDefault<def[key]> extends true ? PropName<key> : never : PropName<key>]-?: InferLiteralDef<UnwrapProperty<def[key]>>;
|
|
129
|
+
};
|
|
130
|
+
type LiteralOptional<def extends object> = {
|
|
131
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? HasDefault<def[key]> extends true ? never : PropName<key> : never]?: InferLiteralDef<UnwrapProperty<def[key]>>;
|
|
132
|
+
};
|
|
133
|
+
type LiteralRequiredIn<def extends object> = {
|
|
134
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? never : HasDefault<def[key]> extends true ? never : PropName<key>]-?: InferLiteralDefIn<UnwrapProperty<def[key]>>;
|
|
37
135
|
};
|
|
38
|
-
type
|
|
39
|
-
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? HasDefault<def[key]> extends true ?
|
|
136
|
+
type LiteralOptionalIn<def extends object> = {
|
|
137
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true ? PropName<key> : HasDefault<def[key]> extends true ? PropName<key> : never]?: InferLiteralDefIn<UnwrapProperty<def[key]>>;
|
|
40
138
|
};
|
|
41
|
-
/** Object-literal
|
|
42
|
-
export type InferObjectLiteral<def extends object> = Simplify<
|
|
139
|
+
/** Object-literal inference that unwraps embedded schema values one level deep. */
|
|
140
|
+
export type InferObjectLiteral<def extends object> = Simplify<LiteralRequired<def> & LiteralOptional<def>>;
|
|
141
|
+
/** Input-side object-literal inference (embedded schemas contribute `inferIn`). */
|
|
142
|
+
export type InferObjectLiteralIn<def extends object> = Simplify<LiteralRequiredIn<def> & LiteralOptionalIn<def>>;
|
|
143
|
+
type InstanceOf<ctor> = ctor extends abstract new (...args: never[]) => infer instance ? instance : never;
|
|
144
|
+
type SpreadOutput<def> = InferDef<def> extends readonly (infer element)[] ? element[] : never[];
|
|
145
|
+
type SpreadInput<def> = InferDefIn<def> extends readonly (infer element)[] ? element[] : never[];
|
|
146
|
+
type InferTupleOutput<defs extends readonly unknown[], result extends unknown[] = []> = defs extends readonly [] ? result : defs extends readonly ["...", infer spread, ...infer rest] ? [...result, ...SpreadOutput<spread>, ...InferTupleOutput<rest>] : defs extends readonly [infer head, ...infer rest] ? head extends readonly [infer value, "?"] ? InferTupleOutput<rest, [...result, InferDef<value>?]> : head extends readonly [infer value, "=", unknown] ? InferTupleOutput<rest, [...result, InferDef<value>]> : InferTupleOutput<rest, [...result, InferDef<head>]> : result;
|
|
147
|
+
type InferTupleInput<defs extends readonly unknown[], result extends unknown[] = []> = defs extends readonly [] ? result : defs extends readonly ["...", infer spread, ...infer rest] ? [...result, ...SpreadInput<spread>, ...InferTupleInput<rest>] : defs extends readonly [infer head, ...infer rest] ? head extends readonly [infer value, "?" | "=", ...unknown[]] ? InferTupleInput<rest, [...result, InferDefIn<value>?]> : InferTupleInput<rest, [...result, InferDefIn<head>]> : result;
|
|
148
|
+
/**
|
|
149
|
+
* True only for `any` (`1 & any` is `any`; `0 extends any` holds). During
|
|
150
|
+
* relation checking TypeScript instantiates these aliases permissively with
|
|
151
|
+
* every type parameter replaced by `any`, and under `any` the object branch
|
|
152
|
+
* recurses forever (`any["..."]` is `any` again). Cutting `any` off up front
|
|
153
|
+
* makes permissive instantiation terminate immediately, mirroring ArkType's
|
|
154
|
+
* `anyOrNever` guards.
|
|
155
|
+
*/
|
|
156
|
+
type IsAny<def> = 0 extends 1 & def ? true : false;
|
|
43
157
|
/** Infer the validated output type produced by a definition. */
|
|
44
|
-
export type InferDef<def = unknown> = def extends {
|
|
45
|
-
infer: infer output;
|
|
46
|
-
} ? output : def extends string ? InferString<def> : def extends readonly [infer element, "[]"] ? InferDef<element>[] : def extends object ? InferObject<def> : unknown;
|
|
47
|
-
/**
|
|
48
|
-
export type InferDefIn<def = unknown> =
|
|
158
|
+
export type InferDef<def = unknown> = IsAny<def> extends true ? unknown : def extends {
|
|
159
|
+
readonly infer: infer output;
|
|
160
|
+
} ? output : def extends string ? InferString<def> : def extends RegExp ? string : def extends readonly [infer element, "[]"] ? InferDef<element>[] : def extends readonly [infer left, "|", infer right] ? InferDef<left> | InferDef<right> : def extends readonly [infer left, "&", infer right] ? InferDef<left> & InferDef<right> : def extends readonly [unknown, "=>", (...args: never[]) => infer output] ? output : def extends readonly [unknown, "|>", infer output] ? InferDef<output> : def extends readonly [infer base, ":", unknown] | readonly [infer base, "@", unknown] ? InferDef<base> : def extends readonly ["keyof", infer base] ? keyof InferDef<base> : def extends readonly ["instanceof", ...infer constructors] ? InstanceOf<constructors[number]> : def extends readonly ["===", ...infer values] ? values[number] : def extends readonly unknown[] ? InferTupleOutput<def> : def extends object ? InferObject<def> : unknown;
|
|
161
|
+
/** Infer values accepted before defaults and morphs are applied. */
|
|
162
|
+
export type InferDefIn<def = unknown> = IsAny<def> extends true ? unknown : def extends {
|
|
163
|
+
readonly inferIn: infer input;
|
|
164
|
+
} ? input : def extends string ? InferStringIn<def> : def extends RegExp ? string : def extends readonly [infer element, "[]"] ? InferDefIn<element>[] : def extends readonly [infer left, "|", infer right] ? InferDefIn<left> | InferDefIn<right> : def extends readonly [infer left, "&", infer right] ? InferDefIn<left> & InferDefIn<right> : def extends readonly [infer input, "=>", unknown] | readonly [infer input, "|>", unknown] ? InferDefIn<input> : def extends readonly [infer base, ":", unknown] | readonly [infer base, "@", unknown] ? InferDefIn<base> : def extends readonly ["keyof", infer base] ? keyof InferDefIn<base> : def extends readonly ["instanceof", ...infer constructors] ? InstanceOf<constructors[number]> : def extends readonly ["===", ...infer values] ? values[number] : def extends readonly unknown[] ? InferTupleInput<def> : def extends object ? InferObjectIn<def> : unknown;
|
|
49
165
|
export {};
|
package/dist/types/interp.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tree-walking validator used for a schema's first few calls
|
|
3
|
-
*
|
|
2
|
+
* Tree-walking validator used for a schema's first few calls and as the
|
|
3
|
+
* targeted fallback for recursive or predicate-only JIT subtrees.
|
|
4
4
|
*
|
|
5
5
|
* Semantics must stay in lockstep with `compile.ts`:
|
|
6
6
|
* - success returns the output value; the input is returned as-is unless the
|
|
@@ -10,8 +10,17 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { OmpErrors } from "./errors.js";
|
|
12
12
|
import { type IR } from "./ir.js";
|
|
13
|
-
/**
|
|
14
|
-
export declare function
|
|
13
|
+
/** Return an independent runtime value for a prevalidated static default. */
|
|
14
|
+
export declare function materializeDefault(payload: unknown): unknown;
|
|
15
|
+
/**
|
|
16
|
+
* Validate `value` against `ir`; returns output value or `OmpErrors`.
|
|
17
|
+
* `path` seeds the traversal location so nested step callbacks observe
|
|
18
|
+
* absolute ctx.path values when a compiled parent delegates a subtree;
|
|
19
|
+
* resulting error paths are then already absolute.
|
|
20
|
+
*/
|
|
21
|
+
export declare function walk(ir: IR, value: unknown, path?: PropertyKey[]): unknown;
|
|
22
|
+
/** True when a union failure can be replaced with a more specific nested error. */
|
|
23
|
+
export declare function canRefineUnionFailure(member: IR): boolean;
|
|
15
24
|
/**
|
|
16
25
|
* Detailed failure for a union: descend into the member the value was clearly
|
|
17
26
|
* aimed at — unique runtime-kind match, else an object member whose literal
|
|
@@ -21,4 +30,4 @@ export declare function walk(ir: IR, value: unknown): unknown;
|
|
|
21
30
|
*/
|
|
22
31
|
export declare function unionFail(ir: IR & {
|
|
23
32
|
k: "union";
|
|
24
|
-
}, v: unknown, path: PropertyKey[]): OmpErrors;
|
|
33
|
+
}, v: unknown, path: PropertyKey[], expected?: string): OmpErrors;
|
package/dist/types/ir.d.ts
CHANGED
|
@@ -9,8 +9,27 @@
|
|
|
9
9
|
* consumed by the interpreter (`interp.ts`), the JIT compiler (`compile.ts`),
|
|
10
10
|
* and the JSON Schema emitter (`json-schema.ts`).
|
|
11
11
|
*/
|
|
12
|
+
import { type ErrorConfig, OmpErrors } from "./errors.js";
|
|
12
13
|
/** Brand carried by `Type` instances so the parser can embed them in defs. */
|
|
13
14
|
export declare const IR_BRAND: unique symbol;
|
|
15
|
+
declare const kMorph: unique symbol;
|
|
16
|
+
declare const kMorphOwner: unique symbol;
|
|
17
|
+
declare const kAlias: unique symbol;
|
|
18
|
+
declare const kAliasOwner: unique symbol;
|
|
19
|
+
declare const kSimple: unique symbol;
|
|
20
|
+
declare const kSimpleOwner: unique symbol;
|
|
21
|
+
interface IRAnalysis {
|
|
22
|
+
[kMorph]?: boolean;
|
|
23
|
+
[kMorphOwner]?: object;
|
|
24
|
+
[kAlias]?: boolean;
|
|
25
|
+
[kAliasOwner]?: object;
|
|
26
|
+
[kSimple]?: boolean;
|
|
27
|
+
[kSimpleOwner]?: object;
|
|
28
|
+
/** Node-local metadata used for shallow error formatting. */
|
|
29
|
+
cfg?: ErrorConfig;
|
|
30
|
+
/** True when `desc` was derived from the node itself rather than authored via `.describe()`. */
|
|
31
|
+
descAuto?: boolean;
|
|
32
|
+
}
|
|
14
33
|
/**
|
|
15
34
|
* The parser-facing surface of an embedded `Type` instance.
|
|
16
35
|
* `type.ts` implements this on every schema it creates.
|
|
@@ -21,17 +40,51 @@ export interface EmbeddableSchema {
|
|
|
21
40
|
ir: IR;
|
|
22
41
|
/** True when the schema carries `.pipe()`/`.narrow()` steps. */
|
|
23
42
|
hasSteps: boolean;
|
|
43
|
+
/** Output IR of the last `.to(target)` step, when statically known. */
|
|
44
|
+
stepOut?: IR;
|
|
45
|
+
/** True when the last pipe step is bare — output shape statically unknown. */
|
|
46
|
+
opaqueOutput?: boolean;
|
|
24
47
|
/** `.default()` payload; a function is a factory invoked per fill. */
|
|
25
48
|
defaultValue?: unknown;
|
|
26
49
|
hasDefault: boolean;
|
|
50
|
+
/** Precomputed output for a non-factory default after validation and morphs. */
|
|
51
|
+
defaultOutput?: unknown;
|
|
52
|
+
hasDefaultOutput?: boolean;
|
|
27
53
|
/** `.describe()` annotation, emitted into JSON Schema. */
|
|
28
54
|
description?: string;
|
|
29
55
|
/** Full validate+morph pipeline (identical to calling the schema). */
|
|
30
|
-
run(value: unknown): unknown;
|
|
56
|
+
run(value: unknown, path?: readonly PropertyKey[]): unknown;
|
|
31
57
|
}
|
|
32
58
|
/** Policy for undeclared object keys. */
|
|
33
59
|
export type Extras = "keep" | "reject" | "delete";
|
|
34
|
-
|
|
60
|
+
/** Constructor accepted by `type.instanceOf` and tuple `instanceof` expressions. */
|
|
61
|
+
export type Constructor = abstract new (...args: never[]) => object;
|
|
62
|
+
/** Context available to in-definition morph callbacks. */
|
|
63
|
+
export interface MorphContext {
|
|
64
|
+
/** Return a validation error at the current path. */
|
|
65
|
+
error(expectation: string): OmpErrors;
|
|
66
|
+
/** Alias of `error` matching ArkType's rejection vocabulary. */
|
|
67
|
+
reject(expectation: string): OmpErrors;
|
|
68
|
+
}
|
|
69
|
+
/** One fixed tuple position, optionally absent or defaulted. */
|
|
70
|
+
export interface TupleItemIR {
|
|
71
|
+
val: IR;
|
|
72
|
+
opt: boolean;
|
|
73
|
+
def?: unknown;
|
|
74
|
+
defFactory?: boolean;
|
|
75
|
+
hasDefault?: boolean;
|
|
76
|
+
/** True once the default has been validated and static morph output precomputed. */
|
|
77
|
+
defValidated?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/** Fixed, optional, variadic, and postfix tuple sequence. */
|
|
80
|
+
export interface TupleIR {
|
|
81
|
+
k: "tuple";
|
|
82
|
+
prefix: TupleItemIR[];
|
|
83
|
+
variadic?: IR;
|
|
84
|
+
postfix: IR[];
|
|
85
|
+
desc?: string;
|
|
86
|
+
}
|
|
87
|
+
export type IR = IRAnalysis & ({
|
|
35
88
|
k: "unknown";
|
|
36
89
|
desc?: string;
|
|
37
90
|
} | {
|
|
@@ -46,6 +99,12 @@ export type IR = {
|
|
|
46
99
|
} | {
|
|
47
100
|
k: "bigint";
|
|
48
101
|
desc?: string;
|
|
102
|
+
} | {
|
|
103
|
+
k: "symbol";
|
|
104
|
+
desc?: string;
|
|
105
|
+
} | {
|
|
106
|
+
k: "never";
|
|
107
|
+
desc?: string;
|
|
49
108
|
}
|
|
50
109
|
/** Any non-null object (the bare `object` keyword). */
|
|
51
110
|
| {
|
|
@@ -64,6 +123,7 @@ export type IR = {
|
|
|
64
123
|
xmin?: boolean;
|
|
65
124
|
xmax?: boolean;
|
|
66
125
|
int?: boolean;
|
|
126
|
+
divisor?: number;
|
|
67
127
|
desc?: string;
|
|
68
128
|
} | {
|
|
69
129
|
k: "lit";
|
|
@@ -73,43 +133,103 @@ export type IR = {
|
|
|
73
133
|
k: "union";
|
|
74
134
|
members: IR[];
|
|
75
135
|
desc?: string;
|
|
136
|
+
} | {
|
|
137
|
+
k: "intersection";
|
|
138
|
+
members: IR[];
|
|
139
|
+
desc?: string;
|
|
76
140
|
} | {
|
|
77
141
|
k: "array";
|
|
78
142
|
el: IR;
|
|
79
143
|
min?: number;
|
|
80
144
|
max?: number;
|
|
81
145
|
desc?: string;
|
|
82
|
-
} | {
|
|
146
|
+
} | TupleIR | {
|
|
83
147
|
k: "object";
|
|
84
148
|
props: PropIR[];
|
|
85
149
|
index?: IR;
|
|
150
|
+
symbolIndex?: IR;
|
|
151
|
+
patternIndexes?: {
|
|
152
|
+
key: IR;
|
|
153
|
+
val: IR;
|
|
154
|
+
}[];
|
|
86
155
|
extras: Extras;
|
|
87
156
|
desc?: string;
|
|
157
|
+
} | {
|
|
158
|
+
k: "refine";
|
|
159
|
+
base: IR;
|
|
160
|
+
pred: (value: unknown) => boolean | OmpErrors;
|
|
161
|
+
expected: string;
|
|
162
|
+
json?: Record<string, unknown>;
|
|
163
|
+
desc?: string;
|
|
164
|
+
} | {
|
|
165
|
+
k: "morph";
|
|
166
|
+
input: IR;
|
|
167
|
+
fn: (value: unknown, context: MorphContext) => unknown;
|
|
168
|
+
out?: IR;
|
|
169
|
+
desc?: string;
|
|
170
|
+
} | {
|
|
171
|
+
k: "instance";
|
|
172
|
+
ctor: Constructor;
|
|
173
|
+
expected: string;
|
|
174
|
+
desc?: string;
|
|
175
|
+
} | {
|
|
176
|
+
k: "alias";
|
|
177
|
+
name: string;
|
|
178
|
+
resolve: () => IR;
|
|
179
|
+
desc?: string;
|
|
88
180
|
}
|
|
89
181
|
/** Embedded schema with runtime steps; validated by calling `run`. */
|
|
90
182
|
| {
|
|
91
183
|
k: "sub";
|
|
92
184
|
schema: EmbeddableSchema;
|
|
93
185
|
desc?: string;
|
|
94
|
-
};
|
|
186
|
+
});
|
|
95
187
|
export interface PropIR {
|
|
96
|
-
key:
|
|
188
|
+
key: PropertyKey;
|
|
97
189
|
opt: boolean;
|
|
98
190
|
val: IR;
|
|
99
191
|
/** Default payload (value, or factory when `defFactory`); missing key is filled. */
|
|
100
192
|
def?: unknown;
|
|
101
193
|
defFactory?: boolean;
|
|
102
194
|
hasDefault?: boolean;
|
|
195
|
+
/** True once the default has been validated and static morph output precomputed. */
|
|
196
|
+
defValidated?: boolean;
|
|
103
197
|
}
|
|
104
198
|
/** Definition input accepted by `type()` and object property values. */
|
|
105
|
-
export type Def = string | EmbeddableSchema | readonly
|
|
106
|
-
readonly [k: string]:
|
|
199
|
+
export type Def = string | RegExp | Date | EmbeddableSchema | readonly unknown[] | {
|
|
200
|
+
readonly [k: string]: unknown;
|
|
107
201
|
};
|
|
202
|
+
/** Resolve named scope aliases and, when present, scoped generic invocations. */
|
|
203
|
+
export interface AliasResolver {
|
|
204
|
+
(name: string): IR | undefined;
|
|
205
|
+
hasGeneric?(name: string): boolean;
|
|
206
|
+
generic?(name: string, arguments_: readonly IR[]): IR | undefined;
|
|
207
|
+
}
|
|
208
|
+
/** Declare that `resolve` only intercepts `this` (see THIS_ONLY_RESOLVERS). */
|
|
209
|
+
export declare function markThisOnlyResolver(resolve: AliasResolver): void;
|
|
210
|
+
/** Install the assignability comparator used by `Extract`/`Exclude`. */
|
|
211
|
+
export declare function useAssignability(compare: (source: IR, target: IR) => boolean): void;
|
|
212
|
+
/**
|
|
213
|
+
* Distribute `base` over its union members, keeping those assignable to
|
|
214
|
+
* `target` (`keepAssignable`) or those that are not (`Exclude`).
|
|
215
|
+
*/
|
|
216
|
+
export declare function distributeFilter(base: IR, target: IR, keepAssignable: boolean): IR;
|
|
108
217
|
/** Embed a schema value: inline pure structure, keep `sub` nodes for stepped schemas. */
|
|
109
218
|
export declare function embed(schema: EmbeddableSchema): IR;
|
|
110
|
-
/**
|
|
111
|
-
export declare function
|
|
219
|
+
/** Build the runtime schema for an object's or tuple's keys. */
|
|
220
|
+
export declare function keyOf(node: IR): IR;
|
|
221
|
+
/** Parse a definition, optionally resolving names from an enclosing scope. */
|
|
222
|
+
export declare function parseDef(def: unknown, resolve?: AliasResolver): IR;
|
|
223
|
+
/** Whether `ir` needs no construction-time normalization or morph analysis. */
|
|
224
|
+
export declare function isSimpleIR(ir: IR): boolean;
|
|
112
225
|
/** True when validating `ir` can produce an output different from its input. */
|
|
113
226
|
export declare function hasMorph(ir: IR): boolean;
|
|
227
|
+
/**
|
|
228
|
+
* True when a traversal of `ir` can revisit nodes through recursive aliases,
|
|
229
|
+
* requiring cycle guards in the interpreter. Embedded sub-schemas run their
|
|
230
|
+
* own guarded traversal and are intentionally not inspected.
|
|
231
|
+
*/
|
|
232
|
+
export declare function hasAlias(ir: IR): boolean;
|
|
114
233
|
/** Human-readable expectation for error messages, e.g. `"a string"`. */
|
|
115
234
|
export declare function expectedOf(ir: IR): string;
|
|
235
|
+
export {};
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import type { IR } from "./ir.js";
|
|
2
2
|
export interface JsonSchemaOptions {
|
|
3
3
|
description?: string;
|
|
4
|
+
target?: string;
|
|
5
|
+
dialect?: string | null;
|
|
6
|
+
/**
|
|
7
|
+
* Which side of morphs and defaults to describe:
|
|
8
|
+
* - `'input'` — accepted payloads: morphs emit their input shape, defaulted
|
|
9
|
+
* properties are optional (with `default` annotations).
|
|
10
|
+
* - `'output'` — produced values: morphs emit their output shape, defaulted
|
|
11
|
+
* properties are required (always present after validation).
|
|
12
|
+
* Unset keeps the hybrid legacy behavior (morph output, defaults optional).
|
|
13
|
+
*/
|
|
14
|
+
io?: "input" | "output";
|
|
15
|
+
fallback?: (context: {
|
|
16
|
+
base: Record<string, unknown>;
|
|
17
|
+
}) => unknown;
|
|
4
18
|
}
|
|
5
19
|
type JsonSchema = Record<string, unknown>;
|
|
6
|
-
/** Emit the
|
|
20
|
+
/** Emit the requested JSON Schema dialect represented by an IR tree. */
|
|
7
21
|
export declare function irToJsonSchema(ir: IR, options?: JsonSchemaOptions): JsonSchema;
|
|
8
22
|
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IR } from "./ir.js";
|
|
2
|
+
/** Lower a built-in keyword into fresh validation IR. */
|
|
3
|
+
export declare function keywordIR(name: string): IR | undefined;
|
|
4
|
+
/** Lower a regular expression into a string refinement. */
|
|
5
|
+
export declare function patternIR(regex: RegExp): IR;
|
|
6
|
+
/** Lower an ArkType-style template literal into a string pattern. */
|
|
7
|
+
export declare function templateIR(source: string): IR;
|