@oh-my-pi/pi-coding-agent 16.0.5 → 16.0.7
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 +60 -0
- package/dist/cli.js +1945 -1386
- package/dist/types/advisor/advise-tool.d.ts +22 -19
- package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
- package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
- package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
- package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
- package/dist/types/cli/ttsr-cli.d.ts +39 -0
- package/dist/types/commands/ttsr.d.ts +57 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
- package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
- package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
- package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
- package/dist/types/commit/changelog/generate.d.ts +12 -13
- package/dist/types/commit/shared-llm.d.ts +10 -37
- package/dist/types/config/config-file.d.ts +4 -4
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +625 -990
- package/dist/types/config/models-config.d.ts +229 -217
- package/dist/types/config/settings-schema.d.ts +53 -23
- package/dist/types/edit/hashline/params.d.ts +7 -11
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/edit/modes/apply-patch.d.ts +4 -5
- package/dist/types/edit/modes/patch.d.ts +15 -24
- package/dist/types/edit/modes/replace.d.ts +16 -17
- package/dist/types/eval/js/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
- package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
- package/dist/types/extensibility/extensions/types.d.ts +6 -3
- package/dist/types/extensibility/hooks/types.d.ts +7 -4
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
- package/dist/types/extensibility/typebox.d.ts +80 -58
- package/dist/types/goals/tools/goal-tool.d.ts +11 -24
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lsp/index.d.ts +11 -26
- package/dist/types/lsp/types.d.ts +12 -28
- package/dist/types/mcp/client.d.ts +8 -0
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -1
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/setup-wizard/index.d.ts +1 -0
- package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/sdk.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +4 -0
- package/dist/types/startup-splash.d.ts +12 -0
- package/dist/types/task/types.d.ts +47 -48
- package/dist/types/tools/ask.d.ts +26 -27
- package/dist/types/tools/ast-edit.d.ts +17 -17
- package/dist/types/tools/ast-grep.d.ts +12 -13
- package/dist/types/tools/bash.d.ts +20 -17
- package/dist/types/tools/browser.d.ts +46 -71
- package/dist/types/tools/checkpoint.d.ts +14 -15
- package/dist/types/tools/debug.d.ts +82 -145
- package/dist/types/tools/eval.d.ts +30 -40
- package/dist/types/tools/find.d.ts +17 -18
- package/dist/types/tools/gh.d.ts +49 -78
- package/dist/types/tools/image-gen.d.ts +20 -36
- package/dist/types/tools/inspect-image.d.ts +10 -11
- package/dist/types/tools/irc.d.ts +22 -33
- package/dist/types/tools/job.d.ts +11 -12
- package/dist/types/tools/learn.d.ts +21 -28
- package/dist/types/tools/manage-skill.d.ts +13 -22
- package/dist/types/tools/memory-edit.d.ts +15 -24
- package/dist/types/tools/memory-recall.d.ts +7 -8
- package/dist/types/tools/memory-reflect.d.ts +9 -10
- package/dist/types/tools/memory-retain.d.ts +13 -14
- package/dist/types/tools/read.d.ts +7 -8
- package/dist/types/tools/resolve.d.ts +11 -18
- package/dist/types/tools/review.d.ts +9 -15
- package/dist/types/tools/search-tool-bm25.d.ts +9 -10
- package/dist/types/tools/search.d.ts +16 -17
- package/dist/types/tools/ssh.d.ts +14 -15
- package/dist/types/tools/todo.d.ts +27 -43
- package/dist/types/tools/tts.d.ts +8 -9
- package/dist/types/tools/write.d.ts +9 -10
- package/dist/types/tui/index.d.ts +1 -0
- package/dist/types/tui/width-aware-text.d.ts +23 -0
- package/dist/types/utils/markit.d.ts +10 -1
- package/dist/types/web/search/index.d.ts +17 -28
- package/dist/types/web/search/providers/perplexity.d.ts +0 -2
- package/dist/types/web/search/types.d.ts +32 -26
- package/package.json +14 -13
- package/scripts/omp +1 -1
- package/src/advisor/__tests__/advisor.test.ts +44 -1
- package/src/advisor/advise-tool.ts +34 -11
- package/src/autoresearch/tools/init-experiment.ts +13 -16
- package/src/autoresearch/tools/log-experiment.ts +15 -18
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +4 -4
- package/src/cli/ttsr-cli.ts +995 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +7 -1
- package/src/commands/ttsr.ts +125 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +7 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +18 -15
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +8 -20
- package/src/commit/agentic/tools/split-commit.ts +19 -23
- package/src/commit/analysis/summary.ts +7 -5
- package/src/commit/changelog/generate.ts +15 -11
- package/src/commit/shared-llm.ts +17 -24
- package/src/config/config-file.ts +13 -15
- package/src/config/keybindings.ts +6 -0
- package/src/config/models-config-schema.ts +206 -179
- package/src/config/settings-schema.ts +34 -0
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-import-type.md +2 -2
- package/src/discovery/builtin-rules/ts-no-any.md +11 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
- package/src/edit/hashline/params.ts +12 -11
- package/src/edit/index.ts +5 -4
- package/src/edit/modes/apply-patch.ts +4 -4
- package/src/edit/modes/patch.ts +15 -18
- package/src/edit/modes/replace.ts +13 -17
- package/src/edit/renderer.ts +0 -1
- package/src/eval/agent-bridge.ts +11 -13
- package/src/eval/completion-bridge.ts +25 -17
- package/src/eval/js/context-manager.ts +17 -2
- package/src/eval/js/index.ts +1 -1
- package/src/eval/py/executor.ts +2 -2
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +6 -3
- package/src/extensibility/custom-tools/loader.ts +4 -2
- package/src/extensibility/custom-tools/types.ts +8 -5
- package/src/extensibility/extensions/loader.ts +4 -2
- package/src/extensibility/extensions/types.ts +6 -3
- package/src/extensibility/hooks/loader.ts +5 -2
- package/src/extensibility/hooks/types.ts +7 -4
- package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
- package/src/extensibility/tool-proxy.ts +4 -1
- package/src/extensibility/typebox.ts +778 -251
- package/src/goals/guided-setup.ts +12 -3
- package/src/goals/tools/goal-tool.ts +6 -6
- package/src/index.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +11 -9
- package/src/lsp/types.ts +13 -27
- package/src/main.ts +19 -18
- package/src/mcp/client.ts +38 -13
- package/src/mcp/render.ts +102 -89
- package/src/modes/components/agent-hub.ts +11 -4
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/custom-editor.ts +18 -0
- package/src/modes/components/status-line/component.ts +8 -1
- package/src/modes/components/tool-execution.ts +17 -10
- package/src/modes/controllers/btw-controller.ts +69 -1
- package/src/modes/controllers/input-controller.ts +29 -0
- package/src/modes/interactive-mode.ts +38 -8
- package/src/modes/setup-wizard/index.ts +1 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/theme/theme.ts +133 -143
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/context-usage.ts +9 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/sdk.ts +21 -4
- package/src/session/agent-session.ts +173 -33
- package/src/session/session-history-format.ts +11 -2
- package/src/session/snapcompact-inline.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +3 -10
- package/src/startup-splash.ts +19 -0
- package/src/task/executor.ts +11 -6
- package/src/task/types.ts +44 -41
- package/src/tool-discovery/tool-index.ts +17 -4
- package/src/tools/ask.ts +14 -14
- package/src/tools/ast-edit.ts +17 -14
- package/src/tools/ast-grep.ts +10 -9
- package/src/tools/bash.ts +15 -10
- package/src/tools/browser/launch.ts +13 -0
- package/src/tools/browser.ts +26 -32
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +72 -69
- package/src/tools/eval.ts +18 -19
- package/src/tools/find.ts +20 -13
- package/src/tools/gh.ts +29 -49
- package/src/tools/image-gen.ts +27 -32
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +12 -12
- package/src/tools/job.ts +6 -6
- package/src/tools/learn.ts +11 -14
- package/src/tools/manage-skill.ts +19 -23
- package/src/tools/memory-edit.ts +8 -8
- package/src/tools/memory-recall.ts +4 -4
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +9 -11
- package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
- package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
- package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
- package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
- package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
- package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
- package/src/tools/read.ts +169 -13
- package/src/tools/report-tool-issue.ts +6 -6
- package/src/tools/resolve.ts +6 -6
- package/src/tools/review.ts +10 -12
- package/src/tools/search-tool-bm25.ts +5 -5
- package/src/tools/search.ts +20 -29
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +16 -19
- package/src/tools/tts.ts +16 -15
- package/src/tools/write.ts +5 -5
- package/src/tui/index.ts +1 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/markit.ts +17 -2
- package/src/web/search/index.ts +9 -9
- package/src/web/search/providers/perplexity.ts +373 -126
- package/src/web/search/types.ts +28 -48
|
@@ -1,61 +1,176 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Minimal `@sinclair/typebox` runtime compatibility shim
|
|
2
|
+
* Minimal `@sinclair/typebox` runtime compatibility shim.
|
|
3
3
|
*
|
|
4
4
|
* Historically the coding agent injected the real `@sinclair/typebox` (~5MB
|
|
5
5
|
* dependency) into extensions, hooks, custom tools, and custom commands so
|
|
6
6
|
* they could author parameter schemas as `Type.Object({ name: Type.String() })`.
|
|
7
|
-
* Internally everything already runs through Zod (`wire.ts`, `validation.ts`);
|
|
8
|
-
* the only reason TypeBox remained was extension-author compat.
|
|
9
7
|
*
|
|
10
|
-
* This module
|
|
11
|
-
* return Zod schemas. Output is indistinguishable from hand-written Zod inside
|
|
12
|
-
* the agent pipeline:
|
|
8
|
+
* This module provides the subset those integrations depend on:
|
|
13
9
|
*
|
|
14
|
-
* -
|
|
15
|
-
* - `
|
|
16
|
-
*
|
|
10
|
+
* - TypeBox-style `Type.*` builders.
|
|
11
|
+
* - Runtime validation through `schema.safeParse(input)` and `schema.__validator(input)`.
|
|
12
|
+
* - Enumerable JSON Schema keywords so `{ ...schema }`, `JSON.stringify(schema)`,
|
|
13
|
+
* and `toolWireSchema({ parameters: schema })` all see the same schema.
|
|
17
14
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `@sinclair/typebox` directly in their own package.
|
|
15
|
+
* Internal validator fields and methods are intentionally non-enumerable. The
|
|
16
|
+
* object should look like JSON Schema at every serialization/wire boundary and
|
|
17
|
+
* like a small validator at runtime.
|
|
22
18
|
*/
|
|
23
19
|
|
|
24
|
-
import { areJsonValuesEqual
|
|
25
|
-
import {
|
|
26
|
-
type ZodArray,
|
|
27
|
-
type ZodEnum,
|
|
28
|
-
type ZodObject,
|
|
29
|
-
type ZodOptional,
|
|
30
|
-
type ZodRawShape,
|
|
31
|
-
type ZodType,
|
|
32
|
-
z,
|
|
33
|
-
} from "zod/v4";
|
|
20
|
+
import { areJsonValuesEqual } from "@oh-my-pi/pi-ai/utils/schema";
|
|
34
21
|
|
|
35
22
|
// ---------------------------------------------------------------------------
|
|
36
23
|
// Type aliases — exported so `import type { Static, TSchema } from "..."`
|
|
37
24
|
// patterns keep compiling at the call site.
|
|
38
25
|
// ---------------------------------------------------------------------------
|
|
39
26
|
|
|
40
|
-
export type TSchema =
|
|
41
|
-
export type Static<T extends
|
|
42
|
-
export type TAny =
|
|
43
|
-
export type TUnknown =
|
|
44
|
-
export type TNever =
|
|
45
|
-
export type TNull =
|
|
46
|
-
export type TString =
|
|
47
|
-
export type TNumber =
|
|
48
|
-
export type TInteger =
|
|
49
|
-
export type TBoolean =
|
|
50
|
-
export type TLiteral<
|
|
51
|
-
export type TArray<
|
|
52
|
-
export type TObject<
|
|
53
|
-
export type TOptional<
|
|
54
|
-
export type TUnion<_T extends readonly
|
|
55
|
-
export type TEnum<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
27
|
+
export type TSchema = ArkSchema;
|
|
28
|
+
export type Static<T extends ArkSchema> = T["__infer"];
|
|
29
|
+
export type TAny = ArkSchema;
|
|
30
|
+
export type TUnknown = ArkSchema;
|
|
31
|
+
export type TNever = ArkSchema;
|
|
32
|
+
export type TNull = ArkSchema;
|
|
33
|
+
export type TString = ArkSchema;
|
|
34
|
+
export type TNumber = ArkSchema;
|
|
35
|
+
export type TInteger = ArkSchema;
|
|
36
|
+
export type TBoolean = ArkSchema;
|
|
37
|
+
export type TLiteral<_V extends string | number | boolean> = ArkSchema;
|
|
38
|
+
export type TArray<_E extends ArkSchema> = ArkSchema;
|
|
39
|
+
export type TObject<_P extends Record<string, ArkSchema> = Record<string, ArkSchema>> = ArkSchema;
|
|
40
|
+
export type TOptional<_E extends ArkSchema> = ArkSchema;
|
|
41
|
+
export type TUnion<_T extends readonly ArkSchema[] = readonly ArkSchema[]> = ArkSchema;
|
|
42
|
+
export type TEnum<_T extends readonly (string | number)[] = readonly (string | number)[]> = ArkSchema;
|
|
43
|
+
export type TRecord<_K extends ArkSchema, _V extends ArkSchema> = ArkSchema;
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// ArkSchema wrapper — JSON Schema object with hidden validator metadata
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
const VALIDATION_FAILURE = Symbol("pi.typebox.validationFailure");
|
|
50
|
+
|
|
51
|
+
interface ValidationFailure {
|
|
52
|
+
message: string;
|
|
53
|
+
readonly [VALIDATION_FAILURE]: true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface SafeParseSuccess {
|
|
57
|
+
success: true;
|
|
58
|
+
data: unknown;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface SafeParseFailure {
|
|
62
|
+
success: false;
|
|
63
|
+
error: ValidationFailure;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface SchemaInternals {
|
|
67
|
+
optional?: boolean;
|
|
68
|
+
metadata?: Record<string, unknown>;
|
|
69
|
+
properties?: Record<string, ArkSchema>;
|
|
70
|
+
additionalProperties?: boolean | ArkSchema;
|
|
71
|
+
inner?: ArkSchema;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* JSON-Schema-shaped object with non-enumerable runtime helpers.
|
|
76
|
+
* Validators return the validated data or a marked `{ message }` failure.
|
|
77
|
+
*/
|
|
78
|
+
interface ArkSchema {
|
|
79
|
+
__validator: (data: unknown) => unknown;
|
|
80
|
+
__metadata?: Record<string, unknown>;
|
|
81
|
+
__optional?: true;
|
|
82
|
+
__properties?: Record<string, ArkSchema>;
|
|
83
|
+
__additionalProperties?: boolean | ArkSchema;
|
|
84
|
+
__infer?: unknown;
|
|
85
|
+
__inner?: ArkSchema;
|
|
86
|
+
safeParse(input: unknown): SafeParseSuccess | SafeParseFailure;
|
|
87
|
+
toJSON(): Record<string, unknown>;
|
|
88
|
+
[key: string]: unknown;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function defineHidden(target: object, key: PropertyKey, value: unknown): void {
|
|
92
|
+
Object.defineProperty(target, key, {
|
|
93
|
+
value,
|
|
94
|
+
writable: true,
|
|
95
|
+
configurable: true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function validationFailure(message: string): ValidationFailure {
|
|
100
|
+
const failure = { message } as ValidationFailure;
|
|
101
|
+
defineHidden(failure, VALIDATION_FAILURE, true);
|
|
102
|
+
return failure;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isValidationFailure(value: unknown): value is ValidationFailure {
|
|
106
|
+
if (value === null || typeof value !== "object") return false;
|
|
107
|
+
const candidate = value as { readonly [VALIDATION_FAILURE]?: unknown };
|
|
108
|
+
return candidate[VALIDATION_FAILURE] === true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function schemaJsonValue(value: unknown): unknown {
|
|
112
|
+
if (Array.isArray(value)) return value.map(schemaJsonValue);
|
|
113
|
+
if (value === null || typeof value !== "object") return value;
|
|
114
|
+
const result: Record<string, unknown> = {};
|
|
115
|
+
for (const key in value) {
|
|
116
|
+
result[key] = schemaJsonValue((value as Record<string, unknown>)[key]);
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function jsonSchemaOf(schema: ArkSchema): Record<string, unknown> {
|
|
122
|
+
const result: Record<string, unknown> = {};
|
|
123
|
+
for (const key in schema) result[key] = schemaJsonValue(schema[key]);
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createArkSchema(
|
|
128
|
+
validator: (data: unknown) => unknown,
|
|
129
|
+
jsonSchema: Record<string, unknown> = {},
|
|
130
|
+
internals: SchemaInternals = {},
|
|
131
|
+
): ArkSchema {
|
|
132
|
+
const schema = { ...jsonSchema } as ArkSchema;
|
|
133
|
+
const metadata = internals.metadata ?? {};
|
|
134
|
+
defineHidden(schema, "__validator", validator);
|
|
135
|
+
defineHidden(schema, "__metadata", metadata);
|
|
136
|
+
if (internals.optional) defineHidden(schema, "__optional", true);
|
|
137
|
+
if (internals.properties) defineHidden(schema, "__properties", internals.properties);
|
|
138
|
+
if (internals.additionalProperties !== undefined) {
|
|
139
|
+
defineHidden(schema, "__additionalProperties", internals.additionalProperties);
|
|
140
|
+
}
|
|
141
|
+
if (internals.inner) defineHidden(schema, "__inner", internals.inner);
|
|
142
|
+
defineHidden(schema, "safeParse", (input: unknown): SafeParseSuccess | SafeParseFailure => {
|
|
143
|
+
const result = validator(input);
|
|
144
|
+
if (isValidationFailure(result)) return { success: false, error: result };
|
|
145
|
+
return { success: true, data: result };
|
|
146
|
+
});
|
|
147
|
+
defineHidden(schema, "toJSON", () => jsonSchemaOf(schema));
|
|
148
|
+
return schema;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function cloneSchemaWith(
|
|
152
|
+
schema: ArkSchema,
|
|
153
|
+
jsonSchema: Record<string, unknown>,
|
|
154
|
+
internals?: SchemaInternals,
|
|
155
|
+
): ArkSchema {
|
|
156
|
+
return createArkSchema(schema.__validator, jsonSchema, {
|
|
157
|
+
metadata: internals?.metadata ?? schema.__metadata,
|
|
158
|
+
optional: internals?.optional ?? schema.__optional === true,
|
|
159
|
+
properties: internals?.properties ?? schema.__properties,
|
|
160
|
+
additionalProperties: internals?.additionalProperties ?? schema.__additionalProperties,
|
|
161
|
+
inner: internals?.inner ?? schema.__inner,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function withMetadata(schema: ArkSchema, newMeta: Record<string, unknown>): ArkSchema {
|
|
166
|
+
return cloneSchemaWith(
|
|
167
|
+
schema,
|
|
168
|
+
{ ...jsonSchemaOf(schema), ...newMeta },
|
|
169
|
+
{
|
|
170
|
+
metadata: { ...(schema.__metadata ?? {}), ...newMeta },
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
}
|
|
59
174
|
|
|
60
175
|
// ---------------------------------------------------------------------------
|
|
61
176
|
// Option shapes — loose subset of JSON Schema metadata + per-type constraints.
|
|
@@ -97,288 +212,700 @@ interface ObjectOpts extends Meta {
|
|
|
97
212
|
* TypeBox default: extra keys are preserved. Set `false` to reject unknowns,
|
|
98
213
|
* `true` to allow any, or a schema to validate them.
|
|
99
214
|
*/
|
|
100
|
-
additionalProperties?: boolean |
|
|
215
|
+
additionalProperties?: boolean | ArkSchema;
|
|
101
216
|
}
|
|
102
217
|
|
|
103
218
|
// ---------------------------------------------------------------------------
|
|
104
219
|
// Helpers
|
|
105
220
|
// ---------------------------------------------------------------------------
|
|
106
221
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
* fallback) ships the raw Zod internals (`def`, `_zod`, object-shaped `enum`,
|
|
114
|
-
* `"type":"enum"`) — neither valid JSON Schema nor parseable Zod. See
|
|
115
|
-
* issue #1101 for the symptoms when this leaks into a tool's `input_schema`.
|
|
116
|
-
*
|
|
117
|
-
* Idempotent: re-stamping the same instance is a no-op.
|
|
118
|
-
*/
|
|
119
|
-
function wire<T extends ZodType>(schema: T): T {
|
|
120
|
-
if (!Object.hasOwn(schema as object, "toJSON")) {
|
|
121
|
-
Object.defineProperty(schema as object, "toJSON", {
|
|
122
|
-
value: function toJSON(this: ZodType) {
|
|
123
|
-
return zodToWireSchema(this);
|
|
124
|
-
},
|
|
125
|
-
enumerable: false,
|
|
126
|
-
writable: true,
|
|
127
|
-
configurable: true,
|
|
128
|
-
});
|
|
222
|
+
function applyMeta(schema: ArkSchema, opts: Meta | undefined): ArkSchema {
|
|
223
|
+
if (!opts) return schema;
|
|
224
|
+
const metadata: Record<string, unknown> = {};
|
|
225
|
+
for (const key in opts) {
|
|
226
|
+
if (key === "additionalProperties") continue;
|
|
227
|
+
metadata[key] = opts[key];
|
|
129
228
|
}
|
|
130
|
-
return schema;
|
|
229
|
+
return withMetadata(schema, metadata);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function createStringValidator(
|
|
233
|
+
baseValidator: (data: unknown) => unknown,
|
|
234
|
+
opts?: StringOpts,
|
|
235
|
+
): (data: unknown) => unknown {
|
|
236
|
+
return (data: unknown) => {
|
|
237
|
+
const result = baseValidator(data);
|
|
238
|
+
if (isValidationFailure(result)) return result;
|
|
239
|
+
if (typeof result !== "string") return validationFailure("Expected string");
|
|
240
|
+
if (opts?.minLength !== undefined && result.length < opts.minLength) {
|
|
241
|
+
return validationFailure(`String must have at least ${opts.minLength} characters`);
|
|
242
|
+
}
|
|
243
|
+
if (opts?.maxLength !== undefined && result.length > opts.maxLength) {
|
|
244
|
+
return validationFailure(`String must have at most ${opts.maxLength} characters`);
|
|
245
|
+
}
|
|
246
|
+
if (opts?.pattern !== undefined) {
|
|
247
|
+
const regex = new RegExp(opts.pattern);
|
|
248
|
+
if (!regex.test(result)) return validationFailure(`String must match pattern ${opts.pattern}`);
|
|
249
|
+
}
|
|
250
|
+
return result;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function createFormatStringValidator(format: string): (data: unknown) => unknown {
|
|
255
|
+
return (data: unknown) => {
|
|
256
|
+
if (typeof data !== "string") return validationFailure("Expected string");
|
|
257
|
+
switch (format) {
|
|
258
|
+
case "email": {
|
|
259
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
260
|
+
return emailRegex.test(data) ? data : validationFailure("Invalid email format");
|
|
261
|
+
}
|
|
262
|
+
case "url":
|
|
263
|
+
case "uri":
|
|
264
|
+
try {
|
|
265
|
+
new URL(data);
|
|
266
|
+
return data;
|
|
267
|
+
} catch {
|
|
268
|
+
return validationFailure("Invalid URL format");
|
|
269
|
+
}
|
|
270
|
+
case "uuid": {
|
|
271
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
272
|
+
return uuidRegex.test(data) ? data : validationFailure("Invalid UUID format");
|
|
273
|
+
}
|
|
274
|
+
case "date": {
|
|
275
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
276
|
+
if (!dateRegex.test(data)) return validationFailure("Invalid date format (YYYY-MM-DD)");
|
|
277
|
+
const date = new Date(data);
|
|
278
|
+
return Number.isNaN(date.getTime()) ? validationFailure("Invalid date") : data;
|
|
279
|
+
}
|
|
280
|
+
case "date-time": {
|
|
281
|
+
const dateTime = new Date(data);
|
|
282
|
+
return Number.isNaN(dateTime.getTime()) ? validationFailure("Invalid date-time format") : data;
|
|
283
|
+
}
|
|
284
|
+
case "time": {
|
|
285
|
+
const timeRegex = /^\d{2}:\d{2}:\d{2}(.\d{3})?([+-]\d{2}:\d{2}|Z)?$/;
|
|
286
|
+
return timeRegex.test(data) ? data : validationFailure("Invalid time format");
|
|
287
|
+
}
|
|
288
|
+
case "ipv4": {
|
|
289
|
+
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
290
|
+
if (!ipv4Regex.test(data)) return validationFailure("Invalid IPv4 format");
|
|
291
|
+
const parts = data.split(".").map(Number);
|
|
292
|
+
return parts.some(part => part > 255) ? validationFailure("Invalid IPv4 address") : data;
|
|
293
|
+
}
|
|
294
|
+
case "ipv6": {
|
|
295
|
+
const ipv6Regex = /^([\da-f]{1,4}:){7}[\da-f]{1,4}$/i;
|
|
296
|
+
return ipv6Regex.test(data) ? data : validationFailure("Invalid IPv6 format");
|
|
297
|
+
}
|
|
298
|
+
default:
|
|
299
|
+
return data;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
131
302
|
}
|
|
132
303
|
|
|
133
|
-
function
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
304
|
+
function createNumberValidator(isInteger = false): (data: unknown) => unknown {
|
|
305
|
+
return (data: unknown) => {
|
|
306
|
+
if (typeof data !== "number" || Number.isNaN(data)) {
|
|
307
|
+
return validationFailure(`Expected ${isInteger ? "integer" : "number"}`);
|
|
308
|
+
}
|
|
309
|
+
if (isInteger && !Number.isInteger(data)) return validationFailure("Expected integer");
|
|
310
|
+
return data;
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function createConstrainedNumberValidator(
|
|
315
|
+
baseValidator: (data: unknown) => unknown,
|
|
316
|
+
opts?: NumberOpts,
|
|
317
|
+
): (data: unknown) => unknown {
|
|
318
|
+
return (data: unknown) => {
|
|
319
|
+
const result = baseValidator(data);
|
|
320
|
+
if (isValidationFailure(result)) return result;
|
|
321
|
+
if (typeof result !== "number") return validationFailure("Expected number");
|
|
322
|
+
if (opts?.minimum !== undefined && result < opts.minimum) {
|
|
323
|
+
return validationFailure(`Number must be at least ${opts.minimum}`);
|
|
324
|
+
}
|
|
325
|
+
if (opts?.maximum !== undefined && result > opts.maximum) {
|
|
326
|
+
return validationFailure(`Number must be at most ${opts.maximum}`);
|
|
327
|
+
}
|
|
328
|
+
if (opts?.exclusiveMinimum !== undefined && result <= opts.exclusiveMinimum) {
|
|
329
|
+
return validationFailure(`Number must be greater than ${opts.exclusiveMinimum}`);
|
|
330
|
+
}
|
|
331
|
+
if (opts?.exclusiveMaximum !== undefined && result >= opts.exclusiveMaximum) {
|
|
332
|
+
return validationFailure(`Number must be less than ${opts.exclusiveMaximum}`);
|
|
333
|
+
}
|
|
334
|
+
if (opts?.multipleOf !== undefined && result % opts.multipleOf !== 0) {
|
|
335
|
+
return validationFailure(`Number must be a multiple of ${opts.multipleOf}`);
|
|
336
|
+
}
|
|
337
|
+
return result;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
138
340
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
341
|
+
function createArrayValidator(itemValidator: ArkSchema, opts?: ArrayOpts): (data: unknown) => unknown {
|
|
342
|
+
return (data: unknown) => {
|
|
343
|
+
if (!Array.isArray(data)) return validationFailure("Expected array");
|
|
344
|
+
if (opts?.minItems !== undefined && data.length < opts.minItems) {
|
|
345
|
+
return validationFailure(`Array must have at least ${opts.minItems} items`);
|
|
143
346
|
}
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
|
|
347
|
+
if (opts?.maxItems !== undefined && data.length > opts.maxItems) {
|
|
348
|
+
return validationFailure(`Array must have at most ${opts.maxItems} items`);
|
|
349
|
+
}
|
|
350
|
+
if (opts?.uniqueItems === true) {
|
|
351
|
+
for (let i = 0; i < data.length; i++) {
|
|
352
|
+
for (let j = i + 1; j < data.length; j++) {
|
|
353
|
+
if (areJsonValuesEqual(data[i], data[j])) return validationFailure("Array items must be unique");
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const itemValidatorFn = itemValidator.__validator;
|
|
358
|
+
for (let i = 0; i < data.length; i++) {
|
|
359
|
+
const itemResult = itemValidatorFn(data[i]);
|
|
360
|
+
if (isValidationFailure(itemResult)) {
|
|
361
|
+
return validationFailure(`Item at index ${i}: ${itemResult.message || "Invalid"}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return data;
|
|
365
|
+
};
|
|
147
366
|
}
|
|
148
367
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
368
|
+
function createTupleValidator(itemSchemas: ArkSchema[]): (data: unknown) => unknown {
|
|
369
|
+
return (data: unknown) => {
|
|
370
|
+
if (!Array.isArray(data)) return validationFailure("Expected array");
|
|
371
|
+
if (data.length !== itemSchemas.length) {
|
|
372
|
+
return validationFailure(`Expected tuple of length ${itemSchemas.length}, got ${data.length}`);
|
|
373
|
+
}
|
|
374
|
+
for (let i = 0; i < itemSchemas.length; i++) {
|
|
375
|
+
const itemValidator = itemSchemas[i].__validator;
|
|
376
|
+
const itemResult = itemValidator(data[i]);
|
|
377
|
+
if (isValidationFailure(itemResult)) {
|
|
378
|
+
return validationFailure(`Item at index ${i}: ${itemResult.message || "Invalid"}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return data;
|
|
382
|
+
};
|
|
383
|
+
}
|
|
152
384
|
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
385
|
+
function createObjectValidator(properties: Record<string, ArkSchema>, opts?: ObjectOpts): (data: unknown) => unknown {
|
|
386
|
+
return (data: unknown) => {
|
|
387
|
+
if (!data || typeof data !== "object") return validationFailure("Expected object");
|
|
388
|
+
const obj = data as Record<string, unknown>;
|
|
389
|
+
const result: Record<string, unknown> = {};
|
|
390
|
+
|
|
391
|
+
const keys = new Set<string>();
|
|
392
|
+
for (const key in obj) {
|
|
393
|
+
keys.add(key);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
for (const key in properties) {
|
|
397
|
+
const schema = properties[key];
|
|
398
|
+
const validated = schema.__validator(obj[key]);
|
|
399
|
+
if (isValidationFailure(validated)) {
|
|
400
|
+
return validationFailure(`Property ${key}: ${validated.message || "Invalid"}`);
|
|
401
|
+
}
|
|
402
|
+
if (obj[key] !== undefined || schema.__optional !== true) {
|
|
403
|
+
result[key] = validated;
|
|
404
|
+
}
|
|
405
|
+
keys.delete(key);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const additionalProperties = opts?.additionalProperties;
|
|
409
|
+
if (additionalProperties === false) {
|
|
410
|
+
if (keys.size > 0) return validationFailure(`Unexpected properties: ${Array.from(keys).join(", ")}`);
|
|
411
|
+
} else if (additionalProperties === true || additionalProperties === undefined) {
|
|
412
|
+
for (const key of keys) result[key] = obj[key];
|
|
413
|
+
} else {
|
|
414
|
+
const additionalValidator = additionalProperties.__validator;
|
|
415
|
+
for (const key of keys) {
|
|
416
|
+
const validated = additionalValidator(obj[key]);
|
|
417
|
+
if (isValidationFailure(validated)) {
|
|
418
|
+
return validationFailure(`Property ${key}: ${validated.message || "Invalid"}`);
|
|
419
|
+
}
|
|
420
|
+
result[key] = validated;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return result;
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function createUnionValidator(schemas: ArkSchema[]): (data: unknown) => unknown {
|
|
428
|
+
return (data: unknown) => {
|
|
429
|
+
if (schemas.length === 0) return validationFailure("Cannot validate empty union");
|
|
430
|
+
const errors: string[] = [];
|
|
431
|
+
for (const schema of schemas) {
|
|
432
|
+
const result = schema.__validator(data);
|
|
433
|
+
if (!isValidationFailure(result)) return result;
|
|
434
|
+
errors.push(result.message || "Invalid");
|
|
435
|
+
}
|
|
436
|
+
return validationFailure(`Failed all union options: ${errors.join("; ")}`);
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function createIntersectionValidator(schemas: ArkSchema[]): (data: unknown) => unknown {
|
|
441
|
+
return (data: unknown) => {
|
|
442
|
+
let result = data;
|
|
443
|
+
for (const schema of schemas) {
|
|
444
|
+
result = schema.__validator(result);
|
|
445
|
+
if (isValidationFailure(result)) return result;
|
|
446
|
+
}
|
|
447
|
+
return result;
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function createOptionalValidator(schema: ArkSchema): (data: unknown) => unknown {
|
|
452
|
+
const baseValidator = schema.__validator;
|
|
453
|
+
return (data: unknown) => (data === undefined ? undefined : baseValidator(data));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function createNullableValidator(schema: ArkSchema): (data: unknown) => unknown {
|
|
457
|
+
const baseValidator = schema.__validator;
|
|
458
|
+
return (data: unknown) => (data === null ? null : baseValidator(data));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function createRecordValidator(keySchema: ArkSchema, valueSchema: ArkSchema): (data: unknown) => unknown {
|
|
462
|
+
return (data: unknown) => {
|
|
463
|
+
if (!data || typeof data !== "object") return validationFailure("Expected object");
|
|
464
|
+
const obj = data as Record<string, unknown>;
|
|
465
|
+
const result: Record<string, unknown> = {};
|
|
466
|
+
const keyValidator = keySchema.__validator;
|
|
467
|
+
const valueValidator = valueSchema.__validator;
|
|
468
|
+
for (const key in obj) {
|
|
469
|
+
const value = obj[key];
|
|
470
|
+
const keyResult = keyValidator(key);
|
|
471
|
+
if (isValidationFailure(keyResult)) {
|
|
472
|
+
return validationFailure(`Key ${key}: ${keyResult.message || "Invalid"}`);
|
|
473
|
+
}
|
|
474
|
+
const valueResult = valueValidator(value);
|
|
475
|
+
if (isValidationFailure(valueResult)) {
|
|
476
|
+
return validationFailure(`Key ${key}: ${valueResult.message || "Invalid"}`);
|
|
477
|
+
}
|
|
478
|
+
result[key] = valueResult;
|
|
479
|
+
}
|
|
480
|
+
return result;
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function isArrayIndexKey(key: string): boolean {
|
|
485
|
+
if (!/^(?:0|[1-9]\d*)$/.test(key)) return false;
|
|
486
|
+
const index = Number(key);
|
|
487
|
+
return Number.isSafeInteger(index) && index >= 0;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function uniqueLiteralValues(values: readonly (string | number | boolean)[]): Array<string | number | boolean> {
|
|
491
|
+
const unique: Array<string | number | boolean> = [];
|
|
492
|
+
for (const value of values) {
|
|
493
|
+
if (!unique.some(existing => existing === value)) unique.push(value);
|
|
194
494
|
}
|
|
195
|
-
return
|
|
495
|
+
return unique;
|
|
196
496
|
}
|
|
197
497
|
|
|
198
|
-
function
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
498
|
+
function requiredKeys(properties: Record<string, ArkSchema>): string[] | undefined {
|
|
499
|
+
const required: string[] = [];
|
|
500
|
+
for (const key in properties) {
|
|
501
|
+
if (properties[key].__optional !== true) {
|
|
502
|
+
required.push(key);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return required.length === 0 ? undefined : required;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function objectJsonSchema(properties: Record<string, ArkSchema>, opts?: ObjectOpts): Record<string, unknown> {
|
|
509
|
+
const propertySchemas: Record<string, unknown> = {};
|
|
510
|
+
for (const key in properties) propertySchemas[key] = jsonSchemaOf(properties[key]);
|
|
511
|
+
const schema: Record<string, unknown> = { type: "object", properties: propertySchemas };
|
|
512
|
+
const required = requiredKeys(properties);
|
|
513
|
+
if (required) schema.required = required;
|
|
514
|
+
const additionalProperties = opts?.additionalProperties;
|
|
515
|
+
if (additionalProperties !== undefined) {
|
|
516
|
+
schema.additionalProperties =
|
|
517
|
+
typeof additionalProperties === "boolean" ? additionalProperties : jsonSchemaOf(additionalProperties);
|
|
518
|
+
}
|
|
519
|
+
return schema;
|
|
207
520
|
}
|
|
208
521
|
|
|
209
|
-
function
|
|
210
|
-
|
|
522
|
+
function schemaWithoutOptional(schema: ArkSchema): ArkSchema {
|
|
523
|
+
const base = schema.__inner ?? schema;
|
|
524
|
+
return createArkSchema(base.__validator, jsonSchemaOf(base), {
|
|
525
|
+
metadata: base.__metadata,
|
|
526
|
+
properties: base.__properties,
|
|
527
|
+
additionalProperties: base.__additionalProperties,
|
|
528
|
+
});
|
|
211
529
|
}
|
|
212
530
|
|
|
213
|
-
|
|
214
|
-
|
|
531
|
+
// ---------------------------------------------------------------------------
|
|
532
|
+
// Builders
|
|
533
|
+
// ---------------------------------------------------------------------------
|
|
534
|
+
|
|
535
|
+
function tString(opts?: StringOpts): ArkSchema {
|
|
536
|
+
let validator: (data: unknown) => unknown = opts?.format
|
|
537
|
+
? createFormatStringValidator(opts.format)
|
|
538
|
+
: (data: unknown) => (typeof data === "string" ? data : validationFailure("Expected string"));
|
|
539
|
+
validator = createStringValidator(validator, opts);
|
|
540
|
+
return applyMeta(createArkSchema(validator, { type: "string" }), opts);
|
|
215
541
|
}
|
|
216
542
|
|
|
217
|
-
function
|
|
218
|
-
|
|
543
|
+
function tNumber(opts?: NumberOpts): ArkSchema {
|
|
544
|
+
const validator = createConstrainedNumberValidator(createNumberValidator(false), opts);
|
|
545
|
+
return applyMeta(createArkSchema(validator, { type: "number" }), opts);
|
|
219
546
|
}
|
|
220
547
|
|
|
221
|
-
function
|
|
222
|
-
|
|
548
|
+
function tInteger(opts?: NumberOpts): ArkSchema {
|
|
549
|
+
const validator = createConstrainedNumberValidator(createNumberValidator(true), opts);
|
|
550
|
+
return applyMeta(createArkSchema(validator, { type: "integer" }), opts);
|
|
223
551
|
}
|
|
224
552
|
|
|
225
|
-
function
|
|
226
|
-
|
|
553
|
+
function tBoolean(opts?: Meta): ArkSchema {
|
|
554
|
+
const validator = (data: unknown) => (typeof data === "boolean" ? data : validationFailure("Expected boolean"));
|
|
555
|
+
return applyMeta(createArkSchema(validator, { type: "boolean" }), opts);
|
|
227
556
|
}
|
|
228
557
|
|
|
229
|
-
function
|
|
230
|
-
|
|
558
|
+
function tNull(opts?: Meta): ArkSchema {
|
|
559
|
+
const validator = (data: unknown) => (data === null ? data : validationFailure("Expected null"));
|
|
560
|
+
return applyMeta(createArkSchema(validator, { type: "null" }), opts);
|
|
231
561
|
}
|
|
232
562
|
|
|
233
|
-
function
|
|
234
|
-
return
|
|
563
|
+
function tAny(opts?: Meta): ArkSchema {
|
|
564
|
+
return applyMeta(
|
|
565
|
+
createArkSchema((data: unknown) => data, {}),
|
|
566
|
+
opts,
|
|
567
|
+
);
|
|
235
568
|
}
|
|
236
569
|
|
|
237
|
-
function
|
|
238
|
-
return
|
|
570
|
+
function tUnknown(opts?: Meta): ArkSchema {
|
|
571
|
+
return applyMeta(
|
|
572
|
+
createArkSchema((data: unknown) => data, {}),
|
|
573
|
+
opts,
|
|
574
|
+
);
|
|
239
575
|
}
|
|
240
576
|
|
|
241
|
-
function
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
577
|
+
function tNever(opts?: Meta): ArkSchema {
|
|
578
|
+
return applyMeta(
|
|
579
|
+
createArkSchema(() => validationFailure("Never type does not accept any value"), { not: {} }),
|
|
580
|
+
opts,
|
|
581
|
+
);
|
|
245
582
|
}
|
|
246
583
|
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
for (let i = 1; i < schemas.length; i++) out = z.intersection(out, schemas[i] as ZodType) as ZodType;
|
|
252
|
-
return withMeta(out, opts);
|
|
584
|
+
function tLiteral<V extends string | number | boolean>(value: V, opts?: Meta): ArkSchema {
|
|
585
|
+
const validator = (data: unknown) =>
|
|
586
|
+
data === value ? data : validationFailure(`Expected literal ${JSON.stringify(value)}`);
|
|
587
|
+
return applyMeta(createArkSchema(validator, { const: value }), opts);
|
|
253
588
|
}
|
|
254
589
|
|
|
255
|
-
function
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
590
|
+
function tUnion<T extends readonly ArkSchema[]>(schemas: T, opts?: Meta): ArkSchema {
|
|
591
|
+
if (schemas.length === 0)
|
|
592
|
+
return applyMeta(
|
|
593
|
+
createArkSchema(() => validationFailure("Empty union"), { not: {} }),
|
|
594
|
+
opts,
|
|
595
|
+
);
|
|
596
|
+
if (schemas.length === 1) return applyMeta(schemas[0], opts);
|
|
597
|
+
const validator = createUnionValidator([...schemas]);
|
|
598
|
+
return applyMeta(createArkSchema(validator, { anyOf: schemas.map(jsonSchemaOf) }), opts);
|
|
259
599
|
}
|
|
260
600
|
|
|
261
|
-
function
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
601
|
+
function tIntersect(schemas: readonly ArkSchema[], opts?: Meta): ArkSchema {
|
|
602
|
+
if (schemas.length === 0)
|
|
603
|
+
return applyMeta(
|
|
604
|
+
createArkSchema((data: unknown) => data, {}),
|
|
605
|
+
opts,
|
|
606
|
+
);
|
|
607
|
+
if (schemas.length === 1) return applyMeta(schemas[0], opts);
|
|
608
|
+
const validator = createIntersectionValidator([...schemas]);
|
|
609
|
+
return applyMeta(createArkSchema(validator, { allOf: schemas.map(jsonSchemaOf) }), opts);
|
|
267
610
|
}
|
|
268
611
|
|
|
269
|
-
function literalUnion(values: readonly (string | number | boolean)[], opts?: Meta):
|
|
612
|
+
function literalUnion(values: readonly (string | number | boolean)[], opts?: Meta): ArkSchema {
|
|
270
613
|
const unique = uniqueLiteralValues(values);
|
|
271
|
-
if (unique.length === 0)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
614
|
+
if (unique.length === 0)
|
|
615
|
+
return applyMeta(
|
|
616
|
+
createArkSchema(() => validationFailure("Empty literal union"), { not: {} }),
|
|
617
|
+
opts,
|
|
618
|
+
);
|
|
619
|
+
if (unique.length === 1) return tLiteral(unique[0] as string | number | boolean, opts);
|
|
620
|
+
const validator = (data: unknown) => {
|
|
621
|
+
for (const value of unique) if (data === value) return data;
|
|
622
|
+
return validationFailure(`Expected one of: ${unique.join(", ")}`);
|
|
623
|
+
};
|
|
624
|
+
return applyMeta(createArkSchema(validator, { enum: unique }), opts);
|
|
279
625
|
}
|
|
626
|
+
|
|
280
627
|
function tEnum<T extends Record<string, string | number> | readonly (string | number)[]>(
|
|
281
628
|
values: T,
|
|
282
629
|
opts?: Meta,
|
|
283
|
-
):
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
630
|
+
): ArkSchema {
|
|
631
|
+
let list: (string | number)[];
|
|
632
|
+
if (Array.isArray(values)) {
|
|
633
|
+
list = values;
|
|
634
|
+
} else {
|
|
635
|
+
list = [];
|
|
636
|
+
for (const key in values) {
|
|
637
|
+
const value = values[key];
|
|
638
|
+
if (!(isArrayIndexKey(key) && typeof value === "string")) {
|
|
639
|
+
list.push(value as string | number);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
289
643
|
return literalUnion(list, opts);
|
|
290
644
|
}
|
|
291
645
|
|
|
292
|
-
function tArray<E extends
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
646
|
+
function tArray<E extends ArkSchema>(item: E, opts?: ArrayOpts): ArkSchema {
|
|
647
|
+
const validator = createArrayValidator(item, opts);
|
|
648
|
+
return applyMeta(createArkSchema(validator, { type: "array", items: jsonSchemaOf(item) }), opts);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function tTuple(items: readonly ArkSchema[], opts?: Meta): ArkSchema {
|
|
652
|
+
const validator = createTupleValidator([...items]);
|
|
653
|
+
return applyMeta(
|
|
654
|
+
createArkSchema(validator, {
|
|
655
|
+
type: "array",
|
|
656
|
+
prefixItems: items.map(jsonSchemaOf),
|
|
657
|
+
minItems: items.length,
|
|
658
|
+
maxItems: items.length,
|
|
659
|
+
}),
|
|
660
|
+
opts,
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function tObject<P extends Record<string, ArkSchema>>(properties: P, opts?: ObjectOpts): ArkSchema {
|
|
665
|
+
const props = properties as Record<string, ArkSchema>;
|
|
666
|
+
const validator = createObjectValidator(props, opts);
|
|
667
|
+
const schema = createArkSchema(validator, objectJsonSchema(props, opts), {
|
|
668
|
+
properties: props,
|
|
669
|
+
additionalProperties: opts?.additionalProperties,
|
|
670
|
+
});
|
|
671
|
+
return applyMeta(schema, opts);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function literalRecordKeys(keySchema: ArkSchema): string[] | null {
|
|
675
|
+
const json = jsonSchemaOf(keySchema);
|
|
676
|
+
if ("const" in json) return [String(json.const)];
|
|
677
|
+
const values = json.enum;
|
|
678
|
+
if (!Array.isArray(values)) return null;
|
|
679
|
+
const keys: string[] = [];
|
|
680
|
+
for (const value of values) {
|
|
681
|
+
const type = typeof value;
|
|
682
|
+
if (type !== "string" && type !== "number" && type !== "boolean") return null;
|
|
683
|
+
keys.push(String(value));
|
|
308
684
|
}
|
|
309
|
-
return
|
|
685
|
+
return keys;
|
|
310
686
|
}
|
|
311
687
|
|
|
312
|
-
function
|
|
313
|
-
|
|
688
|
+
function createRecordJson(keySchema: ArkSchema, valueSchema: ArkSchema): Record<string, unknown> {
|
|
689
|
+
const valueJson = jsonSchemaOf(valueSchema);
|
|
690
|
+
const keys = literalRecordKeys(keySchema);
|
|
691
|
+
if (keys) {
|
|
692
|
+
const properties: Record<string, unknown> = {};
|
|
693
|
+
for (const key of keys) properties[key] = valueJson;
|
|
694
|
+
return { type: "object", properties, required: keys, additionalProperties: false };
|
|
695
|
+
}
|
|
696
|
+
const json: Record<string, unknown> = { type: "object", additionalProperties: valueJson };
|
|
697
|
+
const keyJson = jsonSchemaOf(keySchema);
|
|
698
|
+
if (Object.keys(keyJson).length > 0) json.propertyNames = keyJson;
|
|
699
|
+
return json;
|
|
314
700
|
}
|
|
315
701
|
|
|
316
|
-
function
|
|
317
|
-
const
|
|
318
|
-
return
|
|
702
|
+
function tRecord<K extends ArkSchema, V extends ArkSchema>(key: K, value: V, opts?: Meta): ArkSchema {
|
|
703
|
+
const validator = createRecordValidator(key, value);
|
|
704
|
+
return applyMeta(createArkSchema(validator, createRecordJson(key, value)), opts);
|
|
319
705
|
}
|
|
320
706
|
|
|
321
|
-
function
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
obj = obj.strict() as unknown as ZodObject<P>;
|
|
329
|
-
} else if (ap === undefined || ap === true) {
|
|
330
|
-
// TypeBox preserves unknown keys by default; Zod's default is `.strip()`.
|
|
331
|
-
obj = obj.loose() as unknown as ZodObject<P>;
|
|
332
|
-
} else {
|
|
333
|
-
obj = obj.catchall(ap) as unknown as ZodObject<P>;
|
|
334
|
-
}
|
|
335
|
-
return withMeta(obj, opts);
|
|
707
|
+
function tOptional<E extends ArkSchema>(schema: E, opts?: Meta): ArkSchema {
|
|
708
|
+
const validator = createOptionalValidator(schema);
|
|
709
|
+
const optional = createArkSchema(validator, jsonSchemaOf(schema), {
|
|
710
|
+
optional: true,
|
|
711
|
+
inner: schema,
|
|
712
|
+
});
|
|
713
|
+
return applyMeta(optional, opts);
|
|
336
714
|
}
|
|
337
715
|
|
|
338
|
-
function
|
|
339
|
-
|
|
716
|
+
function tNullable<E extends ArkSchema>(schema: E, opts?: Meta): ArkSchema {
|
|
717
|
+
const validator = createNullableValidator(schema);
|
|
718
|
+
return applyMeta(createArkSchema(validator, { anyOf: [jsonSchemaOf(schema), { type: "null" }] }), opts);
|
|
340
719
|
}
|
|
341
720
|
|
|
342
|
-
function
|
|
343
|
-
|
|
344
|
-
return
|
|
721
|
+
function tReadonly<E extends ArkSchema>(schema: E): ArkSchema {
|
|
722
|
+
// TypeBox's `Type.Readonly` is purely a marker; runtime validation is identical.
|
|
723
|
+
return schema;
|
|
345
724
|
}
|
|
346
725
|
|
|
347
|
-
function
|
|
348
|
-
|
|
726
|
+
function tPartial<_P extends Record<string, ArkSchema>>(obj: ArkSchema): ArkSchema {
|
|
727
|
+
if (obj.__properties) {
|
|
728
|
+
const properties: Record<string, ArkSchema> = {};
|
|
729
|
+
for (const key in obj.__properties) {
|
|
730
|
+
const schema = obj.__properties[key];
|
|
731
|
+
properties[key] = schema.__optional === true ? schema : tOptional(schema);
|
|
732
|
+
}
|
|
733
|
+
return tObject(properties, { additionalProperties: obj.__additionalProperties });
|
|
734
|
+
}
|
|
735
|
+
const objValidator = obj.__validator;
|
|
736
|
+
const metadata = jsonSchemaOf(obj);
|
|
737
|
+
delete metadata.required;
|
|
738
|
+
return createArkSchema(objValidator, metadata);
|
|
349
739
|
}
|
|
350
740
|
|
|
351
|
-
function
|
|
352
|
-
|
|
353
|
-
|
|
741
|
+
function tRequired<_P extends Record<string, ArkSchema>>(obj: ArkSchema): ArkSchema {
|
|
742
|
+
if (obj.__properties) {
|
|
743
|
+
const properties: Record<string, ArkSchema> = {};
|
|
744
|
+
for (const key in obj.__properties) {
|
|
745
|
+
properties[key] = schemaWithoutOptional(obj.__properties[key]);
|
|
746
|
+
}
|
|
747
|
+
return tObject(properties, { additionalProperties: obj.__additionalProperties });
|
|
748
|
+
}
|
|
749
|
+
const metadata = jsonSchemaOf(obj);
|
|
750
|
+
if (metadata.properties && typeof metadata.properties === "object" && !Array.isArray(metadata.properties)) {
|
|
751
|
+
const properties = metadata.properties as Record<string, unknown>;
|
|
752
|
+
const required: string[] = [];
|
|
753
|
+
for (const key in properties) {
|
|
754
|
+
required.push(key);
|
|
755
|
+
}
|
|
756
|
+
metadata.required = required;
|
|
757
|
+
}
|
|
758
|
+
return createArkSchema(obj.__validator, metadata);
|
|
354
759
|
}
|
|
355
760
|
|
|
356
|
-
function
|
|
357
|
-
|
|
358
|
-
|
|
761
|
+
function tPick<P extends Record<string, ArkSchema>, K extends keyof P>(obj: ArkSchema, keys: readonly K[]): ArkSchema {
|
|
762
|
+
const keySet = new Set([...keys].map(String));
|
|
763
|
+
if (obj.__properties) {
|
|
764
|
+
const properties: Record<string, ArkSchema> = {};
|
|
765
|
+
for (const key of keySet) {
|
|
766
|
+
const schema = obj.__properties[key];
|
|
767
|
+
if (schema) properties[key] = schema;
|
|
768
|
+
}
|
|
769
|
+
return tObject(properties, { additionalProperties: obj.__additionalProperties });
|
|
770
|
+
}
|
|
771
|
+
const validator = (data: unknown) => {
|
|
772
|
+
if (!data || typeof data !== "object") {
|
|
773
|
+
return validationFailure("Expected object");
|
|
774
|
+
}
|
|
359
775
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
776
|
+
const result: Record<string, unknown> = {};
|
|
777
|
+
const obj_data = data as Record<string, unknown>;
|
|
363
778
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
779
|
+
for (const key of keySet) {
|
|
780
|
+
if (key in obj_data) {
|
|
781
|
+
result[key] = obj_data[key];
|
|
782
|
+
}
|
|
783
|
+
}
|
|
368
784
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
785
|
+
return result;
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
const metadata = jsonSchemaOf(obj);
|
|
789
|
+
if (metadata.properties && typeof metadata.properties === "object" && !Array.isArray(metadata.properties)) {
|
|
790
|
+
const properties = metadata.properties as Record<string, unknown>;
|
|
791
|
+
const filteredProps: Record<string, unknown> = {};
|
|
792
|
+
for (const key in properties) {
|
|
793
|
+
if (keySet.has(key)) {
|
|
794
|
+
filteredProps[key] = properties[key];
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
metadata.properties = filteredProps;
|
|
798
|
+
}
|
|
799
|
+
if (Array.isArray(metadata.required)) {
|
|
800
|
+
metadata.required = metadata.required.filter(key => typeof key === "string" && keySet.has(key));
|
|
801
|
+
}
|
|
802
|
+
return createArkSchema(validator, metadata);
|
|
372
803
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
804
|
+
|
|
805
|
+
function tOmit<P extends Record<string, ArkSchema>, K extends keyof P>(obj: ArkSchema, keys: readonly K[]): ArkSchema {
|
|
806
|
+
const keySet = new Set([...keys].map(String));
|
|
807
|
+
if (obj.__properties) {
|
|
808
|
+
const properties: Record<string, ArkSchema> = {};
|
|
809
|
+
for (const key in obj.__properties) {
|
|
810
|
+
if (!keySet.has(key)) {
|
|
811
|
+
properties[key] = obj.__properties[key];
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return tObject(properties, { additionalProperties: obj.__additionalProperties });
|
|
815
|
+
}
|
|
816
|
+
const validator = (data: unknown) => {
|
|
817
|
+
if (!data || typeof data !== "object") {
|
|
818
|
+
return validationFailure("Expected object");
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
const result: Record<string, unknown> = {};
|
|
822
|
+
const obj_data = data as Record<string, unknown>;
|
|
823
|
+
|
|
824
|
+
for (const key in obj_data) {
|
|
825
|
+
if (!keySet.has(key)) {
|
|
826
|
+
result[key] = obj_data[key];
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return result;
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
const metadata = jsonSchemaOf(obj);
|
|
834
|
+
if (metadata.properties && typeof metadata.properties === "object" && !Array.isArray(metadata.properties)) {
|
|
835
|
+
const properties = metadata.properties as Record<string, unknown>;
|
|
836
|
+
const filteredProps: Record<string, unknown> = {};
|
|
837
|
+
for (const key in properties) {
|
|
838
|
+
if (!keySet.has(key)) {
|
|
839
|
+
filteredProps[key] = properties[key];
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
metadata.properties = filteredProps;
|
|
843
|
+
}
|
|
844
|
+
if (Array.isArray(metadata.required)) {
|
|
845
|
+
metadata.required = metadata.required.filter(key => typeof key === "string" && !keySet.has(key));
|
|
846
|
+
}
|
|
847
|
+
return createArkSchema(validator, metadata);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function tComposite(objects: readonly ArkSchema[], opts?: Meta): ArkSchema {
|
|
851
|
+
// Composite flattens object schemas into one
|
|
852
|
+
if (objects.length === 0) {
|
|
853
|
+
return applyMeta(
|
|
854
|
+
createArkSchema(
|
|
855
|
+
(data: unknown) => (data && typeof data === "object" ? data : validationFailure("Expected object")),
|
|
856
|
+
{
|
|
857
|
+
type: "object",
|
|
858
|
+
},
|
|
859
|
+
),
|
|
860
|
+
opts,
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (objects.length === 1) {
|
|
865
|
+
return applyMeta(objects[0], opts);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
let canFlatten = true;
|
|
869
|
+
const properties: Record<string, ArkSchema> = {};
|
|
870
|
+
for (const schema of objects) {
|
|
871
|
+
if (!schema.__properties) {
|
|
872
|
+
canFlatten = false;
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
for (const key in schema.__properties) {
|
|
876
|
+
properties[key] = schema.__properties[key];
|
|
877
|
+
}
|
|
380
878
|
}
|
|
381
|
-
return
|
|
879
|
+
if (canFlatten) return tObject(properties, opts as ObjectOpts | undefined);
|
|
880
|
+
|
|
881
|
+
// Merge all object validators
|
|
882
|
+
const validator = (data: unknown) => {
|
|
883
|
+
if (!data || typeof data !== "object") {
|
|
884
|
+
return validationFailure("Expected object");
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const result = {} as Record<string, unknown>;
|
|
888
|
+
const obj_data = data as Record<string, unknown>;
|
|
889
|
+
|
|
890
|
+
for (const schema of objects) {
|
|
891
|
+
const schemaValidator = schema.__validator;
|
|
892
|
+
const schemaResult = schemaValidator(obj_data);
|
|
893
|
+
|
|
894
|
+
if (isValidationFailure(schemaResult)) {
|
|
895
|
+
return schemaResult;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
if (typeof schemaResult === "object" && schemaResult !== null) {
|
|
899
|
+
for (const key in schemaResult) {
|
|
900
|
+
result[key] = (schemaResult as Record<string, unknown>)[key];
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
return result;
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
return applyMeta(createArkSchema(validator, { allOf: objects.map(jsonSchemaOf) }), opts);
|
|
382
909
|
}
|
|
383
910
|
|
|
384
911
|
// ---------------------------------------------------------------------------
|