toolcraft-openapi 0.0.122 → 0.0.124
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/dist/composition.json +6 -1
- package/node_modules/toolcraft-schema/LICENSE +21 -0
- package/node_modules/toolcraft-schema/README.md +89 -0
- package/node_modules/toolcraft-schema/dist/index.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/index.compile-check.js +17 -0
- package/node_modules/toolcraft-schema/dist/index.d.ts +184 -0
- package/node_modules/toolcraft-schema/dist/index.js +295 -0
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.d.ts +11 -0
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.js +390 -0
- package/node_modules/toolcraft-schema/dist/json-schema/evaluate.d.ts +3 -0
- package/node_modules/toolcraft-schema/dist/json-schema/evaluate.js +442 -0
- package/node_modules/toolcraft-schema/dist/json-schema/index.d.ts +5 -0
- package/node_modules/toolcraft-schema/dist/json-schema/index.js +19 -0
- package/node_modules/toolcraft-schema/dist/json-schema/types.d.ts +33 -0
- package/node_modules/toolcraft-schema/dist/json-schema/types.js +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/utils.d.ts +19 -0
- package/node_modules/toolcraft-schema/dist/json-schema/utils.js +171 -0
- package/node_modules/toolcraft-schema/dist/json-schema-document.d.ts +14 -0
- package/node_modules/toolcraft-schema/dist/json-schema-document.js +17 -0
- package/node_modules/toolcraft-schema/dist/json.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json.compile-check.js +2 -0
- package/node_modules/toolcraft-schema/dist/json.d.ts +10 -0
- package/node_modules/toolcraft-schema/dist/json.js +5 -0
- package/node_modules/toolcraft-schema/dist/oneof.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/oneof.compile-check.js +12 -0
- package/node_modules/toolcraft-schema/dist/oneof.d.ts +15 -0
- package/node_modules/toolcraft-schema/dist/oneof.js +18 -0
- package/node_modules/toolcraft-schema/dist/record.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/record.compile-check.js +2 -0
- package/node_modules/toolcraft-schema/dist/record.d.ts +5 -0
- package/node_modules/toolcraft-schema/dist/record.js +6 -0
- package/node_modules/toolcraft-schema/dist/union.compile-check.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/union.compile-check.js +9 -0
- package/node_modules/toolcraft-schema/dist/union.d.ts +8 -0
- package/node_modules/toolcraft-schema/dist/union.js +45 -0
- package/node_modules/toolcraft-schema/dist/validate.d.ts +17 -0
- package/node_modules/toolcraft-schema/dist/validate.js +379 -0
- package/node_modules/toolcraft-schema/package.json +33 -0
- package/package.json +6 -4
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { dialectFor, escapePointer, fragmentOf, isObject, isSchema, resolveUri, withoutFragment } from "./utils.js";
|
|
2
|
+
const schemaMapKeywords = new Set([
|
|
3
|
+
"$defs",
|
|
4
|
+
"definitions",
|
|
5
|
+
"properties",
|
|
6
|
+
"patternProperties",
|
|
7
|
+
"dependentSchemas",
|
|
8
|
+
"dependencies"
|
|
9
|
+
]);
|
|
10
|
+
const schemaArrayKeywords = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
|
|
11
|
+
const schemaKeywords = new Set([
|
|
12
|
+
"additionalItems",
|
|
13
|
+
"additionalProperties",
|
|
14
|
+
"contains",
|
|
15
|
+
"else",
|
|
16
|
+
"if",
|
|
17
|
+
"items",
|
|
18
|
+
"not",
|
|
19
|
+
"propertyNames",
|
|
20
|
+
"then",
|
|
21
|
+
"unevaluatedItems",
|
|
22
|
+
"unevaluatedProperties"
|
|
23
|
+
]);
|
|
24
|
+
const draftTypes = ["null", "boolean", "object", "array", "number", "integer", "string"];
|
|
25
|
+
const draftTypeSet = new Set(draftTypes);
|
|
26
|
+
const metaSchemaKeywordProperties = {
|
|
27
|
+
type: {
|
|
28
|
+
anyOf: [
|
|
29
|
+
{ enum: draftTypes },
|
|
30
|
+
{ type: "array", items: { enum: draftTypes }, minItems: 1, uniqueItems: true }
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
minLength: { type: "integer", minimum: 0 },
|
|
34
|
+
maxLength: { type: "integer", minimum: 0 },
|
|
35
|
+
minItems: { type: "integer", minimum: 0 },
|
|
36
|
+
maxItems: { type: "integer", minimum: 0 },
|
|
37
|
+
minProperties: { type: "integer", minimum: 0 },
|
|
38
|
+
maxProperties: { type: "integer", minimum: 0 }
|
|
39
|
+
};
|
|
40
|
+
const builtInRegistry = {
|
|
41
|
+
"https://json-schema.org/draft/2020-12/schema": {
|
|
42
|
+
$id: "https://json-schema.org/draft/2020-12/schema",
|
|
43
|
+
type: ["object", "boolean"],
|
|
44
|
+
properties: {
|
|
45
|
+
...metaSchemaKeywordProperties,
|
|
46
|
+
$defs: { type: "object", additionalProperties: { $ref: "#" } }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"http://json-schema.org/draft-07/schema": {
|
|
50
|
+
$id: "http://json-schema.org/draft-07/schema#",
|
|
51
|
+
type: ["object", "boolean"],
|
|
52
|
+
properties: {
|
|
53
|
+
...metaSchemaKeywordProperties,
|
|
54
|
+
definitions: { type: "object", additionalProperties: { $ref: "#" } }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function mapKey(dialect, uri) {
|
|
59
|
+
return `${dialect}\0${uri}`;
|
|
60
|
+
}
|
|
61
|
+
function assertSchemaArray(value, keyword) {
|
|
62
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every(isSchema)) {
|
|
63
|
+
throw new Error(`${keyword} must be a non-empty array of schemas.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function assertStringArray(value, keyword) {
|
|
67
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
|
|
68
|
+
throw new Error(`${keyword} must be an array of strings.`);
|
|
69
|
+
}
|
|
70
|
+
if (new Set(value).size !== value.length) {
|
|
71
|
+
throw new Error(`${keyword} must contain unique strings.`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function assertSchemaMap(value, keyword) {
|
|
75
|
+
if (!isObject(value) || !Object.values(value).every(isSchema)) {
|
|
76
|
+
throw new Error(`${keyword} must be an object containing schemas.`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function assertNonNegativeInteger(value, keyword) {
|
|
80
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
81
|
+
throw new Error(`${keyword} must be a non-negative integer.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function validateSchemaObject(schema, dialect) {
|
|
85
|
+
if (schema.type !== undefined) {
|
|
86
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
87
|
+
if (types.length === 0 ||
|
|
88
|
+
!types.every((type) => typeof type === "string" && draftTypeSet.has(type)) ||
|
|
89
|
+
new Set(types).size !== types.length) {
|
|
90
|
+
throw new Error("type must be a JSON Schema type or a unique array of JSON Schema types.");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const keyword of ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"]) {
|
|
94
|
+
if (schema[keyword] !== undefined && typeof schema[keyword] !== "number") {
|
|
95
|
+
throw new Error(`${keyword} must be a number.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (schema.multipleOf !== undefined &&
|
|
99
|
+
(typeof schema.multipleOf !== "number" || schema.multipleOf <= 0)) {
|
|
100
|
+
throw new Error("multipleOf must be a number greater than zero.");
|
|
101
|
+
}
|
|
102
|
+
for (const keyword of [
|
|
103
|
+
"minLength",
|
|
104
|
+
"maxLength",
|
|
105
|
+
"minItems",
|
|
106
|
+
"maxItems",
|
|
107
|
+
"minContains",
|
|
108
|
+
"maxContains",
|
|
109
|
+
"minProperties",
|
|
110
|
+
"maxProperties"
|
|
111
|
+
]) {
|
|
112
|
+
if (schema[keyword] !== undefined) {
|
|
113
|
+
assertNonNegativeInteger(schema[keyword], keyword);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (schema.pattern !== undefined && typeof schema.pattern !== "string") {
|
|
117
|
+
throw new Error("pattern must be a string.");
|
|
118
|
+
}
|
|
119
|
+
for (const keyword of ["uniqueItems", "$recursiveAnchor"]) {
|
|
120
|
+
if (schema[keyword] !== undefined && typeof schema[keyword] !== "boolean") {
|
|
121
|
+
throw new Error(`${keyword} must be a boolean.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (schema.required !== undefined) {
|
|
125
|
+
assertStringArray(schema.required, "required");
|
|
126
|
+
}
|
|
127
|
+
if (schema.dependentRequired !== undefined) {
|
|
128
|
+
if (!isObject(schema.dependentRequired)) {
|
|
129
|
+
throw new Error("dependentRequired must be an object containing string arrays.");
|
|
130
|
+
}
|
|
131
|
+
for (const dependencies of Object.values(schema.dependentRequired)) {
|
|
132
|
+
assertStringArray(dependencies, "dependentRequired");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (const keyword of schemaMapKeywords) {
|
|
136
|
+
const value = schema[keyword];
|
|
137
|
+
if (value === undefined || keyword === "dependencies")
|
|
138
|
+
continue;
|
|
139
|
+
assertSchemaMap(value, keyword);
|
|
140
|
+
}
|
|
141
|
+
if (schema.dependencies !== undefined) {
|
|
142
|
+
if (!isObject(schema.dependencies)) {
|
|
143
|
+
throw new Error("dependencies must be an object containing schemas or string arrays.");
|
|
144
|
+
}
|
|
145
|
+
for (const dependency of Object.values(schema.dependencies)) {
|
|
146
|
+
if (!isSchema(dependency))
|
|
147
|
+
assertStringArray(dependency, "dependencies");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
151
|
+
if (schema[keyword] !== undefined)
|
|
152
|
+
assertSchemaArray(schema[keyword], keyword);
|
|
153
|
+
}
|
|
154
|
+
if (schema.prefixItems !== undefined) {
|
|
155
|
+
if (!Array.isArray(schema.prefixItems) || !schema.prefixItems.every(isSchema)) {
|
|
156
|
+
throw new Error("prefixItems must be an array of schemas.");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const keyword of schemaKeywords) {
|
|
160
|
+
const value = schema[keyword];
|
|
161
|
+
if (value === undefined)
|
|
162
|
+
continue;
|
|
163
|
+
if (keyword === "items" && dialect === "draft7" && Array.isArray(value)) {
|
|
164
|
+
if (!value.every(isSchema))
|
|
165
|
+
throw new Error("items must contain only schemas.");
|
|
166
|
+
}
|
|
167
|
+
else if (!isSchema(value)) {
|
|
168
|
+
throw new Error(`${keyword} must be a schema.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (schema.enum !== undefined && !Array.isArray(schema.enum)) {
|
|
172
|
+
throw new Error("enum must be an array.");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export function compileGraph(schema, options = {}) {
|
|
176
|
+
if (!isSchema(schema)) {
|
|
177
|
+
throw new Error("JSON Schema must be a boolean or object.");
|
|
178
|
+
}
|
|
179
|
+
const locations = new Map();
|
|
180
|
+
const resources = new Map();
|
|
181
|
+
const anchors = new Map();
|
|
182
|
+
const dynamicAnchors = new Map();
|
|
183
|
+
const nodesBySchema = new WeakMap();
|
|
184
|
+
const pendingReferences = [];
|
|
185
|
+
const registry = { ...builtInRegistry, ...options.registry };
|
|
186
|
+
const rootBase = "https://toolcraft.invalid/root";
|
|
187
|
+
function validationVocabularyFor(schemaObject) {
|
|
188
|
+
const metaSchema = schemaObject.$schema;
|
|
189
|
+
if (typeof metaSchema !== "string") {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
const registered = registry[metaSchema];
|
|
193
|
+
if (!isObject(registered) || !isObject(registered.$vocabulary)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return Object.prototype.hasOwnProperty.call(registered.$vocabulary, "https://json-schema.org/draft/2020-12/vocab/validation");
|
|
197
|
+
}
|
|
198
|
+
function recordNode(schemaObject, node) {
|
|
199
|
+
if (schemaObject === undefined) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const nodes = nodesBySchema.get(schemaObject) ?? [];
|
|
203
|
+
nodes.push(node);
|
|
204
|
+
nodesBySchema.set(schemaObject, nodes);
|
|
205
|
+
}
|
|
206
|
+
function setLocation(dialect, uri, node) {
|
|
207
|
+
locations.set(mapKey(dialect, uri), node);
|
|
208
|
+
}
|
|
209
|
+
function scan(currentSchema, inheritedDialect, inheritedBase, resourceRoot, pointer, validationVocabulary, documentId) {
|
|
210
|
+
const schemaObject = isObject(currentSchema) ? currentSchema : undefined;
|
|
211
|
+
const dialect = schemaObject === undefined ? inheritedDialect : dialectFor(schemaObject, inheritedDialect);
|
|
212
|
+
if (schemaObject !== undefined) {
|
|
213
|
+
validateSchemaObject(schemaObject, dialect);
|
|
214
|
+
}
|
|
215
|
+
const declaredId = dialect === "draft7" ? (schemaObject?.$id ?? schemaObject?.id) : schemaObject?.$id;
|
|
216
|
+
const effectiveId = dialect === "draft7" && typeof schemaObject?.$ref === "string" ? undefined : declaredId;
|
|
217
|
+
const baseUri = typeof effectiveId === "string" ? resolveUri(effectiveId, inheritedBase) : inheritedBase;
|
|
218
|
+
const node = {
|
|
219
|
+
schema: currentSchema,
|
|
220
|
+
documentId,
|
|
221
|
+
dialect,
|
|
222
|
+
baseUri,
|
|
223
|
+
resourceUri: resourceRoot?.resourceUri ?? withoutFragment(baseUri),
|
|
224
|
+
resourceRoot: undefined,
|
|
225
|
+
pointer,
|
|
226
|
+
validationVocabulary,
|
|
227
|
+
children: new Map()
|
|
228
|
+
};
|
|
229
|
+
recordNode(schemaObject, node);
|
|
230
|
+
if (resourceRoot !== undefined) {
|
|
231
|
+
setLocation(resourceRoot.dialect, resolveUri(`#${encodeURI(pointer)}`, resourceRoot.resourceUri), node);
|
|
232
|
+
}
|
|
233
|
+
const startsResource = resourceRoot === undefined || withoutFragment(baseUri) !== resourceRoot.resourceUri;
|
|
234
|
+
node.resourceRoot = startsResource ? node : resourceRoot;
|
|
235
|
+
node.resourceUri = startsResource ? withoutFragment(baseUri) : resourceRoot.resourceUri;
|
|
236
|
+
const resourcePointer = startsResource ? "" : pointer;
|
|
237
|
+
node.pointer = resourcePointer;
|
|
238
|
+
if (startsResource) {
|
|
239
|
+
resources.set(mapKey(node.dialect, node.resourceUri), node);
|
|
240
|
+
setLocation(node.dialect, node.resourceUri, node);
|
|
241
|
+
}
|
|
242
|
+
setLocation(node.dialect, resolveUri(`#${encodeURI(resourcePointer)}`, node.resourceUri), node);
|
|
243
|
+
if (schemaObject === undefined) {
|
|
244
|
+
return node;
|
|
245
|
+
}
|
|
246
|
+
if (typeof schemaObject.$anchor === "string") {
|
|
247
|
+
anchors.set(mapKey(node.dialect, `${node.resourceUri}#${schemaObject.$anchor}`), node);
|
|
248
|
+
}
|
|
249
|
+
if (typeof schemaObject.$dynamicAnchor === "string") {
|
|
250
|
+
const anchorUri = `${node.resourceUri}#${schemaObject.$dynamicAnchor}`;
|
|
251
|
+
anchors.set(mapKey(node.dialect, anchorUri), node);
|
|
252
|
+
dynamicAnchors.set(mapKey(node.dialect, anchorUri), node);
|
|
253
|
+
}
|
|
254
|
+
if (dialect === "draft7" && typeof effectiveId === "string" && fragmentOf(baseUri) !== "") {
|
|
255
|
+
anchors.set(mapKey(dialect, baseUri), node);
|
|
256
|
+
}
|
|
257
|
+
for (const keyword of ["$ref", "$dynamicRef", "$recursiveRef"]) {
|
|
258
|
+
const reference = schemaObject[keyword];
|
|
259
|
+
if (reference !== undefined) {
|
|
260
|
+
if (typeof reference !== "string") {
|
|
261
|
+
throw new Error(`${keyword} must be a string.`);
|
|
262
|
+
}
|
|
263
|
+
pendingReferences.push({ node, reference });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (typeof schemaObject.pattern === "string") {
|
|
267
|
+
new RegExp(schemaObject.pattern, "u");
|
|
268
|
+
}
|
|
269
|
+
if (isObject(schemaObject.patternProperties)) {
|
|
270
|
+
for (const pattern of Object.keys(schemaObject.patternProperties)) {
|
|
271
|
+
new RegExp(pattern, "u");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const nextValidationVocabulary = resourceRoot === undefined ? validationVocabularyFor(schemaObject) : validationVocabulary;
|
|
275
|
+
for (const [keyword, value] of Object.entries(schemaObject)) {
|
|
276
|
+
if (schemaMapKeywords.has(keyword) && isObject(value)) {
|
|
277
|
+
for (const [key, childSchema] of Object.entries(value)) {
|
|
278
|
+
if (isSchema(childSchema)) {
|
|
279
|
+
node.children.set(`${keyword}/${key}`, scan(childSchema, dialect, baseUri, node.resourceRoot, `${resourcePointer}/${escapePointer(keyword)}/${escapePointer(key)}`, nextValidationVocabulary, documentId));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else if (schemaArrayKeywords.has(keyword) && Array.isArray(value)) {
|
|
284
|
+
value.forEach((childSchema, index) => {
|
|
285
|
+
if (isSchema(childSchema)) {
|
|
286
|
+
node.children.set(`${keyword}/${index}`, scan(childSchema, dialect, baseUri, node.resourceRoot, `${resourcePointer}/${escapePointer(keyword)}/${index}`, nextValidationVocabulary, documentId));
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
else if (schemaKeywords.has(keyword)) {
|
|
291
|
+
if (keyword === "items" && Array.isArray(value)) {
|
|
292
|
+
value.forEach((childSchema, index) => {
|
|
293
|
+
if (isSchema(childSchema)) {
|
|
294
|
+
node.children.set(`items/${index}`, scan(childSchema, dialect, baseUri, node.resourceRoot, `${resourcePointer}/items/${index}`, nextValidationVocabulary, documentId));
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
else if (isSchema(value)) {
|
|
299
|
+
node.children.set(keyword, scan(value, dialect, baseUri, node.resourceRoot, `${resourcePointer}/${escapePointer(keyword)}`, nextValidationVocabulary, documentId));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return node;
|
|
304
|
+
}
|
|
305
|
+
const root = scan(schema, "draft2020-12", rootBase, undefined, "", true, "root");
|
|
306
|
+
resources.set(mapKey(root.dialect, rootBase), root);
|
|
307
|
+
setLocation(root.dialect, rootBase, root);
|
|
308
|
+
setLocation(root.dialect, `${rootBase}#`, root);
|
|
309
|
+
for (const [uri, registeredSchema] of Object.entries(registry)) {
|
|
310
|
+
if (!isSchema(registeredSchema)) {
|
|
311
|
+
throw new Error(`Registered JSON Schema ${uri} must be a boolean or object.`);
|
|
312
|
+
}
|
|
313
|
+
for (const inheritedDialect of ["draft2020-12", "draft7"]) {
|
|
314
|
+
const remote = scan(registeredSchema, inheritedDialect, uri, undefined, "", true, `${inheritedDialect}:${uri}`);
|
|
315
|
+
resources.set(mapKey(remote.dialect, withoutFragment(uri)), remote);
|
|
316
|
+
setLocation(remote.dialect, withoutFragment(uri), remote);
|
|
317
|
+
setLocation(remote.dialect, `${withoutFragment(uri)}#`, remote);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function findByDialect(map, dialect, uri) {
|
|
321
|
+
const alternate = dialect === "draft7" ? "draft2020-12" : "draft7";
|
|
322
|
+
return map.get(mapKey(dialect, uri)) ?? map.get(mapKey(alternate, uri));
|
|
323
|
+
}
|
|
324
|
+
function resolve(node, reference) {
|
|
325
|
+
const absolute = resolveUri(reference, node.baseUri);
|
|
326
|
+
const direct = findByDialect(locations, node.dialect, absolute) ??
|
|
327
|
+
findByDialect(resources, node.dialect, absolute) ??
|
|
328
|
+
findByDialect(anchors, node.dialect, absolute);
|
|
329
|
+
if (direct !== undefined) {
|
|
330
|
+
return direct;
|
|
331
|
+
}
|
|
332
|
+
const resource = findByDialect(resources, node.dialect, withoutFragment(absolute));
|
|
333
|
+
const fragment = fragmentOf(absolute);
|
|
334
|
+
if (resource !== undefined && fragment === "") {
|
|
335
|
+
return resource;
|
|
336
|
+
}
|
|
337
|
+
if (fragment.startsWith("/")) {
|
|
338
|
+
const pointerTarget = findByDialect(locations, node.dialect, `${withoutFragment(absolute)}#${fragment}`);
|
|
339
|
+
if (pointerTarget !== undefined) {
|
|
340
|
+
return pointerTarget;
|
|
341
|
+
}
|
|
342
|
+
if (resource !== undefined && isObject(resource.schema)) {
|
|
343
|
+
let target = resource.schema;
|
|
344
|
+
for (const segment of fragment.slice(1).split("/")) {
|
|
345
|
+
if (!isObject(target) && !Array.isArray(target)) {
|
|
346
|
+
target = undefined;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
const key = decodeURIComponent(segment).replaceAll("~1", "/").replaceAll("~0", "~");
|
|
350
|
+
target = Array.isArray(target) ? target[Number(key)] : target[key];
|
|
351
|
+
}
|
|
352
|
+
if (isObject(target)) {
|
|
353
|
+
const targetNode = nodesBySchema
|
|
354
|
+
.get(target)
|
|
355
|
+
?.find((candidate) => candidate.dialect === node.dialect);
|
|
356
|
+
if (targetNode !== undefined) {
|
|
357
|
+
return targetNode;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
throw new Error(`Unresolvable $ref: ${reference}`);
|
|
363
|
+
}
|
|
364
|
+
const reachableDocuments = new Set([root.documentId]);
|
|
365
|
+
let discoveredDocument = true;
|
|
366
|
+
while (discoveredDocument) {
|
|
367
|
+
discoveredDocument = false;
|
|
368
|
+
for (const pending of pendingReferences) {
|
|
369
|
+
if (!reachableDocuments.has(pending.node.documentId)) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const target = resolve(pending.node, pending.reference);
|
|
373
|
+
if (!reachableDocuments.has(target.documentId)) {
|
|
374
|
+
reachableDocuments.add(target.documentId);
|
|
375
|
+
discoveredDocument = true;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
root,
|
|
381
|
+
locations,
|
|
382
|
+
resources,
|
|
383
|
+
anchors,
|
|
384
|
+
dynamicAnchors,
|
|
385
|
+
resolve,
|
|
386
|
+
dynamicAnchor(scope, name) {
|
|
387
|
+
return dynamicAnchors.get(mapKey(scope.dialect, `${scope.resourceUri}#${name}`));
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|