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 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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.304",
3
+ "version": "0.0.306",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",