@json-schema-engine/core 0.0.1
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/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/coverage.d.ts +30 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +81 -0
- package/dist/coverage.js.map +1 -0
- package/dist/cursor.d.ts +16 -0
- package/dist/cursor.d.ts.map +1 -0
- package/dist/cursor.js +28 -0
- package/dist/cursor.js.map +1 -0
- package/dist/dialect.d.ts +316 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +135 -0
- package/dist/dialect.js.map +1 -0
- package/dist/engine.d.ts +203 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +493 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +182 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +370 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +49 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +128 -0
- package/dist/json.js.map +1 -0
- package/dist/keywords/applicator.d.ts +57 -0
- package/dist/keywords/applicator.d.ts.map +1 -0
- package/dist/keywords/applicator.js +789 -0
- package/dist/keywords/applicator.js.map +1 -0
- package/dist/keywords/core.d.ts +42 -0
- package/dist/keywords/core.d.ts.map +1 -0
- package/dist/keywords/core.js +139 -0
- package/dist/keywords/core.js.map +1 -0
- package/dist/keywords/format.d.ts +43 -0
- package/dist/keywords/format.d.ts.map +1 -0
- package/dist/keywords/format.js +101 -0
- package/dist/keywords/format.js.map +1 -0
- package/dist/keywords/metaschemas2019.d.ts +3 -0
- package/dist/keywords/metaschemas2019.d.ts.map +1 -0
- package/dist/keywords/metaschemas2019.js +406 -0
- package/dist/keywords/metaschemas2019.js.map +1 -0
- package/dist/keywords/metaschemas2020.d.ts +3 -0
- package/dist/keywords/metaschemas2020.d.ts.map +1 -0
- package/dist/keywords/metaschemas2020.js +441 -0
- package/dist/keywords/metaschemas2020.js.map +1 -0
- package/dist/keywords/metaschemas6.d.ts +3 -0
- package/dist/keywords/metaschemas6.d.ts.map +1 -0
- package/dist/keywords/metaschemas6.js +221 -0
- package/dist/keywords/metaschemas6.js.map +1 -0
- package/dist/keywords/metaschemas7.d.ts +3 -0
- package/dist/keywords/metaschemas7.d.ts.map +1 -0
- package/dist/keywords/metaschemas7.js +248 -0
- package/dist/keywords/metaschemas7.js.map +1 -0
- package/dist/keywords/unevaluated.d.ts +14 -0
- package/dist/keywords/unevaluated.d.ts.map +1 -0
- package/dist/keywords/unevaluated.js +296 -0
- package/dist/keywords/unevaluated.js.map +1 -0
- package/dist/keywords/validation.d.ts +21 -0
- package/dist/keywords/validation.d.ts.map +1 -0
- package/dist/keywords/validation.js +332 -0
- package/dist/keywords/validation.js.map +1 -0
- package/dist/keywords/vocab2019.d.ts +36 -0
- package/dist/keywords/vocab2019.d.ts.map +1 -0
- package/dist/keywords/vocab2019.js +585 -0
- package/dist/keywords/vocab2019.js.map +1 -0
- package/dist/keywords/vocab2020.d.ts +20 -0
- package/dist/keywords/vocab2020.d.ts.map +1 -0
- package/dist/keywords/vocab2020.js +55 -0
- package/dist/keywords/vocab2020.js.map +1 -0
- package/dist/keywords/vocab7.d.ts +46 -0
- package/dist/keywords/vocab7.d.ts.map +1 -0
- package/dist/keywords/vocab7.js +305 -0
- package/dist/keywords/vocab7.js.map +1 -0
- package/dist/loader.d.ts +41 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +13 -0
- package/dist/loader.js.map +1 -0
- package/dist/lowering.d.ts +371 -0
- package/dist/lowering.d.ts.map +1 -0
- package/dist/lowering.js +59 -0
- package/dist/lowering.js.map +1 -0
- package/dist/output.d.ts +251 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +357 -0
- package/dist/output.js.map +1 -0
- package/dist/records.d.ts +71 -0
- package/dist/records.d.ts.map +1 -0
- package/dist/records.js +150 -0
- package/dist/records.js.map +1 -0
- package/dist/ref.d.ts +10 -0
- package/dist/ref.d.ts.map +1 -0
- package/dist/ref.js +5 -0
- package/dist/ref.js.map +1 -0
- package/dist/regex.d.ts +45 -0
- package/dist/regex.d.ts.map +1 -0
- package/dist/regex.js +163 -0
- package/dist/regex.js.map +1 -0
- package/dist/registry.d.ts +141 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +407 -0
- package/dist/registry.js.map +1 -0
- package/dist/result.d.ts +109 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +112 -0
- package/dist/result.js.map +1 -0
- package/dist/uri.d.ts +18 -0
- package/dist/uri.d.ts.map +1 -0
- package/dist/uri.js +27 -0
- package/dist/uri.js.map +1 -0
- package/dist/walk.d.ts +25 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +65 -0
- package/dist/walk.js.map +1 -0
- package/package.json +32 -0
- package/src/coverage.ts +88 -0
- package/src/cursor.ts +42 -0
- package/src/dialect.ts +428 -0
- package/src/engine.ts +741 -0
- package/src/index.ts +699 -0
- package/src/json.ts +137 -0
- package/src/keywords/applicator.ts +895 -0
- package/src/keywords/core.ts +168 -0
- package/src/keywords/format.ts +143 -0
- package/src/keywords/metaschemas2019.ts +410 -0
- package/src/keywords/metaschemas2020.ts +443 -0
- package/src/keywords/metaschemas6.ts +226 -0
- package/src/keywords/metaschemas7.ts +253 -0
- package/src/keywords/unevaluated.ts +329 -0
- package/src/keywords/validation.ts +627 -0
- package/src/keywords/vocab2019.ts +676 -0
- package/src/keywords/vocab2020.ts +82 -0
- package/src/keywords/vocab7.ts +393 -0
- package/src/loader.ts +60 -0
- package/src/lowering.ts +414 -0
- package/src/output.ts +651 -0
- package/src/records.ts +226 -0
- package/src/ref.ts +14 -0
- package/src/regex.ts +171 -0
- package/src/registry.ts +481 -0
- package/src/result.ts +252 -0
- package/src/uri.ts +35 -0
- package/src/walk.ts +88 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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"}
|
package/dist/uri.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Thrown when a reference cannot be resolved to an absolute URI. */
|
|
2
|
+
export declare class UnresolvableRefError extends Error {
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a reference URI against a base URI to an absolute URI.
|
|
6
|
+
* @throws UnresolvableRefError if the pair does not form a valid URI.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveUri(ref: string, base: string): string;
|
|
9
|
+
/** A URI split into its resource part and decoded fragment. */
|
|
10
|
+
export interface SplitUri {
|
|
11
|
+
/** URI without fragment. */
|
|
12
|
+
resource: string;
|
|
13
|
+
/** Decoded fragment, `null` if absent. */
|
|
14
|
+
fragment: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** Splits a URI into its resource and decoded fragment parts. */
|
|
17
|
+
export declare function splitFragment(uri: string): SplitUri;
|
|
18
|
+
//# sourceMappingURL=uri.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uri.d.ts","sourceRoot":"","sources":["../src/uri.ts"],"names":[],"mappings":"AAEA,qEAAqE;AACrE,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAElD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5D;AAED,+DAA+D;AAC/D,MAAM,WAAW,QAAQ;IACvB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAQnD"}
|
package/dist/uri.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// URI handling for schema identification and reference resolution.
|
|
2
|
+
/** Thrown when a reference cannot be resolved to an absolute URI. */
|
|
3
|
+
export class UnresolvableRefError extends Error {
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a reference URI against a base URI to an absolute URI.
|
|
7
|
+
* @throws UnresolvableRefError if the pair does not form a valid URI.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveUri(ref, base) {
|
|
10
|
+
try {
|
|
11
|
+
return new URL(ref, base).href;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new UnresolvableRefError(`cannot resolve '${ref}' against '${base}'`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Splits a URI into its resource and decoded fragment parts. */
|
|
18
|
+
export function splitFragment(uri) {
|
|
19
|
+
const i = uri.indexOf("#");
|
|
20
|
+
return i === -1
|
|
21
|
+
? { resource: uri, fragment: null }
|
|
22
|
+
: {
|
|
23
|
+
resource: uri.slice(0, i),
|
|
24
|
+
fragment: decodeURIComponent(uri.slice(i + 1)),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=uri.js.map
|
package/dist/uri.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uri.js","sourceRoot":"","sources":["../src/uri.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,qEAAqE;AACrE,MAAM,OAAO,oBAAqB,SAAQ,KAAK;CAAG;AAElD;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,IAAY;IAClD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,oBAAoB,CAAC,mBAAmB,GAAG,cAAc,IAAI,GAAG,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAUD,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAC;QACb,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE;QACnC,CAAC,CAAC;YACE,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YACzB,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;SAC/C,CAAC;AACR,CAAC"}
|
package/dist/walk.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { JsonValue } from "./json.js";
|
|
2
|
+
import { Dialect } from "./dialect.js";
|
|
3
|
+
/**
|
|
4
|
+
* One schema position visited by {@link walkSchema}.
|
|
5
|
+
*/
|
|
6
|
+
export interface SchemaWalkVisit {
|
|
7
|
+
/** The value in schema position: an object or a boolean. */
|
|
8
|
+
readonly node: JsonValue;
|
|
9
|
+
/** JSON Pointer from the walk root. */
|
|
10
|
+
readonly pointer: string;
|
|
11
|
+
/** Applying keyword in the parent schema object; `null` at the root. */
|
|
12
|
+
readonly keyword: string | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Walks every schema position of `schema` reachable through the dialect's
|
|
16
|
+
* keyword facts (`analyze().subschemas`), visiting parents before children.
|
|
17
|
+
*
|
|
18
|
+
* Tolerant by design: values in schema position that are neither objects
|
|
19
|
+
* nor booleans are skipped, as are positions under keyword values whose
|
|
20
|
+
* `analyze()` rejects them — unknown keywords' values are never descended.
|
|
21
|
+
* Nesting beyond `maxDepth` still throws {@link MaxDepthExceededError}: the
|
|
22
|
+
* depth bound is a security posture, not a validity judgment.
|
|
23
|
+
*/
|
|
24
|
+
export declare function walkSchema(schema: JsonValue, dialect: Pick<Dialect, "keywords">, visit: (v: SchemaWalkVisit) => void, maxDepth?: number): void;
|
|
25
|
+
//# sourceMappingURL=walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,SAAS,EAA2B,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAClC,KAAK,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,EACnC,QAAQ,GAAE,MAA0B,GACnC,IAAI,CAgDN"}
|
package/dist/walk.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Public schema-position walk (M8.6): the descent knowledge lives in each
|
|
2
|
+
// keyword's analyze() facts, so adapters and tools stop hardcoding their own
|
|
3
|
+
// applicator tables. Unlike the registry's registration walk (which owns
|
|
4
|
+
// base-URI rebasing, anchor indexing, and reference collection), this walk
|
|
5
|
+
// is deliberately tolerant: it runs on unvalidated input — e.g. a strict-
|
|
6
|
+
// mode check before registration — where a malformed keyword value must be
|
|
7
|
+
// skipped, not thrown. Registration keeps its InvalidSchemaError job.
|
|
8
|
+
import { isObject, escapeSegment } from "./json.js";
|
|
9
|
+
import { DEFAULT_MAX_DEPTH, MaxDepthExceededError } from "./registry.js";
|
|
10
|
+
/**
|
|
11
|
+
* Walks every schema position of `schema` reachable through the dialect's
|
|
12
|
+
* keyword facts (`analyze().subschemas`), visiting parents before children.
|
|
13
|
+
*
|
|
14
|
+
* Tolerant by design: values in schema position that are neither objects
|
|
15
|
+
* nor booleans are skipped, as are positions under keyword values whose
|
|
16
|
+
* `analyze()` rejects them — unknown keywords' values are never descended.
|
|
17
|
+
* Nesting beyond `maxDepth` still throws {@link MaxDepthExceededError}: the
|
|
18
|
+
* depth bound is a security posture, not a validity judgment.
|
|
19
|
+
*/
|
|
20
|
+
export function walkSchema(schema, dialect, visit, maxDepth = DEFAULT_MAX_DEPTH) {
|
|
21
|
+
const step = (node, pointer, keyword, depth) => {
|
|
22
|
+
if (depth > maxDepth) {
|
|
23
|
+
throw new MaxDepthExceededError(`schema nesting exceeds maxDepth (${maxDepth}) at '#${pointer}'`);
|
|
24
|
+
}
|
|
25
|
+
if (typeof node === "boolean") {
|
|
26
|
+
visit({ node, pointer, keyword });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!isObject(node))
|
|
30
|
+
return;
|
|
31
|
+
visit({ node, pointer, keyword });
|
|
32
|
+
for (const [name, value] of Object.entries(node)) {
|
|
33
|
+
const behavior = dialect.keywords.get(name)?.behavior;
|
|
34
|
+
if (!behavior?.analyze)
|
|
35
|
+
continue;
|
|
36
|
+
let positions;
|
|
37
|
+
try {
|
|
38
|
+
positions = behavior.analyze(value, { schema: node }).subschemas;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (!positions)
|
|
44
|
+
continue;
|
|
45
|
+
for (const relPath of positions) {
|
|
46
|
+
let child = value;
|
|
47
|
+
let suffix = "/" + escapeSegment(name);
|
|
48
|
+
for (const seg of relPath) {
|
|
49
|
+
child =
|
|
50
|
+
child === null || typeof child !== "object"
|
|
51
|
+
? undefined
|
|
52
|
+
: Array.isArray(child)
|
|
53
|
+
? child[seg]
|
|
54
|
+
: child[seg];
|
|
55
|
+
suffix += "/" + escapeSegment(String(seg));
|
|
56
|
+
}
|
|
57
|
+
if (child !== undefined) {
|
|
58
|
+
step(child, pointer + suffix, name, depth + 1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
step(schema, "", null, 0);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=walk.js.map
|
package/dist/walk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,6EAA6E;AAC7E,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,sEAAsE;AAEtE,OAAO,EAAa,QAAQ,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAczE;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CACxB,MAAiB,EACjB,OAAkC,EAClC,KAAmC,EACnC,WAAmB,iBAAiB;IAEpC,MAAM,IAAI,GAAG,CACX,IAAe,EACf,OAAe,EACf,OAAsB,EACtB,KAAa,EACP,EAAE;QACR,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,qBAAqB,CAC7B,oCAAoC,QAAQ,UAAU,OAAO,GAAG,CACjE,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO;QAC5B,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAElC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;YACtD,IAAI,CAAC,QAAQ,EAAE,OAAO;gBAAE,SAAS;YACjC,IAAI,SAAS,CAAC;YACd,IAAI,CAAC;gBACH,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC;YACnE,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,CAAC,SAAS;gBAAE,SAAS;YACzB,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;gBAChC,IAAI,KAAK,GAA0B,KAAK,CAAC;gBACzC,IAAI,MAAM,GAAG,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;gBACvC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;oBAC1B,KAAK;wBACH,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;4BACzC,CAAC,CAAC,SAAS;4BACX,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gCACpB,CAAC,CAAC,KAAK,CAAC,GAAa,CAAC;gCACtB,CAAC,CAAE,KAAmC,CAAC,GAAa,CAAC,CAAC;oBAC5D,MAAM,IAAI,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC7C,CAAC;gBACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC5B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@json-schema-engine/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Interpreter core: registry, dialects, evaluation, annotations, and output rendering for draft 2020-12/2019-09/draft-07/draft-06",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"jse-source": "./src/index.ts",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=22"
|
|
20
|
+
},
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/handrews/json-schema-engine.git",
|
|
30
|
+
"directory": "packages/core"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/coverage.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Compiled-tier port of the consumer keywords' channel-reading semantics
|
|
2
|
+
// (COMPILED-CONSUMERS.md). The runtime coverage channel is a flat array of
|
|
3
|
+
// RAW DEPENDENCY DATA — the same values `ctx.produce` yields — merged with
|
|
4
|
+
// mark/truncate at application boundaries by the serializer (phase B). These
|
|
5
|
+
// helpers fold that channel exactly as the interpreter's `unevaluated*`
|
|
6
|
+
// evaluate() folds visible dependency records, dispatching on VALUE SHAPE
|
|
7
|
+
// (the shape is unambiguous, so one mixed names+indexes channel is sound):
|
|
8
|
+
//
|
|
9
|
+
// - `string[]` evaluated property names (properties/patternProperties/
|
|
10
|
+
// additionalProperties/unevaluatedProperties values); folds
|
|
11
|
+
// into name coverage. Empty array is a no-op.
|
|
12
|
+
// - `true` full index coverage (items / contains-all /
|
|
13
|
+
// prefixItems-covered-whole-array / unevaluatedItems values).
|
|
14
|
+
// - `number` n prefix coverage of indexes 0..n, i.e. coveredPrefix = n+1
|
|
15
|
+
// (prefixItems' largest-applied-index value).
|
|
16
|
+
// - `number[]` individual covered indexes (contains' matched list).
|
|
17
|
+
//
|
|
18
|
+
// Name folding ignores non-`string[]` entries; index folding ignores
|
|
19
|
+
// `string[]` entries; `[]` is a no-op for both.
|
|
20
|
+
|
|
21
|
+
import { DependencyRecord } from "./engine.js";
|
|
22
|
+
import { Cursor } from "./cursor.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Folded name coverage: the set of evaluated property names. Ports
|
|
26
|
+
* `unevaluatedProperties.evaluate`'s seen-set union — every `string[]` entry
|
|
27
|
+
* contributes its names; other shapes are ignored.
|
|
28
|
+
*/
|
|
29
|
+
export function foldNameCoverage(channel: readonly unknown[]): Set<string> {
|
|
30
|
+
const seen = new Set<string>();
|
|
31
|
+
for (const entry of channel) {
|
|
32
|
+
if (Array.isArray(entry)) {
|
|
33
|
+
for (const el of entry) {
|
|
34
|
+
if (typeof el === "string") seen.add(el);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return seen;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Folded index coverage over an array of the given length. Ports
|
|
43
|
+
* `unevaluatedItems.evaluate`'s coveredPrefix/coveredIdx computation: `true`
|
|
44
|
+
* covers the whole array, a `number` extends the covered prefix to value+1
|
|
45
|
+
* (max-folded), a `number[]` marks individual indexes.
|
|
46
|
+
*/
|
|
47
|
+
export function foldIndexCoverage(
|
|
48
|
+
channel: readonly unknown[],
|
|
49
|
+
length: number,
|
|
50
|
+
): { coveredPrefix: number; coveredIdx: Set<number> } {
|
|
51
|
+
let coveredPrefix = 0;
|
|
52
|
+
const coveredIdx = new Set<number>();
|
|
53
|
+
for (const entry of channel) {
|
|
54
|
+
if (entry === true) {
|
|
55
|
+
coveredPrefix = length;
|
|
56
|
+
} else if (typeof entry === "number") {
|
|
57
|
+
coveredPrefix = Math.max(coveredPrefix, entry + 1);
|
|
58
|
+
} else if (Array.isArray(entry)) {
|
|
59
|
+
// number[] (contains' matched list); string[] contributes no indexes.
|
|
60
|
+
for (const el of entry) {
|
|
61
|
+
if (typeof el === "number") coveredIdx.add(el);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { coveredPrefix, coveredIdx };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Channel entries a fragment (interpreted island) contributes: the data of
|
|
70
|
+
* coverage-producer dependency records at the island's root cursor.
|
|
71
|
+
* `evaluateFragment` returns root-frame surviving records with cursor
|
|
72
|
+
* identity intact; filtering to the fragment's root `cursor` keeps coverage
|
|
73
|
+
* per-instance-location, and filtering to `coverageIds`
|
|
74
|
+
* (SchemaRegistry.coverageIds) keeps only the producers whose data the
|
|
75
|
+
* shape-dispatching folds above understand — other dependency data (an
|
|
76
|
+
* `if` outcome, say) never reaches the channel. Data passes through untouched.
|
|
77
|
+
*/
|
|
78
|
+
export function harvestCoverage(
|
|
79
|
+
dependencies: readonly DependencyRecord[],
|
|
80
|
+
cursor: Cursor,
|
|
81
|
+
coverageIds: ReadonlySet<string>,
|
|
82
|
+
): unknown[] {
|
|
83
|
+
const out: unknown[] = [];
|
|
84
|
+
for (const d of dependencies) {
|
|
85
|
+
if (d.cursor === cursor && coverageIds.has(d.behaviorId)) out.push(d.data);
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
package/src/cursor.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Instance cursor: a parent-linked position in the instance document.
|
|
2
|
+
//
|
|
3
|
+
// Cursor identity carries semantic weight: in-place applicators ($ref, allOf,
|
|
4
|
+
// if, ...) apply subschemas to the *same* cursor object, while child
|
|
5
|
+
// applicators (properties, items, ...) create child cursors. The channel's
|
|
6
|
+
// visibility query keys on this identity (engine.ts). JSON Pointer strings
|
|
7
|
+
// are materialized lazily, only when output units escape.
|
|
8
|
+
|
|
9
|
+
import { JsonValue, escapeSegment } from "./json.js";
|
|
10
|
+
|
|
11
|
+
/** A parent-linked position in the instance document. */
|
|
12
|
+
export interface Cursor {
|
|
13
|
+
readonly value: JsonValue;
|
|
14
|
+
readonly parent: Cursor | null;
|
|
15
|
+
readonly segment: string | number | null;
|
|
16
|
+
/** Lazily cached JSON Pointer; use {@link instancePointer}. */
|
|
17
|
+
ptr?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Creates a cursor at the instance document root. */
|
|
21
|
+
export const rootCursor = (value: JsonValue): Cursor => ({
|
|
22
|
+
value,
|
|
23
|
+
parent: null,
|
|
24
|
+
segment: null,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/** Creates a cursor for a named or indexed child of `parent`. */
|
|
28
|
+
export const childCursor = (
|
|
29
|
+
parent: Cursor,
|
|
30
|
+
segment: string | number,
|
|
31
|
+
value: JsonValue,
|
|
32
|
+
): Cursor => ({ value, parent, segment });
|
|
33
|
+
|
|
34
|
+
/** Computes the JSON Pointer for a cursor's instance location, caching the result. */
|
|
35
|
+
export function instancePointer(cursor: Cursor): string {
|
|
36
|
+
if (cursor.ptr !== undefined) return cursor.ptr;
|
|
37
|
+
if (cursor.parent === null) return (cursor.ptr = "");
|
|
38
|
+
return (cursor.ptr =
|
|
39
|
+
instancePointer(cursor.parent) +
|
|
40
|
+
"/" +
|
|
41
|
+
escapeSegment(String(cursor.segment)));
|
|
42
|
+
}
|