@maroonedog/luq 2.0.0 → 2.1.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 +100 -10
- package/dist/chain/create-chain-node.js +22 -1
- package/dist/chain/create-chain-node.mjs +22 -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/validation-plan.types.d.ts +10 -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/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/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/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/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 +2 -2
- package/dist/plugins/string-min/string-min.mjs +3 -3
- package/dist/runtime/decide-presence.js +2 -1
- package/dist/runtime/decide-presence.mjs +2 -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 +12 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.mjs +17 -0
- package/package.json +10 -3
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// ===========================================================================
|
|
2
|
+
// L8 src/json-schema/schema-registry.ts — どの URI がどのスキーマを指すか。
|
|
3
|
+
//
|
|
4
|
+
// Draft-07 §8.2 では `$id` が二つの働きをする: 場所を示す URI はベースを
|
|
5
|
+
// 立て直し、`#name` の形は**位置に依らない名前**を付ける。どちらも
|
|
6
|
+
// 「この URI はこのノード」という索引で、それがこのファイルの責務である。
|
|
7
|
+
// ポインタ (`#/definitions/x`) を辿るのは場所の話なので resolve-ref.ts が持つ。
|
|
8
|
+
//
|
|
9
|
+
// 外部文書は**呼び出し側が渡したものだけ**である。Luq は取りに行かない:
|
|
10
|
+
// 関数ではなく地図 (`externalDocuments`) を受けるのは、そうすれば
|
|
11
|
+
// * スキーマに書かれた URI でプロセスがソケットを開くことがない (SSRF)、
|
|
12
|
+
// * 変換が同期のままで、CSP でも動く、
|
|
13
|
+
// * 何が読まれうるかが呼び出し側のコードに全部書いてある、
|
|
14
|
+
// の三つが同時に成り立つからで、非同期ローダーではどれも失われる。
|
|
15
|
+
// ===========================================================================
|
|
16
|
+
import { isDraft07Schema, isSchemaObject } from "./draft07.types.mjs";
|
|
17
|
+
import { isArray, isPlainObject } from "../types/index.mjs";
|
|
18
|
+
import { nextBaseUri, normalizeUri, readAnchor } from "./uri-reference.mjs";
|
|
19
|
+
/** Keywords whose value is ONE schema. */
|
|
20
|
+
const SCHEMA_VALUED = Object.freeze([
|
|
21
|
+
"additionalProperties",
|
|
22
|
+
"additionalItems",
|
|
23
|
+
"contains",
|
|
24
|
+
"propertyNames",
|
|
25
|
+
"not",
|
|
26
|
+
"if",
|
|
27
|
+
"then",
|
|
28
|
+
"else",
|
|
29
|
+
]);
|
|
30
|
+
/** Keywords whose value is an ARRAY of schemas. `items` is in both lists. */
|
|
31
|
+
const SCHEMA_LIST_VALUED = Object.freeze([
|
|
32
|
+
"allOf",
|
|
33
|
+
"anyOf",
|
|
34
|
+
"oneOf",
|
|
35
|
+
]);
|
|
36
|
+
/** Keywords whose value is a MAP of name to schema. */
|
|
37
|
+
const SCHEMA_MAP_VALUED = Object.freeze([
|
|
38
|
+
"properties",
|
|
39
|
+
"patternProperties",
|
|
40
|
+
"definitions",
|
|
41
|
+
"$defs",
|
|
42
|
+
"dependencies",
|
|
43
|
+
]);
|
|
44
|
+
function walkSchema(schema, base, document, register) {
|
|
45
|
+
if (!isSchemaObject(schema))
|
|
46
|
+
return;
|
|
47
|
+
const id = typeof schema.$id === "string" ? schema.$id : undefined;
|
|
48
|
+
const here = nextBaseUri(base, id);
|
|
49
|
+
// A base-setting `$id` starts a new resource, so the node becomes the
|
|
50
|
+
// document every relative pointer under it resolves against.
|
|
51
|
+
const startsResource = id !== undefined && here !== base;
|
|
52
|
+
const inner = startsResource ? schema : document;
|
|
53
|
+
if (startsResource) {
|
|
54
|
+
register(normalizeUri(here), { schema, baseUri: here, document: schema });
|
|
55
|
+
}
|
|
56
|
+
const anchor = readAnchor(id);
|
|
57
|
+
if (anchor !== undefined) {
|
|
58
|
+
register(normalizeUri(`${here}#${anchor}`), {
|
|
59
|
+
schema,
|
|
60
|
+
baseUri: here,
|
|
61
|
+
document: inner,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
walkMembers(schema, here, inner, register);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read by ENTRY rather than by indexing the typed node: Draft07SchemaObject
|
|
68
|
+
* declares its keywords and carries no index signature, and reaching a keyword
|
|
69
|
+
* by name would need an assertion — src/core/type-erasure.ts is the only place
|
|
70
|
+
* permitted to write one, and this is not it.
|
|
71
|
+
*/
|
|
72
|
+
function walkMembers(schema, base, document, register) {
|
|
73
|
+
for (const [keyword, value] of Object.entries(schema)) {
|
|
74
|
+
if (keyword === "items") {
|
|
75
|
+
// The one keyword with both shapes, so it is walked as both.
|
|
76
|
+
walkIfSchema(value, base, document, register);
|
|
77
|
+
walkList(value, base, document, register);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (SCHEMA_VALUED.includes(keyword)) {
|
|
81
|
+
walkIfSchema(value, base, document, register);
|
|
82
|
+
}
|
|
83
|
+
else if (SCHEMA_LIST_VALUED.includes(keyword)) {
|
|
84
|
+
walkList(value, base, document, register);
|
|
85
|
+
}
|
|
86
|
+
else if (SCHEMA_MAP_VALUED.includes(keyword)) {
|
|
87
|
+
walkMap(value, base, document, register);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function walkIfSchema(value, base, document, register) {
|
|
92
|
+
if (!isDraft07Schema(value))
|
|
93
|
+
return;
|
|
94
|
+
walkSchema(value, base, document, register);
|
|
95
|
+
}
|
|
96
|
+
function walkList(value, base, document, register) {
|
|
97
|
+
if (!isArray(value))
|
|
98
|
+
return;
|
|
99
|
+
for (const member of value)
|
|
100
|
+
walkIfSchema(member, base, document, register);
|
|
101
|
+
}
|
|
102
|
+
function walkMap(value, base, document, register) {
|
|
103
|
+
if (!isPlainObject(value))
|
|
104
|
+
return;
|
|
105
|
+
for (const member of Object.values(value)) {
|
|
106
|
+
walkIfSchema(member, base, document, register);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Every `$id` in `root` and in each supplied external document.
|
|
111
|
+
*
|
|
112
|
+
* The external map's KEY is a URI in its own right: the suite (and real
|
|
113
|
+
* callers) fetch `http://host/a.json` into a document that does not repeat its
|
|
114
|
+
* own `$id`, so the key is what a `$ref` will name. A document that DOES carry
|
|
115
|
+
* `$id` registers that too, and the `$id` wins where they disagree — it is the
|
|
116
|
+
* document's own statement about its identity.
|
|
117
|
+
*/
|
|
118
|
+
export function createSchemaRegistry(root, externalDocuments = {}) {
|
|
119
|
+
const byUri = new Map();
|
|
120
|
+
const register = (uri, target) => {
|
|
121
|
+
if (!byUri.has(uri))
|
|
122
|
+
byUri.set(uri, target);
|
|
123
|
+
};
|
|
124
|
+
for (const [uri, document] of Object.entries(externalDocuments)) {
|
|
125
|
+
if (!isDraft07Schema(document))
|
|
126
|
+
continue;
|
|
127
|
+
const key = normalizeUri(uri);
|
|
128
|
+
register(key, { schema: document, baseUri: key, document });
|
|
129
|
+
}
|
|
130
|
+
for (const [uri, document] of Object.entries(externalDocuments)) {
|
|
131
|
+
if (!isDraft07Schema(document))
|
|
132
|
+
continue;
|
|
133
|
+
walkSchema(document, normalizeUri(uri), document, register);
|
|
134
|
+
}
|
|
135
|
+
walkSchema(root, "", root, register);
|
|
136
|
+
return {
|
|
137
|
+
findIdentified: (uri) => byUri.get(normalizeUri(uri)),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -7,6 +7,7 @@ exports.expandSchemaRules = expandSchemaRules;
|
|
|
7
7
|
const collect_definitions_1 = require("./collect-definitions");
|
|
8
8
|
const compose_conditional_1 = require("./compose-conditional");
|
|
9
9
|
const compose_keyword_1 = require("./compose-keyword");
|
|
10
|
+
const declare_additional_properties_1 = require("./declare-additional-properties");
|
|
10
11
|
const declare_object_keywords_1 = require("./declare-object-keywords");
|
|
11
12
|
const declare_required_properties_1 = require("./declare-required-properties");
|
|
12
13
|
const declare_scalar_keywords_1 = require("./declare-scalar-keywords");
|
|
@@ -103,7 +104,7 @@ function expandSchemaRules(schema, context) {
|
|
|
103
104
|
...(0, flatten_array_schema_1.declareArrayRules)(schema, context),
|
|
104
105
|
...(0, declare_object_keywords_1.declareObjectRules)(schema, context),
|
|
105
106
|
...(0, declare_value_keywords_1.declareConstRules)(schema, context),
|
|
106
|
-
...(0,
|
|
107
|
+
...(0, declare_additional_properties_1.declareAdditionalPropertiesSchema)(schema, context),
|
|
107
108
|
...(0, declare_required_properties_1.declareRequiredProperties)(schema, context),
|
|
108
109
|
];
|
|
109
110
|
for (const keyword of Object.keys(schema)) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { isDefinitionContainer } from "./collect-definitions.mjs";
|
|
2
2
|
import { composeConditional } from "./compose-conditional.mjs";
|
|
3
3
|
import { composeAllOf, composeAnyOf, composeNot, composeOneOf, } from "./compose-keyword.mjs";
|
|
4
|
-
import { declareAdditionalPropertiesSchema
|
|
4
|
+
import { declareAdditionalPropertiesSchema } from "./declare-additional-properties.mjs";
|
|
5
|
+
import { declareDependencies, declareObjectRules, declarePatternProperties, declarePropertyNames, readPropertyChildren, } from "./declare-object-keywords.mjs";
|
|
5
6
|
import { declareRequiredProperties } from "./declare-required-properties.mjs";
|
|
6
7
|
import { declareFormatRules, declareNumberRules, declareStringRules, } from "./declare-scalar-keywords.mjs";
|
|
7
8
|
import { declareConstRules, declareEnumRules, declareIntegerRules, declareTypeRules, } from "./declare-value-keywords.mjs";
|
|
@@ -3,6 +3,7 @@ import type { CompositeBranch, Rule } from "../plugin-kit/compiled-rule";
|
|
|
3
3
|
import type { RuleBuildContext } from "../plugin-kit/rule-build-context";
|
|
4
4
|
import type { MessageContextExtra } from "../types";
|
|
5
5
|
import type { Draft07Schema, Draft07SchemaObject } from "./draft07.types";
|
|
6
|
+
import type { RefScope } from "./ref-scope";
|
|
6
7
|
import type { JsonSchemaBag } from "./json-schema-bag.types";
|
|
7
8
|
/**
|
|
8
9
|
* The nineteen keywords `draft07KeywordMap` marks `structural`. Written out by
|
|
@@ -31,7 +32,13 @@ export interface ChildSchema {
|
|
|
31
32
|
export interface StructuralContext {
|
|
32
33
|
readonly bag: JsonSchemaBag;
|
|
33
34
|
readonly build: ChainBuildContext;
|
|
34
|
-
|
|
35
|
+
/**
|
|
36
|
+
* WHERE this node is being read from. It replaces the bare `root` this
|
|
37
|
+
* interface used to carry: a `$ref` is a URI reference, so resolving one
|
|
38
|
+
* needs the base URI in force and the index of what a URI can name, and a
|
|
39
|
+
* root cannot express either. See ref-scope.ts.
|
|
40
|
+
*/
|
|
41
|
+
readonly scope: RefScope;
|
|
35
42
|
/**
|
|
36
43
|
* The `$ref` pointers already entered on this branch of the conversion. A
|
|
37
44
|
* repeat is a RECURSIVE definition, whose expansion does not terminate, so
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** A URI with its fragment split off. `resource` is what identifies a document. */
|
|
2
|
+
export interface SplitUri {
|
|
3
|
+
readonly resource: string;
|
|
4
|
+
/** Without the leading "#". Empty when the URI carries no fragment. */
|
|
5
|
+
readonly fragment: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function splitUri(uri: string): SplitUri;
|
|
8
|
+
/**
|
|
9
|
+
* `reference` resolved against `base`, both as written in the document.
|
|
10
|
+
*
|
|
11
|
+
* A base that is not itself absolute cannot resolve anything — that is the
|
|
12
|
+
* document that declared no `$id`, and it is the common case — so the
|
|
13
|
+
* reference is returned unchanged rather than guessed at. Callers then look
|
|
14
|
+
* the reference up verbatim, which is exactly what a local `#/...` pointer
|
|
15
|
+
* needs.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveUriReference(base: string, reference: string): string;
|
|
18
|
+
/** The same normalisation resolution applies, for a URI used as a KEY. */
|
|
19
|
+
export declare function normalizeUri(uri: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* The base URI in force inside a node that declares `$id`.
|
|
22
|
+
*
|
|
23
|
+
* `$id: "#name"` is an ANCHOR, not a base: §8.2.3 gives it a plain-name
|
|
24
|
+
* fragment, and the base stays whatever it was. Treating it as a base would
|
|
25
|
+
* silently move every `$ref` written next to it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function nextBaseUri(base: string, id: string | undefined): string;
|
|
28
|
+
/** The anchor `$id` declares, if it declares one: `"#name"` -> `"name"`. */
|
|
29
|
+
export declare function readAnchor(id: string | undefined): string | undefined;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ===========================================================================
|
|
3
|
+
// L8 src/json-schema/uri-reference.ts — `$id` と `$ref` の URI 演算。
|
|
4
|
+
//
|
|
5
|
+
// Draft-07 §8.2 の `$id` はベース URI を立て、`$ref` はその上で解決される。
|
|
6
|
+
// つまり `$ref` は「文書内のポインタ」ではなく **URI 参照** であり、
|
|
7
|
+
// 同じ文字列でも、どの `$id` の下に書かれたかで別の場所を指す。
|
|
8
|
+
//
|
|
9
|
+
// 解決は WHATWG URL に任せる。RFC 3986 §5.3 を手で書き直しても、
|
|
10
|
+
// パーセント符号化・ドットセグメント・スキーム相対 (`//host/x`) の三つで
|
|
11
|
+
// 必ずずれる — そして URL はブラウザにも Node にもある標準グローバルで、
|
|
12
|
+
// eval も new Function も使わない (check:no-dynamic-code が見ている)。
|
|
13
|
+
//
|
|
14
|
+
// ネットワークには**触れない**。ここにあるのは文字列演算だけで、
|
|
15
|
+
// 解決した URI をどう手に入れるかは呼び出し側の責任である
|
|
16
|
+
// (schema-registry.ts の externalDocuments)。スキーマが書いた URI で
|
|
17
|
+
// このプロセスがソケットを開くことは無い。
|
|
18
|
+
// ===========================================================================
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.splitUri = splitUri;
|
|
21
|
+
exports.resolveUriReference = resolveUriReference;
|
|
22
|
+
exports.normalizeUri = normalizeUri;
|
|
23
|
+
exports.nextBaseUri = nextBaseUri;
|
|
24
|
+
exports.readAnchor = readAnchor;
|
|
25
|
+
function splitUri(uri) {
|
|
26
|
+
const hash = uri.indexOf("#");
|
|
27
|
+
if (hash < 0)
|
|
28
|
+
return { resource: uri, fragment: "" };
|
|
29
|
+
return { resource: uri.slice(0, hash), fragment: uri.slice(hash + 1) };
|
|
30
|
+
}
|
|
31
|
+
// RFC 3986 §3.1: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ":".
|
|
32
|
+
// Scanned by code point rather than matched by a regex, because
|
|
33
|
+
// test/unit/json-schema/core/format-map.test.ts forbids a character class
|
|
34
|
+
// anywhere in this layer — 1.x grew a second and a third format table that
|
|
35
|
+
// way, and the rule that prevents it is worth more than the two lines it costs.
|
|
36
|
+
const COLON = 58;
|
|
37
|
+
const PLUS = 43;
|
|
38
|
+
const HYPHEN = 45;
|
|
39
|
+
const DOT = 46;
|
|
40
|
+
function isLetter(code) {
|
|
41
|
+
return (code > 64 && code < 91) || (code > 96 && code < 123);
|
|
42
|
+
}
|
|
43
|
+
function isDigit(code) {
|
|
44
|
+
return code > 47 && code < 58;
|
|
45
|
+
}
|
|
46
|
+
function isSchemeChar(code) {
|
|
47
|
+
return (isLetter(code) ||
|
|
48
|
+
isDigit(code) ||
|
|
49
|
+
code === PLUS ||
|
|
50
|
+
code === HYPHEN ||
|
|
51
|
+
code === DOT);
|
|
52
|
+
}
|
|
53
|
+
/** True when the reference names its own scheme, so no base is needed. */
|
|
54
|
+
function isAbsolute(reference) {
|
|
55
|
+
if (reference.length === 0 || !isLetter(reference.charCodeAt(0))) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
for (let index = 1; index < reference.length; index += 1) {
|
|
59
|
+
const code = reference.charCodeAt(index);
|
|
60
|
+
if (code === COLON)
|
|
61
|
+
return true;
|
|
62
|
+
if (!isSchemeChar(code))
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `reference` resolved against `base`, both as written in the document.
|
|
69
|
+
*
|
|
70
|
+
* A base that is not itself absolute cannot resolve anything — that is the
|
|
71
|
+
* document that declared no `$id`, and it is the common case — so the
|
|
72
|
+
* reference is returned unchanged rather than guessed at. Callers then look
|
|
73
|
+
* the reference up verbatim, which is exactly what a local `#/...` pointer
|
|
74
|
+
* needs.
|
|
75
|
+
*/
|
|
76
|
+
function resolveUriReference(base, reference) {
|
|
77
|
+
if (base === "" || !isAbsolute(base)) {
|
|
78
|
+
return isAbsolute(reference) ? normalizeUri(reference) : reference;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return new URL(reference, base).href;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return reference;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** The same normalisation resolution applies, for a URI used as a KEY. */
|
|
88
|
+
function normalizeUri(uri) {
|
|
89
|
+
if (!isAbsolute(uri))
|
|
90
|
+
return uri;
|
|
91
|
+
try {
|
|
92
|
+
return new URL(uri).href;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return uri;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The base URI in force inside a node that declares `$id`.
|
|
100
|
+
*
|
|
101
|
+
* `$id: "#name"` is an ANCHOR, not a base: §8.2.3 gives it a plain-name
|
|
102
|
+
* fragment, and the base stays whatever it was. Treating it as a base would
|
|
103
|
+
* silently move every `$ref` written next to it.
|
|
104
|
+
*/
|
|
105
|
+
function nextBaseUri(base, id) {
|
|
106
|
+
if (id === undefined || id === "" || id.startsWith("#"))
|
|
107
|
+
return base;
|
|
108
|
+
return resolveUriReference(base, id);
|
|
109
|
+
}
|
|
110
|
+
/** The anchor `$id` declares, if it declares one: `"#name"` -> `"name"`. */
|
|
111
|
+
function readAnchor(id) {
|
|
112
|
+
if (id === undefined || !id.startsWith("#") || id.length === 1) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const anchor = id.slice(1);
|
|
116
|
+
// A pointer fragment is not an anchor; `#/a/b` names a location.
|
|
117
|
+
return anchor.startsWith("/") ? undefined : anchor;
|
|
118
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// ===========================================================================
|
|
2
|
+
// L8 src/json-schema/uri-reference.ts — `$id` と `$ref` の URI 演算。
|
|
3
|
+
//
|
|
4
|
+
// Draft-07 §8.2 の `$id` はベース URI を立て、`$ref` はその上で解決される。
|
|
5
|
+
// つまり `$ref` は「文書内のポインタ」ではなく **URI 参照** であり、
|
|
6
|
+
// 同じ文字列でも、どの `$id` の下に書かれたかで別の場所を指す。
|
|
7
|
+
//
|
|
8
|
+
// 解決は WHATWG URL に任せる。RFC 3986 §5.3 を手で書き直しても、
|
|
9
|
+
// パーセント符号化・ドットセグメント・スキーム相対 (`//host/x`) の三つで
|
|
10
|
+
// 必ずずれる — そして URL はブラウザにも Node にもある標準グローバルで、
|
|
11
|
+
// eval も new Function も使わない (check:no-dynamic-code が見ている)。
|
|
12
|
+
//
|
|
13
|
+
// ネットワークには**触れない**。ここにあるのは文字列演算だけで、
|
|
14
|
+
// 解決した URI をどう手に入れるかは呼び出し側の責任である
|
|
15
|
+
// (schema-registry.ts の externalDocuments)。スキーマが書いた URI で
|
|
16
|
+
// このプロセスがソケットを開くことは無い。
|
|
17
|
+
// ===========================================================================
|
|
18
|
+
export function splitUri(uri) {
|
|
19
|
+
const hash = uri.indexOf("#");
|
|
20
|
+
if (hash < 0)
|
|
21
|
+
return { resource: uri, fragment: "" };
|
|
22
|
+
return { resource: uri.slice(0, hash), fragment: uri.slice(hash + 1) };
|
|
23
|
+
}
|
|
24
|
+
// RFC 3986 §3.1: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ":".
|
|
25
|
+
// Scanned by code point rather than matched by a regex, because
|
|
26
|
+
// test/unit/json-schema/core/format-map.test.ts forbids a character class
|
|
27
|
+
// anywhere in this layer — 1.x grew a second and a third format table that
|
|
28
|
+
// way, and the rule that prevents it is worth more than the two lines it costs.
|
|
29
|
+
const COLON = 58;
|
|
30
|
+
const PLUS = 43;
|
|
31
|
+
const HYPHEN = 45;
|
|
32
|
+
const DOT = 46;
|
|
33
|
+
function isLetter(code) {
|
|
34
|
+
return (code > 64 && code < 91) || (code > 96 && code < 123);
|
|
35
|
+
}
|
|
36
|
+
function isDigit(code) {
|
|
37
|
+
return code > 47 && code < 58;
|
|
38
|
+
}
|
|
39
|
+
function isSchemeChar(code) {
|
|
40
|
+
return (isLetter(code) ||
|
|
41
|
+
isDigit(code) ||
|
|
42
|
+
code === PLUS ||
|
|
43
|
+
code === HYPHEN ||
|
|
44
|
+
code === DOT);
|
|
45
|
+
}
|
|
46
|
+
/** True when the reference names its own scheme, so no base is needed. */
|
|
47
|
+
function isAbsolute(reference) {
|
|
48
|
+
if (reference.length === 0 || !isLetter(reference.charCodeAt(0))) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
for (let index = 1; index < reference.length; index += 1) {
|
|
52
|
+
const code = reference.charCodeAt(index);
|
|
53
|
+
if (code === COLON)
|
|
54
|
+
return true;
|
|
55
|
+
if (!isSchemeChar(code))
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* `reference` resolved against `base`, both as written in the document.
|
|
62
|
+
*
|
|
63
|
+
* A base that is not itself absolute cannot resolve anything — that is the
|
|
64
|
+
* document that declared no `$id`, and it is the common case — so the
|
|
65
|
+
* reference is returned unchanged rather than guessed at. Callers then look
|
|
66
|
+
* the reference up verbatim, which is exactly what a local `#/...` pointer
|
|
67
|
+
* needs.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveUriReference(base, reference) {
|
|
70
|
+
if (base === "" || !isAbsolute(base)) {
|
|
71
|
+
return isAbsolute(reference) ? normalizeUri(reference) : reference;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
return new URL(reference, base).href;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return reference;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** The same normalisation resolution applies, for a URI used as a KEY. */
|
|
81
|
+
export function normalizeUri(uri) {
|
|
82
|
+
if (!isAbsolute(uri))
|
|
83
|
+
return uri;
|
|
84
|
+
try {
|
|
85
|
+
return new URL(uri).href;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return uri;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The base URI in force inside a node that declares `$id`.
|
|
93
|
+
*
|
|
94
|
+
* `$id: "#name"` is an ANCHOR, not a base: §8.2.3 gives it a plain-name
|
|
95
|
+
* fragment, and the base stays whatever it was. Treating it as a base would
|
|
96
|
+
* silently move every `$ref` written next to it.
|
|
97
|
+
*/
|
|
98
|
+
export function nextBaseUri(base, id) {
|
|
99
|
+
if (id === undefined || id === "" || id.startsWith("#"))
|
|
100
|
+
return base;
|
|
101
|
+
return resolveUriReference(base, id);
|
|
102
|
+
}
|
|
103
|
+
/** The anchor `$id` declares, if it declares one: `"#name"` -> `"name"`. */
|
|
104
|
+
export function readAnchor(id) {
|
|
105
|
+
if (id === undefined || !id.startsWith("#") || id.length === 1) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const anchor = id.slice(1);
|
|
109
|
+
// A pointer fragment is not an anchor; `#/a/b` names a location.
|
|
110
|
+
return anchor.startsWith("/") ? undefined : anchor;
|
|
111
|
+
}
|
|
@@ -35,9 +35,32 @@ function writeInto(container, keys, depth, value) {
|
|
|
35
35
|
* conjure an array — the declaration grammar cannot express `items[0]`, so a
|
|
36
36
|
* numeric key here is a Record key.
|
|
37
37
|
*/
|
|
38
|
+
/**
|
|
39
|
+
* キーを own プロパティとして置く。**代入演算子を使わない。**
|
|
40
|
+
*
|
|
41
|
+
* `copy[key] = value` は key が "__proto__" のとき Object.prototype の
|
|
42
|
+
* アクセサ (setter) を呼び、own プロパティを作る代わりにプロトタイプを
|
|
43
|
+
* 差し替えてしまう。defineProperty はアクセサを見ずに own プロパティを
|
|
44
|
+
* 定義するので、"__proto__" という名前のプロパティを安全に持てる。
|
|
45
|
+
*
|
|
46
|
+
* "constructor" と "prototype" は Object.prototype 上でデータプロパティ
|
|
47
|
+
* なので代入でも own プロパティになるが、キーごとに分岐すると分岐のほうを
|
|
48
|
+
* 間違えるので一律にこちらを通す。
|
|
49
|
+
*
|
|
50
|
+
* これが「宣言パスに __proto__ を書けるようにする」の前提。書き込みが安全に
|
|
51
|
+
* なったので、パス文法の側で拒否する必要が無くなった (reserved-segment.ts)。
|
|
52
|
+
*/
|
|
53
|
+
function putOwnProperty(target, key, value) {
|
|
54
|
+
Object.defineProperty(target, key, {
|
|
55
|
+
value,
|
|
56
|
+
writable: true,
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
38
61
|
function vivify(key, written) {
|
|
39
62
|
const created = {};
|
|
40
|
-
created
|
|
63
|
+
putOwnProperty(created, key, written);
|
|
41
64
|
return created;
|
|
42
65
|
}
|
|
43
66
|
function copyWith(container, key, value) {
|
|
@@ -49,7 +72,9 @@ function copyWith(container, key, value) {
|
|
|
49
72
|
copy[index] = value;
|
|
50
73
|
return copy;
|
|
51
74
|
}
|
|
75
|
+
// スプレッドは own の列挙可能プロパティを CreateDataProperty で写すので、
|
|
76
|
+
// ここでは setter は動かない。危ないのは下の代入だけ。
|
|
52
77
|
const copy = { ...container };
|
|
53
|
-
copy
|
|
78
|
+
putOwnProperty(copy, key, value);
|
|
54
79
|
return copy;
|
|
55
80
|
}
|
|
@@ -32,9 +32,32 @@ function writeInto(container, keys, depth, value) {
|
|
|
32
32
|
* conjure an array — the declaration grammar cannot express `items[0]`, so a
|
|
33
33
|
* numeric key here is a Record key.
|
|
34
34
|
*/
|
|
35
|
+
/**
|
|
36
|
+
* キーを own プロパティとして置く。**代入演算子を使わない。**
|
|
37
|
+
*
|
|
38
|
+
* `copy[key] = value` は key が "__proto__" のとき Object.prototype の
|
|
39
|
+
* アクセサ (setter) を呼び、own プロパティを作る代わりにプロトタイプを
|
|
40
|
+
* 差し替えてしまう。defineProperty はアクセサを見ずに own プロパティを
|
|
41
|
+
* 定義するので、"__proto__" という名前のプロパティを安全に持てる。
|
|
42
|
+
*
|
|
43
|
+
* "constructor" と "prototype" は Object.prototype 上でデータプロパティ
|
|
44
|
+
* なので代入でも own プロパティになるが、キーごとに分岐すると分岐のほうを
|
|
45
|
+
* 間違えるので一律にこちらを通す。
|
|
46
|
+
*
|
|
47
|
+
* これが「宣言パスに __proto__ を書けるようにする」の前提。書き込みが安全に
|
|
48
|
+
* なったので、パス文法の側で拒否する必要が無くなった (reserved-segment.ts)。
|
|
49
|
+
*/
|
|
50
|
+
function putOwnProperty(target, key, value) {
|
|
51
|
+
Object.defineProperty(target, key, {
|
|
52
|
+
value,
|
|
53
|
+
writable: true,
|
|
54
|
+
enumerable: true,
|
|
55
|
+
configurable: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
35
58
|
function vivify(key, written) {
|
|
36
59
|
const created = {};
|
|
37
|
-
created
|
|
60
|
+
putOwnProperty(created, key, written);
|
|
38
61
|
return created;
|
|
39
62
|
}
|
|
40
63
|
function copyWith(container, key, value) {
|
|
@@ -46,7 +69,9 @@ function copyWith(container, key, value) {
|
|
|
46
69
|
copy[index] = value;
|
|
47
70
|
return copy;
|
|
48
71
|
}
|
|
72
|
+
// スプレッドは own の列挙可能プロパティを CreateDataProperty で写すので、
|
|
73
|
+
// ここでは setter は動かない。危ないのは下の代入だけ。
|
|
49
74
|
const copy = { ...container };
|
|
50
|
-
copy
|
|
75
|
+
putOwnProperty(copy, key, value);
|
|
51
76
|
return copy;
|
|
52
77
|
}
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* `Object.prototype` 上に同名のものがあるキー。
|
|
3
|
+
*
|
|
4
|
+
* **これはもう拒否リストではない。** 以前はこの3つを宣言パスに書けなくして
|
|
5
|
+
* いたが、その拒否は過剰だった。危険なのは書き込みだけで、しかも本当に危ない
|
|
6
|
+
* のは "__proto__" ひとつである:
|
|
7
|
+
*
|
|
8
|
+
* 読み取り create-value-reader.ts が hasOwnProperty.call で own プロパティ
|
|
9
|
+
* しか読まないので、プロトタイプは最初から辿らない
|
|
10
|
+
* 書き込み `target[key] = value` は "__proto__" のとき Object.prototype の
|
|
11
|
+
* **アクセサ** を呼び、own プロパティを作らずプロトタイプを差し替える。
|
|
12
|
+
* "constructor" と "prototype" はデータプロパティなので代入でも
|
|
13
|
+
* own プロパティになるだけで、汚染にはならない
|
|
14
|
+
*
|
|
15
|
+
* create-value-writer.ts が代入をやめて defineProperty に移したことで、
|
|
16
|
+
* この経路が閉じた。したがって名前で拒否する必要が無くなり、
|
|
17
|
+
* `{ "properties": { "__proto__": ... } }` のようなスキーマを検証できる
|
|
18
|
+
* ようになった (JSON-Schema-Test-Suite の properties.json が要求している)。
|
|
19
|
+
*
|
|
20
|
+
* リスト自体は残す。テストが「この3つを書いても Object.prototype が汚れない」
|
|
21
|
+
* ことを名指しで確認するのに使う (test/unit/path/create-value-writer.test.ts)。
|
|
22
|
+
*/
|
|
5
23
|
export declare const RESERVED_SEGMENTS: readonly string[];
|
|
6
24
|
/** Thrown at BUILD time for every malformed or unsafe path. A declared rule
|
|
7
25
|
* must either execute or fail loudly; the legacy silent `return () => null`
|
|
@@ -13,10 +13,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.PathSyntaxError = exports.RESERVED_SEGMENTS = void 0;
|
|
14
14
|
exports.isReservedSegment = isReservedSegment;
|
|
15
15
|
exports.assertDeclarableKey = assertDeclarableKey;
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
/**
|
|
17
|
+
* `Object.prototype` 上に同名のものがあるキー。
|
|
18
|
+
*
|
|
19
|
+
* **これはもう拒否リストではない。** 以前はこの3つを宣言パスに書けなくして
|
|
20
|
+
* いたが、その拒否は過剰だった。危険なのは書き込みだけで、しかも本当に危ない
|
|
21
|
+
* のは "__proto__" ひとつである:
|
|
22
|
+
*
|
|
23
|
+
* 読み取り create-value-reader.ts が hasOwnProperty.call で own プロパティ
|
|
24
|
+
* しか読まないので、プロトタイプは最初から辿らない
|
|
25
|
+
* 書き込み `target[key] = value` は "__proto__" のとき Object.prototype の
|
|
26
|
+
* **アクセサ** を呼び、own プロパティを作らずプロトタイプを差し替える。
|
|
27
|
+
* "constructor" と "prototype" はデータプロパティなので代入でも
|
|
28
|
+
* own プロパティになるだけで、汚染にはならない
|
|
29
|
+
*
|
|
30
|
+
* create-value-writer.ts が代入をやめて defineProperty に移したことで、
|
|
31
|
+
* この経路が閉じた。したがって名前で拒否する必要が無くなり、
|
|
32
|
+
* `{ "properties": { "__proto__": ... } }` のようなスキーマを検証できる
|
|
33
|
+
* ようになった (JSON-Schema-Test-Suite の properties.json が要求している)。
|
|
34
|
+
*
|
|
35
|
+
* リスト自体は残す。テストが「この3つを書いても Object.prototype が汚れない」
|
|
36
|
+
* ことを名指しで確認するのに使う (test/unit/path/create-value-writer.test.ts)。
|
|
37
|
+
*/
|
|
20
38
|
exports.RESERVED_SEGMENTS = Object.freeze([
|
|
21
39
|
"__proto__",
|
|
22
40
|
"constructor",
|
|
@@ -62,7 +80,9 @@ function assertDeclarableKey(key, source) {
|
|
|
62
80
|
throw new PathSyntaxError(source, `the key ${JSON.stringify(key)} contains a bracket; "[*]" is the only ` +
|
|
63
81
|
"bracket form and it must trail a key");
|
|
64
82
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
// 予約セグメントの拒否はここから外した。理由は RESERVED_SEGMENTS の
|
|
84
|
+
// コメントに書いてある。要約すると、危険なのは書き込みだけで、その書き込みは
|
|
85
|
+
// create-value-writer.ts が defineProperty に移したので安全になった。
|
|
86
|
+
// 名前で拒否する必要が無くなり、JSON Schema が "__proto__" というキーを
|
|
87
|
+
// 持つオブジェクトを検証できるようになった。
|
|
68
88
|
}
|
|
@@ -8,10 +8,28 @@
|
|
|
8
8
|
// Putting the class the other way round would make the two files mutually
|
|
9
9
|
// dependent for no gain.
|
|
10
10
|
// ===========================================================================
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
/**
|
|
12
|
+
* `Object.prototype` 上に同名のものがあるキー。
|
|
13
|
+
*
|
|
14
|
+
* **これはもう拒否リストではない。** 以前はこの3つを宣言パスに書けなくして
|
|
15
|
+
* いたが、その拒否は過剰だった。危険なのは書き込みだけで、しかも本当に危ない
|
|
16
|
+
* のは "__proto__" ひとつである:
|
|
17
|
+
*
|
|
18
|
+
* 読み取り create-value-reader.ts が hasOwnProperty.call で own プロパティ
|
|
19
|
+
* しか読まないので、プロトタイプは最初から辿らない
|
|
20
|
+
* 書き込み `target[key] = value` は "__proto__" のとき Object.prototype の
|
|
21
|
+
* **アクセサ** を呼び、own プロパティを作らずプロトタイプを差し替える。
|
|
22
|
+
* "constructor" と "prototype" はデータプロパティなので代入でも
|
|
23
|
+
* own プロパティになるだけで、汚染にはならない
|
|
24
|
+
*
|
|
25
|
+
* create-value-writer.ts が代入をやめて defineProperty に移したことで、
|
|
26
|
+
* この経路が閉じた。したがって名前で拒否する必要が無くなり、
|
|
27
|
+
* `{ "properties": { "__proto__": ... } }` のようなスキーマを検証できる
|
|
28
|
+
* ようになった (JSON-Schema-Test-Suite の properties.json が要求している)。
|
|
29
|
+
*
|
|
30
|
+
* リスト自体は残す。テストが「この3つを書いても Object.prototype が汚れない」
|
|
31
|
+
* ことを名指しで確認するのに使う (test/unit/path/create-value-writer.test.ts)。
|
|
32
|
+
*/
|
|
15
33
|
export const RESERVED_SEGMENTS = Object.freeze([
|
|
16
34
|
"__proto__",
|
|
17
35
|
"constructor",
|
|
@@ -56,7 +74,9 @@ export function assertDeclarableKey(key, source) {
|
|
|
56
74
|
throw new PathSyntaxError(source, `the key ${JSON.stringify(key)} contains a bracket; "[*]" is the only ` +
|
|
57
75
|
"bracket form and it must trail a key");
|
|
58
76
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
// 予約セグメントの拒否はここから外した。理由は RESERVED_SEGMENTS の
|
|
78
|
+
// コメントに書いてある。要約すると、危険なのは書き込みだけで、その書き込みは
|
|
79
|
+
// create-value-writer.ts が defineProperty に移したので安全になった。
|
|
80
|
+
// 名前で拒否する必要が無くなり、JSON Schema が "__proto__" というキーを
|
|
81
|
+
// 持つオブジェクトを検証できるようになった。
|
|
62
82
|
}
|