@schema-ts/core 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +243 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -15
- package/dist/index.d.ts +81 -15
- package/dist/index.js +243 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1332,7 +1332,7 @@ function validateSchema(schema, value, instancePath = "", schemaPath = "#", fast
|
|
|
1332
1332
|
}
|
|
1333
1333
|
|
|
1334
1334
|
// src/effective.ts
|
|
1335
|
-
function resolveEffectiveSchema(validator, schema, value, keywordLocation, instanceLocation) {
|
|
1335
|
+
function resolveEffectiveSchema(validator, schema, value, keywordLocation, instanceLocation, isRequired = true) {
|
|
1336
1336
|
let effective = schema;
|
|
1337
1337
|
if (effective.if) {
|
|
1338
1338
|
const output = validator.validate(
|
|
@@ -1398,7 +1398,6 @@ function resolveEffectiveSchema(validator, schema, value, keywordLocation, insta
|
|
|
1398
1398
|
instanceLocation
|
|
1399
1399
|
);
|
|
1400
1400
|
effective = mergeSchema(effective, res.effectiveSchema);
|
|
1401
|
-
break;
|
|
1402
1401
|
}
|
|
1403
1402
|
}
|
|
1404
1403
|
const { anyOf: _, ...rest } = effective;
|
|
@@ -1437,6 +1436,13 @@ function resolveEffectiveSchema(validator, schema, value, keywordLocation, insta
|
|
|
1437
1436
|
} else {
|
|
1438
1437
|
type = detectSchemaType(value);
|
|
1439
1438
|
}
|
|
1439
|
+
if (!isRequired && value === void 0) {
|
|
1440
|
+
return {
|
|
1441
|
+
effectiveSchema: effective,
|
|
1442
|
+
type,
|
|
1443
|
+
error: void 0
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1440
1446
|
const validationOutput = validator.validate(
|
|
1441
1447
|
effective,
|
|
1442
1448
|
value,
|
|
@@ -1470,6 +1476,19 @@ function mergeSchemaArrays(a, b) {
|
|
|
1470
1476
|
if (b === void 0) return a;
|
|
1471
1477
|
return [...a, ...b];
|
|
1472
1478
|
}
|
|
1479
|
+
function mergeSchemaMap(base, override) {
|
|
1480
|
+
if (base === void 0) return override;
|
|
1481
|
+
if (override === void 0) return base;
|
|
1482
|
+
const merged = { ...base };
|
|
1483
|
+
for (const [key, schema] of Object.entries(override)) {
|
|
1484
|
+
if (merged[key]) {
|
|
1485
|
+
merged[key] = mergeSchema(merged[key], schema);
|
|
1486
|
+
} else {
|
|
1487
|
+
merged[key] = schema;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
return merged;
|
|
1491
|
+
}
|
|
1473
1492
|
function mergeSchema(base, override) {
|
|
1474
1493
|
if (!override) return base;
|
|
1475
1494
|
const merged = {
|
|
@@ -1496,17 +1515,16 @@ function mergeSchema(base, override) {
|
|
|
1496
1515
|
...override.dependentRequired
|
|
1497
1516
|
};
|
|
1498
1517
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
...override.properties
|
|
1503
|
-
};
|
|
1518
|
+
const mergedProperties = mergeSchemaMap(base.properties, override.properties);
|
|
1519
|
+
if (mergedProperties !== void 0) {
|
|
1520
|
+
merged.properties = mergedProperties;
|
|
1504
1521
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1522
|
+
const mergedPatternProperties = mergeSchemaMap(
|
|
1523
|
+
base.patternProperties,
|
|
1524
|
+
override.patternProperties
|
|
1525
|
+
);
|
|
1526
|
+
if (mergedPatternProperties !== void 0) {
|
|
1527
|
+
merged.patternProperties = mergedPatternProperties;
|
|
1510
1528
|
}
|
|
1511
1529
|
if (base.items && override.items) {
|
|
1512
1530
|
merged.items = mergeSchema(base.items, override.items);
|
|
@@ -1541,11 +1559,12 @@ function mergeSchema(base, override) {
|
|
|
1541
1559
|
merged[keyword] = mergedArray;
|
|
1542
1560
|
}
|
|
1543
1561
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1562
|
+
const mergedDependentSchemas = mergeSchemaMap(
|
|
1563
|
+
base.dependentSchemas,
|
|
1564
|
+
override.dependentSchemas
|
|
1565
|
+
);
|
|
1566
|
+
if (mergedDependentSchemas !== void 0) {
|
|
1567
|
+
merged.dependentSchemas = mergedDependentSchemas;
|
|
1549
1568
|
}
|
|
1550
1569
|
return merged;
|
|
1551
1570
|
}
|
|
@@ -1766,7 +1785,8 @@ function getSubSchema(schema, key) {
|
|
|
1766
1785
|
}
|
|
1767
1786
|
|
|
1768
1787
|
// src/default.ts
|
|
1769
|
-
function getDefaultValue(schema,
|
|
1788
|
+
function getDefaultValue(schema, options = {}) {
|
|
1789
|
+
const { value, strategy = "explicit" } = options;
|
|
1770
1790
|
if (value === void 0) {
|
|
1771
1791
|
if (schema.const !== void 0) return schema.const;
|
|
1772
1792
|
if (schema.default !== void 0) return schema.default;
|
|
@@ -1775,6 +1795,7 @@ function getDefaultValue(schema, value) {
|
|
|
1775
1795
|
if (type === "object" || !type && schema.properties) {
|
|
1776
1796
|
let obj;
|
|
1777
1797
|
if (value === void 0) {
|
|
1798
|
+
if (strategy === "explicit") return void 0;
|
|
1778
1799
|
obj = {};
|
|
1779
1800
|
} else if (typeof value === "object" && value !== null) {
|
|
1780
1801
|
obj = value;
|
|
@@ -1784,9 +1805,9 @@ function getDefaultValue(schema, value) {
|
|
|
1784
1805
|
if (schema.properties) {
|
|
1785
1806
|
for (const [key, subschema] of Object.entries(schema.properties)) {
|
|
1786
1807
|
if (obj[key] !== void 0) {
|
|
1787
|
-
obj[key] = getDefaultValue(subschema, obj[key]);
|
|
1808
|
+
obj[key] = getDefaultValue(subschema, { value: obj[key], strategy });
|
|
1788
1809
|
} else if (schema.required?.includes(key)) {
|
|
1789
|
-
obj[key] = getDefaultValue(subschema);
|
|
1810
|
+
obj[key] = getDefaultValue(subschema, { strategy });
|
|
1790
1811
|
}
|
|
1791
1812
|
}
|
|
1792
1813
|
}
|
|
@@ -1795,6 +1816,7 @@ function getDefaultValue(schema, value) {
|
|
|
1795
1816
|
if (type === "array") {
|
|
1796
1817
|
let arr;
|
|
1797
1818
|
if (value === void 0) {
|
|
1819
|
+
if (strategy === "explicit") return void 0;
|
|
1798
1820
|
arr = [];
|
|
1799
1821
|
} else if (Array.isArray(value)) {
|
|
1800
1822
|
arr = value;
|
|
@@ -1804,16 +1826,19 @@ function getDefaultValue(schema, value) {
|
|
|
1804
1826
|
if (schema.prefixItems) {
|
|
1805
1827
|
schema.prefixItems.forEach((subschema, index) => {
|
|
1806
1828
|
if (index < arr.length) {
|
|
1807
|
-
arr[index] = getDefaultValue(subschema,
|
|
1829
|
+
arr[index] = getDefaultValue(subschema, {
|
|
1830
|
+
value: arr[index],
|
|
1831
|
+
strategy
|
|
1832
|
+
});
|
|
1808
1833
|
} else if (value === void 0) {
|
|
1809
|
-
arr.push(getDefaultValue(subschema));
|
|
1834
|
+
arr.push(getDefaultValue(subschema, { strategy }));
|
|
1810
1835
|
}
|
|
1811
1836
|
});
|
|
1812
1837
|
}
|
|
1813
1838
|
if (value !== void 0 && schema.items) {
|
|
1814
1839
|
const startIndex = schema.prefixItems ? schema.prefixItems.length : 0;
|
|
1815
1840
|
for (let i = startIndex; i < arr.length; i++) {
|
|
1816
|
-
arr[i] = getDefaultValue(schema.items, arr[i]);
|
|
1841
|
+
arr[i] = getDefaultValue(schema.items, { value: arr[i], strategy });
|
|
1817
1842
|
}
|
|
1818
1843
|
}
|
|
1819
1844
|
return arr;
|
|
@@ -1821,6 +1846,9 @@ function getDefaultValue(schema, value) {
|
|
|
1821
1846
|
if (value !== void 0) {
|
|
1822
1847
|
return value;
|
|
1823
1848
|
}
|
|
1849
|
+
if (strategy === "explicit") {
|
|
1850
|
+
return void 0;
|
|
1851
|
+
}
|
|
1824
1852
|
switch (type) {
|
|
1825
1853
|
case "string":
|
|
1826
1854
|
return "";
|
|
@@ -2027,21 +2055,28 @@ var SchemaRuntime = class {
|
|
|
2027
2055
|
value;
|
|
2028
2056
|
version = 0;
|
|
2029
2057
|
rootSchema = {};
|
|
2058
|
+
options;
|
|
2030
2059
|
/**
|
|
2031
2060
|
* Create a new SchemaRuntime instance.
|
|
2032
2061
|
*
|
|
2033
2062
|
* @param validator - The validator instance for schema validation
|
|
2034
2063
|
* @param schema - The JSON Schema definition (will be normalized and dereferenced)
|
|
2035
2064
|
* @param value - The initial data value to manage
|
|
2065
|
+
* @param options - Runtime configuration options
|
|
2036
2066
|
*
|
|
2037
2067
|
* @example
|
|
2038
2068
|
* const validator = new Validator();
|
|
2039
2069
|
* const schema = { type: "object", properties: { name: { type: "string" } } };
|
|
2040
2070
|
* const runtime = new SchemaRuntime(validator, schema, { name: "Alice" });
|
|
2041
2071
|
*/
|
|
2042
|
-
constructor(validator, schema, value) {
|
|
2072
|
+
constructor(validator, schema, value, options = {}) {
|
|
2043
2073
|
this.validator = validator;
|
|
2044
2074
|
this.value = value;
|
|
2075
|
+
this.options = {
|
|
2076
|
+
autoFillDefaults: "explicit",
|
|
2077
|
+
removeEmptyContainers: "auto",
|
|
2078
|
+
...options
|
|
2079
|
+
};
|
|
2045
2080
|
const normalized = normalizeSchema(schema);
|
|
2046
2081
|
this.rootSchema = dereferenceSchemaDeep(normalized, normalized);
|
|
2047
2082
|
this.root = this.createEmptyNode("", "#");
|
|
@@ -2095,12 +2130,15 @@ var SchemaRuntime = class {
|
|
|
2095
2130
|
return {
|
|
2096
2131
|
type: "null",
|
|
2097
2132
|
schema: {},
|
|
2133
|
+
// Placeholder, will be set in buildNode
|
|
2098
2134
|
version: -1,
|
|
2135
|
+
// -1 indicates initial construction (not yet built)
|
|
2099
2136
|
instanceLocation,
|
|
2100
2137
|
keywordLocation,
|
|
2101
2138
|
originalSchema: {},
|
|
2102
2139
|
canRemove: false,
|
|
2103
2140
|
canAdd: false,
|
|
2141
|
+
isRequired: false,
|
|
2104
2142
|
children: []
|
|
2105
2143
|
};
|
|
2106
2144
|
}
|
|
@@ -2223,9 +2261,44 @@ var SchemaRuntime = class {
|
|
|
2223
2261
|
if (normalizedPath === ROOT_PATH) return this.value;
|
|
2224
2262
|
return getJsonPointer(this.value, normalizedPath);
|
|
2225
2263
|
}
|
|
2264
|
+
/**
|
|
2265
|
+
* Internal helper to set a value at a normalized path.
|
|
2266
|
+
* Handles both root and non-root paths.
|
|
2267
|
+
*
|
|
2268
|
+
* @param normalizedPath - The normalized JSON Pointer path
|
|
2269
|
+
* @param value - The value to set
|
|
2270
|
+
* @returns true if successful, false if the path cannot be set
|
|
2271
|
+
*/
|
|
2272
|
+
setValueAtPath(normalizedPath, value) {
|
|
2273
|
+
if (normalizedPath === ROOT_PATH) {
|
|
2274
|
+
this.value = value;
|
|
2275
|
+
return true;
|
|
2276
|
+
}
|
|
2277
|
+
return setJsonPointer(this.value, normalizedPath, value);
|
|
2278
|
+
}
|
|
2279
|
+
/**
|
|
2280
|
+
* Internal method to remove a value at a path.
|
|
2281
|
+
* Shared logic for removeValue and setValue(undefined).
|
|
2282
|
+
* @param path - The normalized path to remove
|
|
2283
|
+
* @param canRemove - Whether the removal is allowed (pre-checked by caller)
|
|
2284
|
+
* @returns true if successful, false if removal failed
|
|
2285
|
+
*/
|
|
2286
|
+
removeValueInternal(path) {
|
|
2287
|
+
const success = removeJsonPointer(this.value, path);
|
|
2288
|
+
if (!success) {
|
|
2289
|
+
return false;
|
|
2290
|
+
}
|
|
2291
|
+
const lastSlash = path.lastIndexOf("/");
|
|
2292
|
+
const parentPath = lastSlash <= 0 ? ROOT_PATH : path.substring(0, lastSlash);
|
|
2293
|
+
const reconcilePath = this.cleanupEmptyContainers(parentPath);
|
|
2294
|
+
this.reconcile(reconcilePath);
|
|
2295
|
+
this.notify({ type: "value", path: reconcilePath });
|
|
2296
|
+
return true;
|
|
2297
|
+
}
|
|
2226
2298
|
/**
|
|
2227
2299
|
* Remove a node at the specified path.
|
|
2228
2300
|
* This deletes the value from the data structure (array splice or object delete).
|
|
2301
|
+
* After removal, may also remove empty parent containers based on removeEmptyContainers option.
|
|
2229
2302
|
* @param path - The path to remove
|
|
2230
2303
|
* @returns true if successful, false if the path cannot be removed
|
|
2231
2304
|
*/
|
|
@@ -2238,15 +2311,54 @@ var SchemaRuntime = class {
|
|
|
2238
2311
|
if (!node || !node.canRemove) {
|
|
2239
2312
|
return false;
|
|
2240
2313
|
}
|
|
2241
|
-
|
|
2314
|
+
return this.removeValueInternal(normalizedPath);
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Clean up empty parent containers after element removal.
|
|
2318
|
+
* Recursively removes empty arrays/objects based on removeEmptyContainers option.
|
|
2319
|
+
* @param path - The path to check for empty container
|
|
2320
|
+
* @returns The topmost parent path for reconciliation
|
|
2321
|
+
*/
|
|
2322
|
+
cleanupEmptyContainers(path) {
|
|
2323
|
+
const strategy = this.options.removeEmptyContainers;
|
|
2324
|
+
if (strategy === "never" || path === ROOT_PATH) {
|
|
2325
|
+
return path;
|
|
2326
|
+
}
|
|
2327
|
+
const node = this.findNode(path);
|
|
2328
|
+
if (!node) {
|
|
2329
|
+
return path;
|
|
2330
|
+
}
|
|
2331
|
+
const value = this.getValue(path);
|
|
2332
|
+
const isEmpty = Array.isArray(value) && value.length === 0 || value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
2333
|
+
if (!isEmpty) {
|
|
2334
|
+
return path;
|
|
2335
|
+
}
|
|
2336
|
+
const shouldRemove = strategy === "always" || strategy === "auto" && !node.isRequired;
|
|
2337
|
+
if (!shouldRemove) {
|
|
2338
|
+
return path;
|
|
2339
|
+
}
|
|
2340
|
+
const success = removeJsonPointer(this.value, path);
|
|
2242
2341
|
if (!success) {
|
|
2243
|
-
return
|
|
2342
|
+
return path;
|
|
2244
2343
|
}
|
|
2245
|
-
const lastSlash =
|
|
2246
|
-
const parentPath = lastSlash <= 0 ? ROOT_PATH :
|
|
2247
|
-
this.
|
|
2248
|
-
|
|
2249
|
-
|
|
2344
|
+
const lastSlash = path.lastIndexOf("/");
|
|
2345
|
+
const parentPath = lastSlash <= 0 ? ROOT_PATH : path.substring(0, lastSlash);
|
|
2346
|
+
return this.cleanupEmptyContainers(parentPath);
|
|
2347
|
+
}
|
|
2348
|
+
/**
|
|
2349
|
+
* Get default value for a schema, respecting autoFillDefaults option.
|
|
2350
|
+
* Falls back to 'always' strategy if configured strategy returns undefined.
|
|
2351
|
+
*/
|
|
2352
|
+
getDefaultValueForAdd(schema) {
|
|
2353
|
+
const strategy = this.options.autoFillDefaults;
|
|
2354
|
+
let defaultValue;
|
|
2355
|
+
if (strategy && strategy !== "never") {
|
|
2356
|
+
defaultValue = getDefaultValue(schema, { strategy });
|
|
2357
|
+
}
|
|
2358
|
+
if (defaultValue === void 0) {
|
|
2359
|
+
defaultValue = getDefaultValue(schema, { strategy: "always" });
|
|
2360
|
+
}
|
|
2361
|
+
return defaultValue;
|
|
2250
2362
|
}
|
|
2251
2363
|
/**
|
|
2252
2364
|
* Add a new child to an array or object at the specified parent path.
|
|
@@ -2263,8 +2375,23 @@ var SchemaRuntime = class {
|
|
|
2263
2375
|
if (!parentNode || !parentNode.canAdd) {
|
|
2264
2376
|
return false;
|
|
2265
2377
|
}
|
|
2266
|
-
|
|
2378
|
+
let parentValue = this.getValue(normalizedPath);
|
|
2267
2379
|
const parentSchema = parentNode.schema;
|
|
2380
|
+
if (parentNode.type === "array") {
|
|
2381
|
+
if (Array.isArray(parentValue)) ; else if (parentValue === void 0 || parentValue === null) {
|
|
2382
|
+
parentValue = [];
|
|
2383
|
+
this.setValueAtPath(normalizedPath, parentValue);
|
|
2384
|
+
} else {
|
|
2385
|
+
return false;
|
|
2386
|
+
}
|
|
2387
|
+
} else if (parentNode.type === "object") {
|
|
2388
|
+
if (parentValue && typeof parentValue === "object") ; else if (parentValue === void 0 || parentValue === null) {
|
|
2389
|
+
parentValue = {};
|
|
2390
|
+
this.setValueAtPath(normalizedPath, parentValue);
|
|
2391
|
+
} else {
|
|
2392
|
+
return false;
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2268
2395
|
if (parentNode.type === "array" && Array.isArray(parentValue)) {
|
|
2269
2396
|
const newIndex = parentValue.length;
|
|
2270
2397
|
const { schema: subschema, keywordLocationToken } = getSubSchema(
|
|
@@ -2274,14 +2401,14 @@ var SchemaRuntime = class {
|
|
|
2274
2401
|
if (!keywordLocationToken) {
|
|
2275
2402
|
return false;
|
|
2276
2403
|
}
|
|
2277
|
-
const defaultValue = initialValue !== void 0 ? initialValue :
|
|
2404
|
+
const defaultValue = initialValue !== void 0 ? initialValue : this.getDefaultValueForAdd(subschema);
|
|
2278
2405
|
const itemPath = jsonPointerJoin(normalizedPath, String(newIndex));
|
|
2279
2406
|
const success = setJsonPointer(this.value, itemPath, defaultValue);
|
|
2280
2407
|
if (!success) return false;
|
|
2281
2408
|
this.reconcile(normalizedPath);
|
|
2282
2409
|
this.notify({ type: "value", path: normalizedPath });
|
|
2283
2410
|
return true;
|
|
2284
|
-
} else if (parentNode.type === "object" &&
|
|
2411
|
+
} else if (parentNode.type === "object" && typeof parentValue === "object") {
|
|
2285
2412
|
if (!key) {
|
|
2286
2413
|
return false;
|
|
2287
2414
|
}
|
|
@@ -2292,7 +2419,7 @@ var SchemaRuntime = class {
|
|
|
2292
2419
|
if (!keywordLocationToken) {
|
|
2293
2420
|
return false;
|
|
2294
2421
|
}
|
|
2295
|
-
const defaultValue = initialValue !== void 0 ? initialValue :
|
|
2422
|
+
const defaultValue = initialValue !== void 0 ? initialValue : this.getDefaultValueForAdd(subschema);
|
|
2296
2423
|
const propertyPath = jsonPointerJoin(normalizedPath, key);
|
|
2297
2424
|
const success = setJsonPointer(this.value, propertyPath, defaultValue);
|
|
2298
2425
|
if (!success) return false;
|
|
@@ -2307,22 +2434,28 @@ var SchemaRuntime = class {
|
|
|
2307
2434
|
* Creates intermediate containers (objects/arrays) as needed.
|
|
2308
2435
|
* Triggers reconciliation and notifies subscribers.
|
|
2309
2436
|
*
|
|
2437
|
+
* When value is undefined and the field is not required, the field will be
|
|
2438
|
+
* removed from the parent container (similar to removeValue behavior).
|
|
2439
|
+
*
|
|
2310
2440
|
* @param path - The JSON Pointer path (e.g., "/user/name", "" for root)
|
|
2311
|
-
* @param value - The new value to set
|
|
2441
|
+
* @param value - The new value to set. If undefined and field is optional, removes the field.
|
|
2312
2442
|
* @returns true if successful, false if the path cannot be set
|
|
2313
2443
|
*
|
|
2314
2444
|
* @example
|
|
2315
2445
|
* runtime.setValue("/name", "Bob"); // set name to "Bob"
|
|
2316
2446
|
* runtime.setValue("", { name: "Alice" }); // replace entire root value
|
|
2447
|
+
* runtime.setValue("/optional", undefined); // remove optional field
|
|
2317
2448
|
*/
|
|
2318
2449
|
setValue(path, value) {
|
|
2319
2450
|
const normalizedPath = normalizeRootPath(path);
|
|
2320
|
-
if (
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2451
|
+
if (value === void 0 && normalizedPath !== ROOT_PATH) {
|
|
2452
|
+
const node = this.findNode(normalizedPath);
|
|
2453
|
+
if (node && !node.isRequired) {
|
|
2454
|
+
return this.removeValueInternal(normalizedPath);
|
|
2455
|
+
}
|
|
2325
2456
|
}
|
|
2457
|
+
const success = this.setValueAtPath(normalizedPath, value);
|
|
2458
|
+
if (!success) return false;
|
|
2326
2459
|
this.reconcile(normalizedPath);
|
|
2327
2460
|
this.notify({ type: "value", path: normalizedPath });
|
|
2328
2461
|
return true;
|
|
@@ -2386,22 +2519,56 @@ var SchemaRuntime = class {
|
|
|
2386
2519
|
* This handles cases like if-then-else where new properties with defaults
|
|
2387
2520
|
* may appear when conditions change.
|
|
2388
2521
|
*
|
|
2522
|
+
* Container initialization rules:
|
|
2523
|
+
* - Root node is always considered required and will be initialized if it has defaults or required properties
|
|
2524
|
+
* - Nested containers are initialized only if they are in parent's required array
|
|
2525
|
+
*
|
|
2389
2526
|
* @param instanceLocation - The path to the node
|
|
2390
2527
|
* @param newSchema - The new effective schema
|
|
2391
2528
|
* @param type - The schema type
|
|
2529
|
+
* @param isRequired - Whether this node is required by its parent
|
|
2392
2530
|
*/
|
|
2393
|
-
applySchemaDefaults(instanceLocation, newSchema, type) {
|
|
2394
|
-
const
|
|
2531
|
+
applySchemaDefaults(instanceLocation, newSchema, type, isRequired = true) {
|
|
2532
|
+
const strategy = this.options.autoFillDefaults;
|
|
2533
|
+
if (strategy === "never") {
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
let value = this.getValue(instanceLocation);
|
|
2537
|
+
const isRoot = instanceLocation === ROOT_PATH;
|
|
2395
2538
|
if (type === "object" && newSchema.properties) {
|
|
2539
|
+
const requiredSet = new Set(newSchema.required || []);
|
|
2540
|
+
const hasAnyDefaults = Object.entries(newSchema.properties).some(
|
|
2541
|
+
([key, subschema]) => {
|
|
2542
|
+
const defaultValue = getDefaultValue(subschema, { strategy });
|
|
2543
|
+
if (defaultValue !== void 0) return true;
|
|
2544
|
+
const isChildRequired = requiredSet.has(key);
|
|
2545
|
+
if (isChildRequired && (subschema.type === "object" || subschema.type === "array")) {
|
|
2546
|
+
return true;
|
|
2547
|
+
}
|
|
2548
|
+
return false;
|
|
2549
|
+
}
|
|
2550
|
+
);
|
|
2551
|
+
const shouldInitialize = (value === void 0 || value === null) && hasAnyDefaults && (isRoot || isRequired);
|
|
2552
|
+
if (shouldInitialize) {
|
|
2553
|
+
value = {};
|
|
2554
|
+
this.setValueAtPath(normalizeRootPath(instanceLocation), value);
|
|
2555
|
+
}
|
|
2396
2556
|
const obj = value && typeof value === "object" ? value : null;
|
|
2397
2557
|
if (!obj) return;
|
|
2398
2558
|
for (const [key, subschema] of Object.entries(newSchema.properties)) {
|
|
2399
2559
|
const hasValue = obj[key] !== void 0;
|
|
2560
|
+
const isChildRequired = requiredSet.has(key);
|
|
2400
2561
|
if (!hasValue) {
|
|
2401
|
-
const defaultValue = getDefaultValue(subschema);
|
|
2562
|
+
const defaultValue = getDefaultValue(subschema, { strategy });
|
|
2402
2563
|
if (defaultValue !== void 0) {
|
|
2403
2564
|
const propertyPath = jsonPointerJoin(instanceLocation, key);
|
|
2404
2565
|
setJsonPointer(this.value, propertyPath, defaultValue);
|
|
2566
|
+
} else if (isChildRequired && subschema.type === "object") {
|
|
2567
|
+
const propertyPath = jsonPointerJoin(instanceLocation, key);
|
|
2568
|
+
setJsonPointer(this.value, propertyPath, {});
|
|
2569
|
+
} else if (isChildRequired && subschema.type === "array") {
|
|
2570
|
+
const propertyPath = jsonPointerJoin(instanceLocation, key);
|
|
2571
|
+
setJsonPointer(this.value, propertyPath, []);
|
|
2405
2572
|
}
|
|
2406
2573
|
}
|
|
2407
2574
|
}
|
|
@@ -2412,7 +2579,8 @@ var SchemaRuntime = class {
|
|
|
2412
2579
|
if (!arr) return;
|
|
2413
2580
|
for (let i = 0; i < newSchema.prefixItems.length; i++) {
|
|
2414
2581
|
if (arr[i] === void 0) {
|
|
2415
|
-
const
|
|
2582
|
+
const itemSchema = newSchema.prefixItems[i];
|
|
2583
|
+
const defaultValue = getDefaultValue(itemSchema, { strategy });
|
|
2416
2584
|
if (defaultValue !== void 0) {
|
|
2417
2585
|
const itemPath = jsonPointerJoin(instanceLocation, String(i));
|
|
2418
2586
|
setJsonPointer(this.value, itemPath, defaultValue);
|
|
@@ -2436,7 +2604,7 @@ var SchemaRuntime = class {
|
|
|
2436
2604
|
}
|
|
2437
2605
|
}
|
|
2438
2606
|
const newChildren = [];
|
|
2439
|
-
const processChild = (childKey, childSchema, childkeywordLocation, canRemove = false) => {
|
|
2607
|
+
const processChild = (childKey, childSchema, childkeywordLocation, canRemove = false, isRequired = false) => {
|
|
2440
2608
|
const childinstanceLocation = jsonPointerJoin(instanceLocation, childKey);
|
|
2441
2609
|
let childNode = oldChildrenMap.get(childinstanceLocation);
|
|
2442
2610
|
if (childNode) {
|
|
@@ -2449,7 +2617,8 @@ var SchemaRuntime = class {
|
|
|
2449
2617
|
);
|
|
2450
2618
|
}
|
|
2451
2619
|
childNode.canRemove = canRemove;
|
|
2452
|
-
|
|
2620
|
+
childNode.isRequired = isRequired;
|
|
2621
|
+
this.buildNode(childNode, childSchema, { ...options, isRequired });
|
|
2453
2622
|
newChildren.push(childNode);
|
|
2454
2623
|
};
|
|
2455
2624
|
switch (type) {
|
|
@@ -2462,11 +2631,13 @@ var SchemaRuntime = class {
|
|
|
2462
2631
|
effectiveSchema.properties
|
|
2463
2632
|
)) {
|
|
2464
2633
|
processedKeys.add(key);
|
|
2634
|
+
const isChildRequired = effectiveSchema.required?.includes(key) ?? false;
|
|
2465
2635
|
processChild(
|
|
2466
2636
|
key,
|
|
2467
2637
|
subschema,
|
|
2468
2638
|
`${keywordLocation}/properties/${key}`,
|
|
2469
|
-
false
|
|
2639
|
+
false,
|
|
2640
|
+
isChildRequired
|
|
2470
2641
|
);
|
|
2471
2642
|
}
|
|
2472
2643
|
}
|
|
@@ -2481,7 +2652,9 @@ var SchemaRuntime = class {
|
|
|
2481
2652
|
key,
|
|
2482
2653
|
subschema,
|
|
2483
2654
|
`${keywordLocation}/patternProperties/${jsonPointerEscape(pattern)}`,
|
|
2484
|
-
true
|
|
2655
|
+
true,
|
|
2656
|
+
false
|
|
2657
|
+
// patternProperties are never required
|
|
2485
2658
|
);
|
|
2486
2659
|
}
|
|
2487
2660
|
}
|
|
@@ -2495,7 +2668,9 @@ var SchemaRuntime = class {
|
|
|
2495
2668
|
key,
|
|
2496
2669
|
subschema,
|
|
2497
2670
|
`${keywordLocation}/additionalProperties`,
|
|
2498
|
-
true
|
|
2671
|
+
true,
|
|
2672
|
+
false
|
|
2673
|
+
// additionalProperties are never required
|
|
2499
2674
|
);
|
|
2500
2675
|
}
|
|
2501
2676
|
}
|
|
@@ -2513,7 +2688,9 @@ var SchemaRuntime = class {
|
|
|
2513
2688
|
String(i),
|
|
2514
2689
|
effectiveSchema.prefixItems[i],
|
|
2515
2690
|
`${keywordLocation}/prefixItems/${i}`,
|
|
2516
|
-
false
|
|
2691
|
+
false,
|
|
2692
|
+
true
|
|
2693
|
+
// array items are always considered required
|
|
2517
2694
|
);
|
|
2518
2695
|
}
|
|
2519
2696
|
}
|
|
@@ -2523,7 +2700,9 @@ var SchemaRuntime = class {
|
|
|
2523
2700
|
String(i),
|
|
2524
2701
|
effectiveSchema.items,
|
|
2525
2702
|
`${keywordLocation}/items`,
|
|
2703
|
+
true,
|
|
2526
2704
|
true
|
|
2705
|
+
// array items are always considered required
|
|
2527
2706
|
);
|
|
2528
2707
|
}
|
|
2529
2708
|
}
|
|
@@ -2540,6 +2719,7 @@ var SchemaRuntime = class {
|
|
|
2540
2719
|
* Build/update a FieldNode in place.
|
|
2541
2720
|
* Updates the node's schema, type, error, and children based on the current value.
|
|
2542
2721
|
* @param schema - Optional. If provided, updates node.originalSchema. Otherwise uses existing.
|
|
2722
|
+
* @param isRequired - Whether this node is required by its parent schema.
|
|
2543
2723
|
*/
|
|
2544
2724
|
buildNode(node, schema, options = {}) {
|
|
2545
2725
|
const { keywordLocation, instanceLocation } = node;
|
|
@@ -2557,17 +2737,25 @@ var SchemaRuntime = class {
|
|
|
2557
2737
|
if (schemaChanged) {
|
|
2558
2738
|
this.updateNodeDependencies(node, schema);
|
|
2559
2739
|
}
|
|
2740
|
+
const isRequired = options.isRequired ?? true;
|
|
2560
2741
|
const { type, effectiveSchema, error } = resolveEffectiveSchema(
|
|
2561
2742
|
this.validator,
|
|
2562
2743
|
node.originalSchema,
|
|
2563
2744
|
value,
|
|
2564
2745
|
keywordLocation,
|
|
2565
|
-
instanceLocation
|
|
2746
|
+
instanceLocation,
|
|
2747
|
+
isRequired
|
|
2566
2748
|
);
|
|
2567
|
-
const
|
|
2568
|
-
const
|
|
2749
|
+
const isInitialBuild = node.version === -1;
|
|
2750
|
+
const effectiveSchemaChanged = isInitialBuild || !deepEqual(effectiveSchema, node.schema) || type !== node.type;
|
|
2751
|
+
const errorChanged = isInitialBuild || !deepEqual(error, node.error);
|
|
2569
2752
|
if (effectiveSchemaChanged) {
|
|
2570
|
-
this.applySchemaDefaults(
|
|
2753
|
+
this.applySchemaDefaults(
|
|
2754
|
+
instanceLocation,
|
|
2755
|
+
effectiveSchema,
|
|
2756
|
+
type,
|
|
2757
|
+
isRequired
|
|
2758
|
+
);
|
|
2571
2759
|
}
|
|
2572
2760
|
node.schema = effectiveSchema;
|
|
2573
2761
|
node.type = type;
|