docusaurus-plugin-generate-schema-docs 1.8.4 → 1.8.5
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 +10 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof-multi.json +12 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof.json +30 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-edge-cases.json +24 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-non-object.json +15 -0
- package/__tests__/generateEventDocs.anchor.test.js +1 -1
- package/__tests__/generateEventDocs.nested.test.js +1 -1
- package/__tests__/generateEventDocs.partials.test.js +1 -1
- package/__tests__/generateEventDocs.test.js +506 -1
- package/__tests__/generateEventDocs.versioned.test.js +1 -1
- package/__tests__/helpers/buildExampleFromSchema.test.js +240 -0
- package/__tests__/helpers/constraintSchemaPaths.test.js +208 -0
- package/__tests__/helpers/continuingLinesStyle.test.js +492 -0
- package/__tests__/helpers/exampleModel.test.js +209 -0
- package/__tests__/helpers/file-system.test.js +73 -1
- package/__tests__/helpers/getConstraints.test.js +27 -0
- package/__tests__/helpers/mergeSchema.test.js +94 -0
- package/__tests__/helpers/processSchema.test.js +291 -1
- package/__tests__/helpers/schema-doc-template.test.js +54 -0
- package/__tests__/helpers/schema-processing.test.js +122 -2
- package/__tests__/helpers/schemaToExamples.test.js +1007 -0
- package/__tests__/helpers/schemaToTableData.mutations.test.js +970 -0
- package/__tests__/helpers/schemaToTableData.test.js +157 -0
- package/__tests__/helpers/snippetTargets.test.js +432 -0
- package/__tests__/helpers/trackingTargets.test.js +319 -0
- package/__tests__/helpers/validator.test.js +385 -1
- package/__tests__/index.test.js +436 -0
- package/__tests__/syncGtm.test.js +139 -3
- package/__tests__/update-schema-ids.test.js +70 -1
- package/__tests__/validateSchemas-integration.test.js +2 -2
- package/__tests__/validateSchemas.test.js +142 -1
- package/generateEventDocs.js +21 -1
- package/helpers/constraintSchemaPaths.js +10 -14
- package/helpers/schemaToTableData.js +538 -492
- package/helpers/trackingTargets.js +26 -3
- package/helpers/validator.js +18 -4
- package/index.js +1 -2
- package/package.json +1 -1
- package/scripts/sync-gtm.js +25 -7
|
@@ -7,8 +7,7 @@ import { getExamples } from './example-helper';
|
|
|
7
7
|
* nested group gets a unique visual position on the right side.
|
|
8
8
|
*/
|
|
9
9
|
function computeOwnBracket(level, parentGroupBrackets) {
|
|
10
|
-
|
|
11
|
-
return { level, bracketIndex };
|
|
10
|
+
return { level, bracketIndex: parentGroupBrackets.length };
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
function materializeConditionalBranchSchema(branchSchema, parentSchema) {
|
|
@@ -30,45 +29,146 @@ function materializeConditionalBranchSchema(branchSchema, parentSchema) {
|
|
|
30
29
|
.map((name) => [name, parentSchema.properties[name]]),
|
|
31
30
|
);
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
...branchSchema,
|
|
39
|
-
type: 'object',
|
|
40
|
-
properties: branchProperties,
|
|
41
|
-
};
|
|
32
|
+
return Object.keys(branchProperties).length === 0
|
|
33
|
+
? branchSchema
|
|
34
|
+
: { ...branchSchema, type: 'object', properties: branchProperties };
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
function hasRenderableAdditionalProperties(schemaNode) {
|
|
45
38
|
return !!(
|
|
46
|
-
schemaNode &&
|
|
47
|
-
schemaNode.additionalProperties &&
|
|
39
|
+
schemaNode?.additionalProperties &&
|
|
48
40
|
typeof schemaNode.additionalProperties === 'object' &&
|
|
49
41
|
!Array.isArray(schemaNode.additionalProperties)
|
|
50
42
|
);
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
function getRenderablePatternProperties(schemaNode) {
|
|
54
|
-
if (!schemaNode?.patternProperties)
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
|
|
46
|
+
if (!schemaNode?.patternProperties) return [];
|
|
58
47
|
return Object.entries(schemaNode.patternProperties)
|
|
59
|
-
.filter(
|
|
60
|
-
|
|
61
|
-
patternSchema &&
|
|
62
|
-
typeof patternSchema === 'object' &&
|
|
63
|
-
!Array.isArray(patternSchema),
|
|
64
|
-
)
|
|
65
|
-
.map(([pattern, patternSchema]) => [
|
|
48
|
+
.filter(([, s]) => s && typeof s === 'object' && !Array.isArray(s))
|
|
49
|
+
.map(([pattern, s]) => [
|
|
66
50
|
`patternProperties /${pattern}/`,
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
51
|
+
{ ...s, 'x-schema-keyword-row': true },
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isEffectivelyEmpty(schemaNode) {
|
|
56
|
+
if (
|
|
57
|
+
schemaNode.type !== 'object' &&
|
|
58
|
+
typeof schemaNode.properties === 'undefined'
|
|
59
|
+
) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (schemaNode.oneOf || schemaNode.anyOf || schemaNode.if) return false;
|
|
63
|
+
if (!schemaNode.properties || Object.keys(schemaNode.properties).length === 0)
|
|
64
|
+
return true;
|
|
65
|
+
return Object.values(schemaNode.properties).every(isEffectivelyEmpty);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- Step 4: extracted containerType resolution ---
|
|
69
|
+
function resolveContainerType(
|
|
70
|
+
propSchema,
|
|
71
|
+
{
|
|
72
|
+
hasNestedProperties,
|
|
73
|
+
hasAdditionalProperties,
|
|
74
|
+
hasArrayItems,
|
|
75
|
+
isChoiceWrapper,
|
|
76
|
+
isConditionalWrapper,
|
|
77
|
+
choiceOptionsAreObjects,
|
|
78
|
+
},
|
|
79
|
+
) {
|
|
80
|
+
if (hasNestedProperties || hasAdditionalProperties) return 'object';
|
|
81
|
+
if (
|
|
82
|
+
isChoiceWrapper &&
|
|
83
|
+
(propSchema.type === 'object' || choiceOptionsAreObjects)
|
|
84
|
+
)
|
|
85
|
+
return 'object';
|
|
86
|
+
if (isConditionalWrapper && propSchema.type === 'object') return 'object';
|
|
87
|
+
if (hasArrayItems) return 'array';
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns whether a property schema has children and what container type it renders as.
|
|
93
|
+
* Also returns flags used for child-building dispatch.
|
|
94
|
+
*/
|
|
95
|
+
function getContainerInfo(propSchema) {
|
|
96
|
+
const isChoiceWrapper = !!(propSchema.oneOf || propSchema.anyOf);
|
|
97
|
+
const isConditionalWrapper = !!(
|
|
98
|
+
propSchema.if &&
|
|
99
|
+
(propSchema.then || propSchema.else)
|
|
100
|
+
);
|
|
101
|
+
const hasNestedProperties = !!propSchema.properties;
|
|
102
|
+
const hasAdditionalProperties = hasRenderableAdditionalProperties(propSchema);
|
|
103
|
+
const hasArrayItems =
|
|
104
|
+
propSchema.type === 'array' &&
|
|
105
|
+
!!(propSchema.items?.properties || propSchema.items?.if);
|
|
106
|
+
const choiceOptionsAreObjects =
|
|
107
|
+
isChoiceWrapper &&
|
|
108
|
+
(propSchema.oneOf || propSchema.anyOf).some(
|
|
109
|
+
(opt) => opt.type === 'object' || opt.properties,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const hasChildren =
|
|
113
|
+
hasNestedProperties ||
|
|
114
|
+
hasAdditionalProperties ||
|
|
115
|
+
hasArrayItems ||
|
|
116
|
+
isChoiceWrapper ||
|
|
117
|
+
isConditionalWrapper;
|
|
118
|
+
|
|
119
|
+
const containerType = resolveContainerType(propSchema, {
|
|
120
|
+
hasNestedProperties,
|
|
121
|
+
hasAdditionalProperties,
|
|
122
|
+
hasArrayItems,
|
|
123
|
+
isChoiceWrapper,
|
|
124
|
+
isConditionalWrapper,
|
|
125
|
+
choiceOptionsAreObjects,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
hasChildren,
|
|
130
|
+
containerType,
|
|
131
|
+
isChoiceWrapper,
|
|
132
|
+
isConditionalWrapper,
|
|
133
|
+
hasArrayItems,
|
|
134
|
+
hasAdditionalProperties,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// --- Step 1: Row factory functions ---
|
|
139
|
+
function makeBaseRow(overrides) {
|
|
140
|
+
return {
|
|
141
|
+
hasChildren: false,
|
|
142
|
+
containerType: null,
|
|
143
|
+
...overrides,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function makePropertyRow(fields) {
|
|
148
|
+
return makeBaseRow({ type: 'property', ...fields });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function makeChoiceRow(fields) {
|
|
152
|
+
return makeBaseRow({ type: 'choice', ...fields });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function makeConditionalRow(fields) {
|
|
156
|
+
return makeBaseRow({ type: 'conditional', ...fields });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// --- Step 2: buildPropEntries helper ---
|
|
160
|
+
function buildPropEntries(subSchema, patternPropertyEntries) {
|
|
161
|
+
const entries = subSchema.properties
|
|
162
|
+
? Object.entries(subSchema.properties)
|
|
163
|
+
: [];
|
|
164
|
+
if (hasRenderableAdditionalProperties(subSchema)) {
|
|
165
|
+
entries.push([
|
|
166
|
+
'additionalProperties',
|
|
167
|
+
{ ...subSchema.additionalProperties, 'x-schema-keyword-row': true },
|
|
71
168
|
]);
|
|
169
|
+
}
|
|
170
|
+
entries.push(...patternPropertyEntries);
|
|
171
|
+
return entries;
|
|
72
172
|
}
|
|
73
173
|
|
|
74
174
|
function processOptions(
|
|
@@ -83,179 +183,111 @@ function processOptions(
|
|
|
83
183
|
) {
|
|
84
184
|
return choices.map((optionSchema, index) => {
|
|
85
185
|
const optionTitle = optionSchema.title || 'Option';
|
|
186
|
+
const isLast = index === choices.length - 1 && choiceIsLastInGroup;
|
|
86
187
|
|
|
87
|
-
|
|
88
|
-
// If it is NOT the last option, its children must not close the visual tree branch.
|
|
89
|
-
const isLastOption = index === choices.length - 1;
|
|
90
|
-
|
|
91
|
-
let optionRows = [];
|
|
92
|
-
|
|
93
|
-
// This is a primitive type (string, number, etc.) within a choice
|
|
188
|
+
let rows;
|
|
94
189
|
if (optionSchema.type && !optionSchema.properties) {
|
|
190
|
+
// Primitive type within a choice
|
|
95
191
|
const isRequired = requiredArray.includes(path[path.length - 1]);
|
|
96
192
|
const constraints = getConstraints(optionSchema);
|
|
97
|
-
if (isRequired)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
constraints: constraints,
|
|
114
|
-
// Keep connector lines open when the enclosing choice block isn't truly last.
|
|
115
|
-
isLastInGroup: isLastOption && choiceIsLastInGroup,
|
|
116
|
-
hasChildren: false,
|
|
117
|
-
containerType: null,
|
|
118
|
-
continuingLevels: [...continuingLevels],
|
|
119
|
-
groupBrackets: [...groupBrackets],
|
|
120
|
-
});
|
|
193
|
+
if (isRequired) constraints.unshift('required');
|
|
194
|
+
rows = [
|
|
195
|
+
makePropertyRow({
|
|
196
|
+
name: path.length > 0 ? path[path.length - 1] : optionTitle,
|
|
197
|
+
path: [...path, `(${optionTitle})`],
|
|
198
|
+
level,
|
|
199
|
+
required: isRequired,
|
|
200
|
+
propertyType: optionSchema.type,
|
|
201
|
+
description: optionSchema.description,
|
|
202
|
+
examples: getExamples(optionSchema),
|
|
203
|
+
constraints,
|
|
204
|
+
isLastInGroup: isLast,
|
|
205
|
+
continuingLevels: [...continuingLevels],
|
|
206
|
+
groupBrackets: [...groupBrackets],
|
|
207
|
+
}),
|
|
208
|
+
];
|
|
121
209
|
} else {
|
|
122
|
-
|
|
123
|
-
optionRows = schemaToTableData(
|
|
210
|
+
rows = schemaToTableData(
|
|
124
211
|
optionSchema,
|
|
125
|
-
// If nested in a property (like payment_method), the sub-properties start at the same level as the choice
|
|
126
|
-
// Otherwise, they are one level deeper.
|
|
127
212
|
level,
|
|
128
213
|
isNestedInProperty ? [] : path,
|
|
129
214
|
continuingLevels,
|
|
130
|
-
|
|
215
|
+
isLast,
|
|
131
216
|
groupBrackets,
|
|
132
217
|
);
|
|
133
218
|
}
|
|
134
219
|
|
|
135
|
-
return {
|
|
136
|
-
title: optionTitle,
|
|
137
|
-
description: optionSchema.description,
|
|
138
|
-
rows: optionRows,
|
|
139
|
-
};
|
|
220
|
+
return { title: optionTitle, description: optionSchema.description, rows };
|
|
140
221
|
});
|
|
141
222
|
}
|
|
142
223
|
|
|
143
|
-
|
|
144
|
-
schema,
|
|
145
|
-
level = 0,
|
|
146
|
-
path = [],
|
|
147
|
-
parentContinuingLevels = [],
|
|
148
|
-
isLastOption = true,
|
|
149
|
-
parentGroupBrackets = [],
|
|
150
|
-
) {
|
|
151
|
-
const flatRows = [];
|
|
224
|
+
// --- Row building (module-level, each function returns its rows) ---
|
|
152
225
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
!schemaNode.properties ||
|
|
165
|
-
Object.keys(schemaNode.properties).length === 0
|
|
166
|
-
) {
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
return Object.values(schemaNode.properties).every(isEffectivelyEmpty);
|
|
170
|
-
}
|
|
226
|
+
function buildConditionalRow(
|
|
227
|
+
subSchema,
|
|
228
|
+
currentLevel,
|
|
229
|
+
currentPath,
|
|
230
|
+
continuingLevels,
|
|
231
|
+
currentGroupBrackets = [],
|
|
232
|
+
ownContinuingLevels,
|
|
233
|
+
conditionalIsLastInGroup = true,
|
|
234
|
+
) {
|
|
235
|
+
const ownBracket = computeOwnBracket(currentLevel, currentGroupBrackets);
|
|
236
|
+
const innerGroupBrackets = [...currentGroupBrackets, ownBracket];
|
|
171
237
|
|
|
172
|
-
|
|
173
|
-
subSchema,
|
|
238
|
+
const conditionRows = schemaToTableData(
|
|
239
|
+
subSchema.if,
|
|
174
240
|
currentLevel,
|
|
175
241
|
currentPath,
|
|
176
242
|
continuingLevels,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
243
|
+
false, // branches always follow condition rows, so they are never "last"
|
|
244
|
+
innerGroupBrackets,
|
|
245
|
+
).map((row) => ({ ...row, isCondition: true }));
|
|
246
|
+
|
|
247
|
+
const hasElse = !!subSchema.else;
|
|
248
|
+
const branches = [];
|
|
249
|
+
|
|
250
|
+
if (subSchema.then) {
|
|
251
|
+
branches.push({
|
|
252
|
+
title: 'Then',
|
|
253
|
+
description: subSchema.then.description,
|
|
254
|
+
rows: schemaToTableData(
|
|
255
|
+
materializeConditionalBranchSchema(subSchema.then, subSchema),
|
|
256
|
+
currentLevel,
|
|
257
|
+
currentPath,
|
|
258
|
+
continuingLevels,
|
|
259
|
+
!hasElse && conditionalIsLastInGroup,
|
|
260
|
+
innerGroupBrackets,
|
|
261
|
+
),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
185
264
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const hasElse = !!subSchema.else;
|
|
201
|
-
|
|
202
|
-
const branches = [];
|
|
203
|
-
if (hasThen) {
|
|
204
|
-
// Then is NOT the last branch if Else exists — use innerContinuingLevels
|
|
205
|
-
// to keep the parent line flowing. If Then IS the last branch, use original.
|
|
206
|
-
const thenLevels = hasElse ? innerContinuingLevels : continuingLevels;
|
|
207
|
-
const thenSchema = materializeConditionalBranchSchema(
|
|
208
|
-
subSchema.then,
|
|
209
|
-
subSchema,
|
|
210
|
-
);
|
|
211
|
-
branches.push({
|
|
212
|
-
title: 'Then',
|
|
213
|
-
description: subSchema.then.description,
|
|
214
|
-
rows: schemaToTableData(
|
|
215
|
-
thenSchema,
|
|
216
|
-
currentLevel,
|
|
217
|
-
currentPath,
|
|
218
|
-
thenLevels,
|
|
219
|
-
// Keep branch connectors open if this conditional block isn't truly last.
|
|
220
|
-
!hasElse && conditionalIsLastInGroup,
|
|
221
|
-
innerGroupBrackets,
|
|
222
|
-
),
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
if (hasElse) {
|
|
226
|
-
// Else is always the last branch — use original continuingLevels
|
|
227
|
-
const elseSchema = materializeConditionalBranchSchema(
|
|
228
|
-
subSchema.else,
|
|
229
|
-
subSchema,
|
|
230
|
-
);
|
|
231
|
-
branches.push({
|
|
232
|
-
title: 'Else',
|
|
233
|
-
description: subSchema.else.description,
|
|
234
|
-
rows: schemaToTableData(
|
|
235
|
-
elseSchema,
|
|
236
|
-
currentLevel,
|
|
237
|
-
currentPath,
|
|
238
|
-
continuingLevels,
|
|
239
|
-
conditionalIsLastInGroup,
|
|
240
|
-
innerGroupBrackets,
|
|
241
|
-
),
|
|
242
|
-
});
|
|
243
|
-
}
|
|
265
|
+
if (hasElse) {
|
|
266
|
+
branches.push({
|
|
267
|
+
title: 'Else',
|
|
268
|
+
description: subSchema.else.description,
|
|
269
|
+
rows: schemaToTableData(
|
|
270
|
+
materializeConditionalBranchSchema(subSchema.else, subSchema),
|
|
271
|
+
currentLevel,
|
|
272
|
+
currentPath,
|
|
273
|
+
continuingLevels,
|
|
274
|
+
conditionalIsLastInGroup,
|
|
275
|
+
innerGroupBrackets,
|
|
276
|
+
),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
244
279
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
: innerContinuingLevels;
|
|
280
|
+
// ownContinuingLevels (when provided) includes currentLevel for the row's
|
|
281
|
+
// header/toggle rendering, since sibling properties' tree lines must continue.
|
|
282
|
+
const rowContinuingLevels = ownContinuingLevels
|
|
283
|
+
? [...new Set([...continuingLevels, ...ownContinuingLevels])]
|
|
284
|
+
: continuingLevels;
|
|
251
285
|
|
|
252
|
-
|
|
253
|
-
|
|
286
|
+
return [
|
|
287
|
+
makeConditionalRow({
|
|
254
288
|
path: [...currentPath, 'if/then/else'],
|
|
255
289
|
level: currentLevel,
|
|
256
290
|
isLastInGroup: conditionalIsLastInGroup,
|
|
257
|
-
hasChildren: false,
|
|
258
|
-
containerType: null,
|
|
259
291
|
continuingLevels: [...rowContinuingLevels],
|
|
260
292
|
groupBrackets: [...currentGroupBrackets],
|
|
261
293
|
condition: {
|
|
@@ -264,335 +296,340 @@ export function schemaToTableData(
|
|
|
264
296
|
rows: conditionRows,
|
|
265
297
|
},
|
|
266
298
|
branches,
|
|
267
|
-
})
|
|
299
|
+
}),
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Dispatches child row building after a property row has been pushed.
|
|
304
|
+
function buildPropertyChildren(
|
|
305
|
+
ctx,
|
|
306
|
+
propSchema,
|
|
307
|
+
currentLevel,
|
|
308
|
+
newPath,
|
|
309
|
+
childContinuingLevels,
|
|
310
|
+
currentGroupBrackets,
|
|
311
|
+
isLast,
|
|
312
|
+
{
|
|
313
|
+
isChoiceWrapper,
|
|
314
|
+
isConditionalWrapper,
|
|
315
|
+
hasArrayItems,
|
|
316
|
+
hasAdditionalProperties,
|
|
317
|
+
},
|
|
318
|
+
) {
|
|
319
|
+
const rows = [];
|
|
320
|
+
|
|
321
|
+
if (propSchema.properties) {
|
|
322
|
+
rows.push(
|
|
323
|
+
...buildRows(
|
|
324
|
+
ctx,
|
|
325
|
+
propSchema,
|
|
326
|
+
currentLevel + 1,
|
|
327
|
+
newPath,
|
|
328
|
+
propSchema.required,
|
|
329
|
+
childContinuingLevels,
|
|
330
|
+
currentGroupBrackets,
|
|
331
|
+
),
|
|
332
|
+
);
|
|
333
|
+
} else if (propSchema.type === 'object' && hasAdditionalProperties) {
|
|
334
|
+
rows.push(
|
|
335
|
+
...buildRows(
|
|
336
|
+
ctx,
|
|
337
|
+
propSchema,
|
|
338
|
+
currentLevel + 1,
|
|
339
|
+
newPath,
|
|
340
|
+
[],
|
|
341
|
+
childContinuingLevels,
|
|
342
|
+
currentGroupBrackets,
|
|
343
|
+
),
|
|
344
|
+
);
|
|
345
|
+
} else if (hasArrayItems) {
|
|
346
|
+
const itemPath = [...newPath, '[n]'];
|
|
347
|
+
if (propSchema.items.properties) {
|
|
348
|
+
rows.push(
|
|
349
|
+
...buildRows(
|
|
350
|
+
ctx,
|
|
351
|
+
propSchema.items,
|
|
352
|
+
currentLevel + 1,
|
|
353
|
+
itemPath,
|
|
354
|
+
propSchema.items.required,
|
|
355
|
+
childContinuingLevels,
|
|
356
|
+
currentGroupBrackets,
|
|
357
|
+
),
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
if (
|
|
361
|
+
propSchema.items.if &&
|
|
362
|
+
(propSchema.items.then || propSchema.items.else)
|
|
363
|
+
) {
|
|
364
|
+
rows.push(
|
|
365
|
+
...buildConditionalRow(
|
|
366
|
+
propSchema.items,
|
|
367
|
+
currentLevel + 1,
|
|
368
|
+
itemPath,
|
|
369
|
+
childContinuingLevels,
|
|
370
|
+
currentGroupBrackets,
|
|
371
|
+
undefined,
|
|
372
|
+
isLast,
|
|
373
|
+
),
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
} else if (isChoiceWrapper) {
|
|
377
|
+
// Complex choice property (e.g. payment_method): property row already pushed,
|
|
378
|
+
// now add the nested choice row.
|
|
379
|
+
const choiceType = propSchema.oneOf ? 'oneOf' : 'anyOf';
|
|
380
|
+
const ownBracket = computeOwnBracket(
|
|
381
|
+
currentLevel + 1,
|
|
382
|
+
currentGroupBrackets,
|
|
383
|
+
);
|
|
384
|
+
const innerBrackets = [...currentGroupBrackets, ownBracket];
|
|
385
|
+
rows.push(
|
|
386
|
+
makeChoiceRow({
|
|
387
|
+
choiceType,
|
|
388
|
+
path: [...newPath, choiceType],
|
|
389
|
+
level: currentLevel + 1,
|
|
390
|
+
title: propSchema.title,
|
|
391
|
+
description: null,
|
|
392
|
+
isLastInGroup: true,
|
|
393
|
+
continuingLevels: childContinuingLevels,
|
|
394
|
+
groupBrackets: [...currentGroupBrackets],
|
|
395
|
+
options: processOptions(
|
|
396
|
+
propSchema[choiceType],
|
|
397
|
+
currentLevel + 1,
|
|
398
|
+
newPath,
|
|
399
|
+
true,
|
|
400
|
+
propSchema.required,
|
|
401
|
+
childContinuingLevels,
|
|
402
|
+
innerBrackets,
|
|
403
|
+
),
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
268
406
|
}
|
|
269
407
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
408
|
+
// Handle if/then/else nested inside a property without its own properties.
|
|
409
|
+
// When propSchema HAS properties, the recursive buildRows call above
|
|
410
|
+
// already handles if/then/else via the root-level check at the end of buildRows.
|
|
411
|
+
if (isConditionalWrapper && !propSchema.properties) {
|
|
412
|
+
rows.push(
|
|
413
|
+
...buildConditionalRow(
|
|
414
|
+
propSchema,
|
|
415
|
+
currentLevel + 1,
|
|
416
|
+
newPath,
|
|
417
|
+
childContinuingLevels,
|
|
418
|
+
currentGroupBrackets,
|
|
419
|
+
undefined,
|
|
420
|
+
isLast,
|
|
421
|
+
),
|
|
422
|
+
);
|
|
423
|
+
}
|
|
279
424
|
|
|
280
|
-
|
|
425
|
+
return rows;
|
|
426
|
+
}
|
|
281
427
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
428
|
+
function buildPropertyRows(
|
|
429
|
+
ctx,
|
|
430
|
+
subSchema,
|
|
431
|
+
currentLevel,
|
|
432
|
+
currentPath,
|
|
433
|
+
requiredFromParent,
|
|
434
|
+
continuingLevels,
|
|
435
|
+
currentGroupBrackets,
|
|
436
|
+
visiblePropEntries,
|
|
437
|
+
hasSiblingChoices,
|
|
438
|
+
) {
|
|
439
|
+
const rows = [];
|
|
440
|
+
|
|
441
|
+
visiblePropEntries.forEach(([name, propSchema], index) => {
|
|
442
|
+
const newPath = [...currentPath, name];
|
|
443
|
+
const isLastProp =
|
|
444
|
+
index === visiblePropEntries.length - 1 && !hasSiblingChoices;
|
|
445
|
+
const isLast =
|
|
446
|
+
isLastProp && (currentLevel !== ctx.topLevel || ctx.isLastOption);
|
|
447
|
+
|
|
448
|
+
// If this is not the last item, add currentLevel to continuing levels for children
|
|
449
|
+
// so ancestor tree lines keep flowing through all descendants.
|
|
450
|
+
const childContinuingLevels = isLast
|
|
451
|
+
? [...continuingLevels]
|
|
452
|
+
: [...continuingLevels, currentLevel];
|
|
453
|
+
|
|
454
|
+
const {
|
|
455
|
+
hasChildren,
|
|
456
|
+
containerType,
|
|
457
|
+
isChoiceWrapper,
|
|
458
|
+
isConditionalWrapper,
|
|
459
|
+
hasArrayItems,
|
|
460
|
+
hasAdditionalProperties,
|
|
461
|
+
} = getContainerInfo(propSchema);
|
|
462
|
+
|
|
463
|
+
// A "simple" choice has scalar options (no nested properties) and renders inline.
|
|
464
|
+
// A "complex" choice has object options and needs its own property row first.
|
|
465
|
+
const isSimpleChoice = isChoiceWrapper && containerType === null;
|
|
466
|
+
|
|
467
|
+
if (isSimpleChoice) {
|
|
468
|
+
const choiceType = propSchema.oneOf ? 'oneOf' : 'anyOf';
|
|
469
|
+
const ownBracket = computeOwnBracket(currentLevel, currentGroupBrackets);
|
|
470
|
+
rows.push(
|
|
471
|
+
makeChoiceRow({
|
|
472
|
+
choiceType,
|
|
473
|
+
name,
|
|
474
|
+
path: newPath,
|
|
475
|
+
level: currentLevel,
|
|
476
|
+
title: propSchema.title,
|
|
477
|
+
description: propSchema.description,
|
|
478
|
+
isLastInGroup: isLast,
|
|
479
|
+
continuingLevels: [...continuingLevels],
|
|
480
|
+
groupBrackets: [...currentGroupBrackets],
|
|
481
|
+
options: processOptions(
|
|
482
|
+
propSchema[choiceType],
|
|
483
|
+
currentLevel,
|
|
484
|
+
newPath,
|
|
485
|
+
false,
|
|
486
|
+
subSchema.required || requiredFromParent,
|
|
487
|
+
childContinuingLevels,
|
|
488
|
+
[...currentGroupBrackets, ownBracket],
|
|
489
|
+
isLast,
|
|
490
|
+
),
|
|
491
|
+
}),
|
|
492
|
+
);
|
|
493
|
+
} else {
|
|
494
|
+
const isRequired =
|
|
495
|
+
(subSchema.required || requiredFromParent)?.includes(name) || false;
|
|
496
|
+
const constraints = getConstraints(propSchema);
|
|
497
|
+
if (isRequired) constraints.unshift('required');
|
|
498
|
+
|
|
499
|
+
rows.push(
|
|
500
|
+
makePropertyRow({
|
|
501
|
+
name,
|
|
502
|
+
path: newPath,
|
|
503
|
+
level: currentLevel,
|
|
504
|
+
required: isRequired,
|
|
505
|
+
propertyType:
|
|
506
|
+
propSchema.type || (propSchema.enum ? 'enum' : 'object'),
|
|
507
|
+
description: propSchema.description,
|
|
508
|
+
examples: getExamples(propSchema),
|
|
509
|
+
constraints,
|
|
510
|
+
isLastInGroup: isLast,
|
|
511
|
+
hasChildren,
|
|
512
|
+
containerType,
|
|
513
|
+
continuingLevels: [...continuingLevels],
|
|
514
|
+
groupBrackets: [...currentGroupBrackets],
|
|
515
|
+
isSchemaKeywordRow: propSchema['x-schema-keyword-row'] === true,
|
|
516
|
+
keepConnectorOpen: propSchema['x-keep-connector-open'] === true,
|
|
517
|
+
}),
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
rows.push(
|
|
521
|
+
...buildPropertyChildren(
|
|
522
|
+
ctx,
|
|
523
|
+
propSchema,
|
|
524
|
+
currentLevel,
|
|
525
|
+
newPath,
|
|
526
|
+
childContinuingLevels,
|
|
527
|
+
currentGroupBrackets,
|
|
528
|
+
isLast,
|
|
293
529
|
{
|
|
294
|
-
|
|
295
|
-
|
|
530
|
+
isChoiceWrapper,
|
|
531
|
+
isConditionalWrapper,
|
|
532
|
+
hasArrayItems,
|
|
533
|
+
hasAdditionalProperties,
|
|
296
534
|
},
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
propEntries.push(...patternPropertyEntries);
|
|
300
|
-
const hasSiblingChoices = !!(
|
|
301
|
-
subSchema.oneOf ||
|
|
302
|
-
subSchema.anyOf ||
|
|
303
|
-
subSchema.if
|
|
535
|
+
),
|
|
304
536
|
);
|
|
305
|
-
|
|
306
|
-
// Filter out properties that should be skipped to get accurate count
|
|
307
|
-
const visiblePropEntries = propEntries.filter(([name, propSchema]) => {
|
|
308
|
-
return !(
|
|
309
|
-
propSchema['x-gtm-clear'] === true && isEffectivelyEmpty(propSchema)
|
|
310
|
-
);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
visiblePropEntries.forEach(([name, propSchema], index) => {
|
|
314
|
-
const newPath = [...currentPath, name];
|
|
315
|
-
|
|
316
|
-
const isLastProp =
|
|
317
|
-
index === visiblePropEntries.length - 1 && !hasSiblingChoices;
|
|
318
|
-
|
|
319
|
-
// Updated Logic:
|
|
320
|
-
// A property is visually "last" only if it is the last property
|
|
321
|
-
// AND (it is deeper in the hierarchy OR the parent option itself is the last one).
|
|
322
|
-
const isLast = isLastProp && (currentLevel !== level || isLastOption);
|
|
323
|
-
|
|
324
|
-
const isChoiceWrapper = !!(propSchema.oneOf || propSchema.anyOf);
|
|
325
|
-
const isConditionalWrapper = !!(
|
|
326
|
-
propSchema.if &&
|
|
327
|
-
(propSchema.then || propSchema.else)
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
// Determine if this property has children and what type
|
|
331
|
-
const hasNestedProperties = !!propSchema.properties;
|
|
332
|
-
const hasAdditionalProperties =
|
|
333
|
-
hasRenderableAdditionalProperties(propSchema);
|
|
334
|
-
const hasArrayItems =
|
|
335
|
-
propSchema.type === 'array' &&
|
|
336
|
-
!!(propSchema.items?.properties || propSchema.items?.if);
|
|
337
|
-
const hasNestedChoice = isChoiceWrapper;
|
|
338
|
-
const hasNestedConditional = isConditionalWrapper;
|
|
339
|
-
const hasChildren =
|
|
340
|
-
hasNestedProperties ||
|
|
341
|
-
hasAdditionalProperties ||
|
|
342
|
-
hasArrayItems ||
|
|
343
|
-
hasNestedChoice ||
|
|
344
|
-
hasNestedConditional;
|
|
345
|
-
|
|
346
|
-
// Determine container type for the symbol
|
|
347
|
-
let containerType = null;
|
|
348
|
-
const choiceOptions = propSchema.oneOf || propSchema.anyOf || [];
|
|
349
|
-
const choiceOptionsAreObjects =
|
|
350
|
-
isChoiceWrapper &&
|
|
351
|
-
choiceOptions.some((opt) => opt.type === 'object' || opt.properties);
|
|
352
|
-
if (
|
|
353
|
-
hasNestedProperties ||
|
|
354
|
-
hasAdditionalProperties ||
|
|
355
|
-
(isChoiceWrapper && propSchema.type === 'object') ||
|
|
356
|
-
(isConditionalWrapper && propSchema.type === 'object') ||
|
|
357
|
-
choiceOptionsAreObjects
|
|
358
|
-
) {
|
|
359
|
-
containerType = 'object';
|
|
360
|
-
} else if (hasArrayItems) {
|
|
361
|
-
containerType = 'array';
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Calculate continuing levels for children
|
|
365
|
-
// If this is not the last item, add current level to continuing levels for children
|
|
366
|
-
// If this IS the last item, don't add currentLevel (no more siblings at this level).
|
|
367
|
-
// We keep all existing continuingLevels intact — they represent ancestor lines
|
|
368
|
-
// that must continue through all descendants regardless of last-child status.
|
|
369
|
-
const childContinuingLevels = isLast
|
|
370
|
-
? [...continuingLevels]
|
|
371
|
-
: [...continuingLevels, currentLevel];
|
|
372
|
-
|
|
373
|
-
// A "simple" choice property like user_id: { oneOf: [{ type: "string" }, { type: "integer" }] }
|
|
374
|
-
// where the options are scalar types (no nested properties). These get unwrapped
|
|
375
|
-
// into a choice row directly without their own property row.
|
|
376
|
-
// In contrast, choice wrappers whose options are objects with properties
|
|
377
|
-
// (like contact_method) need their own property row to start a nesting level.
|
|
378
|
-
const isSimpleChoice =
|
|
379
|
-
isChoiceWrapper &&
|
|
380
|
-
!propSchema.properties &&
|
|
381
|
-
propSchema.type !== 'object' &&
|
|
382
|
-
!(propSchema.oneOf || propSchema.anyOf).some((opt) => opt.properties);
|
|
383
|
-
|
|
384
|
-
if (isSimpleChoice) {
|
|
385
|
-
const choiceType = propSchema.oneOf ? 'oneOf' : 'anyOf';
|
|
386
|
-
const choices = propSchema[choiceType];
|
|
387
|
-
const ownBracket = computeOwnBracket(
|
|
388
|
-
currentLevel,
|
|
389
|
-
currentGroupBrackets,
|
|
390
|
-
);
|
|
391
|
-
const innerGroupBrackets = [...currentGroupBrackets, ownBracket];
|
|
392
|
-
flatRows.push({
|
|
393
|
-
type: 'choice',
|
|
394
|
-
choiceType,
|
|
395
|
-
name,
|
|
396
|
-
path: newPath,
|
|
397
|
-
level: currentLevel,
|
|
398
|
-
title: propSchema.title,
|
|
399
|
-
description: propSchema.description,
|
|
400
|
-
isLastInGroup: isLast,
|
|
401
|
-
hasChildren: false,
|
|
402
|
-
containerType: null,
|
|
403
|
-
continuingLevels: [...continuingLevels],
|
|
404
|
-
groupBrackets: [...currentGroupBrackets],
|
|
405
|
-
options: processOptions(
|
|
406
|
-
choices,
|
|
407
|
-
currentLevel,
|
|
408
|
-
newPath,
|
|
409
|
-
false,
|
|
410
|
-
subSchema.required || requiredFromParent,
|
|
411
|
-
childContinuingLevels,
|
|
412
|
-
innerGroupBrackets,
|
|
413
|
-
isLast,
|
|
414
|
-
),
|
|
415
|
-
});
|
|
416
|
-
} else {
|
|
417
|
-
// This is a "normal" property or a complex one with a nested choice.
|
|
418
|
-
const isRequired =
|
|
419
|
-
(subSchema.required || requiredFromParent)?.includes(name) || false;
|
|
420
|
-
const constraints = getConstraints(propSchema);
|
|
421
|
-
if (isRequired) {
|
|
422
|
-
constraints.unshift('required');
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
flatRows.push({
|
|
426
|
-
type: 'property',
|
|
427
|
-
name,
|
|
428
|
-
path: newPath,
|
|
429
|
-
level: currentLevel,
|
|
430
|
-
required: isRequired,
|
|
431
|
-
propertyType:
|
|
432
|
-
propSchema.type || (propSchema.enum ? 'enum' : 'object'),
|
|
433
|
-
description: propSchema.description,
|
|
434
|
-
examples: getExamples(propSchema),
|
|
435
|
-
constraints,
|
|
436
|
-
isLastInGroup: isLast,
|
|
437
|
-
hasChildren,
|
|
438
|
-
containerType,
|
|
439
|
-
continuingLevels: [...continuingLevels],
|
|
440
|
-
groupBrackets: [...currentGroupBrackets],
|
|
441
|
-
isSchemaKeywordRow: propSchema['x-schema-keyword-row'] === true,
|
|
442
|
-
keepConnectorOpen: propSchema['x-keep-connector-open'] === true,
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
if (propSchema.properties) {
|
|
446
|
-
buildRows(
|
|
447
|
-
propSchema,
|
|
448
|
-
currentLevel + 1,
|
|
449
|
-
newPath,
|
|
450
|
-
propSchema.required,
|
|
451
|
-
childContinuingLevels,
|
|
452
|
-
currentGroupBrackets,
|
|
453
|
-
);
|
|
454
|
-
} else if (propSchema.type === 'object' && hasAdditionalProperties) {
|
|
455
|
-
buildRows(
|
|
456
|
-
propSchema,
|
|
457
|
-
currentLevel + 1,
|
|
458
|
-
newPath,
|
|
459
|
-
[],
|
|
460
|
-
childContinuingLevels,
|
|
461
|
-
currentGroupBrackets,
|
|
462
|
-
);
|
|
463
|
-
} else if (
|
|
464
|
-
propSchema.type === 'array' &&
|
|
465
|
-
(propSchema.items?.properties || propSchema.items?.if)
|
|
466
|
-
) {
|
|
467
|
-
if (propSchema.items.properties) {
|
|
468
|
-
buildRows(
|
|
469
|
-
propSchema.items,
|
|
470
|
-
currentLevel + 1,
|
|
471
|
-
[...newPath, '[n]'],
|
|
472
|
-
propSchema.items.required,
|
|
473
|
-
childContinuingLevels,
|
|
474
|
-
currentGroupBrackets,
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
// Handle if/then/else inside array items
|
|
478
|
-
if (
|
|
479
|
-
propSchema.items.if &&
|
|
480
|
-
(propSchema.items.then || propSchema.items.else)
|
|
481
|
-
) {
|
|
482
|
-
buildConditionalRow(
|
|
483
|
-
propSchema.items,
|
|
484
|
-
currentLevel + 1,
|
|
485
|
-
[...newPath, '[n]'],
|
|
486
|
-
childContinuingLevels,
|
|
487
|
-
currentGroupBrackets,
|
|
488
|
-
undefined,
|
|
489
|
-
isLast,
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
} else if (isChoiceWrapper) {
|
|
493
|
-
// This handles the "complex" choice property like payment_method.
|
|
494
|
-
// A property row has already been created above, now we add the choice row.
|
|
495
|
-
const choiceType = propSchema.oneOf ? 'oneOf' : 'anyOf';
|
|
496
|
-
const choices = propSchema[choiceType];
|
|
497
|
-
const complexOwnBracket = computeOwnBracket(
|
|
498
|
-
currentLevel + 1,
|
|
499
|
-
currentGroupBrackets,
|
|
500
|
-
);
|
|
501
|
-
const complexInnerBrackets = [
|
|
502
|
-
...currentGroupBrackets,
|
|
503
|
-
complexOwnBracket,
|
|
504
|
-
];
|
|
505
|
-
flatRows.push({
|
|
506
|
-
type: 'choice',
|
|
507
|
-
choiceType,
|
|
508
|
-
path: [...newPath, choiceType], // Make path unique
|
|
509
|
-
level: currentLevel + 1,
|
|
510
|
-
title: propSchema.title,
|
|
511
|
-
description: null,
|
|
512
|
-
isLastInGroup: true,
|
|
513
|
-
hasChildren: false,
|
|
514
|
-
containerType: null,
|
|
515
|
-
continuingLevels: childContinuingLevels,
|
|
516
|
-
groupBrackets: [...currentGroupBrackets],
|
|
517
|
-
options: processOptions(
|
|
518
|
-
choices,
|
|
519
|
-
currentLevel + 1,
|
|
520
|
-
newPath,
|
|
521
|
-
true,
|
|
522
|
-
propSchema.required,
|
|
523
|
-
childContinuingLevels,
|
|
524
|
-
complexInnerBrackets,
|
|
525
|
-
),
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Handle if/then/else nested inside a property without its own properties.
|
|
530
|
-
// When propSchema HAS properties, the recursive buildRows call above
|
|
531
|
-
// already handles if/then/else via the root-level check at the end of buildRows.
|
|
532
|
-
if (isConditionalWrapper && !propSchema.properties) {
|
|
533
|
-
buildConditionalRow(
|
|
534
|
-
propSchema,
|
|
535
|
-
currentLevel + 1,
|
|
536
|
-
newPath,
|
|
537
|
-
childContinuingLevels,
|
|
538
|
-
currentGroupBrackets,
|
|
539
|
-
undefined,
|
|
540
|
-
isLast,
|
|
541
|
-
);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
537
|
}
|
|
538
|
+
});
|
|
546
539
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
540
|
+
return rows;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function buildRows(
|
|
544
|
+
ctx,
|
|
545
|
+
subSchema,
|
|
546
|
+
currentLevel,
|
|
547
|
+
currentPath,
|
|
548
|
+
requiredFromParent = [],
|
|
549
|
+
continuingLevels = [],
|
|
550
|
+
currentGroupBrackets = [],
|
|
551
|
+
) {
|
|
552
|
+
if (!subSchema) return [];
|
|
553
|
+
|
|
554
|
+
const rows = [];
|
|
555
|
+
const patternPropertyEntries = getRenderablePatternProperties(subSchema);
|
|
556
|
+
const hasAnyProperties =
|
|
557
|
+
subSchema.properties ||
|
|
558
|
+
hasRenderableAdditionalProperties(subSchema) ||
|
|
559
|
+
patternPropertyEntries.length > 0;
|
|
560
|
+
|
|
561
|
+
if (hasAnyProperties) {
|
|
562
|
+
const propEntries = buildPropEntries(subSchema, patternPropertyEntries);
|
|
563
|
+
const hasSiblingChoices = !!(
|
|
564
|
+
subSchema.oneOf ||
|
|
565
|
+
subSchema.anyOf ||
|
|
566
|
+
subSchema.if
|
|
567
|
+
);
|
|
568
|
+
const visiblePropEntries = propEntries.filter(
|
|
569
|
+
([, propSchema]) =>
|
|
570
|
+
!(propSchema['x-gtm-clear'] === true && isEffectivelyEmpty(propSchema)),
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
rows.push(
|
|
574
|
+
...buildPropertyRows(
|
|
575
|
+
ctx,
|
|
576
|
+
subSchema,
|
|
577
|
+
currentLevel,
|
|
578
|
+
currentPath,
|
|
579
|
+
requiredFromParent,
|
|
580
|
+
continuingLevels,
|
|
581
|
+
currentGroupBrackets,
|
|
582
|
+
visiblePropEntries,
|
|
583
|
+
hasSiblingChoices,
|
|
584
|
+
),
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// When properties coexist with root-level choices or conditionals,
|
|
589
|
+
// the header/toggle rows need the tree line at currentLevel to continue.
|
|
590
|
+
// Only used for the row's own continuingLevels — NOT propagated to inner rows.
|
|
591
|
+
const hasProperties =
|
|
592
|
+
subSchema.properties && Object.keys(subSchema.properties).length > 0;
|
|
593
|
+
const ownContinuingLevels =
|
|
594
|
+
hasProperties && !continuingLevels.includes(currentLevel)
|
|
595
|
+
? [...continuingLevels, currentLevel]
|
|
596
|
+
: [...continuingLevels];
|
|
597
|
+
|
|
598
|
+
const choiceType = subSchema.oneOf
|
|
599
|
+
? 'oneOf'
|
|
600
|
+
: subSchema.anyOf
|
|
601
|
+
? 'anyOf'
|
|
602
|
+
: null;
|
|
603
|
+
if (choiceType) {
|
|
604
|
+
const ownBracket = computeOwnBracket(currentLevel, currentGroupBrackets);
|
|
605
|
+
const choiceIsLastInGroup =
|
|
606
|
+
ctx.isLastOption && !(subSchema.if && (subSchema.then || subSchema.else));
|
|
607
|
+
rows.push(
|
|
608
|
+
makeChoiceRow({
|
|
571
609
|
choiceType,
|
|
572
610
|
path: currentPath,
|
|
573
611
|
level: currentLevel,
|
|
574
612
|
title: subSchema.title,
|
|
575
613
|
description: subSchema.description,
|
|
576
614
|
isLastInGroup: choiceIsLastInGroup,
|
|
577
|
-
hasChildren: false,
|
|
578
|
-
containerType: null,
|
|
579
615
|
continuingLevels: [...ownContinuingLevels],
|
|
580
616
|
groupBrackets: [...currentGroupBrackets],
|
|
581
617
|
options: processOptions(
|
|
582
|
-
|
|
618
|
+
subSchema[choiceType],
|
|
583
619
|
currentLevel,
|
|
584
620
|
currentPath,
|
|
585
621
|
false,
|
|
586
622
|
subSchema.required || requiredFromParent,
|
|
587
623
|
continuingLevels,
|
|
588
|
-
|
|
624
|
+
[...currentGroupBrackets, ownBracket],
|
|
589
625
|
choiceIsLastInGroup,
|
|
590
626
|
),
|
|
591
|
-
})
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
627
|
+
}),
|
|
628
|
+
);
|
|
629
|
+
} else if (!subSchema.properties && subSchema.type) {
|
|
630
|
+
// Root-level primitive schema
|
|
631
|
+
rows.push(
|
|
632
|
+
makePropertyRow({
|
|
596
633
|
name: subSchema.title || '<value>',
|
|
597
634
|
path: currentPath,
|
|
598
635
|
level: currentLevel,
|
|
@@ -602,30 +639,40 @@ export function schemaToTableData(
|
|
|
602
639
|
examples: getExamples(subSchema),
|
|
603
640
|
constraints: getConstraints(subSchema),
|
|
604
641
|
isLastInGroup: true,
|
|
605
|
-
hasChildren: false,
|
|
606
|
-
containerType: null,
|
|
607
642
|
continuingLevels: [...continuingLevels],
|
|
608
643
|
groupBrackets: [...currentGroupBrackets],
|
|
609
|
-
})
|
|
610
|
-
|
|
644
|
+
}),
|
|
645
|
+
);
|
|
646
|
+
}
|
|
611
647
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
// Inner rows (condition, branches) use the original continuingLevels.
|
|
616
|
-
buildConditionalRow(
|
|
648
|
+
if (subSchema.if && (subSchema.then || subSchema.else)) {
|
|
649
|
+
rows.push(
|
|
650
|
+
...buildConditionalRow(
|
|
617
651
|
subSchema,
|
|
618
652
|
currentLevel,
|
|
619
653
|
currentPath,
|
|
620
654
|
continuingLevels,
|
|
621
655
|
currentGroupBrackets,
|
|
622
656
|
hasProperties ? [...ownContinuingLevels] : undefined,
|
|
623
|
-
isLastOption,
|
|
624
|
-
)
|
|
625
|
-
|
|
657
|
+
ctx.isLastOption,
|
|
658
|
+
),
|
|
659
|
+
);
|
|
626
660
|
}
|
|
627
661
|
|
|
628
|
-
|
|
662
|
+
return rows;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export function schemaToTableData(
|
|
666
|
+
schema,
|
|
667
|
+
level = 0,
|
|
668
|
+
path = [],
|
|
669
|
+
parentContinuingLevels = [],
|
|
670
|
+
isLastOption = true,
|
|
671
|
+
parentGroupBrackets = [],
|
|
672
|
+
) {
|
|
673
|
+
const ctx = { topLevel: level, isLastOption };
|
|
674
|
+
return buildRows(
|
|
675
|
+
ctx,
|
|
629
676
|
schema,
|
|
630
677
|
level,
|
|
631
678
|
path,
|
|
@@ -633,5 +680,4 @@ export function schemaToTableData(
|
|
|
633
680
|
parentContinuingLevels,
|
|
634
681
|
parentGroupBrackets,
|
|
635
682
|
);
|
|
636
|
-
return flatRows;
|
|
637
683
|
}
|