@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
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSchemaRegistry = createSchemaRegistry;
|
|
4
|
+
// ===========================================================================
|
|
5
|
+
// L8 src/json-schema/schema-registry.ts — どの URI がどのスキーマを指すか。
|
|
6
|
+
//
|
|
7
|
+
// Draft-07 §8.2 では `$id` が二つの働きをする: 場所を示す URI はベースを
|
|
8
|
+
// 立て直し、`#name` の形は**位置に依らない名前**を付ける。どちらも
|
|
9
|
+
// 「この URI はこのノード」という索引で、それがこのファイルの責務である。
|
|
10
|
+
// ポインタ (`#/definitions/x`) を辿るのは場所の話なので resolve-ref.ts が持つ。
|
|
11
|
+
//
|
|
12
|
+
// 外部文書は**呼び出し側が渡したものだけ**である。Luq は取りに行かない:
|
|
13
|
+
// 関数ではなく地図 (`externalDocuments`) を受けるのは、そうすれば
|
|
14
|
+
// * スキーマに書かれた URI でプロセスがソケットを開くことがない (SSRF)、
|
|
15
|
+
// * 変換が同期のままで、CSP でも動く、
|
|
16
|
+
// * 何が読まれうるかが呼び出し側のコードに全部書いてある、
|
|
17
|
+
// の三つが同時に成り立つからで、非同期ローダーではどれも失われる。
|
|
18
|
+
// ===========================================================================
|
|
19
|
+
const draft07_types_1 = require("./draft07.types");
|
|
20
|
+
const types_1 = require("../types");
|
|
21
|
+
const uri_reference_1 = require("./uri-reference");
|
|
22
|
+
/** Keywords whose value is ONE schema. */
|
|
23
|
+
const SCHEMA_VALUED = Object.freeze([
|
|
24
|
+
"additionalProperties",
|
|
25
|
+
"additionalItems",
|
|
26
|
+
"contains",
|
|
27
|
+
"propertyNames",
|
|
28
|
+
"not",
|
|
29
|
+
"if",
|
|
30
|
+
"then",
|
|
31
|
+
"else",
|
|
32
|
+
]);
|
|
33
|
+
/** Keywords whose value is an ARRAY of schemas. `items` is in both lists. */
|
|
34
|
+
const SCHEMA_LIST_VALUED = Object.freeze([
|
|
35
|
+
"allOf",
|
|
36
|
+
"anyOf",
|
|
37
|
+
"oneOf",
|
|
38
|
+
]);
|
|
39
|
+
/** Keywords whose value is a MAP of name to schema. */
|
|
40
|
+
const SCHEMA_MAP_VALUED = Object.freeze([
|
|
41
|
+
"properties",
|
|
42
|
+
"patternProperties",
|
|
43
|
+
"definitions",
|
|
44
|
+
"$defs",
|
|
45
|
+
"dependencies",
|
|
46
|
+
]);
|
|
47
|
+
function walkSchema(schema, base, document, register) {
|
|
48
|
+
if (!(0, draft07_types_1.isSchemaObject)(schema))
|
|
49
|
+
return;
|
|
50
|
+
const id = typeof schema.$id === "string" ? schema.$id : undefined;
|
|
51
|
+
const here = (0, uri_reference_1.nextBaseUri)(base, id);
|
|
52
|
+
// A base-setting `$id` starts a new resource, so the node becomes the
|
|
53
|
+
// document every relative pointer under it resolves against.
|
|
54
|
+
const startsResource = id !== undefined && here !== base;
|
|
55
|
+
const inner = startsResource ? schema : document;
|
|
56
|
+
if (startsResource) {
|
|
57
|
+
register((0, uri_reference_1.normalizeUri)(here), { schema, baseUri: here, document: schema });
|
|
58
|
+
}
|
|
59
|
+
const anchor = (0, uri_reference_1.readAnchor)(id);
|
|
60
|
+
if (anchor !== undefined) {
|
|
61
|
+
register((0, uri_reference_1.normalizeUri)(`${here}#${anchor}`), {
|
|
62
|
+
schema,
|
|
63
|
+
baseUri: here,
|
|
64
|
+
document: inner,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
walkMembers(schema, here, inner, register);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read by ENTRY rather than by indexing the typed node: Draft07SchemaObject
|
|
71
|
+
* declares its keywords and carries no index signature, and reaching a keyword
|
|
72
|
+
* by name would need an assertion — src/core/type-erasure.ts is the only place
|
|
73
|
+
* permitted to write one, and this is not it.
|
|
74
|
+
*/
|
|
75
|
+
function walkMembers(schema, base, document, register) {
|
|
76
|
+
for (const [keyword, value] of Object.entries(schema)) {
|
|
77
|
+
if (keyword === "items") {
|
|
78
|
+
// The one keyword with both shapes, so it is walked as both.
|
|
79
|
+
walkIfSchema(value, base, document, register);
|
|
80
|
+
walkList(value, base, document, register);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (SCHEMA_VALUED.includes(keyword)) {
|
|
84
|
+
walkIfSchema(value, base, document, register);
|
|
85
|
+
}
|
|
86
|
+
else if (SCHEMA_LIST_VALUED.includes(keyword)) {
|
|
87
|
+
walkList(value, base, document, register);
|
|
88
|
+
}
|
|
89
|
+
else if (SCHEMA_MAP_VALUED.includes(keyword)) {
|
|
90
|
+
walkMap(value, base, document, register);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function walkIfSchema(value, base, document, register) {
|
|
95
|
+
if (!(0, draft07_types_1.isDraft07Schema)(value))
|
|
96
|
+
return;
|
|
97
|
+
walkSchema(value, base, document, register);
|
|
98
|
+
}
|
|
99
|
+
function walkList(value, base, document, register) {
|
|
100
|
+
if (!(0, types_1.isArray)(value))
|
|
101
|
+
return;
|
|
102
|
+
for (const member of value)
|
|
103
|
+
walkIfSchema(member, base, document, register);
|
|
104
|
+
}
|
|
105
|
+
function walkMap(value, base, document, register) {
|
|
106
|
+
if (!(0, types_1.isPlainObject)(value))
|
|
107
|
+
return;
|
|
108
|
+
for (const member of Object.values(value)) {
|
|
109
|
+
walkIfSchema(member, base, document, register);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Every `$id` in `root` and in each supplied external document.
|
|
114
|
+
*
|
|
115
|
+
* The external map's KEY is a URI in its own right: the suite (and real
|
|
116
|
+
* callers) fetch `http://host/a.json` into a document that does not repeat its
|
|
117
|
+
* own `$id`, so the key is what a `$ref` will name. A document that DOES carry
|
|
118
|
+
* `$id` registers that too, and the `$id` wins where they disagree — it is the
|
|
119
|
+
* document's own statement about its identity.
|
|
120
|
+
*/
|
|
121
|
+
function createSchemaRegistry(root, externalDocuments = {}) {
|
|
122
|
+
const byUri = new Map();
|
|
123
|
+
const register = (uri, target) => {
|
|
124
|
+
if (!byUri.has(uri))
|
|
125
|
+
byUri.set(uri, target);
|
|
126
|
+
};
|
|
127
|
+
for (const [uri, document] of Object.entries(externalDocuments)) {
|
|
128
|
+
if (!(0, draft07_types_1.isDraft07Schema)(document))
|
|
129
|
+
continue;
|
|
130
|
+
const key = (0, uri_reference_1.normalizeUri)(uri);
|
|
131
|
+
register(key, { schema: document, baseUri: key, document });
|
|
132
|
+
}
|
|
133
|
+
for (const [uri, document] of Object.entries(externalDocuments)) {
|
|
134
|
+
if (!(0, draft07_types_1.isDraft07Schema)(document))
|
|
135
|
+
continue;
|
|
136
|
+
walkSchema(document, (0, uri_reference_1.normalizeUri)(uri), document, register);
|
|
137
|
+
}
|
|
138
|
+
walkSchema(root, "", root, register);
|
|
139
|
+
return {
|
|
140
|
+
findIdentified: (uri) => byUri.get((0, uri_reference_1.normalizeUri)(uri)),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -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
|
}
|