toolcraft-schema 0.0.304 → 0.0.306
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/README.md +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/json-schema/properties.d.ts +16 -0
- package/dist/json-schema/properties.js +129 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ and JSON Schema generation.
|
|
|
11
11
|
- Runtime validation with `validateValue()`
|
|
12
12
|
- JSON Schema serialization via `toJsonSchema()`
|
|
13
13
|
- JSON Schema document serialization via `toJsonSchemaDocument()`
|
|
14
|
+
- Native JSON Schema compilation and property projection with reference support
|
|
14
15
|
|
|
15
16
|
## Usage
|
|
16
17
|
|
|
@@ -83,6 +84,16 @@ const validation = validateValue(schema, {
|
|
|
83
84
|
- Invalid input returns `{ ok: false, issues }` with path-aware diagnostics.
|
|
84
85
|
- Validation applies defaults from schema descriptors.
|
|
85
86
|
|
|
87
|
+
`compileJsonSchema(document, options)` validates complete native JSON Schema
|
|
88
|
+
documents. `projectJsonSchemaProperties(document, options)` supplies stable
|
|
89
|
+
property names, unconditional required metadata, resolved schema annotations,
|
|
90
|
+
and candidate validators for CLI generation. It follows references, compositions,
|
|
91
|
+
embedded resource IDs and conditional declarations using the same compiler.
|
|
92
|
+
Property candidate validators accept values matching at least one declaration;
|
|
93
|
+
validate the complete object to enforce branch-dependent and combined constraints.
|
|
94
|
+
Both functions accept a `registry` for external schema documents without network
|
|
95
|
+
fetching.
|
|
96
|
+
|
|
86
97
|
## Environment Variables
|
|
87
98
|
|
|
88
99
|
This package exposes no environment variables.
|
package/dist/index.d.ts
CHANGED
|
@@ -185,6 +185,8 @@ export { isJsonValue } from "./json.js";
|
|
|
185
185
|
export { cloneDefaultValue } from "./clone-default.js";
|
|
186
186
|
export { unicodeLength } from "./json-schema/utils.js";
|
|
187
187
|
export { compileJsonSchema, formatIssues } from "./json-schema/index.js";
|
|
188
|
+
export { projectJsonSchemaProperties } from "./json-schema/properties.js";
|
|
189
|
+
export type { JsonSchemaProperty } from "./json-schema/properties.js";
|
|
188
190
|
export { normalizeLegacyNullability } from "./json-schema/normalize-nullability.js";
|
|
189
191
|
export { withJsonSchema, nativeJsonSchema } from "./native-json-schema.js";
|
|
190
192
|
export type { NativeSchema } from "./native-json-schema.js";
|
package/dist/index.js
CHANGED
|
@@ -329,5 +329,6 @@ export { isJsonValue } from "./json.js";
|
|
|
329
329
|
export { cloneDefaultValue } from "./clone-default.js";
|
|
330
330
|
export { unicodeLength } from "./json-schema/utils.js";
|
|
331
331
|
export { compileJsonSchema, formatIssues } from "./json-schema/index.js";
|
|
332
|
+
export { projectJsonSchemaProperties } from "./json-schema/properties.js";
|
|
332
333
|
export { normalizeLegacyNullability } from "./json-schema/normalize-nullability.js";
|
|
333
334
|
export { withJsonSchema, nativeJsonSchema } from "./native-json-schema.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ValidationResult } from "../validate.js";
|
|
2
|
+
import type { CompileJsonSchemaOptions } from "./types.js";
|
|
3
|
+
export interface JsonSchemaProperty {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
/** Required regardless of the object's selected alternative or condition. */
|
|
6
|
+
readonly required: boolean;
|
|
7
|
+
/** Isolated annotation/schema copies, including resolved references. */
|
|
8
|
+
readonly schemas: readonly unknown[];
|
|
9
|
+
/** Candidate hint: accepts a value matching at least one advertised declaration.
|
|
10
|
+
* Validate the complete object to enforce compositions and conditional rules. */
|
|
11
|
+
validate(value: unknown): ValidationResult<unknown>;
|
|
12
|
+
}
|
|
13
|
+
/** Project names and candidate validators while retaining the native reference graph.
|
|
14
|
+
* Property hints cover declarations in refs, compositions and conditional branches;
|
|
15
|
+
* full object validation remains necessary for branch-dependent constraints. */
|
|
16
|
+
export declare function projectJsonSchemaProperties(schema: unknown, options?: CompileJsonSchemaOptions): readonly JsonSchemaProperty[];
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { compileGraph } from "./compiler.js";
|
|
2
|
+
import { evaluateSchema } from "./evaluate.js";
|
|
3
|
+
import { isObject } from "./utils.js";
|
|
4
|
+
function referenceNodes(graph, node) {
|
|
5
|
+
if (!isObject(node.schema))
|
|
6
|
+
return [];
|
|
7
|
+
return ["$ref", "$dynamicRef", "$recursiveRef"].flatMap(keyword => typeof node.schema === "object" && typeof node.schema[keyword] === "string"
|
|
8
|
+
? [graph.resolve(node, node.schema[keyword])] : []);
|
|
9
|
+
}
|
|
10
|
+
function ignoresSiblings(node) {
|
|
11
|
+
return node.dialect === "draft7" && isObject(node.schema) && typeof node.schema.$ref === "string";
|
|
12
|
+
}
|
|
13
|
+
function requiredFor(graph, node, name, ancestors = new Set()) {
|
|
14
|
+
if (!isObject(node.schema) || ancestors.has(node))
|
|
15
|
+
return false;
|
|
16
|
+
const next = new Set(ancestors).add(node);
|
|
17
|
+
const referenced = referenceNodes(graph, node).some(target => requiredFor(graph, target, name, next));
|
|
18
|
+
if (ignoresSiblings(node))
|
|
19
|
+
return referenced;
|
|
20
|
+
if (referenced || (Array.isArray(node.schema.required) && node.schema.required.includes(name)))
|
|
21
|
+
return true;
|
|
22
|
+
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
23
|
+
const branches = node.schema[keyword];
|
|
24
|
+
if (!Array.isArray(branches))
|
|
25
|
+
continue;
|
|
26
|
+
const requirements = branches.map((_branch, index) => {
|
|
27
|
+
const child = node.children.get(`${keyword}/${index}`);
|
|
28
|
+
return child !== undefined && requiredFor(graph, child, name, next);
|
|
29
|
+
});
|
|
30
|
+
if (keyword === "allOf" ? requirements.some(Boolean) : requirements.every(Boolean))
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function annotationSchemas(graph, root) {
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const schemas = [];
|
|
38
|
+
const visit = (node) => {
|
|
39
|
+
if (seen.has(node))
|
|
40
|
+
return;
|
|
41
|
+
seen.add(node);
|
|
42
|
+
if (!ignoresSiblings(node))
|
|
43
|
+
schemas.push(structuredClone(node.schema));
|
|
44
|
+
for (const target of referenceNodes(graph, node))
|
|
45
|
+
visit(target);
|
|
46
|
+
if (ignoresSiblings(node) || !isObject(node.schema))
|
|
47
|
+
return;
|
|
48
|
+
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
49
|
+
const branches = node.schema[keyword];
|
|
50
|
+
if (!Array.isArray(branches))
|
|
51
|
+
continue;
|
|
52
|
+
branches.forEach((_branch, index) => {
|
|
53
|
+
const child = node.children.get(`${keyword}/${index}`);
|
|
54
|
+
if (child)
|
|
55
|
+
visit(child);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
visit(root);
|
|
60
|
+
return schemas;
|
|
61
|
+
}
|
|
62
|
+
/** Project names and candidate validators while retaining the native reference graph.
|
|
63
|
+
* Property hints cover declarations in refs, compositions and conditional branches;
|
|
64
|
+
* full object validation remains necessary for branch-dependent constraints. */
|
|
65
|
+
export function projectJsonSchemaProperties(schema, options = {}) {
|
|
66
|
+
const graph = compileGraph(structuredClone(schema), {
|
|
67
|
+
...options, ...(options.registry === undefined ? {} : { registry: structuredClone(options.registry) })
|
|
68
|
+
});
|
|
69
|
+
const declarations = new Map();
|
|
70
|
+
const seen = new Set();
|
|
71
|
+
const visit = (node) => {
|
|
72
|
+
if (seen.has(node) || !isObject(node.schema))
|
|
73
|
+
return;
|
|
74
|
+
seen.add(node);
|
|
75
|
+
for (const target of referenceNodes(graph, node))
|
|
76
|
+
visit(target);
|
|
77
|
+
if (ignoresSiblings(node))
|
|
78
|
+
return;
|
|
79
|
+
if (isObject(node.schema.properties)) {
|
|
80
|
+
for (const name of Object.keys(node.schema.properties)) {
|
|
81
|
+
const child = node.children.get(`properties/${name}`);
|
|
82
|
+
if (!child)
|
|
83
|
+
continue;
|
|
84
|
+
const sources = declarations.get(name) ?? new Set();
|
|
85
|
+
sources.add(child);
|
|
86
|
+
declarations.set(name, sources);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
90
|
+
const branches = node.schema[keyword];
|
|
91
|
+
if (!Array.isArray(branches))
|
|
92
|
+
continue;
|
|
93
|
+
branches.forEach((_branch, index) => {
|
|
94
|
+
const child = node.children.get(`${keyword}/${index}`);
|
|
95
|
+
if (child)
|
|
96
|
+
visit(child);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
for (const keyword of ["then", "else", "dependentSchemas"]) {
|
|
100
|
+
if (keyword === "dependentSchemas" && isObject(node.schema.dependentSchemas)) {
|
|
101
|
+
for (const key of Object.keys(node.schema.dependentSchemas)) {
|
|
102
|
+
const child = node.children.get(`dependentSchemas/${key}`);
|
|
103
|
+
if (child)
|
|
104
|
+
visit(child);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const child = node.children.get(keyword);
|
|
109
|
+
if (child)
|
|
110
|
+
visit(child);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
visit(graph.root);
|
|
115
|
+
return [...declarations.keys()].sort().map(name => {
|
|
116
|
+
const sources = [...declarations.get(name)];
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
required: requiredFor(graph, graph.root, name),
|
|
120
|
+
schemas: sources.flatMap(node => annotationSchemas(graph, node)),
|
|
121
|
+
validate(value) {
|
|
122
|
+
const results = sources.map(node => evaluateSchema({ ...graph, root: node }, value));
|
|
123
|
+
if (results.some(result => result.valid))
|
|
124
|
+
return { ok: true, value };
|
|
125
|
+
return { ok: false, issues: results.flatMap(result => result.issues) };
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|