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