@maroonedog/luq 2.0.0 → 2.2.0
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 +364 -57
- package/dist/builder/builder-surface.types.d.ts +1 -0
- package/dist/builder/create-builder.js +9 -0
- package/dist/builder/create-builder.mjs +9 -0
- package/dist/builder/field-builder.types.d.ts +13 -0
- package/dist/chain/bundle-paths.types.d.ts +26 -0
- package/dist/chain/bundle-paths.types.js +2 -0
- package/dist/chain/bundle-paths.types.mjs +1 -0
- package/dist/chain/chain-method.types.d.ts +8 -3
- package/dist/chain/create-chain-node.js +22 -1
- package/dist/chain/create-chain-node.mjs +22 -1
- package/dist/chain/resolve-args.types.d.ts +4 -2
- package/dist/compile/compile-array-node.d.ts +0 -7
- package/dist/compile/compile-array-node.js +4 -0
- package/dist/compile/compile-array-node.mjs +4 -0
- package/dist/compile/compile-field.js +10 -1
- package/dist/compile/compile-field.mjs +10 -1
- package/dist/compile/resolve-conditional-presence.js +1 -0
- package/dist/compile/resolve-conditional-presence.mjs +1 -0
- package/dist/compile/resolve-presence.js +4 -0
- package/dist/compile/resolve-presence.mjs +4 -0
- package/dist/compile/split-rules-by-kind.js +31 -7
- package/dist/compile/split-rules-by-kind.mjs +31 -7
- package/dist/compile/validation-plan.types.d.ts +33 -0
- package/dist/json-schema/build-from-schema.js +2 -3
- package/dist/json-schema/build-from-schema.mjs +2 -3
- package/dist/json-schema/collect-definitions.d.ts +30 -0
- package/dist/json-schema/collect-definitions.js +47 -3
- package/dist/json-schema/collect-definitions.mjs +46 -4
- package/dist/json-schema/collect-sub-schema-rules.d.ts +12 -1
- package/dist/json-schema/collect-sub-schema-rules.js +37 -7
- package/dist/json-schema/collect-sub-schema-rules.mjs +38 -8
- package/dist/json-schema/create-structural-context.d.ts +13 -3
- package/dist/json-schema/create-structural-context.js +72 -6
- package/dist/json-schema/create-structural-context.mjs +72 -7
- package/dist/json-schema/declare-additional-properties.d.ts +19 -0
- package/dist/json-schema/declare-additional-properties.js +42 -0
- package/dist/json-schema/declare-additional-properties.mjs +37 -0
- package/dist/json-schema/declare-object-keywords.d.ts +0 -2
- package/dist/json-schema/declare-object-keywords.js +6 -13
- package/dist/json-schema/declare-object-keywords.mjs +7 -13
- package/dist/json-schema/declare-presence.d.ts +9 -3
- package/dist/json-schema/declare-presence.js +12 -6
- package/dist/json-schema/declare-presence.mjs +12 -6
- package/dist/json-schema/extensions/json-schema/index.d.ts +1 -0
- package/dist/json-schema/extensions/json-schema/json-schema.d.ts +17 -2
- package/dist/json-schema/extensions/json-schema/json-schema.js +15 -7
- package/dist/json-schema/extensions/json-schema/json-schema.mjs +16 -8
- package/dist/json-schema/extensions/json-schema-full-feature/index.d.ts +1 -0
- package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.d.ts +2 -1
- package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.js +3 -1
- package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.mjs +3 -1
- package/dist/json-schema/flatten-schema.js +1 -1
- package/dist/json-schema/flatten-schema.mjs +1 -1
- package/dist/json-schema/follow-json-pointer.d.ts +23 -0
- package/dist/json-schema/follow-json-pointer.js +87 -0
- package/dist/json-schema/follow-json-pointer.mjs +82 -0
- package/dist/json-schema/index.d.ts +7 -2
- package/dist/json-schema/index.js +10 -2
- package/dist/json-schema/index.mjs +5 -2
- package/dist/json-schema/keyword-map-object.d.ts +1 -1
- package/dist/json-schema/ref-resolution-error.d.ts +4 -0
- package/dist/json-schema/ref-resolution-error.js +20 -0
- package/dist/json-schema/ref-resolution-error.mjs +16 -0
- package/dist/json-schema/ref-scope.d.ts +18 -0
- package/dist/json-schema/ref-scope.js +37 -0
- package/dist/json-schema/ref-scope.mjs +33 -0
- package/dist/json-schema/resolve-ref.d.ts +14 -9
- package/dist/json-schema/resolve-ref.js +89 -59
- package/dist/json-schema/resolve-ref.mjs +86 -55
- package/dist/json-schema/schema-registry.d.ts +30 -0
- package/dist/json-schema/schema-registry.js +142 -0
- package/dist/json-schema/schema-registry.mjs +139 -0
- package/dist/json-schema/schema-to-declarations.js +2 -1
- package/dist/json-schema/schema-to-declarations.mjs +2 -1
- package/dist/json-schema/structural-expansion.types.d.ts +8 -1
- package/dist/json-schema/uri-reference.d.ts +29 -0
- package/dist/json-schema/uri-reference.js +118 -0
- package/dist/json-schema/uri-reference.mjs +111 -0
- package/dist/path/create-value-writer.js +27 -2
- package/dist/path/create-value-writer.mjs +27 -2
- package/dist/path/reserved-segment.d.ts +22 -4
- package/dist/path/reserved-segment.js +27 -7
- package/dist/path/reserved-segment.mjs +27 -7
- package/dist/plugin-kit/compiled-rule.d.ts +13 -0
- package/dist/plugin-kit/create-rule.d.ts +11 -0
- package/dist/plugin-kit/create-rule.js +1 -0
- package/dist/plugin-kit/create-rule.mjs +1 -0
- package/dist/plugin-kit/marker.types.d.ts +18 -0
- package/dist/plugin-kit/plugin-definition.d.ts +15 -0
- package/dist/plugin-kit/plugin-definition.js +1 -0
- package/dist/plugin-kit/plugin-definition.mjs +1 -0
- package/dist/plugins/index.generated.d.ts +1 -0
- package/dist/plugins/index.generated.js +4 -2
- package/dist/plugins/index.generated.mjs +1 -0
- package/dist/plugins/manifest.generated.js +1 -0
- package/dist/plugins/manifest.generated.mjs +1 -0
- package/dist/plugins/object-additional-properties/object-additional-properties-schema.d.ts +1 -1
- package/dist/plugins/object-additional-properties/object-additional-properties-schema.js +4 -2
- package/dist/plugins/object-additional-properties/object-additional-properties-schema.mjs +4 -2
- package/dist/plugins/object-additional-properties/object-additional-properties.d.ts +1 -1
- package/dist/plugins/object-additional-properties/object-additional-properties.js +4 -2
- package/dist/plugins/object-additional-properties/object-additional-properties.mjs +4 -2
- package/dist/plugins/object-additional-properties/select-additional-keys.d.ts +15 -0
- package/dist/plugins/object-additional-properties/select-additional-keys.js +51 -0
- package/dist/plugins/object-additional-properties/select-additional-keys.mjs +47 -0
- package/dist/plugins/stitch/stitch.d.ts +28 -8
- package/dist/plugins/stitch-with/index.d.ts +2 -0
- package/dist/plugins/stitch-with/index.js +5 -0
- package/dist/plugins/stitch-with/index.mjs +1 -0
- package/dist/plugins/stitch-with/stitch-with.d.ts +12 -0
- package/dist/plugins/stitch-with/stitch-with.js +94 -0
- package/dist/plugins/stitch-with/stitch-with.mjs +91 -0
- package/dist/plugins/stitchWith.d.ts +1 -0
- package/dist/plugins/stitchWith.js +2 -0
- package/dist/plugins/stitchWith.mjs +1 -0
- package/dist/plugins/string-exact-length/string-exact-length.js +2 -2
- package/dist/plugins/string-exact-length/string-exact-length.mjs +3 -3
- package/dist/plugins/string-max/string-max.js +2 -2
- package/dist/plugins/string-max/string-max.mjs +3 -3
- package/dist/plugins/string-min/string-min.js +21 -3
- package/dist/plugins/string-min/string-min.mjs +21 -3
- package/dist/presets/index.d.ts +1 -0
- package/dist/presets/index.js +9 -0
- package/dist/presets/index.mjs +1 -0
- package/dist/presets/presets.d.ts +165 -0
- package/dist/presets/presets.js +80 -0
- package/dist/presets/presets.mjs +77 -0
- package/dist/runtime/create-field-validator.js +24 -8
- package/dist/runtime/create-field-validator.mjs +24 -8
- package/dist/runtime/create-validator.js +38 -8
- package/dist/runtime/create-validator.mjs +38 -8
- package/dist/runtime/decide-presence.js +2 -1
- package/dist/runtime/decide-presence.mjs +2 -1
- package/dist/runtime/field-rule-context.d.ts +32 -0
- package/dist/runtime/field-rule-context.js +48 -0
- package/dist/runtime/field-rule-context.mjs +44 -0
- package/dist/runtime/index-stack.d.ts +29 -4
- package/dist/runtime/index-stack.js +76 -15
- package/dist/runtime/index-stack.mjs +76 -15
- package/dist/runtime/run-array-node.js +22 -10
- package/dist/runtime/run-array-node.mjs +22 -10
- package/dist/runtime/run-field.js +47 -17
- package/dist/runtime/run-field.mjs +47 -17
- package/dist/runtime/run-plan.js +5 -1
- package/dist/runtime/run-plan.mjs +5 -1
- package/dist/standard-schema/index.d.ts +6 -0
- package/dist/standard-schema/index.js +7 -0
- package/dist/standard-schema/index.mjs +2 -0
- package/dist/standard-schema/split-issue-path.d.ts +9 -0
- package/dist/standard-schema/split-issue-path.js +66 -0
- package/dist/standard-schema/split-issue-path.mjs +63 -0
- package/dist/standard-schema/standard-schema.types.d.ts +46 -0
- package/dist/standard-schema/standard-schema.types.js +13 -0
- package/dist/standard-schema/standard-schema.types.mjs +12 -0
- package/dist/standard-schema/to-standard-schema.d.ts +41 -0
- package/dist/standard-schema/to-standard-schema.js +60 -0
- package/dist/standard-schema/to-standard-schema.mjs +57 -0
- package/dist/standard-schema.d.ts +1 -0
- package/dist/standard-schema.js +2 -0
- package/dist/standard-schema.mjs +1 -0
- package/dist/types/index.d.ts +24 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.mjs +17 -0
- package/package.json +42 -19
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isSchemaObject } from "./draft07.types.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { createLocalScope } from "./ref-scope.mjs";
|
|
3
|
+
import { resolveRefInScope } from "./resolve-ref.mjs";
|
|
4
|
+
import { nextBaseUri } from "./uri-reference.mjs";
|
|
3
5
|
/** `true`: matches every instance. */
|
|
4
6
|
const ALWAYS_SCHEMA = Object.freeze({});
|
|
5
7
|
/** `false`: matches nothing. `not` of the empty schema, so no new rule kind. */
|
|
@@ -28,10 +30,50 @@ export function toSchemaObject(schema) {
|
|
|
28
30
|
* this draft `$ref` replaces the object it appears in.
|
|
29
31
|
*/
|
|
30
32
|
export function resolveSchemaNode(schema, root) {
|
|
33
|
+
return resolveSchemaNodeInScope(schema, createLocalScope(root)).node;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The scope-aware form, and the one the converter uses.
|
|
37
|
+
*
|
|
38
|
+
* The returned scope is not the one passed in: following a `$ref` can cross
|
|
39
|
+
* into another document, and a `$ref` written inside THAT document resolves
|
|
40
|
+
* against ITS base. Returning only the node — which is what the root-taking
|
|
41
|
+
* form above can do — loses that, and is why a two-document schema resolved
|
|
42
|
+
* its second hop against the first document.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveSchemaNodeInScope(schema, scope) {
|
|
31
45
|
const node = toSchemaObject(schema);
|
|
32
|
-
if (node.$ref === undefined)
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
if (node.$ref === undefined) {
|
|
47
|
+
// A node's own `$id` moves the base for everything inside it (§8.2).
|
|
48
|
+
return { node, scope: advanceBase(scope, node) };
|
|
49
|
+
}
|
|
50
|
+
// Following a `$ref` lands in whatever base the TARGET lives in, and
|
|
51
|
+
// resolve-ref already knows it — for a document fetched by URI that is the
|
|
52
|
+
// retrieval URI, which the draft says wins over the document's own `$id`.
|
|
53
|
+
const resolved = resolveRefInScope(node.$ref, scope);
|
|
54
|
+
return { node: toSchemaObject(resolved.schema), scope: resolved.scope };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The ONE place a base URI advances. It was also being done by the caller,
|
|
58
|
+
* and doing it in both meant a relative `$id` was applied twice: a `$ref`
|
|
59
|
+
* of "nested/foo.json" under `$id: "nested/"` went looking for
|
|
60
|
+
* ".../nested/nested/foo.json".
|
|
61
|
+
*
|
|
62
|
+
* A base-setting `$id` starts a new RESOURCE, so the node also becomes the
|
|
63
|
+
* document that `#/definitions/x` written under it resolves against. Moving
|
|
64
|
+
* the base without moving the document is why
|
|
65
|
+
* `{"$id":"a.json","properties":{"foo":{"$id":"b.json","definitions":{...},
|
|
66
|
+
* "allOf":[{"$ref":"#/definitions/inner"}]}}}` looked for `inner` in the
|
|
67
|
+
* OUTER document, where it does not exist.
|
|
68
|
+
*/
|
|
69
|
+
export function advanceBase(scope, node) {
|
|
70
|
+
const id = node.$id;
|
|
71
|
+
if (typeof id !== "string" || id === "")
|
|
72
|
+
return scope;
|
|
73
|
+
const baseUri = nextBaseUri(scope.baseUri, id);
|
|
74
|
+
if (baseUri === scope.baseUri)
|
|
75
|
+
return scope;
|
|
76
|
+
return { ...scope, baseUri, document: node };
|
|
35
77
|
}
|
|
36
78
|
/**
|
|
37
79
|
* The pointer a node forwards to, or undefined. Used by the flattener to spot
|
|
@@ -2,5 +2,16 @@ import type { CompositeBranch, Rule } from "../plugin-kit/compiled-rule";
|
|
|
2
2
|
import type { Draft07Schema } from "./draft07.types";
|
|
3
3
|
import type { StructuralContext } from "./structural-expansion.types";
|
|
4
4
|
export declare function collectSubSchemaRules(schema: Draft07Schema, context: StructuralContext): readonly Rule[];
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Every branch of every composite is built here, so a branch is one shape.
|
|
7
|
+
*
|
|
8
|
+
* The call is to the function above and NOT to `context.collectSubSchemaRules`,
|
|
9
|
+
* and the difference is load-bearing. `context` is already the child context
|
|
10
|
+
* that createStructuralContext produced by DESCENDING through this schema, so
|
|
11
|
+
* a `$ref` here is already recorded in `visitedRefs`. Going through the
|
|
12
|
+
* context would descend the same `$ref` a second time, the recursion guard
|
|
13
|
+
* would see it as a cycle, and the branch would come back with no rules at all
|
|
14
|
+
* — `{"items":[{"$ref":"#/definitions/x"}]}` constrained nothing while the
|
|
15
|
+
* inline form `{"items":[{"type":"integer"}]}` worked.
|
|
16
|
+
*/
|
|
6
17
|
export declare function toSchemaBranch(label: string, schema: Draft07Schema, context: StructuralContext): CompositeBranch;
|
|
@@ -24,18 +24,15 @@ const create_rule_1 = require("../plugin-kit/create-rule");
|
|
|
24
24
|
const types_1 = require("../types");
|
|
25
25
|
const collect_definitions_1 = require("./collect-definitions");
|
|
26
26
|
const declare_presence_1 = require("./declare-presence");
|
|
27
|
-
const declare_value_keywords_1 = require("./declare-value-keywords");
|
|
28
27
|
const flatten_array_schema_1 = require("./flatten-array-schema");
|
|
29
28
|
const schema_to_declarations_1 = require("./schema-to-declarations");
|
|
30
29
|
const NO_BRANCH_FIELDS = Object.freeze([]);
|
|
31
30
|
function toBranchField(child, context) {
|
|
32
|
-
const node = (0, collect_definitions_1.resolveSchemaNode)(child.schema, context.root);
|
|
33
31
|
return {
|
|
34
32
|
path: child.step,
|
|
35
33
|
rules: Object.freeze([
|
|
36
34
|
...(0, declare_presence_1.declarePresenceRules)({
|
|
37
35
|
isRequired: child.isRequired,
|
|
38
|
-
allowsNull: (0, declare_value_keywords_1.permitsNull)(node),
|
|
39
36
|
severity: context.build.config.defaultSeverity,
|
|
40
37
|
}),
|
|
41
38
|
...context.collectSubSchemaRules(child.schema),
|
|
@@ -56,9 +53,31 @@ function composeProperties(children, context) {
|
|
|
56
53
|
}),
|
|
57
54
|
];
|
|
58
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Every sub-schema subject gets the same one-line statement: null is a VALUE
|
|
58
|
+
* here, so the checks run on it.
|
|
59
|
+
*
|
|
60
|
+
* src/runtime/run-field.ts settles presence BEFORE any check, and a subject
|
|
61
|
+
* with no presence rule carries OPEN_PRESENCE, which ends the field on null.
|
|
62
|
+
* A branch subject and an array element both arrive without one, so every
|
|
63
|
+
* check the sub-schema declared was skipped for null: `[null]` passed
|
|
64
|
+
* `{"items":{"type":"boolean"}}`, and `additionalItems: false` accepted a
|
|
65
|
+
* trailing null. Deciding it from `type` alone is not enough either —
|
|
66
|
+
* `false`, `{"not": {}}` and an `enum` without null forbid null while
|
|
67
|
+
* saying nothing about `type`.
|
|
68
|
+
*/
|
|
69
|
+
function declareOwnNullPolicy(context) {
|
|
70
|
+
return (0, declare_presence_1.declarePresenceRules)({
|
|
71
|
+
isRequired: false,
|
|
72
|
+
severity: context.build.config.defaultSeverity,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
59
75
|
function collectSubSchemaRules(schema, context) {
|
|
60
|
-
const node = (0, collect_definitions_1.
|
|
61
|
-
const rules = [
|
|
76
|
+
const node = (0, collect_definitions_1.resolveSchemaNodeInScope)(schema, context.scope).node;
|
|
77
|
+
const rules = [
|
|
78
|
+
...declareOwnNullPolicy(context),
|
|
79
|
+
...(0, schema_to_declarations_1.expandSchemaRules)(node, context),
|
|
80
|
+
];
|
|
62
81
|
const children = (0, schema_to_declarations_1.readChildSchemas)(node);
|
|
63
82
|
const properties = children.filter((child) => child.step !== flatten_array_schema_1.EACH_STEP);
|
|
64
83
|
if (properties.length > 0) {
|
|
@@ -72,11 +91,22 @@ function collectSubSchemaRules(schema, context) {
|
|
|
72
91
|
}
|
|
73
92
|
return Object.freeze(rules);
|
|
74
93
|
}
|
|
75
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Every branch of every composite is built here, so a branch is one shape.
|
|
96
|
+
*
|
|
97
|
+
* The call is to the function above and NOT to `context.collectSubSchemaRules`,
|
|
98
|
+
* and the difference is load-bearing. `context` is already the child context
|
|
99
|
+
* that createStructuralContext produced by DESCENDING through this schema, so
|
|
100
|
+
* a `$ref` here is already recorded in `visitedRefs`. Going through the
|
|
101
|
+
* context would descend the same `$ref` a second time, the recursion guard
|
|
102
|
+
* would see it as a cycle, and the branch would come back with no rules at all
|
|
103
|
+
* — `{"items":[{"$ref":"#/definitions/x"}]}` constrained nothing while the
|
|
104
|
+
* inline form `{"items":[{"type":"integer"}]}` worked.
|
|
105
|
+
*/
|
|
76
106
|
function toSchemaBranch(label, schema, context) {
|
|
77
107
|
return {
|
|
78
108
|
label,
|
|
79
|
-
rules:
|
|
109
|
+
rules: collectSubSchemaRules(schema, context),
|
|
80
110
|
fields: NO_BRANCH_FIELDS,
|
|
81
111
|
};
|
|
82
112
|
}
|
|
@@ -18,20 +18,17 @@
|
|
|
18
18
|
// ===========================================================================
|
|
19
19
|
import { composite, fieldsBranch } from "../plugin-kit/create-rule.mjs";
|
|
20
20
|
import { PASS } from "../types/index.mjs";
|
|
21
|
-
import {
|
|
21
|
+
import { resolveSchemaNodeInScope } from "./collect-definitions.mjs";
|
|
22
22
|
import { declarePresenceRules } from "./declare-presence.mjs";
|
|
23
|
-
import { permitsNull } from "./declare-value-keywords.mjs";
|
|
24
23
|
import { EACH_STEP } from "./flatten-array-schema.mjs";
|
|
25
24
|
import { expandSchemaRules, readChildSchemas } from "./schema-to-declarations.mjs";
|
|
26
25
|
const NO_BRANCH_FIELDS = Object.freeze([]);
|
|
27
26
|
function toBranchField(child, context) {
|
|
28
|
-
const node = resolveSchemaNode(child.schema, context.root);
|
|
29
27
|
return {
|
|
30
28
|
path: child.step,
|
|
31
29
|
rules: Object.freeze([
|
|
32
30
|
...declarePresenceRules({
|
|
33
31
|
isRequired: child.isRequired,
|
|
34
|
-
allowsNull: permitsNull(node),
|
|
35
32
|
severity: context.build.config.defaultSeverity,
|
|
36
33
|
}),
|
|
37
34
|
...context.collectSubSchemaRules(child.schema),
|
|
@@ -52,9 +49,31 @@ function composeProperties(children, context) {
|
|
|
52
49
|
}),
|
|
53
50
|
];
|
|
54
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Every sub-schema subject gets the same one-line statement: null is a VALUE
|
|
54
|
+
* here, so the checks run on it.
|
|
55
|
+
*
|
|
56
|
+
* src/runtime/run-field.ts settles presence BEFORE any check, and a subject
|
|
57
|
+
* with no presence rule carries OPEN_PRESENCE, which ends the field on null.
|
|
58
|
+
* A branch subject and an array element both arrive without one, so every
|
|
59
|
+
* check the sub-schema declared was skipped for null: `[null]` passed
|
|
60
|
+
* `{"items":{"type":"boolean"}}`, and `additionalItems: false` accepted a
|
|
61
|
+
* trailing null. Deciding it from `type` alone is not enough either —
|
|
62
|
+
* `false`, `{"not": {}}` and an `enum` without null forbid null while
|
|
63
|
+
* saying nothing about `type`.
|
|
64
|
+
*/
|
|
65
|
+
function declareOwnNullPolicy(context) {
|
|
66
|
+
return declarePresenceRules({
|
|
67
|
+
isRequired: false,
|
|
68
|
+
severity: context.build.config.defaultSeverity,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
55
71
|
export function collectSubSchemaRules(schema, context) {
|
|
56
|
-
const node =
|
|
57
|
-
const rules = [
|
|
72
|
+
const node = resolveSchemaNodeInScope(schema, context.scope).node;
|
|
73
|
+
const rules = [
|
|
74
|
+
...declareOwnNullPolicy(context),
|
|
75
|
+
...expandSchemaRules(node, context),
|
|
76
|
+
];
|
|
58
77
|
const children = readChildSchemas(node);
|
|
59
78
|
const properties = children.filter((child) => child.step !== EACH_STEP);
|
|
60
79
|
if (properties.length > 0) {
|
|
@@ -68,11 +87,22 @@ export function collectSubSchemaRules(schema, context) {
|
|
|
68
87
|
}
|
|
69
88
|
return Object.freeze(rules);
|
|
70
89
|
}
|
|
71
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Every branch of every composite is built here, so a branch is one shape.
|
|
92
|
+
*
|
|
93
|
+
* The call is to the function above and NOT to `context.collectSubSchemaRules`,
|
|
94
|
+
* and the difference is load-bearing. `context` is already the child context
|
|
95
|
+
* that createStructuralContext produced by DESCENDING through this schema, so
|
|
96
|
+
* a `$ref` here is already recorded in `visitedRefs`. Going through the
|
|
97
|
+
* context would descend the same `$ref` a second time, the recursion guard
|
|
98
|
+
* would see it as a cycle, and the branch would come back with no rules at all
|
|
99
|
+
* — `{"items":[{"$ref":"#/definitions/x"}]}` constrained nothing while the
|
|
100
|
+
* inline form `{"items":[{"type":"integer"}]}` worked.
|
|
101
|
+
*/
|
|
72
102
|
export function toSchemaBranch(label, schema, context) {
|
|
73
103
|
return {
|
|
74
104
|
label,
|
|
75
|
-
rules:
|
|
105
|
+
rules: collectSubSchemaRules(schema, context),
|
|
76
106
|
fields: NO_BRANCH_FIELDS,
|
|
77
107
|
};
|
|
78
108
|
}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import type { ChainBuildContext } from "../chain/create-chain-node";
|
|
2
|
-
import type {
|
|
2
|
+
import type { RefScope } from "./ref-scope";
|
|
3
|
+
import type { Draft07SchemaObject } from "./draft07.types";
|
|
3
4
|
import type { JsonSchemaBag } from "./json-schema-bag.types";
|
|
4
5
|
import type { StructuralContext } from "./structural-expansion.types";
|
|
6
|
+
/** Shared by every context of ONE conversion, so the budget is the whole. */
|
|
7
|
+
interface ExpansionBudget {
|
|
8
|
+
remaining: number;
|
|
9
|
+
}
|
|
5
10
|
export interface ConversionSeed {
|
|
6
11
|
readonly bag: JsonSchemaBag;
|
|
7
|
-
readonly
|
|
12
|
+
readonly scope: RefScope;
|
|
8
13
|
readonly chain: ChainBuildContext;
|
|
14
|
+
/** Created by the entry point; every nested context shares this one. */
|
|
15
|
+
readonly budget?: ExpansionBudget;
|
|
9
16
|
}
|
|
10
|
-
|
|
17
|
+
/** The budget a conversion starts with. One per document, not one per node. */
|
|
18
|
+
export declare function createExpansionBudget(): ExpansionBudget;
|
|
19
|
+
export declare function createStructuralContext(seed: ConversionSeed, node: Draft07SchemaObject, visitedRefs: readonly string[], scope?: RefScope): StructuralContext;
|
|
20
|
+
export {};
|
|
@@ -1,25 +1,91 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createExpansionBudget = createExpansionBudget;
|
|
3
4
|
exports.createStructuralContext = createStructuralContext;
|
|
4
5
|
const collect_definitions_1 = require("./collect-definitions");
|
|
5
6
|
const collect_sub_schema_rules_1 = require("./collect-sub-schema-rules");
|
|
6
7
|
const NO_RULES = Object.freeze([]);
|
|
8
|
+
/**
|
|
9
|
+
* How many times one `$ref` may be entered on a single branch of the
|
|
10
|
+
* conversion, and how much expansion the whole conversion may do.
|
|
11
|
+
*
|
|
12
|
+
* A recursive definition has infinitely many declared paths and Luq declares
|
|
13
|
+
* finite ones, so SOMETHING has to stop. It used to stop at the FIRST repeat,
|
|
14
|
+
* which is why a mutually recursive schema (tree -> node -> tree) checked only
|
|
15
|
+
* two levels and silently accepted anything deeper.
|
|
16
|
+
*
|
|
17
|
+
* Unrolling further is exponential in the number of mutually recursive
|
|
18
|
+
* definitions, and the measurement says so plainly. A three-way cycle
|
|
19
|
+
* (a -> b -> c -> a, each also naming the others) builds in:
|
|
20
|
+
*
|
|
21
|
+
* 1 unroll 17 ms 2 unrolls 55 ms
|
|
22
|
+
* 3 unrolls 538 ms 4 unrolls 9257 ms
|
|
23
|
+
*
|
|
24
|
+
* Three is what the corpus needs (the mutually recursive tree in ref.json
|
|
25
|
+
* puts its invalid value three levels down) and is where the cost is still
|
|
26
|
+
* a fraction of a second. It is a BUILD-time cost, paid once per document.
|
|
27
|
+
*
|
|
28
|
+
* The per-ref cap bounds the depth and leaves the WIDTH to the document, so
|
|
29
|
+
* it is not a bound on the work by itself. EXPANSION_BUDGET is: it caps the
|
|
30
|
+
* total number of `$ref` expansions one conversion may do, whatever shape
|
|
31
|
+
* the document has. It is deliberately far above what a real schema reaches
|
|
32
|
+
* — the whole official corpus stays under it — so it changes nothing for
|
|
33
|
+
* ordinary documents and only stops a pathological one from running away.
|
|
34
|
+
*
|
|
35
|
+
* Running out is not an error. The conversion stops adding rules, exactly as
|
|
36
|
+
* it did at the first repeat before, and the document is checked to the depth
|
|
37
|
+
* that was reached — the same silence as before, moved further out.
|
|
38
|
+
*/
|
|
39
|
+
const MAX_REF_UNROLL = 3;
|
|
40
|
+
const EXPANSION_BUDGET = 100000;
|
|
41
|
+
function countOf(pointers, pointer) {
|
|
42
|
+
let count = 0;
|
|
43
|
+
for (const one of pointers)
|
|
44
|
+
if (one === pointer)
|
|
45
|
+
count += 1;
|
|
46
|
+
return count;
|
|
47
|
+
}
|
|
48
|
+
/** The budget a conversion starts with. One per document, not one per node. */
|
|
49
|
+
function createExpansionBudget() {
|
|
50
|
+
return { remaining: EXPANSION_BUDGET };
|
|
51
|
+
}
|
|
7
52
|
function propertyKeysOf(schema) {
|
|
8
53
|
return Object.freeze(Object.keys(schema.properties ?? {}));
|
|
9
54
|
}
|
|
10
|
-
function createStructuralContext(seed, node, visitedRefs) {
|
|
55
|
+
function createStructuralContext(seed, node, visitedRefs, scope = seed.scope) {
|
|
56
|
+
const budget = seed.budget ?? createExpansionBudget();
|
|
11
57
|
const build = {
|
|
12
58
|
fieldPath: seed.chain.fieldPath,
|
|
13
59
|
declaredSiblingKeys: propertyKeysOf(node),
|
|
14
60
|
config: seed.chain.config,
|
|
15
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* One descent: the node this schema really is, and the context inside it.
|
|
64
|
+
*
|
|
65
|
+
* The RESOLVED node is handed back, and callers must use it rather than the
|
|
66
|
+
* schema they passed in. Resolving the same `$ref` a second time — which
|
|
67
|
+
* is what happens if the original is passed on — moves the base twice, so
|
|
68
|
+
* a relative reference under a relative `$id` doubles its own folder.
|
|
69
|
+
*/
|
|
16
70
|
const descend = (schema) => {
|
|
17
71
|
const pointer = (0, collect_definitions_1.readRefPointer)(schema);
|
|
18
|
-
if (pointer !== undefined &&
|
|
72
|
+
if (pointer !== undefined &&
|
|
73
|
+
countOf(visitedRefs, pointer) >= MAX_REF_UNROLL) {
|
|
19
74
|
return undefined;
|
|
20
75
|
}
|
|
76
|
+
if (pointer !== undefined) {
|
|
77
|
+
if (budget.remaining <= 0)
|
|
78
|
+
return undefined;
|
|
79
|
+
budget.remaining -= 1;
|
|
80
|
+
}
|
|
21
81
|
const seen = pointer === undefined ? visitedRefs : [...visitedRefs, pointer];
|
|
22
|
-
|
|
82
|
+
// The scope moves with the node: following a `$ref` into another
|
|
83
|
+
// document makes THAT document's base the one its own refs resolve in.
|
|
84
|
+
const resolved = (0, collect_definitions_1.resolveSchemaNodeInScope)(schema, scope);
|
|
85
|
+
return {
|
|
86
|
+
node: resolved.node,
|
|
87
|
+
context: createStructuralContext({ ...seed, budget }, resolved.node, seen, resolved.scope),
|
|
88
|
+
};
|
|
23
89
|
};
|
|
24
90
|
const ruleContextFor = (pluginName, code) => ({
|
|
25
91
|
pluginName,
|
|
@@ -32,19 +98,19 @@ function createStructuralContext(seed, node, visitedRefs) {
|
|
|
32
98
|
return {
|
|
33
99
|
bag: seed.bag,
|
|
34
100
|
build,
|
|
35
|
-
|
|
101
|
+
scope,
|
|
36
102
|
visitedRefs,
|
|
37
103
|
collectSubSchemaRules: (schema) => {
|
|
38
104
|
const child = descend(schema);
|
|
39
105
|
return child === undefined
|
|
40
106
|
? NO_RULES
|
|
41
|
-
: (0, collect_sub_schema_rules_1.collectSubSchemaRules)(
|
|
107
|
+
: (0, collect_sub_schema_rules_1.collectSubSchemaRules)(child.node, child.context);
|
|
42
108
|
},
|
|
43
109
|
toBranch: (label, schema) => {
|
|
44
110
|
const child = descend(schema);
|
|
45
111
|
return child === undefined
|
|
46
112
|
? { label, rules: NO_RULES, fields: [] }
|
|
47
|
-
: (0, collect_sub_schema_rules_1.toSchemaBranch)(label,
|
|
113
|
+
: (0, collect_sub_schema_rules_1.toSchemaBranch)(label, child.node, child.context);
|
|
48
114
|
},
|
|
49
115
|
ruleContextFor,
|
|
50
116
|
};
|
|
@@ -1,22 +1,87 @@
|
|
|
1
|
-
import { readRefPointer,
|
|
1
|
+
import { readRefPointer, resolveSchemaNodeInScope, } from "./collect-definitions.mjs";
|
|
2
2
|
import { collectSubSchemaRules, toSchemaBranch, } from "./collect-sub-schema-rules.mjs";
|
|
3
3
|
const NO_RULES = Object.freeze([]);
|
|
4
|
+
/**
|
|
5
|
+
* How many times one `$ref` may be entered on a single branch of the
|
|
6
|
+
* conversion, and how much expansion the whole conversion may do.
|
|
7
|
+
*
|
|
8
|
+
* A recursive definition has infinitely many declared paths and Luq declares
|
|
9
|
+
* finite ones, so SOMETHING has to stop. It used to stop at the FIRST repeat,
|
|
10
|
+
* which is why a mutually recursive schema (tree -> node -> tree) checked only
|
|
11
|
+
* two levels and silently accepted anything deeper.
|
|
12
|
+
*
|
|
13
|
+
* Unrolling further is exponential in the number of mutually recursive
|
|
14
|
+
* definitions, and the measurement says so plainly. A three-way cycle
|
|
15
|
+
* (a -> b -> c -> a, each also naming the others) builds in:
|
|
16
|
+
*
|
|
17
|
+
* 1 unroll 17 ms 2 unrolls 55 ms
|
|
18
|
+
* 3 unrolls 538 ms 4 unrolls 9257 ms
|
|
19
|
+
*
|
|
20
|
+
* Three is what the corpus needs (the mutually recursive tree in ref.json
|
|
21
|
+
* puts its invalid value three levels down) and is where the cost is still
|
|
22
|
+
* a fraction of a second. It is a BUILD-time cost, paid once per document.
|
|
23
|
+
*
|
|
24
|
+
* The per-ref cap bounds the depth and leaves the WIDTH to the document, so
|
|
25
|
+
* it is not a bound on the work by itself. EXPANSION_BUDGET is: it caps the
|
|
26
|
+
* total number of `$ref` expansions one conversion may do, whatever shape
|
|
27
|
+
* the document has. It is deliberately far above what a real schema reaches
|
|
28
|
+
* — the whole official corpus stays under it — so it changes nothing for
|
|
29
|
+
* ordinary documents and only stops a pathological one from running away.
|
|
30
|
+
*
|
|
31
|
+
* Running out is not an error. The conversion stops adding rules, exactly as
|
|
32
|
+
* it did at the first repeat before, and the document is checked to the depth
|
|
33
|
+
* that was reached — the same silence as before, moved further out.
|
|
34
|
+
*/
|
|
35
|
+
const MAX_REF_UNROLL = 3;
|
|
36
|
+
const EXPANSION_BUDGET = 100000;
|
|
37
|
+
function countOf(pointers, pointer) {
|
|
38
|
+
let count = 0;
|
|
39
|
+
for (const one of pointers)
|
|
40
|
+
if (one === pointer)
|
|
41
|
+
count += 1;
|
|
42
|
+
return count;
|
|
43
|
+
}
|
|
44
|
+
/** The budget a conversion starts with. One per document, not one per node. */
|
|
45
|
+
export function createExpansionBudget() {
|
|
46
|
+
return { remaining: EXPANSION_BUDGET };
|
|
47
|
+
}
|
|
4
48
|
function propertyKeysOf(schema) {
|
|
5
49
|
return Object.freeze(Object.keys(schema.properties ?? {}));
|
|
6
50
|
}
|
|
7
|
-
export function createStructuralContext(seed, node, visitedRefs) {
|
|
51
|
+
export function createStructuralContext(seed, node, visitedRefs, scope = seed.scope) {
|
|
52
|
+
const budget = seed.budget ?? createExpansionBudget();
|
|
8
53
|
const build = {
|
|
9
54
|
fieldPath: seed.chain.fieldPath,
|
|
10
55
|
declaredSiblingKeys: propertyKeysOf(node),
|
|
11
56
|
config: seed.chain.config,
|
|
12
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* One descent: the node this schema really is, and the context inside it.
|
|
60
|
+
*
|
|
61
|
+
* The RESOLVED node is handed back, and callers must use it rather than the
|
|
62
|
+
* schema they passed in. Resolving the same `$ref` a second time — which
|
|
63
|
+
* is what happens if the original is passed on — moves the base twice, so
|
|
64
|
+
* a relative reference under a relative `$id` doubles its own folder.
|
|
65
|
+
*/
|
|
13
66
|
const descend = (schema) => {
|
|
14
67
|
const pointer = readRefPointer(schema);
|
|
15
|
-
if (pointer !== undefined &&
|
|
68
|
+
if (pointer !== undefined &&
|
|
69
|
+
countOf(visitedRefs, pointer) >= MAX_REF_UNROLL) {
|
|
16
70
|
return undefined;
|
|
17
71
|
}
|
|
72
|
+
if (pointer !== undefined) {
|
|
73
|
+
if (budget.remaining <= 0)
|
|
74
|
+
return undefined;
|
|
75
|
+
budget.remaining -= 1;
|
|
76
|
+
}
|
|
18
77
|
const seen = pointer === undefined ? visitedRefs : [...visitedRefs, pointer];
|
|
19
|
-
|
|
78
|
+
// The scope moves with the node: following a `$ref` into another
|
|
79
|
+
// document makes THAT document's base the one its own refs resolve in.
|
|
80
|
+
const resolved = resolveSchemaNodeInScope(schema, scope);
|
|
81
|
+
return {
|
|
82
|
+
node: resolved.node,
|
|
83
|
+
context: createStructuralContext({ ...seed, budget }, resolved.node, seen, resolved.scope),
|
|
84
|
+
};
|
|
20
85
|
};
|
|
21
86
|
const ruleContextFor = (pluginName, code) => ({
|
|
22
87
|
pluginName,
|
|
@@ -29,19 +94,19 @@ export function createStructuralContext(seed, node, visitedRefs) {
|
|
|
29
94
|
return {
|
|
30
95
|
bag: seed.bag,
|
|
31
96
|
build,
|
|
32
|
-
|
|
97
|
+
scope,
|
|
33
98
|
visitedRefs,
|
|
34
99
|
collectSubSchemaRules: (schema) => {
|
|
35
100
|
const child = descend(schema);
|
|
36
101
|
return child === undefined
|
|
37
102
|
? NO_RULES
|
|
38
|
-
: collectSubSchemaRules(
|
|
103
|
+
: collectSubSchemaRules(child.node, child.context);
|
|
39
104
|
},
|
|
40
105
|
toBranch: (label, schema) => {
|
|
41
106
|
const child = descend(schema);
|
|
42
107
|
return child === undefined
|
|
43
108
|
? { label, rules: NO_RULES, fields: [] }
|
|
44
|
-
: toSchemaBranch(label,
|
|
109
|
+
: toSchemaBranch(label, child.node, child.context);
|
|
45
110
|
},
|
|
46
111
|
ruleContextFor,
|
|
47
112
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Rule } from "../plugin-kit/compiled-rule";
|
|
2
|
+
import type { ConverterChain } from "./apply-keyword-binding";
|
|
3
|
+
import type { Draft07SchemaObject } from "./draft07.types";
|
|
4
|
+
import type { StructuralContext } from "./structural-expansion.types";
|
|
5
|
+
/**
|
|
6
|
+
* `patternProperties` のキー、つまり正規表現の文字列。
|
|
7
|
+
* additionalProperties が「該当しないキー」を選ぶのに要る。
|
|
8
|
+
*/
|
|
9
|
+
export declare function patternPropertyKeys(schema: Draft07SchemaObject): readonly string[];
|
|
10
|
+
/**
|
|
11
|
+
* BOOLEAN 形。
|
|
12
|
+
*
|
|
13
|
+
* キーワード束縛は自分の値 (boolean) しか運べないので、パターンがあるときだけ
|
|
14
|
+
* プラグインを直接呼ぶ。束縛のほうは残す: キーワード表と「そのメソッドが
|
|
15
|
+
* 実在する」というコンパイル時のゲートはそちらが持っている。
|
|
16
|
+
*/
|
|
17
|
+
export declare function applyAdditionalPropertiesBoolean(chain: ConverterChain<"object">, schema: Draft07SchemaObject, allowed: boolean): ConverterChain<"object">;
|
|
18
|
+
/** SCHEMA 形: 宣言にもパターンにも該当しない値が、そのスキーマに従う。 */
|
|
19
|
+
export declare function declareAdditionalPropertiesSchema(schema: Draft07SchemaObject, context: StructuralContext): readonly Rule[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.patternPropertyKeys = patternPropertyKeys;
|
|
4
|
+
exports.applyAdditionalPropertiesBoolean = applyAdditionalPropertiesBoolean;
|
|
5
|
+
exports.declareAdditionalPropertiesSchema = declareAdditionalPropertiesSchema;
|
|
6
|
+
const apply_keyword_binding_1 = require("./apply-keyword-binding");
|
|
7
|
+
const keyword_map_object_1 = require("./keyword-map-object");
|
|
8
|
+
const NO_RULES = Object.freeze([]);
|
|
9
|
+
/**
|
|
10
|
+
* `patternProperties` のキー、つまり正規表現の文字列。
|
|
11
|
+
* additionalProperties が「該当しないキー」を選ぶのに要る。
|
|
12
|
+
*/
|
|
13
|
+
function patternPropertyKeys(schema) {
|
|
14
|
+
const patterns = schema.patternProperties;
|
|
15
|
+
if (patterns === undefined || patterns === null)
|
|
16
|
+
return Object.freeze([]);
|
|
17
|
+
return Object.freeze(Object.keys(patterns));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* BOOLEAN 形。
|
|
21
|
+
*
|
|
22
|
+
* キーワード束縛は自分の値 (boolean) しか運べないので、パターンがあるときだけ
|
|
23
|
+
* プラグインを直接呼ぶ。束縛のほうは残す: キーワード表と「そのメソッドが
|
|
24
|
+
* 実在する」というコンパイル時のゲートはそちらが持っている。
|
|
25
|
+
*/
|
|
26
|
+
function applyAdditionalPropertiesBoolean(chain, schema, allowed) {
|
|
27
|
+
const patterns = patternPropertyKeys(schema);
|
|
28
|
+
return patterns.length === 0
|
|
29
|
+
? (0, apply_keyword_binding_1.applyKeywordBinding)(chain, keyword_map_object_1.additionalPropertiesBinding, allowed)
|
|
30
|
+
: chain.additionalProperties(allowed, undefined, patterns);
|
|
31
|
+
}
|
|
32
|
+
/** SCHEMA 形: 宣言にもパターンにも該当しない値が、そのスキーマに従う。 */
|
|
33
|
+
function declareAdditionalPropertiesSchema(schema, context) {
|
|
34
|
+
const additional = schema.additionalProperties;
|
|
35
|
+
if (additional === undefined || typeof additional === "boolean") {
|
|
36
|
+
return NO_RULES;
|
|
37
|
+
}
|
|
38
|
+
const plugin = context.bag.objectAdditionalPropertiesSchema;
|
|
39
|
+
return Object.freeze([
|
|
40
|
+
plugin.build(context.ruleContextFor(plugin.name, "additionalProperties"), context.collectSubSchemaRules(additional), undefined, patternPropertyKeys(schema)),
|
|
41
|
+
]);
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { applyKeywordBinding } from "./apply-keyword-binding.mjs";
|
|
2
|
+
import { additionalPropertiesBinding } from "./keyword-map-object.mjs";
|
|
3
|
+
const NO_RULES = Object.freeze([]);
|
|
4
|
+
/**
|
|
5
|
+
* `patternProperties` のキー、つまり正規表現の文字列。
|
|
6
|
+
* additionalProperties が「該当しないキー」を選ぶのに要る。
|
|
7
|
+
*/
|
|
8
|
+
export function patternPropertyKeys(schema) {
|
|
9
|
+
const patterns = schema.patternProperties;
|
|
10
|
+
if (patterns === undefined || patterns === null)
|
|
11
|
+
return Object.freeze([]);
|
|
12
|
+
return Object.freeze(Object.keys(patterns));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* BOOLEAN 形。
|
|
16
|
+
*
|
|
17
|
+
* キーワード束縛は自分の値 (boolean) しか運べないので、パターンがあるときだけ
|
|
18
|
+
* プラグインを直接呼ぶ。束縛のほうは残す: キーワード表と「そのメソッドが
|
|
19
|
+
* 実在する」というコンパイル時のゲートはそちらが持っている。
|
|
20
|
+
*/
|
|
21
|
+
export function applyAdditionalPropertiesBoolean(chain, schema, allowed) {
|
|
22
|
+
const patterns = patternPropertyKeys(schema);
|
|
23
|
+
return patterns.length === 0
|
|
24
|
+
? applyKeywordBinding(chain, additionalPropertiesBinding, allowed)
|
|
25
|
+
: chain.additionalProperties(allowed, undefined, patterns);
|
|
26
|
+
}
|
|
27
|
+
/** SCHEMA 形: 宣言にもパターンにも該当しない値が、そのスキーマに従う。 */
|
|
28
|
+
export function declareAdditionalPropertiesSchema(schema, context) {
|
|
29
|
+
const additional = schema.additionalProperties;
|
|
30
|
+
if (additional === undefined || typeof additional === "boolean") {
|
|
31
|
+
return NO_RULES;
|
|
32
|
+
}
|
|
33
|
+
const plugin = context.bag.objectAdditionalPropertiesSchema;
|
|
34
|
+
return Object.freeze([
|
|
35
|
+
plugin.build(context.ruleContextFor(plugin.name, "additionalProperties"), context.collectSubSchemaRules(additional), undefined, patternPropertyKeys(schema)),
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
@@ -2,8 +2,6 @@ import type { Rule } from "../plugin-kit/compiled-rule";
|
|
|
2
2
|
import type { Draft07Schema, Draft07SchemaObject } from "./draft07.types";
|
|
3
3
|
import type { StructuralContext } from "./structural-expansion.types";
|
|
4
4
|
export declare function declareObjectRules(schema: Draft07SchemaObject, context: StructuralContext): readonly Rule[];
|
|
5
|
-
/** The SCHEMA form of `additionalProperties`: every undeclared value obeys it. */
|
|
6
|
-
export declare function declareAdditionalPropertiesSchema(schema: Draft07SchemaObject, context: StructuralContext): readonly Rule[];
|
|
7
5
|
/** EVERY matching pattern applies; 1.x broke after the first match. */
|
|
8
6
|
export declare function declarePatternProperties(schema: Draft07SchemaObject, context: StructuralContext): readonly Rule[];
|
|
9
7
|
/** A sub-chain over the property NAMES, which are always strings. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.declareObjectRules = declareObjectRules;
|
|
4
|
-
exports.declareAdditionalPropertiesSchema = declareAdditionalPropertiesSchema;
|
|
5
4
|
exports.declarePatternProperties = declarePatternProperties;
|
|
6
5
|
exports.declarePropertyNames = declarePropertyNames;
|
|
7
6
|
exports.declareDependencies = declareDependencies;
|
|
@@ -23,6 +22,7 @@ exports.readPropertyChildren = readPropertyChildren;
|
|
|
23
22
|
const create_chain_node_1 = require("../chain/create-chain-node");
|
|
24
23
|
const create_field_slots_1 = require("../chain/create-field-slots");
|
|
25
24
|
const types_1 = require("../types");
|
|
25
|
+
const declare_additional_properties_1 = require("./declare-additional-properties");
|
|
26
26
|
const apply_keyword_binding_1 = require("./apply-keyword-binding");
|
|
27
27
|
const collect_definitions_1 = require("./collect-definitions");
|
|
28
28
|
const keyword_map_object_1 = require("./keyword-map-object");
|
|
@@ -40,21 +40,14 @@ function declareObjectRules(schema, context) {
|
|
|
40
40
|
}
|
|
41
41
|
const additional = schema.additionalProperties;
|
|
42
42
|
if (typeof additional === "boolean") {
|
|
43
|
-
|
|
43
|
+
// §6.5.4 の対象は「properties にも patternProperties にも該当しない」キー。
|
|
44
|
+
// キーワード束縛は自分の値 (boolean) しか運べないので、パターンがある
|
|
45
|
+
// ときだけプラグインを直接呼ぶ。束縛のほうは残す: キーワード表と
|
|
46
|
+
// 「そのメソッドが実在する」というコンパイル時のゲートはそちらが持つ。
|
|
47
|
+
chain = (0, declare_additional_properties_1.applyAdditionalPropertiesBoolean)(chain, schema, additional);
|
|
44
48
|
}
|
|
45
49
|
return readRules(chain);
|
|
46
50
|
}
|
|
47
|
-
/** The SCHEMA form of `additionalProperties`: every undeclared value obeys it. */
|
|
48
|
-
function declareAdditionalPropertiesSchema(schema, context) {
|
|
49
|
-
const additional = schema.additionalProperties;
|
|
50
|
-
if (additional === undefined || typeof additional === "boolean") {
|
|
51
|
-
return NO_RULES;
|
|
52
|
-
}
|
|
53
|
-
const plugin = context.bag.objectAdditionalPropertiesSchema;
|
|
54
|
-
return Object.freeze([
|
|
55
|
-
plugin.build(context.ruleContextFor(plugin.name, "additionalProperties"), context.collectSubSchemaRules(additional)),
|
|
56
|
-
]);
|
|
57
|
-
}
|
|
58
51
|
/** EVERY matching pattern applies; 1.x broke after the first match. */
|
|
59
52
|
function declarePatternProperties(schema, context) {
|
|
60
53
|
const patterns = schema.patternProperties;
|