codex-configurator 0.2.4 → 0.2.6
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 +45 -17
- package/index.js +1467 -839
- package/package.json +4 -2
- package/src/appState.js +80 -0
- package/src/components/ConfigNavigator.js +633 -430
- package/src/components/Header.js +26 -50
- package/src/configFeatures.js +39 -175
- package/src/configHelp.js +20 -236
- package/src/configParser.js +117 -32
- package/src/configReference.js +1002 -35
- package/src/constants.js +10 -3
- package/src/fileContext.js +118 -0
- package/src/interaction.js +69 -25
- package/src/layout.js +80 -15
- package/src/reference/config-schema.json +2120 -0
- package/src/schemaRuntimeSync.js +282 -0
- package/src/ui/commands.js +699 -0
- package/src/ui/panes/CommandBar.js +90 -0
- package/src/ui/panes/HelpBubble.js +26 -0
- package/src/ui/panes/LayoutShell.js +17 -0
- package/src/ui/panes/StatusLine.js +123 -0
- package/src/variantPresets.js +268 -0
- package/src/reference/config-reference.json +0 -3494
package/src/configReference.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
|
-
const
|
|
4
|
+
const BUNDLED_CONFIG_SCHEMA_DATA = require('./reference/config-schema.json');
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const ROOT_PLACEHOLDER = '<name>';
|
|
7
7
|
const PLACEHOLDER_SEGMENT = /^<[^>]+>$/;
|
|
8
|
+
const KEY_SEGMENT_PLACEHOLDERS = {
|
|
9
|
+
agents: '<name>',
|
|
10
|
+
apps: '<id>',
|
|
11
|
+
js_repl_node_module_dirs: '<path>',
|
|
12
|
+
mcp_servers: '<id>',
|
|
13
|
+
model_providers: '<name>',
|
|
14
|
+
notice: '<name>',
|
|
15
|
+
profiles: '<name>',
|
|
16
|
+
projects: '<path>',
|
|
17
|
+
tools: '<tool>',
|
|
18
|
+
};
|
|
8
19
|
const KIND_PRIORITY = {
|
|
9
20
|
value: 1,
|
|
10
21
|
array: 2,
|
|
@@ -24,6 +35,887 @@ const isCustomIdPlaceholder = (segment) =>
|
|
|
24
35
|
|
|
25
36
|
const normalizeType = (type) => String(type || '').replace(/\s+/g, ' ').trim();
|
|
26
37
|
|
|
38
|
+
const isObject = (value) =>
|
|
39
|
+
value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
40
|
+
|
|
41
|
+
const buildSchemaRevision = (schema) => JSON.stringify(schema);
|
|
42
|
+
|
|
43
|
+
const isPrimitiveValue = (type) =>
|
|
44
|
+
type === 'string' || type === 'number' || type === 'integer' || type === 'boolean';
|
|
45
|
+
|
|
46
|
+
const isObjectLikeReference = (schema) => {
|
|
47
|
+
if (!schema || typeof schema !== 'object') {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(schema.type) && schema.type.includes('object')) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (schema.type === 'object') {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (schema.properties || schema.additionalProperties || schema.patternProperties) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(schema.oneOf) || Array.isArray(schema.allOf) || Array.isArray(schema.anyOf)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const isScalarLikeReference = (schema) => {
|
|
71
|
+
if (!schema || typeof schema !== 'object') {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (isObjectLikeReference(schema)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (Array.isArray(schema.type) && schema.type.length > 0) {
|
|
80
|
+
return schema.type.some(isPrimitiveValue);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isPrimitiveValue(normalizeType(schema.type))) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return Array.isArray(schema.enum);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const getOneOfTypeLabel = (branches, context) => {
|
|
91
|
+
const unique = [...new Set(branches.map((branch) => getTypeLabel(branch, context)).filter(Boolean))];
|
|
92
|
+
if (unique.length === 0) {
|
|
93
|
+
return 'value';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return unique.length === 1 ? unique[0] : unique.join(' | ');
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const getPlaceholderForSegment = (segment, fallback = ROOT_PLACEHOLDER) =>
|
|
100
|
+
segment && KEY_SEGMENT_PLACEHOLDERS[segment] ? KEY_SEGMENT_PLACEHOLDERS[segment] : fallback;
|
|
101
|
+
|
|
102
|
+
const getResolvedDefinition = (schema, definitions, visitedRefs = new Set()) => {
|
|
103
|
+
if (!schema || typeof schema !== 'object' || typeof schema.$ref !== 'string') {
|
|
104
|
+
return { schema, refName: null };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const match = schema.$ref.match(/^#\/definitions\/(.+)$/);
|
|
108
|
+
if (!match) {
|
|
109
|
+
return { schema, refName: null };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const [, refName] = match;
|
|
113
|
+
const definition = definitions.get(refName);
|
|
114
|
+
if (!definition || visitedRefs.has(definition)) {
|
|
115
|
+
return { schema: definition || schema, refName: null };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const nextVisitedRefs = new Set(visitedRefs);
|
|
119
|
+
nextVisitedRefs.add(definition);
|
|
120
|
+
|
|
121
|
+
const [merged] = walkDefinitions(definition, definitions, nextVisitedRefs);
|
|
122
|
+
return {
|
|
123
|
+
schema: merged,
|
|
124
|
+
refName,
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const normalizeDefinition = (schema, definitions, visitedRefs = new Set()) => {
|
|
129
|
+
if (!schema || typeof schema !== 'object') {
|
|
130
|
+
return schema;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const { schema: dereferenced, refName } = getResolvedDefinition(
|
|
134
|
+
schema,
|
|
135
|
+
definitions,
|
|
136
|
+
visitedRefs
|
|
137
|
+
);
|
|
138
|
+
if (dereferenced !== schema) {
|
|
139
|
+
return { ...normalizeDefinition(dereferenced, definitions, visitedRefs), __sourceRef: refName };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (Array.isArray(dereferenced.allOf)) {
|
|
143
|
+
const merged = {};
|
|
144
|
+
dereferenced.allOf.forEach((branch) => {
|
|
145
|
+
const child = normalizeDefinition(branch, definitions, visitedRefs);
|
|
146
|
+
Object.assign(merged, child);
|
|
147
|
+
if (child.properties && !merged.properties) {
|
|
148
|
+
merged.properties = {};
|
|
149
|
+
}
|
|
150
|
+
if (child.properties) {
|
|
151
|
+
merged.properties = { ...merged.properties, ...child.properties };
|
|
152
|
+
}
|
|
153
|
+
if (child.additionalProperties && !merged.additionalProperties) {
|
|
154
|
+
merged.additionalProperties = child.additionalProperties;
|
|
155
|
+
}
|
|
156
|
+
if (child.patternProperties && !merged.patternProperties) {
|
|
157
|
+
merged.patternProperties = child.patternProperties;
|
|
158
|
+
}
|
|
159
|
+
if (child.required && !merged.required) {
|
|
160
|
+
merged.required = child.required;
|
|
161
|
+
}
|
|
162
|
+
if (child.required) {
|
|
163
|
+
merged.required = Array.from(new Set([...(merged.required || []), ...child.required]));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
...merged,
|
|
169
|
+
...dereferenced,
|
|
170
|
+
...{ allOf: undefined },
|
|
171
|
+
__sourceRef: merged.__sourceRef || dereferenced.__sourceRef,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (Array.isArray(dereferenced.oneOf)) {
|
|
176
|
+
return {
|
|
177
|
+
...dereferenced,
|
|
178
|
+
oneOf: dereferenced.oneOf.map((branch) =>
|
|
179
|
+
normalizeDefinition(branch, definitions, visitedRefs)
|
|
180
|
+
),
|
|
181
|
+
__sourceRef: dereferenced.__sourceRef,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (Array.isArray(dereferenced.anyOf)) {
|
|
186
|
+
return {
|
|
187
|
+
...dereferenced,
|
|
188
|
+
anyOf: dereferenced.anyOf.map((branch) =>
|
|
189
|
+
normalizeDefinition(branch, definitions, visitedRefs)
|
|
190
|
+
),
|
|
191
|
+
__sourceRef: dereferenced.__sourceRef,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return { ...dereferenced, __sourceRef: dereferenced.__sourceRef };
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const walkDefinitions = (schema, definitions, visitedRefs = new Set()) => {
|
|
199
|
+
const nodes = [];
|
|
200
|
+
const normalized = normalizeDefinition(schema, definitions, visitedRefs);
|
|
201
|
+
|
|
202
|
+
if (!isObject(normalized)) {
|
|
203
|
+
return [normalized];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
nodes.push(normalized);
|
|
207
|
+
|
|
208
|
+
if (Array.isArray(normalized.allOf)) {
|
|
209
|
+
normalized.allOf.forEach((branch) => {
|
|
210
|
+
nodes.push(...walkDefinitions(branch, definitions, visitedRefs));
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return nodes;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const getEnumSignature = (schema) =>
|
|
218
|
+
Array.isArray(schema?.enum) ? schema.enum.map((value) => String(value)).join(' | ') : null;
|
|
219
|
+
|
|
220
|
+
const mapBranchTypeLabel = (schema, context) => {
|
|
221
|
+
if (schema?.properties && typeof schema.properties === 'object') {
|
|
222
|
+
const parts = Object.entries(schema.properties).map(([segment, child]) => {
|
|
223
|
+
const childNormalized = normalizeDefinition(child, context.definitions);
|
|
224
|
+
const childType =
|
|
225
|
+
childNormalized?.type === 'object' && childNormalized?.properties
|
|
226
|
+
? mapBranchTypeLabel(childNormalized, context)
|
|
227
|
+
: getTypeLabel(childNormalized, context);
|
|
228
|
+
return `${segment} = ${childType}`;
|
|
229
|
+
});
|
|
230
|
+
return `{ ${parts.join(', ')} }`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return getTypeLabel(schema, context);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const getTypeLabel = (schema, context) => {
|
|
237
|
+
const normalized = normalizeDefinition(schema, context.definitions);
|
|
238
|
+
if (!normalized || typeof normalized !== 'object') {
|
|
239
|
+
return 'value';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (Array.isArray(normalized.oneOf) || Array.isArray(normalized.anyOf)) {
|
|
243
|
+
const branches = (normalized.oneOf || normalized.anyOf || []).map((branch) =>
|
|
244
|
+
getTypeLabel(branch, context)
|
|
245
|
+
);
|
|
246
|
+
const unique = [...new Set(branches)].filter(Boolean);
|
|
247
|
+
return unique.length === 1 ? unique[0] : unique.join(' | ');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (
|
|
251
|
+
typeof normalized.__sourceRef === 'string' &&
|
|
252
|
+
normalized.__sourceRef === 'AbsolutePathBuf'
|
|
253
|
+
) {
|
|
254
|
+
return 'string (path)';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (Array.isArray(normalized.enum)) {
|
|
258
|
+
const enumLabel = getEnumSignature(normalized);
|
|
259
|
+
if (enumLabel) {
|
|
260
|
+
return enumLabel;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (Array.isArray(normalized.type)) {
|
|
265
|
+
if (normalized.type.length === 1) {
|
|
266
|
+
return String(normalized.type[0]);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const normalizedType = String(normalized.type || '').trim();
|
|
271
|
+
if (normalizedType === 'array') {
|
|
272
|
+
return `array<${getTypeLabel(normalized.items || { type: 'string' }, context)}>`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (normalizedType === 'object') {
|
|
276
|
+
if (normalized.additionalProperties && normalized.type === 'object') {
|
|
277
|
+
return `map<${getTypeLabel(normalized.additionalProperties, context)}>`;
|
|
278
|
+
}
|
|
279
|
+
return 'table';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (isPrimitiveValue(normalizedType)) {
|
|
283
|
+
return normalizedType;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const enumLabel = getEnumSignature(normalized);
|
|
287
|
+
if (enumLabel) {
|
|
288
|
+
return enumLabel;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (typeof normalized.type === 'string' && normalized.type.length > 0) {
|
|
292
|
+
return normalized.type;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return 'value';
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const toMapType = (valueSchema, context) => {
|
|
299
|
+
const valueType = getTypeLabel(valueSchema, context);
|
|
300
|
+
return `map<${valueType}>`;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const mergeEnumValues = (base = [], added = []) => {
|
|
304
|
+
const next = [...base];
|
|
305
|
+
const seen = new Set(base.map(String));
|
|
306
|
+
|
|
307
|
+
added.forEach((value) => {
|
|
308
|
+
const text = String(value ?? '');
|
|
309
|
+
if (!seen.has(text)) {
|
|
310
|
+
next.push(text);
|
|
311
|
+
seen.add(text);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
return next;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const mergeEnumDescriptions = (base = {}, added = {}) => {
|
|
319
|
+
const next = { ...base };
|
|
320
|
+
if (!added || typeof added !== 'object') {
|
|
321
|
+
return next;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
Object.entries(added).forEach(([enumValue, description]) => {
|
|
325
|
+
const key = String(enumValue);
|
|
326
|
+
if (!Object.prototype.hasOwnProperty.call(next, key) || String(next[key]).trim() === '') {
|
|
327
|
+
next[key] = String(description || '').trim();
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
return next;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const getOneOfEnumDescriptions = (branches) => {
|
|
335
|
+
const mapped = {};
|
|
336
|
+
if (!Array.isArray(branches)) {
|
|
337
|
+
return mapped;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
branches.forEach((branch) => {
|
|
341
|
+
const description = String(branch?.description || '').trim();
|
|
342
|
+
if (!description) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const enumValues = Array.isArray(branch?.enum) ? branch.enum : [];
|
|
347
|
+
enumValues.forEach((value) => {
|
|
348
|
+
mapped[String(value)] = description;
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return mapped;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const mergeOptionType = (current, next) => {
|
|
356
|
+
if (!current) {
|
|
357
|
+
return next;
|
|
358
|
+
}
|
|
359
|
+
if (current === next) {
|
|
360
|
+
return current;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (current.includes(next) || next.includes(current)) {
|
|
364
|
+
return current;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return `${current} | ${next}`;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const getSingleDiscriminatorValue = (schema) => {
|
|
371
|
+
if (!schema || typeof schema !== 'object') {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (Object.prototype.hasOwnProperty.call(schema, 'const')) {
|
|
376
|
+
return schema.const;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (Array.isArray(schema.enum) && schema.enum.length === 1) {
|
|
380
|
+
return schema.enum[0];
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return null;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const getObjectBranchLabel = (branch, index) => {
|
|
387
|
+
const title = String(branch?.title || '').trim();
|
|
388
|
+
if (title) {
|
|
389
|
+
return title;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const properties = branch?.properties && typeof branch.properties === 'object'
|
|
393
|
+
? branch.properties
|
|
394
|
+
: {};
|
|
395
|
+
const propertyEntries = Object.entries(properties);
|
|
396
|
+
|
|
397
|
+
for (const [propertyKey, propertySchema] of propertyEntries) {
|
|
398
|
+
const discriminatorValue = getSingleDiscriminatorValue(propertySchema);
|
|
399
|
+
if (discriminatorValue !== null && typeof discriminatorValue !== 'undefined') {
|
|
400
|
+
return String(discriminatorValue);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const propertyTitle = String(propertySchema?.title || '').trim();
|
|
404
|
+
if (propertyTitle) {
|
|
405
|
+
return propertyTitle;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (Array.isArray(propertySchema?.enum) && propertySchema.enum.length > 0) {
|
|
409
|
+
return `${propertyKey}=${String(propertySchema.enum[0])}`;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const requiredKeys = Array.isArray(branch?.required)
|
|
414
|
+
? branch.required.map((key) => String(key))
|
|
415
|
+
: [];
|
|
416
|
+
if (requiredKeys.length === 1) {
|
|
417
|
+
return requiredKeys[0];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const signatureKeys = propertyEntries
|
|
421
|
+
.map(([propertyKey]) => String(propertyKey))
|
|
422
|
+
.sort((left, right) => left.localeCompare(right));
|
|
423
|
+
if (signatureKeys.length > 0) {
|
|
424
|
+
return signatureKeys.join('+');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (requiredKeys.length > 0) {
|
|
428
|
+
return requiredKeys
|
|
429
|
+
.slice()
|
|
430
|
+
.sort((left, right) => left.localeCompare(right))
|
|
431
|
+
.join('+');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return `object_${index + 1}`;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const getObjectBranchFixedValues = (branch) => {
|
|
438
|
+
const properties = branch?.properties && typeof branch.properties === 'object'
|
|
439
|
+
? branch.properties
|
|
440
|
+
: {};
|
|
441
|
+
const fixedValues = {};
|
|
442
|
+
|
|
443
|
+
Object.entries(properties).forEach(([key, propertySchema]) => {
|
|
444
|
+
const fixedValue = getSingleDiscriminatorValue(propertySchema);
|
|
445
|
+
if (fixedValue !== null && typeof fixedValue !== 'undefined') {
|
|
446
|
+
fixedValues[String(key)] = fixedValue;
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
return fixedValues;
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const buildMixedVariantInfo = (scalarType, objectBranches) => {
|
|
454
|
+
const seenLabels = new Map();
|
|
455
|
+
const objectVariants = objectBranches.map((branch, index) => {
|
|
456
|
+
const baseLabel = getObjectBranchLabel(branch, index);
|
|
457
|
+
const occurrence = (seenLabels.get(baseLabel) || 0) + 1;
|
|
458
|
+
seenLabels.set(baseLabel, occurrence);
|
|
459
|
+
const label = occurrence === 1 ? baseLabel : `${baseLabel} (${occurrence})`;
|
|
460
|
+
const requiredKeys = Array.isArray(branch?.required)
|
|
461
|
+
? branch.required.map((key) => String(key))
|
|
462
|
+
: [];
|
|
463
|
+
const fixedValues = getObjectBranchFixedValues(branch);
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
id: `object_${index + 1}`,
|
|
467
|
+
label,
|
|
468
|
+
requiredKeys,
|
|
469
|
+
fixedValues,
|
|
470
|
+
};
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const requiredObjectKeys = [
|
|
474
|
+
...new Set(
|
|
475
|
+
objectVariants
|
|
476
|
+
.flatMap((variant) => variant.requiredKeys)
|
|
477
|
+
.map((key) => String(key))
|
|
478
|
+
),
|
|
479
|
+
];
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
kind: 'scalar_object',
|
|
483
|
+
scalarType,
|
|
484
|
+
requiredObjectKeys,
|
|
485
|
+
objectVariants,
|
|
486
|
+
};
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const mergeVariantInfo = (current, next) => {
|
|
490
|
+
if (!next || typeof next !== 'object') {
|
|
491
|
+
return current || null;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (!current || typeof current !== 'object') {
|
|
495
|
+
return {
|
|
496
|
+
...next,
|
|
497
|
+
requiredObjectKeys: Array.isArray(next.requiredObjectKeys)
|
|
498
|
+
? [...next.requiredObjectKeys]
|
|
499
|
+
: [],
|
|
500
|
+
objectVariants: Array.isArray(next.objectVariants)
|
|
501
|
+
? next.objectVariants.map((variant) => ({
|
|
502
|
+
...variant,
|
|
503
|
+
requiredKeys: Array.isArray(variant.requiredKeys)
|
|
504
|
+
? [...variant.requiredKeys]
|
|
505
|
+
: [],
|
|
506
|
+
fixedValues:
|
|
507
|
+
variant.fixedValues && typeof variant.fixedValues === 'object'
|
|
508
|
+
? { ...variant.fixedValues }
|
|
509
|
+
: {},
|
|
510
|
+
}))
|
|
511
|
+
: [],
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (current.kind !== 'scalar_object' || next.kind !== 'scalar_object') {
|
|
516
|
+
return current;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const mergedById = new Map();
|
|
520
|
+
|
|
521
|
+
[...(current.objectVariants || []), ...(next.objectVariants || [])].forEach((variant) => {
|
|
522
|
+
const variantId = String(variant?.id || '');
|
|
523
|
+
if (!variantId) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const previous = mergedById.get(variantId);
|
|
528
|
+
if (!previous) {
|
|
529
|
+
mergedById.set(variantId, {
|
|
530
|
+
id: variantId,
|
|
531
|
+
label: String(variant.label || variantId),
|
|
532
|
+
requiredKeys: Array.isArray(variant.requiredKeys)
|
|
533
|
+
? variant.requiredKeys.map((key) => String(key))
|
|
534
|
+
: [],
|
|
535
|
+
fixedValues:
|
|
536
|
+
variant.fixedValues && typeof variant.fixedValues === 'object'
|
|
537
|
+
? { ...variant.fixedValues }
|
|
538
|
+
: {},
|
|
539
|
+
});
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
mergedById.set(variantId, {
|
|
544
|
+
...previous,
|
|
545
|
+
requiredKeys: [
|
|
546
|
+
...new Set([
|
|
547
|
+
...previous.requiredKeys,
|
|
548
|
+
...(Array.isArray(variant.requiredKeys)
|
|
549
|
+
? variant.requiredKeys.map((key) => String(key))
|
|
550
|
+
: []),
|
|
551
|
+
]),
|
|
552
|
+
],
|
|
553
|
+
fixedValues: {
|
|
554
|
+
...previous.fixedValues,
|
|
555
|
+
...(variant.fixedValues && typeof variant.fixedValues === 'object'
|
|
556
|
+
? variant.fixedValues
|
|
557
|
+
: {}),
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
return {
|
|
563
|
+
kind: 'scalar_object',
|
|
564
|
+
scalarType: mergeOptionType(
|
|
565
|
+
String(current.scalarType || ''),
|
|
566
|
+
String(next.scalarType || '')
|
|
567
|
+
),
|
|
568
|
+
requiredObjectKeys: [
|
|
569
|
+
...new Set([
|
|
570
|
+
...(Array.isArray(current.requiredObjectKeys)
|
|
571
|
+
? current.requiredObjectKeys.map((key) => String(key))
|
|
572
|
+
: []),
|
|
573
|
+
...(Array.isArray(next.requiredObjectKeys)
|
|
574
|
+
? next.requiredObjectKeys.map((key) => String(key))
|
|
575
|
+
: []),
|
|
576
|
+
]),
|
|
577
|
+
],
|
|
578
|
+
objectVariants: [...mergedById.values()],
|
|
579
|
+
};
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const addReferenceOption = (optionsByKey, pathSegments, schema, context = {}, overrides = {}) => {
|
|
583
|
+
const normalizedPath = normalizeSegments(pathSegments);
|
|
584
|
+
const key = normalizedPath.join('.');
|
|
585
|
+
if (!key) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const description = String(schema?.description || '').trim();
|
|
590
|
+
const hasDefaultValue = schema && Object.prototype.hasOwnProperty.call(schema, 'default');
|
|
591
|
+
const defaultValue = hasDefaultValue ? schema.default : undefined;
|
|
592
|
+
const typeLabel = overrides.type || getTypeLabel(schema, context);
|
|
593
|
+
const existing = optionsByKey.get(key);
|
|
594
|
+
const enumValues = Array.isArray(schema?.enum)
|
|
595
|
+
? schema.enum.map((value) => String(value))
|
|
596
|
+
: [];
|
|
597
|
+
const enumOptionDescriptions = {
|
|
598
|
+
...(overrides.enumOptionDescriptions || {}),
|
|
599
|
+
};
|
|
600
|
+
const variantInfo = overrides.variantInfo || null;
|
|
601
|
+
|
|
602
|
+
if (!existing) {
|
|
603
|
+
optionsByKey.set(key, {
|
|
604
|
+
key,
|
|
605
|
+
key_path: normalizedPath,
|
|
606
|
+
type: typeLabel,
|
|
607
|
+
enum_values: enumValues,
|
|
608
|
+
enumValues: enumValues,
|
|
609
|
+
enumOptionDescriptions,
|
|
610
|
+
description,
|
|
611
|
+
deprecated: schema?.deprecated === true,
|
|
612
|
+
defaultValue,
|
|
613
|
+
variantInfo,
|
|
614
|
+
});
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
existing.type = mergeOptionType(existing.type, typeLabel);
|
|
619
|
+
existing.enum_values = mergeEnumValues(existing.enum_values, enumValues);
|
|
620
|
+
existing.enumValues = mergeEnumValues(existing.enumValues, enumValues);
|
|
621
|
+
existing.enumOptionDescriptions = mergeEnumDescriptions(
|
|
622
|
+
existing.enumOptionDescriptions,
|
|
623
|
+
enumOptionDescriptions
|
|
624
|
+
);
|
|
625
|
+
existing.variantInfo = mergeVariantInfo(existing.variantInfo, variantInfo);
|
|
626
|
+
if (existing.defaultValue === undefined && hasDefaultValue) {
|
|
627
|
+
existing.defaultValue = defaultValue;
|
|
628
|
+
}
|
|
629
|
+
if (!existing.description && description) {
|
|
630
|
+
existing.description = description;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const collectSchemaOptions = (schema, pathSegments, optionsByKey, context) => {
|
|
635
|
+
const normalized = normalizeDefinition(schema, context.definitions);
|
|
636
|
+
if (!normalized || typeof normalized !== 'object') {
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const normalizedPath = normalizeSegments(pathSegments);
|
|
641
|
+
if (Array.isArray(normalized.allOf) && normalized.allOf.length > 1) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (Array.isArray(normalized.oneOf)) {
|
|
646
|
+
const branchSchemas = normalized.oneOf.map((branch) =>
|
|
647
|
+
normalizeDefinition(branch, context.definitions)
|
|
648
|
+
);
|
|
649
|
+
const enumDescriptionsByValue = getOneOfEnumDescriptions(branchSchemas);
|
|
650
|
+
const scalarBranches = branchSchemas.filter(isScalarLikeReference);
|
|
651
|
+
const objectBranches = branchSchemas.filter(isObjectLikeReference);
|
|
652
|
+
|
|
653
|
+
if (scalarBranches.length > 0 && objectBranches.length > 0) {
|
|
654
|
+
const scalarTypeLabel = getOneOfTypeLabel(scalarBranches, context);
|
|
655
|
+
const scalarEnumValues = scalarBranches.flatMap((branch) =>
|
|
656
|
+
Array.isArray(branch.enum) ? branch.enum : []
|
|
657
|
+
);
|
|
658
|
+
if (normalizedPath.length > 0) {
|
|
659
|
+
const scalarSchema = {
|
|
660
|
+
...normalized,
|
|
661
|
+
enum: scalarEnumValues,
|
|
662
|
+
type: scalarTypeLabel,
|
|
663
|
+
};
|
|
664
|
+
addReferenceOption(
|
|
665
|
+
optionsByKey,
|
|
666
|
+
normalizedPath,
|
|
667
|
+
scalarSchema,
|
|
668
|
+
context,
|
|
669
|
+
{
|
|
670
|
+
type: scalarTypeLabel,
|
|
671
|
+
enumOptionDescriptions: enumDescriptionsByValue,
|
|
672
|
+
variantInfo: buildMixedVariantInfo(scalarTypeLabel, objectBranches),
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
objectBranches.forEach((branch) => {
|
|
678
|
+
const branchNormalized = normalizeDefinition(branch, context.definitions);
|
|
679
|
+
if (branchNormalized?.properties) {
|
|
680
|
+
Object.entries(branchNormalized.properties).forEach(([segment, value]) => {
|
|
681
|
+
collectSchemaOptions(value, [...normalizedPath, segment], optionsByKey, context);
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const branchDescriptions = branchSchemas.map((branch) => {
|
|
690
|
+
if (branch?.type === 'object' && branch?.properties) {
|
|
691
|
+
return mapBranchTypeLabel(branch, context);
|
|
692
|
+
}
|
|
693
|
+
return getTypeLabel(branch, context);
|
|
694
|
+
});
|
|
695
|
+
const unique = [...new Set(branchDescriptions)];
|
|
696
|
+
if (normalizedPath.length > 0) {
|
|
697
|
+
const typeLabel = unique.length === 1 ? unique[0] : unique.join(' | ');
|
|
698
|
+
const scalarEnumValues = branchSchemas
|
|
699
|
+
.flatMap((branch) => (Array.isArray(branch.enum) ? branch.enum : []))
|
|
700
|
+
.filter(
|
|
701
|
+
(value, index, values) =>
|
|
702
|
+
values.findIndex((item) => Object.is(item, value)) === index
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
addReferenceOption(
|
|
706
|
+
optionsByKey,
|
|
707
|
+
normalizedPath,
|
|
708
|
+
{
|
|
709
|
+
...normalized,
|
|
710
|
+
...(scalarEnumValues.length > 0 ? { enum: scalarEnumValues } : {}),
|
|
711
|
+
},
|
|
712
|
+
context,
|
|
713
|
+
{
|
|
714
|
+
type: typeLabel,
|
|
715
|
+
enumOptionDescriptions: enumDescriptionsByValue,
|
|
716
|
+
}
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
normalized.oneOf.forEach((branch) => {
|
|
721
|
+
const branchNormalized = normalizeDefinition(branch, context.definitions);
|
|
722
|
+
if (branchNormalized?.properties) {
|
|
723
|
+
Object.entries(branchNormalized.properties).forEach(([segment, value]) => {
|
|
724
|
+
collectSchemaOptions(value, [...normalizedPath, segment], optionsByKey, context);
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (normalized.type === 'array') {
|
|
732
|
+
if (!normalized.items) {
|
|
733
|
+
addReferenceOption(optionsByKey, normalizedPath, { ...normalized, type: 'array<string>' }, context);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const itemType = normalizeDefinition(normalized.items, context.definitions);
|
|
738
|
+
if (
|
|
739
|
+
itemType &&
|
|
740
|
+
(itemType.type === 'object' ||
|
|
741
|
+
itemType.properties ||
|
|
742
|
+
itemType.additionalProperties ||
|
|
743
|
+
itemType.oneOf ||
|
|
744
|
+
itemType.allOf)
|
|
745
|
+
) {
|
|
746
|
+
const itemPath = [...normalizedPath, '<index>'];
|
|
747
|
+
if (itemType.properties) {
|
|
748
|
+
Object.entries(itemType.properties).forEach(([segment, value]) => {
|
|
749
|
+
collectSchemaOptions(value, [...itemPath, segment], optionsByKey, context);
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (itemType.additionalProperties && itemType.properties) {
|
|
753
|
+
const placeholder = getPlaceholderForSegment(itemPath[itemPath.length - 1], '<index>');
|
|
754
|
+
collectSchemaOptions(
|
|
755
|
+
itemType.additionalProperties,
|
|
756
|
+
[...itemPath, placeholder],
|
|
757
|
+
optionsByKey,
|
|
758
|
+
context
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
addReferenceOption(optionsByKey, normalizedPath, { ...normalized }, context);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
addReferenceOption(
|
|
766
|
+
optionsByKey,
|
|
767
|
+
normalizedPath,
|
|
768
|
+
{ ...normalized, type: `array<${getTypeLabel(itemType, context)}>` },
|
|
769
|
+
context
|
|
770
|
+
);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (normalized.type === 'object' || !normalized.type) {
|
|
775
|
+
const properties = normalized.properties || {};
|
|
776
|
+
const hasProperties = Object.keys(properties).length > 0;
|
|
777
|
+
const hasAdditionalProperties =
|
|
778
|
+
normalized.additionalProperties && typeof normalized.additionalProperties === 'object';
|
|
779
|
+
const hasPatternProperties =
|
|
780
|
+
normalized.patternProperties && typeof normalized.patternProperties === 'object';
|
|
781
|
+
if (!hasProperties && (hasAdditionalProperties || hasPatternProperties)) {
|
|
782
|
+
if (hasAdditionalProperties) {
|
|
783
|
+
const placeholder = getPlaceholderForSegment(
|
|
784
|
+
normalizedPath[normalizedPath.length - 1],
|
|
785
|
+
'<name>'
|
|
786
|
+
);
|
|
787
|
+
const additionalProperties = normalizeDefinition(
|
|
788
|
+
normalized.additionalProperties,
|
|
789
|
+
context.definitions
|
|
790
|
+
);
|
|
791
|
+
const additionalHasChildren =
|
|
792
|
+
additionalProperties?.type === 'object' &&
|
|
793
|
+
(Object.keys(additionalProperties.properties || {}).length > 0 ||
|
|
794
|
+
typeof additionalProperties.additionalProperties === 'object');
|
|
795
|
+
|
|
796
|
+
if (additionalHasChildren) {
|
|
797
|
+
collectSchemaOptions(
|
|
798
|
+
additionalProperties,
|
|
799
|
+
[...normalizedPath, placeholder],
|
|
800
|
+
optionsByKey,
|
|
801
|
+
context
|
|
802
|
+
);
|
|
803
|
+
} else {
|
|
804
|
+
const mapType = toMapType(additionalProperties, context);
|
|
805
|
+
addReferenceOption(
|
|
806
|
+
optionsByKey,
|
|
807
|
+
normalizedPath,
|
|
808
|
+
{ ...normalized, type: mapType },
|
|
809
|
+
context
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
} else {
|
|
813
|
+
addReferenceOption(optionsByKey, normalizedPath, { ...normalized }, context);
|
|
814
|
+
}
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (hasProperties && normalizedPath.length > 0) {
|
|
819
|
+
addReferenceOption(
|
|
820
|
+
optionsByKey,
|
|
821
|
+
normalizedPath,
|
|
822
|
+
{ ...normalized, type: 'table' },
|
|
823
|
+
context
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
Object.entries(properties).forEach(([segment, child]) => {
|
|
828
|
+
collectSchemaOptions(child, [...normalizedPath, segment], optionsByKey, context);
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
if (hasAdditionalProperties) {
|
|
832
|
+
const placeholder = getPlaceholderForSegment(
|
|
833
|
+
normalizedPath[normalizedPath.length - 1],
|
|
834
|
+
'<index>'
|
|
835
|
+
);
|
|
836
|
+
const normalizedAdditional = normalizeDefinition(
|
|
837
|
+
normalized.additionalProperties,
|
|
838
|
+
context.definitions
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
if (normalizedAdditional?.properties) {
|
|
842
|
+
Object.keys(normalizedAdditional.properties || {}).forEach((segment) => {
|
|
843
|
+
collectSchemaOptions(
|
|
844
|
+
normalizedAdditional.properties[segment],
|
|
845
|
+
[...normalizedPath, placeholder, segment],
|
|
846
|
+
optionsByKey,
|
|
847
|
+
context
|
|
848
|
+
);
|
|
849
|
+
});
|
|
850
|
+
} else if (normalizedAdditional?.type === 'object') {
|
|
851
|
+
collectSchemaOptions(
|
|
852
|
+
{ ...normalizedAdditional, type: 'object' },
|
|
853
|
+
[...normalizedPath, placeholder],
|
|
854
|
+
optionsByKey,
|
|
855
|
+
context
|
|
856
|
+
);
|
|
857
|
+
} else {
|
|
858
|
+
const mapType = getTypeLabel(normalizedAdditional, context);
|
|
859
|
+
addReferenceOption(
|
|
860
|
+
optionsByKey,
|
|
861
|
+
[...normalizedPath, placeholder],
|
|
862
|
+
{ ...normalizedAdditional, type: mapType },
|
|
863
|
+
context
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (hasPatternProperties) {
|
|
869
|
+
const patternEntries = Object.entries(normalized.patternProperties);
|
|
870
|
+
patternEntries.forEach(([pattern, child]) => {
|
|
871
|
+
const childType = normalizeDefinition(child, context.definitions);
|
|
872
|
+
if (childType?.type === 'object' || childType?.properties) {
|
|
873
|
+
collectSchemaOptions(childType, [...normalizedPath, `<${pattern}>`], optionsByKey, context);
|
|
874
|
+
} else {
|
|
875
|
+
const mapType = getTypeLabel(childType, context);
|
|
876
|
+
addReferenceOption(
|
|
877
|
+
optionsByKey,
|
|
878
|
+
[...normalizedPath, `<${pattern}>`],
|
|
879
|
+
{ ...childType, type: mapType },
|
|
880
|
+
context
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (normalizedPath.length > 0) {
|
|
890
|
+
addReferenceOption(optionsByKey, normalizedPath, normalized, context);
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
|
|
894
|
+
const buildReferenceOptions = (schema) => {
|
|
895
|
+
const definitions = new Map(Object.entries(schema?.definitions || {}));
|
|
896
|
+
const normalizedSchema = normalizeDefinition(schema, definitions);
|
|
897
|
+
if (!isObject(normalizedSchema)) {
|
|
898
|
+
return [];
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const optionsByKey = new Map();
|
|
902
|
+
|
|
903
|
+
collectSchemaOptions(normalizedSchema, [], optionsByKey, { definitions });
|
|
904
|
+
|
|
905
|
+
const options = [...optionsByKey.values()].sort((left, right) =>
|
|
906
|
+
left.key.localeCompare(right.key)
|
|
907
|
+
);
|
|
908
|
+
|
|
909
|
+
options.forEach((option) => {
|
|
910
|
+
option.top_level = option.key_path[0];
|
|
911
|
+
option.enum_values = option.enum_values || [];
|
|
912
|
+
option.enumValues = option.enumValues || option.enum_values || [];
|
|
913
|
+
option.keyPath = option.key_path;
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
return options;
|
|
917
|
+
};
|
|
918
|
+
|
|
27
919
|
const mapTypeToKind = (type) => {
|
|
28
920
|
const normalizedType = normalizeType(type);
|
|
29
921
|
|
|
@@ -76,28 +968,62 @@ const fullPathMatches = (referencePath, actualPath) =>
|
|
|
76
968
|
const countConcreteSegments = (segments) =>
|
|
77
969
|
segments.reduce((count, segment) => count + (isPlaceholderSegment(segment) ? 0 : 1), 0);
|
|
78
970
|
|
|
79
|
-
const
|
|
80
|
-
|
|
971
|
+
const buildFeatureKeys = (referenceOptions) => [
|
|
972
|
+
...new Set(
|
|
973
|
+
referenceOptions
|
|
974
|
+
.filter(
|
|
975
|
+
(option) =>
|
|
976
|
+
option.keyPath.length === 2 &&
|
|
977
|
+
option.keyPath[0] === 'features' &&
|
|
978
|
+
!isPlaceholderSegment(option.keyPath[1])
|
|
979
|
+
)
|
|
980
|
+
.map((option) => option.keyPath[1])
|
|
981
|
+
),
|
|
982
|
+
].sort((left, right) => left.localeCompare(right));
|
|
81
983
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
984
|
+
const prepareReferenceState = (schema) => {
|
|
985
|
+
if (!isObject(schema)) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
86
988
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
enumValues: Array.isArray(option?.enum_values)
|
|
92
|
-
? option.enum_values.map((value) => String(value))
|
|
93
|
-
: [],
|
|
94
|
-
description: String(option?.description || ''),
|
|
95
|
-
deprecated: option?.deprecated === true,
|
|
96
|
-
};
|
|
97
|
-
})
|
|
98
|
-
: [];
|
|
989
|
+
const referenceOptions = buildReferenceOptions(schema);
|
|
990
|
+
if (!Array.isArray(referenceOptions) || referenceOptions.length === 0) {
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
99
993
|
|
|
100
|
-
|
|
994
|
+
return {
|
|
995
|
+
schema,
|
|
996
|
+
revision: buildSchemaRevision(schema),
|
|
997
|
+
referenceOptions,
|
|
998
|
+
optionsByKey: new Map(referenceOptions.map((option) => [option.key, option])),
|
|
999
|
+
featureKeys: buildFeatureKeys(referenceOptions),
|
|
1000
|
+
};
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
let activeReferenceState = prepareReferenceState(BUNDLED_CONFIG_SCHEMA_DATA);
|
|
1004
|
+
if (!activeReferenceState) {
|
|
1005
|
+
throw new Error('Bundled config schema is invalid.');
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const getReferenceState = () => activeReferenceState;
|
|
1009
|
+
|
|
1010
|
+
export const isReferenceSchemaValid = (schema) => Boolean(prepareReferenceState(schema));
|
|
1011
|
+
|
|
1012
|
+
export const getReferenceSchemaRevision = () => getReferenceState().revision;
|
|
1013
|
+
|
|
1014
|
+
export const setReferenceSchema = (schema) => {
|
|
1015
|
+
const nextState = prepareReferenceState(schema);
|
|
1016
|
+
if (!nextState) {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
if (activeReferenceState?.revision === nextState.revision) {
|
|
1021
|
+
return true;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
activeReferenceState = nextState;
|
|
1025
|
+
return true;
|
|
1026
|
+
};
|
|
101
1027
|
|
|
102
1028
|
const mergeDefinition = (map, definition) => {
|
|
103
1029
|
const existing = map.get(definition.key);
|
|
@@ -117,6 +1043,7 @@ const mergeDefinition = (map, definition) => {
|
|
|
117
1043
|
};
|
|
118
1044
|
|
|
119
1045
|
export const getReferenceOptionForPath = (pathSegments) => {
|
|
1046
|
+
const { optionsByKey, referenceOptions } = getReferenceState();
|
|
120
1047
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
121
1048
|
if (normalizedPath.length === 0) {
|
|
122
1049
|
return null;
|
|
@@ -145,7 +1072,58 @@ export const getReferenceOptionForPath = (pathSegments) => {
|
|
|
145
1072
|
return candidates[0];
|
|
146
1073
|
};
|
|
147
1074
|
|
|
1075
|
+
const getVariantObjectPaths = (normalizedPath, referenceOptions) => {
|
|
1076
|
+
const depth = normalizedPath.length;
|
|
1077
|
+
const paths = referenceOptions
|
|
1078
|
+
.filter((option) => pathPrefixMatches(option.keyPath, normalizedPath))
|
|
1079
|
+
.filter((option) => option.keyPath.length > depth)
|
|
1080
|
+
.map((option) => option.keyPath.slice(depth).join('.'))
|
|
1081
|
+
.filter((relativePath) => relativePath.length > 0);
|
|
1082
|
+
|
|
1083
|
+
return [...new Set(paths)].sort((left, right) => left.localeCompare(right));
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
export const getReferenceVariantForPath = (pathSegments = []) => {
|
|
1087
|
+
const { referenceOptions } = getReferenceState();
|
|
1088
|
+
const normalizedPath = normalizeSegments(pathSegments);
|
|
1089
|
+
if (normalizedPath.length === 0) {
|
|
1090
|
+
return null;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const entry = getReferenceOptionForPath(normalizedPath);
|
|
1094
|
+
const variantInfo = entry?.variantInfo;
|
|
1095
|
+
if (!variantInfo || variantInfo.kind !== 'scalar_object') {
|
|
1096
|
+
return null;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
return {
|
|
1100
|
+
kind: 'scalar_object',
|
|
1101
|
+
scalarType: String(variantInfo.scalarType || entry.type || 'value'),
|
|
1102
|
+
scalarOptions: Array.isArray(entry.enumValues)
|
|
1103
|
+
? entry.enumValues.map((value) => String(value))
|
|
1104
|
+
: [],
|
|
1105
|
+
requiredObjectKeys: Array.isArray(variantInfo.requiredObjectKeys)
|
|
1106
|
+
? variantInfo.requiredObjectKeys.map((key) => String(key))
|
|
1107
|
+
: [],
|
|
1108
|
+
objectVariants: Array.isArray(variantInfo.objectVariants)
|
|
1109
|
+
? variantInfo.objectVariants.map((variant) => ({
|
|
1110
|
+
id: String(variant.id),
|
|
1111
|
+
label: String(variant.label),
|
|
1112
|
+
requiredKeys: Array.isArray(variant.requiredKeys)
|
|
1113
|
+
? variant.requiredKeys.map((key) => String(key))
|
|
1114
|
+
: [],
|
|
1115
|
+
fixedValues:
|
|
1116
|
+
variant.fixedValues && typeof variant.fixedValues === 'object'
|
|
1117
|
+
? { ...variant.fixedValues }
|
|
1118
|
+
: {},
|
|
1119
|
+
}))
|
|
1120
|
+
: [],
|
|
1121
|
+
objectSchemaPaths: getVariantObjectPaths(normalizedPath, referenceOptions),
|
|
1122
|
+
};
|
|
1123
|
+
};
|
|
1124
|
+
|
|
148
1125
|
export const getReferenceTableDefinitions = (pathSegments = []) => {
|
|
1126
|
+
const { referenceOptions } = getReferenceState();
|
|
149
1127
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
150
1128
|
const childDefinitions = new Map();
|
|
151
1129
|
const depth = normalizedPath.length;
|
|
@@ -179,22 +1157,10 @@ export const getReferenceTableDefinitions = (pathSegments = []) => {
|
|
|
179
1157
|
|
|
180
1158
|
export const getReferenceRootDefinitions = () => getReferenceTableDefinitions([]);
|
|
181
1159
|
|
|
182
|
-
const
|
|
183
|
-
...new Set(
|
|
184
|
-
referenceOptions
|
|
185
|
-
.filter(
|
|
186
|
-
(option) =>
|
|
187
|
-
option.keyPath.length === 2 &&
|
|
188
|
-
option.keyPath[0] === 'features' &&
|
|
189
|
-
!isPlaceholderSegment(option.keyPath[1])
|
|
190
|
-
)
|
|
191
|
-
.map((option) => option.keyPath[1])
|
|
192
|
-
),
|
|
193
|
-
].sort((left, right) => left.localeCompare(right));
|
|
194
|
-
|
|
195
|
-
export const getReferenceFeatureKeys = () => featureKeys;
|
|
1160
|
+
export const getReferenceFeatureKeys = () => [...getReferenceState().featureKeys];
|
|
196
1161
|
|
|
197
1162
|
export const getReferenceCustomIdPlaceholder = (pathSegments = []) => {
|
|
1163
|
+
const { referenceOptions } = getReferenceState();
|
|
198
1164
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
199
1165
|
const depth = normalizedPath.length;
|
|
200
1166
|
const placeholders = new Set();
|
|
@@ -219,6 +1185,7 @@ export const getReferenceCustomIdPlaceholder = (pathSegments = []) => {
|
|
|
219
1185
|
};
|
|
220
1186
|
|
|
221
1187
|
export const getReferenceDescendantOptions = (pathSegments = []) => {
|
|
1188
|
+
const { referenceOptions } = getReferenceState();
|
|
222
1189
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
223
1190
|
|
|
224
1191
|
return referenceOptions
|