@datenlotse/jsonjoy-builder 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/components/SchemaEditor/AddFieldButton.js +312 -0
- package/dist/components/SchemaEditor/JsonSchemaEditor.js +166 -0
- package/dist/components/SchemaEditor/JsonSchemaVisualizer.js +96 -0
- package/dist/components/SchemaEditor/SchemaField.js +104 -0
- package/dist/components/SchemaEditor/SchemaFieldList.js +84 -0
- package/dist/components/SchemaEditor/SchemaPropertyEditor.js +249 -0
- package/dist/components/SchemaEditor/SchemaTypeSelector.js +55 -0
- package/dist/components/SchemaEditor/SchemaVisualEditor.js +77 -0
- package/dist/components/SchemaEditor/TypeDropdown.js +72 -0
- package/dist/components/SchemaEditor/TypeEditor.js +71 -0
- package/dist/components/SchemaEditor/types/ArrayEditor.js +173 -0
- package/dist/components/SchemaEditor/types/BooleanEditor.js +107 -0
- package/dist/components/SchemaEditor/types/NumberEditor.js +583 -0
- package/dist/components/SchemaEditor/types/ObjectEditor.js +90 -0
- package/dist/components/SchemaEditor/types/StringEditor.js +542 -0
- package/dist/components/features/JsonValidator.js +239 -0
- package/dist/components/features/SchemaInferencer.js +107 -0
- package/dist/components/ui/badge.js +25 -0
- package/dist/components/ui/button.js +41 -0
- package/dist/components/ui/dialog.js +73 -0
- package/dist/components/ui/input.js +11 -0
- package/dist/components/ui/label.js +13 -0
- package/dist/components/ui/select.js +90 -0
- package/dist/components/ui/switch.js +14 -0
- package/dist/components/ui/tabs.js +24 -0
- package/dist/components/ui/tooltip.js +15 -0
- package/dist/hooks/use-monaco-theme.js +197 -0
- package/dist/hooks/use-translation.js +14 -0
- package/dist/i18n/locales/de.js +143 -0
- package/dist/i18n/locales/en.js +143 -0
- package/dist/i18n/locales/es.js +143 -0
- package/dist/i18n/locales/fr.js +143 -0
- package/dist/i18n/locales/ru.js +143 -0
- package/dist/i18n/locales/uk.js +143 -0
- package/dist/i18n/locales/zh.js +143 -0
- package/dist/i18n/translation-context.js +4 -0
- package/dist/i18n/translation-keys.js +0 -0
- package/dist/index.css +3830 -0
- package/dist/index.d.ts +995 -0
- package/dist/index.js +10 -0
- package/dist/lib/schema-inference.js +266 -0
- package/dist/lib/schemaCompile.js +113 -0
- package/dist/lib/schemaEditor.js +167 -0
- package/dist/lib/utils.js +40 -0
- package/dist/types/jsonSchema.js +98 -0
- package/dist/types/validation.js +215 -0
- package/dist/utils/jsonValidator.js +162 -0
- package/package.json +112 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import JsonSchemaEditor from "./components/SchemaEditor/JsonSchemaEditor.js";
|
|
2
|
+
import JsonSchemaVisualizer from "./components/SchemaEditor/JsonSchemaVisualizer.js";
|
|
3
|
+
import SchemaVisualEditor from "./components/SchemaEditor/SchemaVisualEditor.js";
|
|
4
|
+
export * from "./components/features/JsonValidator.js";
|
|
5
|
+
export * from "./components/features/SchemaInferencer.js";
|
|
6
|
+
export * from "./i18n/locales/de.js";
|
|
7
|
+
export * from "./i18n/locales/en.js";
|
|
8
|
+
export * from "./i18n/translation-context.js";
|
|
9
|
+
export * from "./i18n/translation-keys.js";
|
|
10
|
+
export { JsonSchemaEditor, JsonSchemaVisualizer, SchemaVisualEditor };
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { asObjectSchema } from "../types/jsonSchema.js";
|
|
2
|
+
function mergeSchemas(schema1, schema2) {
|
|
3
|
+
const s1 = asObjectSchema(schema1);
|
|
4
|
+
const s2 = asObjectSchema(schema2);
|
|
5
|
+
if (JSON.stringify(s1) === JSON.stringify(s2)) return schema1;
|
|
6
|
+
if ("integer" === s1.type && "number" === s2.type) return {
|
|
7
|
+
type: "number"
|
|
8
|
+
};
|
|
9
|
+
if ("number" === s1.type && "integer" === s2.type) return {
|
|
10
|
+
type: "number"
|
|
11
|
+
};
|
|
12
|
+
const existingOneOf = Array.isArray(s1.oneOf) ? s1.oneOf : [
|
|
13
|
+
s1
|
|
14
|
+
];
|
|
15
|
+
const newSchemaToAdd = s2;
|
|
16
|
+
if (!existingOneOf.some((s)=>JSON.stringify(s) === JSON.stringify(newSchemaToAdd))) {
|
|
17
|
+
const mergedOneOf = [
|
|
18
|
+
...existingOneOf,
|
|
19
|
+
newSchemaToAdd
|
|
20
|
+
];
|
|
21
|
+
const uniqueSchemas = [
|
|
22
|
+
...new Map(mergedOneOf.map((s)=>[
|
|
23
|
+
JSON.stringify(s),
|
|
24
|
+
s
|
|
25
|
+
])).values()
|
|
26
|
+
];
|
|
27
|
+
if (1 === uniqueSchemas.length) return uniqueSchemas[0];
|
|
28
|
+
return {
|
|
29
|
+
oneOf: uniqueSchemas
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return s1.oneOf ? s1 : {
|
|
33
|
+
oneOf: [
|
|
34
|
+
s1
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function inferObjectSchema(obj) {
|
|
39
|
+
const properties = {};
|
|
40
|
+
const required = [];
|
|
41
|
+
for (const [key, value] of Object.entries(obj)){
|
|
42
|
+
properties[key] = inferSchema(value);
|
|
43
|
+
if (null != value) required.push(key);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
type: "object",
|
|
47
|
+
properties,
|
|
48
|
+
required: required.length > 0 ? required.sort() : void 0
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function detectEnumsInArrayItems(mergedProperties, originalArray, totalItems) {
|
|
52
|
+
if (totalItems < 10 || 0 === Object.keys(mergedProperties).length) return mergedProperties;
|
|
53
|
+
const valueMap = {};
|
|
54
|
+
for (const item of originalArray)for(const key in mergedProperties)if (Object.prototype.hasOwnProperty.call(item, key)) {
|
|
55
|
+
const value = item[key];
|
|
56
|
+
if ("string" == typeof value || "number" == typeof value) {
|
|
57
|
+
if (!valueMap[key]) valueMap[key] = new Set();
|
|
58
|
+
valueMap[key].add(value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const updatedProperties = {
|
|
62
|
+
...mergedProperties
|
|
63
|
+
};
|
|
64
|
+
for(const key in valueMap){
|
|
65
|
+
const distinctValues = Array.from(valueMap[key]);
|
|
66
|
+
if (distinctValues.length > 1 && distinctValues.length <= 10 && distinctValues.length < totalItems / 2) {
|
|
67
|
+
const currentSchema = asObjectSchema(updatedProperties[key]);
|
|
68
|
+
if ("string" === currentSchema.type || "number" === currentSchema.type || "integer" === currentSchema.type) updatedProperties[key] = {
|
|
69
|
+
type: currentSchema.type,
|
|
70
|
+
enum: distinctValues.sort()
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return updatedProperties;
|
|
75
|
+
}
|
|
76
|
+
function detectSemanticFormatsInArrayItems(mergedProperties, originalArray) {
|
|
77
|
+
const updatedProperties = {
|
|
78
|
+
...mergedProperties
|
|
79
|
+
};
|
|
80
|
+
for(const key in updatedProperties){
|
|
81
|
+
const currentSchema = asObjectSchema(updatedProperties[key]);
|
|
82
|
+
if (/coordinates?|coords?|latLon|lonLat|point/i.test(key) && "array" === currentSchema.type) {
|
|
83
|
+
const itemsSchema = asObjectSchema(currentSchema.items);
|
|
84
|
+
if (itemsSchema?.type === "number" || itemsSchema?.type === "integer") {
|
|
85
|
+
let isValidCoordArray = true;
|
|
86
|
+
let coordLength = null;
|
|
87
|
+
for (const item of originalArray)if (Object.prototype.hasOwnProperty.call(item, key) && Array.isArray(item[key])) {
|
|
88
|
+
const arr = item[key];
|
|
89
|
+
if (null === coordLength) coordLength = arr.length;
|
|
90
|
+
if (arr.length !== coordLength || 2 !== arr.length && 3 !== arr.length || !arr.every((v)=>"number" == typeof v)) {
|
|
91
|
+
isValidCoordArray = false;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
} else if (Object.prototype.hasOwnProperty.call(item, key)) {
|
|
95
|
+
isValidCoordArray = false;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
if (isValidCoordArray && null !== coordLength) updatedProperties[key] = {
|
|
99
|
+
type: "array",
|
|
100
|
+
items: {
|
|
101
|
+
type: "number"
|
|
102
|
+
},
|
|
103
|
+
minItems: coordLength,
|
|
104
|
+
maxItems: coordLength
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (/timestamp|createdAt|updatedAt|occurredAt/i.test(key) && "integer" === currentSchema.type) {
|
|
109
|
+
let isTimestampLike = true;
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
const fiftyYearsAgo = now - 1576800000000;
|
|
112
|
+
for (const item of originalArray)if (Object.prototype.hasOwnProperty.call(item, key)) {
|
|
113
|
+
const val = item[key];
|
|
114
|
+
if ("number" != typeof val || !Number.isInteger(val) || val < fiftyYearsAgo) {
|
|
115
|
+
isTimestampLike = false;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (isTimestampLike) updatedProperties[key] = {
|
|
120
|
+
type: "integer",
|
|
121
|
+
format: "unix-timestamp",
|
|
122
|
+
description: "Unix timestamp (likely milliseconds)"
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return updatedProperties;
|
|
127
|
+
}
|
|
128
|
+
function processArrayOfObjects(itemSchemas, originalArray) {
|
|
129
|
+
let mergedProperties = {};
|
|
130
|
+
const propertyCounts = {};
|
|
131
|
+
const totalItems = itemSchemas.length;
|
|
132
|
+
for (const schema of itemSchemas){
|
|
133
|
+
const objSchema = asObjectSchema(schema);
|
|
134
|
+
if (objSchema.properties) for (const [key, value] of Object.entries(objSchema.properties)){
|
|
135
|
+
propertyCounts[key] = (propertyCounts[key] || 0) + 1;
|
|
136
|
+
if (key in mergedProperties) mergedProperties[key] = mergeSchemas(mergedProperties[key], value);
|
|
137
|
+
else mergedProperties[key] = value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const requiredProps = Object.entries(propertyCounts).filter(([_, count])=>count === totalItems).map(([key, _])=>key);
|
|
141
|
+
mergedProperties = detectEnumsInArrayItems(mergedProperties, originalArray, totalItems);
|
|
142
|
+
mergedProperties = detectSemanticFormatsInArrayItems(mergedProperties, originalArray);
|
|
143
|
+
return {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: mergedProperties,
|
|
146
|
+
required: requiredProps.length > 0 ? requiredProps.sort() : void 0
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function inferArraySchema(obj) {
|
|
150
|
+
if (0 === obj.length) return {
|
|
151
|
+
type: "array",
|
|
152
|
+
items: {}
|
|
153
|
+
};
|
|
154
|
+
const itemSchemas = obj.map((item)=>inferSchema(item));
|
|
155
|
+
const firstItemSchema = asObjectSchema(itemSchemas[0]);
|
|
156
|
+
const allSameType = itemSchemas.every((schema)=>asObjectSchema(schema).type === firstItemSchema.type);
|
|
157
|
+
if (allSameType) {
|
|
158
|
+
if ("object" === firstItemSchema.type) {
|
|
159
|
+
const itemsSchema = processArrayOfObjects(itemSchemas, obj);
|
|
160
|
+
return {
|
|
161
|
+
type: "array",
|
|
162
|
+
items: itemsSchema,
|
|
163
|
+
minItems: 0
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
type: "array",
|
|
168
|
+
items: itemSchemas[0],
|
|
169
|
+
minItems: 0
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const uniqueSchemas = [
|
|
173
|
+
...new Map(itemSchemas.map((s)=>[
|
|
174
|
+
JSON.stringify(s),
|
|
175
|
+
s
|
|
176
|
+
])).values()
|
|
177
|
+
];
|
|
178
|
+
if (1 === uniqueSchemas.length && "object" === asObjectSchema(uniqueSchemas[0]).type) return {
|
|
179
|
+
type: "array",
|
|
180
|
+
items: uniqueSchemas[0],
|
|
181
|
+
minItems: 0
|
|
182
|
+
};
|
|
183
|
+
return {
|
|
184
|
+
type: "array",
|
|
185
|
+
items: 1 === uniqueSchemas.length ? uniqueSchemas[0] : {
|
|
186
|
+
oneOf: uniqueSchemas
|
|
187
|
+
},
|
|
188
|
+
minItems: 0
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function inferStringSchema(str) {
|
|
192
|
+
const formats = {
|
|
193
|
+
date: /^\d{4}-\d{2}-\d{2}$/,
|
|
194
|
+
"date-time": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/,
|
|
195
|
+
email: /^[^@]+@[^@]+\.[^@]+$/,
|
|
196
|
+
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
197
|
+
uri: /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i
|
|
198
|
+
};
|
|
199
|
+
for (const [format, regex] of Object.entries(formats))if (regex.test(str)) return {
|
|
200
|
+
type: "string",
|
|
201
|
+
format
|
|
202
|
+
};
|
|
203
|
+
return {
|
|
204
|
+
type: "string"
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function inferNumberSchema(num) {
|
|
208
|
+
return Number.isInteger(num) ? {
|
|
209
|
+
type: "integer"
|
|
210
|
+
} : {
|
|
211
|
+
type: "number"
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function inferSchema(obj) {
|
|
215
|
+
if (null === obj) return {
|
|
216
|
+
type: "null"
|
|
217
|
+
};
|
|
218
|
+
const type = Array.isArray(obj) ? "array" : typeof obj;
|
|
219
|
+
switch(type){
|
|
220
|
+
case "object":
|
|
221
|
+
return inferObjectSchema(obj);
|
|
222
|
+
case "array":
|
|
223
|
+
return inferArraySchema(obj);
|
|
224
|
+
case "string":
|
|
225
|
+
return inferStringSchema(obj);
|
|
226
|
+
case "number":
|
|
227
|
+
return inferNumberSchema(obj);
|
|
228
|
+
case "boolean":
|
|
229
|
+
return {
|
|
230
|
+
type: "boolean"
|
|
231
|
+
};
|
|
232
|
+
default:
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function createSchemaFromJson(jsonObject) {
|
|
237
|
+
const inferredSchema = inferSchema(jsonObject);
|
|
238
|
+
const rootSchema = asObjectSchema(inferredSchema);
|
|
239
|
+
const finalSchema = {
|
|
240
|
+
$schema: "https://json-schema.org/draft-07/schema",
|
|
241
|
+
title: "Generated Schema",
|
|
242
|
+
description: "Generated from JSON data"
|
|
243
|
+
};
|
|
244
|
+
if ("object" === rootSchema.type || rootSchema.properties) {
|
|
245
|
+
finalSchema.type = "object";
|
|
246
|
+
finalSchema.properties = rootSchema.properties;
|
|
247
|
+
if (rootSchema.required) finalSchema.required = rootSchema.required;
|
|
248
|
+
} else if ("array" === rootSchema.type || rootSchema.items) {
|
|
249
|
+
finalSchema.type = "array";
|
|
250
|
+
finalSchema.items = rootSchema.items;
|
|
251
|
+
if (void 0 !== rootSchema.minItems) finalSchema.minItems = rootSchema.minItems;
|
|
252
|
+
if (void 0 !== rootSchema.maxItems) finalSchema.maxItems = rootSchema.maxItems;
|
|
253
|
+
} else if (rootSchema.type) {
|
|
254
|
+
finalSchema.type = "object";
|
|
255
|
+
finalSchema.properties = {
|
|
256
|
+
value: rootSchema
|
|
257
|
+
};
|
|
258
|
+
finalSchema.required = [
|
|
259
|
+
"value"
|
|
260
|
+
];
|
|
261
|
+
finalSchema.title = "Generated Schema (Primitive Root)";
|
|
262
|
+
finalSchema.description = "Input was a primitive value, wrapped in an object.";
|
|
263
|
+
} else finalSchema.type = "object";
|
|
264
|
+
return finalSchema;
|
|
265
|
+
}
|
|
266
|
+
export { createSchemaFromJson, inferSchema };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { copySchema } from "./schemaEditor.js";
|
|
2
|
+
import { isBooleanSchema } from "../types/jsonSchema.js";
|
|
3
|
+
function compileDependentEnums(schema) {
|
|
4
|
+
if (isBooleanSchema(schema)) return schema;
|
|
5
|
+
const transformed = copySchema(schema);
|
|
6
|
+
return transformObject(transformed);
|
|
7
|
+
}
|
|
8
|
+
function transformObject(obj) {
|
|
9
|
+
if (!obj.properties) return obj;
|
|
10
|
+
const props = {
|
|
11
|
+
...obj.properties
|
|
12
|
+
};
|
|
13
|
+
for (const key of Object.keys(props)){
|
|
14
|
+
const child = props[key];
|
|
15
|
+
if ("object" == typeof child && null !== child) props[key] = transformSchema(child);
|
|
16
|
+
}
|
|
17
|
+
const dependentProp = Object.entries(props).find(([, s])=>"object" == typeof s && null !== s && "$dependentEnum" in s && null != s.$dependentEnum);
|
|
18
|
+
if (!dependentProp) return {
|
|
19
|
+
...obj,
|
|
20
|
+
properties: props
|
|
21
|
+
};
|
|
22
|
+
const [propName, propSchema] = dependentProp;
|
|
23
|
+
const dep = propSchema.$dependentEnum;
|
|
24
|
+
if (!dep) return {
|
|
25
|
+
...obj,
|
|
26
|
+
properties: props
|
|
27
|
+
};
|
|
28
|
+
const controllingName = dep.property;
|
|
29
|
+
const valuesMap = dep.values;
|
|
30
|
+
const controllingValues = Object.keys(valuesMap);
|
|
31
|
+
const oneOfBranches = controllingValues.map((ctrlValue)=>{
|
|
32
|
+
const branchProps = {
|
|
33
|
+
...props
|
|
34
|
+
};
|
|
35
|
+
const controllingSchema = branchProps[controllingName];
|
|
36
|
+
if ("object" == typeof controllingSchema && null !== controllingSchema) branchProps[controllingName] = {
|
|
37
|
+
...controllingSchema,
|
|
38
|
+
const: ctrlValue
|
|
39
|
+
};
|
|
40
|
+
else branchProps[controllingName] = {
|
|
41
|
+
const: ctrlValue
|
|
42
|
+
};
|
|
43
|
+
const dependentSchema = branchProps[propName];
|
|
44
|
+
if ("object" == typeof dependentSchema && null !== dependentSchema) {
|
|
45
|
+
const { $dependentEnum: _d, ...rest } = dependentSchema;
|
|
46
|
+
branchProps[propName] = {
|
|
47
|
+
...rest,
|
|
48
|
+
enum: valuesMap[ctrlValue] ?? []
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: branchProps,
|
|
54
|
+
required: obj.required,
|
|
55
|
+
$propertyOrder: obj.$propertyOrder
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
...obj.$schema && {
|
|
60
|
+
$schema: obj.$schema
|
|
61
|
+
},
|
|
62
|
+
...obj.$id && {
|
|
63
|
+
$id: obj.$id
|
|
64
|
+
},
|
|
65
|
+
type: "object",
|
|
66
|
+
oneOf: oneOfBranches
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function transformSchema(schema) {
|
|
70
|
+
if (isBooleanSchema(schema)) return schema;
|
|
71
|
+
const obj = schema;
|
|
72
|
+
if (obj.properties) return transformObject(obj);
|
|
73
|
+
if (obj.items && "object" == typeof obj.items) return {
|
|
74
|
+
...obj,
|
|
75
|
+
items: transformSchema(obj.items)
|
|
76
|
+
};
|
|
77
|
+
if (obj.prefixItems && Array.isArray(obj.prefixItems)) return {
|
|
78
|
+
...obj,
|
|
79
|
+
prefixItems: obj.prefixItems.map((s)=>transformSchema(s))
|
|
80
|
+
};
|
|
81
|
+
if (obj.additionalProperties && "object" == typeof obj.additionalProperties) return {
|
|
82
|
+
...obj,
|
|
83
|
+
additionalProperties: transformSchema(obj.additionalProperties)
|
|
84
|
+
};
|
|
85
|
+
if (obj.oneOf && Array.isArray(obj.oneOf)) return {
|
|
86
|
+
...obj,
|
|
87
|
+
oneOf: obj.oneOf.map(transformSchema)
|
|
88
|
+
};
|
|
89
|
+
if (obj.anyOf && Array.isArray(obj.anyOf)) return {
|
|
90
|
+
...obj,
|
|
91
|
+
anyOf: obj.anyOf.map(transformSchema)
|
|
92
|
+
};
|
|
93
|
+
if (obj.allOf && Array.isArray(obj.allOf)) return {
|
|
94
|
+
...obj,
|
|
95
|
+
allOf: obj.allOf.map(transformSchema)
|
|
96
|
+
};
|
|
97
|
+
if (obj.if && "object" == typeof obj.if) return {
|
|
98
|
+
...obj,
|
|
99
|
+
if: transformSchema(obj.if),
|
|
100
|
+
then: obj.then ? transformSchema(obj.then) : void 0,
|
|
101
|
+
else: obj.else ? transformSchema(obj.else) : void 0
|
|
102
|
+
};
|
|
103
|
+
if (obj.$defs) {
|
|
104
|
+
const defs = {};
|
|
105
|
+
for (const k of Object.keys(obj.$defs))defs[k] = transformSchema(obj.$defs[k]);
|
|
106
|
+
return {
|
|
107
|
+
...obj,
|
|
108
|
+
$defs: defs
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return obj;
|
|
112
|
+
}
|
|
113
|
+
export { compileDependentEnums };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { isBooleanSchema, isObjectSchema } from "../types/jsonSchema.js";
|
|
2
|
+
function copySchema(schema) {
|
|
3
|
+
if ("function" == typeof structuredClone) return structuredClone(schema);
|
|
4
|
+
return JSON.parse(JSON.stringify(schema));
|
|
5
|
+
}
|
|
6
|
+
function updateObjectProperty(schema, propertyName, propertySchema) {
|
|
7
|
+
if (!isObjectSchema(schema)) return schema;
|
|
8
|
+
const newSchema = copySchema(schema);
|
|
9
|
+
if (!newSchema.properties) newSchema.properties = {};
|
|
10
|
+
const isNewProperty = !(propertyName in newSchema.properties);
|
|
11
|
+
newSchema.properties[propertyName] = propertySchema;
|
|
12
|
+
if (newSchema.$propertyOrder) {
|
|
13
|
+
if (isNewProperty && !newSchema.$propertyOrder.includes(propertyName)) newSchema.$propertyOrder.push(propertyName);
|
|
14
|
+
} else newSchema.$propertyOrder = Object.keys(newSchema.properties);
|
|
15
|
+
return newSchema;
|
|
16
|
+
}
|
|
17
|
+
function removeObjectProperty(schema, propertyName) {
|
|
18
|
+
if (!isObjectSchema(schema) || !schema.properties) return schema;
|
|
19
|
+
const newSchema = copySchema(schema);
|
|
20
|
+
const { [propertyName]: _, ...remainingProps } = newSchema.properties;
|
|
21
|
+
newSchema.properties = remainingProps;
|
|
22
|
+
if (newSchema.required) newSchema.required = newSchema.required.filter((name)=>name !== propertyName);
|
|
23
|
+
if (newSchema.$propertyOrder) newSchema.$propertyOrder = newSchema.$propertyOrder.filter((name)=>name !== propertyName);
|
|
24
|
+
return newSchema;
|
|
25
|
+
}
|
|
26
|
+
function updatePropertyRequired(schema, propertyName, required) {
|
|
27
|
+
if (!isObjectSchema(schema)) return schema;
|
|
28
|
+
const newSchema = copySchema(schema);
|
|
29
|
+
if (!newSchema.required) newSchema.required = [];
|
|
30
|
+
if (required) {
|
|
31
|
+
if (!newSchema.required.includes(propertyName)) newSchema.required.push(propertyName);
|
|
32
|
+
} else newSchema.required = newSchema.required.filter((name)=>name !== propertyName);
|
|
33
|
+
return newSchema;
|
|
34
|
+
}
|
|
35
|
+
function updateArrayItems(schema, itemsSchema) {
|
|
36
|
+
if (isObjectSchema(schema) && "array" === schema.type) return {
|
|
37
|
+
...schema,
|
|
38
|
+
items: itemsSchema
|
|
39
|
+
};
|
|
40
|
+
return schema;
|
|
41
|
+
}
|
|
42
|
+
function createFieldSchema(field) {
|
|
43
|
+
const { type, description, default: defaultValue, validation } = field;
|
|
44
|
+
if (isObjectSchema(validation)) {
|
|
45
|
+
const schema = {
|
|
46
|
+
type,
|
|
47
|
+
description,
|
|
48
|
+
...validation
|
|
49
|
+
};
|
|
50
|
+
if (void 0 !== defaultValue) schema.default = defaultValue;
|
|
51
|
+
return schema;
|
|
52
|
+
}
|
|
53
|
+
const schema = validation || {
|
|
54
|
+
type
|
|
55
|
+
};
|
|
56
|
+
if (void 0 !== defaultValue) schema.default = defaultValue;
|
|
57
|
+
return schema;
|
|
58
|
+
}
|
|
59
|
+
function validateFieldName(name) {
|
|
60
|
+
if (!name || "" === name.trim()) return false;
|
|
61
|
+
const validNamePattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
62
|
+
return validNamePattern.test(name);
|
|
63
|
+
}
|
|
64
|
+
function getSchemaProperties(schema) {
|
|
65
|
+
if (!isObjectSchema(schema) || !schema.properties) return [];
|
|
66
|
+
const required = schema.required || [];
|
|
67
|
+
const propertyOrder = schema.$propertyOrder || Object.keys(schema.properties);
|
|
68
|
+
const orderedPropertyNames = propertyOrder.filter((name)=>name in schema.properties);
|
|
69
|
+
const allPropertyNames = Object.keys(schema.properties);
|
|
70
|
+
const missingProperties = allPropertyNames.filter((name)=>!orderedPropertyNames.includes(name));
|
|
71
|
+
const finalOrder = [
|
|
72
|
+
...orderedPropertyNames,
|
|
73
|
+
...missingProperties
|
|
74
|
+
];
|
|
75
|
+
return finalOrder.map((name)=>({
|
|
76
|
+
name,
|
|
77
|
+
schema: schema.properties[name],
|
|
78
|
+
required: required.includes(name)
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
function hasPathTo(graph, from, to) {
|
|
82
|
+
const visited = new Set();
|
|
83
|
+
let current = from;
|
|
84
|
+
while(current){
|
|
85
|
+
if (current === to) return true;
|
|
86
|
+
if (visited.has(current)) break;
|
|
87
|
+
visited.add(current);
|
|
88
|
+
current = graph.get(current);
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
function getEligibleEnumControllingProperties(parentSchema, propertyName) {
|
|
93
|
+
if (!parentSchema.properties) return [];
|
|
94
|
+
const graph = new Map();
|
|
95
|
+
for (const k of Object.keys(parentSchema.properties)){
|
|
96
|
+
const prop = parentSchema.properties[k];
|
|
97
|
+
if ("object" != typeof prop || null === prop) continue;
|
|
98
|
+
const dep = prop.$dependentEnum?.property;
|
|
99
|
+
if (dep) graph.set(k, dep);
|
|
100
|
+
}
|
|
101
|
+
const result = [];
|
|
102
|
+
const siblings = Object.keys(parentSchema.properties).filter((n)=>n !== propertyName);
|
|
103
|
+
for (const name of siblings){
|
|
104
|
+
if (hasPathTo(graph, name, propertyName)) continue;
|
|
105
|
+
const prop = parentSchema.properties[name];
|
|
106
|
+
if ("boolean" == typeof prop) continue;
|
|
107
|
+
if (!prop) continue;
|
|
108
|
+
let values;
|
|
109
|
+
if (Array.isArray(prop.enum) && prop.enum.length > 0) values = prop.enum;
|
|
110
|
+
else if (void 0 !== prop.const) values = [
|
|
111
|
+
prop.const
|
|
112
|
+
];
|
|
113
|
+
if (void 0 !== values) result.push({
|
|
114
|
+
name,
|
|
115
|
+
values
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
function getArrayItemsSchema(schema) {
|
|
121
|
+
if (isBooleanSchema(schema)) return null;
|
|
122
|
+
if ("array" !== schema.type) return null;
|
|
123
|
+
return schema.items || null;
|
|
124
|
+
}
|
|
125
|
+
function renameObjectProperty(schema, oldName, newName) {
|
|
126
|
+
if (!isObjectSchema(schema) || !schema.properties) return schema;
|
|
127
|
+
const newSchema = copySchema(schema);
|
|
128
|
+
const newProperties = {};
|
|
129
|
+
const propertyOrder = newSchema.$propertyOrder || Object.keys(newSchema.properties);
|
|
130
|
+
for (const key of propertyOrder)if (key === oldName) newProperties[newName] = newSchema.properties[oldName];
|
|
131
|
+
else if (key in newSchema.properties) newProperties[key] = newSchema.properties[key];
|
|
132
|
+
for (const [key, value] of Object.entries(newSchema.properties))if (!(key in newProperties)) newProperties[key] = value;
|
|
133
|
+
newSchema.properties = newProperties;
|
|
134
|
+
if (newSchema.required) newSchema.required = newSchema.required.map((field)=>field === oldName ? newName : field);
|
|
135
|
+
if (newSchema.$propertyOrder) newSchema.$propertyOrder = newSchema.$propertyOrder.map((name)=>name === oldName ? newName : name);
|
|
136
|
+
return newSchema;
|
|
137
|
+
}
|
|
138
|
+
function reorderObjectProperty(schema, propertyName, direction) {
|
|
139
|
+
if (!isObjectSchema(schema) || !schema.properties) return schema;
|
|
140
|
+
const newSchema = copySchema(schema);
|
|
141
|
+
if (!newSchema.$propertyOrder) newSchema.$propertyOrder = Object.keys(newSchema.properties);
|
|
142
|
+
const propertyOrder = [
|
|
143
|
+
...newSchema.$propertyOrder
|
|
144
|
+
];
|
|
145
|
+
const currentIndex = propertyOrder.indexOf(propertyName);
|
|
146
|
+
if (-1 === currentIndex) return schema;
|
|
147
|
+
if ("up" === direction && 0 === currentIndex) return schema;
|
|
148
|
+
if ("down" === direction && currentIndex === propertyOrder.length - 1) return schema;
|
|
149
|
+
const targetIndex = "up" === direction ? currentIndex - 1 : currentIndex + 1;
|
|
150
|
+
[propertyOrder[currentIndex], propertyOrder[targetIndex]] = [
|
|
151
|
+
propertyOrder[targetIndex],
|
|
152
|
+
propertyOrder[currentIndex]
|
|
153
|
+
];
|
|
154
|
+
const newProperties = {};
|
|
155
|
+
for (const name of propertyOrder)if (name in newSchema.properties) newProperties[name] = newSchema.properties[name];
|
|
156
|
+
for (const [key, value] of Object.entries(newSchema.properties))if (!(key in newProperties)) newProperties[key] = value;
|
|
157
|
+
newSchema.properties = newProperties;
|
|
158
|
+
newSchema.$propertyOrder = propertyOrder;
|
|
159
|
+
return newSchema;
|
|
160
|
+
}
|
|
161
|
+
function hasChildren(schema) {
|
|
162
|
+
if (!isObjectSchema(schema)) return false;
|
|
163
|
+
if ("object" === schema.type && schema.properties) return Object.keys(schema.properties).length > 0;
|
|
164
|
+
if ("array" === schema.type && schema.items && isObjectSchema(schema.items)) return "object" === schema.items.type && !!schema.items.properties;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
export { copySchema, createFieldSchema, getArrayItemsSchema, getEligibleEnumControllingProperties, getSchemaProperties, hasChildren, removeObjectProperty, renameObjectProperty, reorderObjectProperty, updateArrayItems, updateObjectProperty, updatePropertyRequired, validateFieldName };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
function cn(...inputs) {
|
|
4
|
+
return twMerge(clsx(inputs));
|
|
5
|
+
}
|
|
6
|
+
const getTypeColor = (type)=>{
|
|
7
|
+
switch(type){
|
|
8
|
+
case "string":
|
|
9
|
+
return "text-blue-500 bg-blue-50";
|
|
10
|
+
case "number":
|
|
11
|
+
case "integer":
|
|
12
|
+
return "text-purple-500 bg-purple-50";
|
|
13
|
+
case "boolean":
|
|
14
|
+
return "text-green-500 bg-green-50";
|
|
15
|
+
case "object":
|
|
16
|
+
return "text-orange-500 bg-orange-50";
|
|
17
|
+
case "array":
|
|
18
|
+
return "text-pink-500 bg-pink-50";
|
|
19
|
+
case "null":
|
|
20
|
+
return "text-gray-500 bg-gray-50";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const getTypeLabel = (t, type)=>{
|
|
24
|
+
switch(type){
|
|
25
|
+
case "string":
|
|
26
|
+
return t.schemaTypeString;
|
|
27
|
+
case "number":
|
|
28
|
+
case "integer":
|
|
29
|
+
return t.schemaTypeNumber;
|
|
30
|
+
case "boolean":
|
|
31
|
+
return t.schemaTypeBoolean;
|
|
32
|
+
case "object":
|
|
33
|
+
return t.schemaTypeObject;
|
|
34
|
+
case "array":
|
|
35
|
+
return t.schemaTypeArray;
|
|
36
|
+
case "null":
|
|
37
|
+
return t.schemaTypeNull;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
export { cn, getTypeColor, getTypeLabel };
|