@plasius/schema 1.1.1 → 1.2.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/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  FieldBuilder: () => FieldBuilder,
24
+ IsoLanguageCode: () => IsoLanguageCode,
24
25
  createComponentSchema: () => createComponentSchema,
25
26
  createSchema: () => createSchema,
26
27
  field: () => field,
@@ -28,13 +29,23 @@ __export(index_exports, {
28
29
  getAllSchemas: () => getAllSchemas,
29
30
  getComponentSchema: () => getComponentSchema,
30
31
  getSchemaForType: () => getSchemaForType,
32
+ isExtensionSingleton: () => isExtensionSingleton,
33
+ isExtensionSubtag: () => isExtensionSubtag,
34
+ isIsoLanguageCode: () => isIsoLanguageCode,
35
+ isPrivateUseSingleton: () => isPrivateUseSingleton,
36
+ isPrivateUseSubtag: () => isPrivateUseSubtag,
37
+ isRegionSubtag: () => isRegionSubtag,
38
+ isScriptSubtag: () => isScriptSubtag,
39
+ isVariantSubtag: () => isVariantSubtag,
31
40
  isoCountryCodes: () => isoCountryCodes,
32
41
  isoCurrencyCodes: () => isoCurrencyCodes,
33
42
  registerComponentSchema: () => registerComponentSchema,
43
+ renderSchemaDescription: () => renderSchemaDescription,
34
44
  validateCountryCode: () => validateCountryCode,
35
45
  validateCurrencyCode: () => validateCurrencyCode,
36
46
  validateDateTimeISO: () => validateDateTimeISO,
37
47
  validateEmail: () => validateEmail,
48
+ validateLanguage: () => validateLanguage,
38
49
  validateName: () => validateName,
39
50
  validatePercentage: () => validatePercentage,
40
51
  validatePhone: () => validatePhone,
@@ -466,6 +477,7 @@ var isoCountryCodes = /* @__PURE__ */ new Set([
466
477
  "PM",
467
478
  "PN",
468
479
  "PR",
480
+ "PS",
469
481
  "PT",
470
482
  "PW",
471
483
  "PY",
@@ -668,6 +680,7 @@ var isoCurrencyCodes = /* @__PURE__ */ new Set([
668
680
  "SEK",
669
681
  "SGD",
670
682
  "SHP",
683
+ "SLE",
671
684
  "SLL",
672
685
  "SOS",
673
686
  "SRD",
@@ -1153,49 +1166,151 @@ function enforcePIIField(parentKey, key, value, def, enforcement = "none", error
1153
1166
  return { shortCircuit: false };
1154
1167
  }
1155
1168
  function prepareForStorage(shape, input, encryptFn, hashFn) {
1156
- const result = {};
1157
- for (const key in shape) {
1158
- const def = shape[key];
1159
- if (!def) continue;
1160
- const value = input[key];
1169
+ const build = (def, value, key, out, ctx = {}) => {
1170
+ if (!def) return;
1171
+ const isMissing = value === void 0 || value === null;
1161
1172
  if (def._pii?.action === "encrypt") {
1162
- result[key + "Encrypted"] = encryptFn(value);
1163
- } else if (def._pii?.action === "hash") {
1164
- result[key + "Hash"] = hashFn(value);
1165
- } else {
1166
- result[key] = value;
1173
+ if (!isMissing) out[key + "Encrypted"] = encryptFn(value);
1174
+ return;
1175
+ }
1176
+ if (def._pii?.action === "hash") {
1177
+ if (!isMissing) out[key + "Hash"] = hashFn(value);
1178
+ return;
1179
+ }
1180
+ if (def._pii?.action === "clear") {
1181
+ if (!isMissing) out[key] = null;
1182
+ return;
1183
+ }
1184
+ if (def.type === "object" && def._shape) {
1185
+ const obj = {};
1186
+ for (const [childKey, childDef] of Object.entries(def._shape)) {
1187
+ build(childDef, value?.[childKey], childKey, obj, { parentKey: key });
1188
+ }
1189
+ out[key] = obj;
1190
+ return;
1191
+ }
1192
+ if (def.type === "array" && def.itemType && Array.isArray(value)) {
1193
+ out[key] = value.map((item) => {
1194
+ const obj = {};
1195
+ build(def.itemType, item, key, obj, {
1196
+ parentKey: key,
1197
+ isArrayItem: true
1198
+ });
1199
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1200
+ return obj[key];
1201
+ }
1202
+ return Object.keys(obj).length > 0 ? obj : item;
1203
+ });
1204
+ return;
1205
+ }
1206
+ if (def.type === "ref") {
1207
+ const ref = { ...value };
1208
+ const refShape = def._shape;
1209
+ if (refShape && value) {
1210
+ for (const [childKey, childDef] of Object.entries(refShape)) {
1211
+ build(childDef, value[childKey], childKey, ref, { parentKey: key });
1212
+ }
1213
+ }
1214
+ out[key] = ref;
1215
+ return;
1167
1216
  }
1217
+ out[key] = value;
1218
+ };
1219
+ const result = {};
1220
+ for (const key in shape) {
1221
+ build(shape[key], input?.[key], key, result);
1168
1222
  }
1169
1223
  return result;
1170
1224
  }
1171
1225
  function prepareForRead(shape, stored, decryptFn) {
1226
+ const readValue = (def, key, container, ctx = {}) => {
1227
+ if (!def) return container?.[key];
1228
+ const action = def._pii?.action;
1229
+ if (action === "encrypt") {
1230
+ const enc = container?.[key + "Encrypted"] ?? container?.[`${ctx.parentKey ?? key}Encrypted`];
1231
+ return enc === void 0 ? void 0 : decryptFn(enc);
1232
+ }
1233
+ if (action === "hash") {
1234
+ const h = container?.[key + "Hash"] ?? container?.[`${ctx.parentKey ?? key}Hash`];
1235
+ return h === void 0 ? void 0 : h;
1236
+ }
1237
+ if (action === "clear")
1238
+ return container?.hasOwnProperty(key) ? null : void 0;
1239
+ if (def.type === "object" && def._shape) {
1240
+ const obj = {};
1241
+ const source = container && Object.prototype.hasOwnProperty.call(container, key) ? container[key] : container ?? {};
1242
+ for (const [childKey, childDef] of Object.entries(def._shape)) {
1243
+ obj[childKey] = readValue(childDef, childKey, source);
1244
+ }
1245
+ return obj;
1246
+ }
1247
+ if (def.type === "array" && def.itemType) {
1248
+ const arr = container?.[key];
1249
+ if (!Array.isArray(arr)) return arr;
1250
+ return arr.map(
1251
+ (item) => readValue(def.itemType, key, item, { parentKey: key, isArrayItem: true })
1252
+ );
1253
+ }
1254
+ if (def.type === "ref") {
1255
+ const ref = { ...container?.[key] ?? {} };
1256
+ const refShape = def._shape;
1257
+ if (refShape) {
1258
+ for (const [childKey, childDef] of Object.entries(refShape)) {
1259
+ ref[childKey] = readValue(childDef, childKey, ref);
1260
+ }
1261
+ }
1262
+ return ref;
1263
+ }
1264
+ return container?.[key];
1265
+ };
1172
1266
  const result = {};
1173
1267
  for (const key in shape) {
1174
- const def = shape[key];
1175
- if (!def) continue;
1176
- if (def._pii?.action === "encrypt") {
1177
- result[key] = decryptFn(stored[key + "Encrypted"]);
1178
- } else {
1179
- result[key] = stored[key];
1180
- }
1268
+ result[key] = readValue(shape[key], key, stored);
1181
1269
  }
1182
1270
  return result;
1183
1271
  }
1184
1272
  function sanitizeForLog(shape, data, pseudonymFn) {
1185
- const output = {};
1186
- for (const key in shape) {
1187
- const def = shape[key];
1188
- if (!def) continue;
1189
- const value = data[key];
1273
+ const visit = (def, value, ctx = {}) => {
1274
+ if (!def) return void 0;
1190
1275
  const handling = def._pii?.logHandling;
1191
- if (handling === "omit") continue;
1192
- if (handling === "redact") {
1193
- output[key] = "[REDACTED]";
1194
- } else if (handling === "pseudonym") {
1195
- output[key] = pseudonymFn(value);
1196
- } else {
1197
- output[key] = value;
1276
+ if (handling === "omit") return void 0;
1277
+ if (handling === "redact") return "[REDACTED]";
1278
+ if (handling === "pseudonym") return pseudonymFn(value);
1279
+ if (def.type === "object" && def._shape) {
1280
+ const obj = {};
1281
+ const src = value ?? {};
1282
+ for (const [k, childDef] of Object.entries(def._shape)) {
1283
+ const child = visit(childDef, src[k], { parentKey: k });
1284
+ if (child !== void 0) obj[k] = child;
1285
+ }
1286
+ return obj;
1287
+ }
1288
+ if (def.type === "array" && def.itemType) {
1289
+ if (!Array.isArray(value)) return void 0;
1290
+ const arr = value.map((v) => visit(def.itemType, v, { parentKey: ctx.parentKey })).filter((v) => v !== void 0);
1291
+ return arr;
1292
+ }
1293
+ if (def.type === "ref") {
1294
+ const ref = value ? { type: value.type, id: value.id } : {};
1295
+ const refShape = def._shape;
1296
+ const src = value ?? {};
1297
+ if (refShape) {
1298
+ for (const [k, childDef] of Object.entries(refShape)) {
1299
+ const child = visit(childDef, src[k]);
1300
+ if (child !== void 0) ref[k] = child;
1301
+ }
1302
+ } else if (value) {
1303
+ ref.type = value.type;
1304
+ ref.id = value.id;
1305
+ }
1306
+ return ref;
1198
1307
  }
1308
+ return value;
1309
+ };
1310
+ const output = {};
1311
+ for (const key in shape) {
1312
+ const child = visit(shape[key], data?.[key]);
1313
+ if (child !== void 0) output[key] = child;
1199
1314
  }
1200
1315
  return output;
1201
1316
  }
@@ -1217,23 +1332,106 @@ function getPiiAudit(shape) {
1217
1332
  return piiFields;
1218
1333
  }
1219
1334
  function scrubPiiForDelete(shape, stored) {
1220
- const result = { ...stored };
1221
- for (const key in shape) {
1222
- const def = shape[key];
1223
- if (!def) continue;
1335
+ const result = Array.isArray(stored) ? [...stored] : { ...stored };
1336
+ const setAtPath = (target, path, val) => {
1337
+ let cur = target;
1338
+ for (let i = 0; i < path.length - 1; i++) {
1339
+ const p = path[i];
1340
+ if (p === void 0) return;
1341
+ if (cur[p] === void 0) {
1342
+ const next = path[i + 1];
1343
+ cur[p] = typeof next === "number" ? [] : {};
1344
+ }
1345
+ cur = cur[p];
1346
+ }
1347
+ const last = path[path.length - 1];
1348
+ if (last === void 0) return;
1349
+ cur[last] = val;
1350
+ };
1351
+ const visit = (def, host, path, keyName) => {
1352
+ if (!def) return;
1353
+ const applyPath = (targetKey) => {
1354
+ const last = path[path.length - 1];
1355
+ if (last === void 0) return;
1356
+ if (typeof last === "number") {
1357
+ setAtPath(result, [...path, targetKey], null);
1358
+ } else {
1359
+ setAtPath(result, [...path.slice(0, -1), targetKey], null);
1360
+ }
1361
+ };
1224
1362
  if (def._pii?.action === "encrypt") {
1225
- result[key + "Encrypted"] = null;
1226
- } else if (def._pii?.action === "hash") {
1227
- result[key + "Hash"] = null;
1228
- } else if (def._pii?.action === "clear") {
1229
- result[key] = null;
1363
+ const targetKey = `${keyName}Encrypted`;
1364
+ if (host && Object.prototype.hasOwnProperty.call(host, targetKey)) {
1365
+ applyPath(targetKey);
1366
+ }
1367
+ return;
1230
1368
  }
1369
+ if (def._pii?.action === "hash") {
1370
+ const targetKey = `${keyName}Hash`;
1371
+ if (host && Object.prototype.hasOwnProperty.call(host, targetKey)) {
1372
+ applyPath(targetKey);
1373
+ }
1374
+ return;
1375
+ }
1376
+ if (def._pii?.action === "clear") {
1377
+ if (host && Object.prototype.hasOwnProperty.call(host, keyName)) {
1378
+ setAtPath(result, path, null);
1379
+ }
1380
+ return;
1381
+ }
1382
+ if (def.type === "object" && def._shape) {
1383
+ const obj = host && Object.prototype.hasOwnProperty.call(host, keyName) ? host[keyName] : host;
1384
+ for (const [k, childDef] of Object.entries(def._shape)) {
1385
+ visit(childDef, obj, [...path, k], k);
1386
+ }
1387
+ return;
1388
+ }
1389
+ if (def.type === "array" && def.itemType) {
1390
+ const arr = host?.[keyName];
1391
+ if (Array.isArray(arr)) {
1392
+ arr.forEach(
1393
+ (item, idx) => visit(def.itemType, item, [...path, idx], keyName)
1394
+ );
1395
+ }
1396
+ return;
1397
+ }
1398
+ if (def.type === "ref") {
1399
+ const refShape = def._shape;
1400
+ const ref = host?.[keyName];
1401
+ if (refShape && ref) {
1402
+ for (const [k, childDef] of Object.entries(refShape)) {
1403
+ visit(childDef, ref, [...path, k], k);
1404
+ }
1405
+ }
1406
+ }
1407
+ };
1408
+ for (const key in shape) {
1409
+ visit(shape[key], stored, [key], key);
1231
1410
  }
1232
1411
  return result;
1233
1412
  }
1234
1413
 
1235
1414
  // src/schema.ts
1236
1415
  var globalSchemaRegistry = /* @__PURE__ */ new Map();
1416
+ function deepClone(value) {
1417
+ const seen = /* @__PURE__ */ new WeakMap();
1418
+ const cloneAny = (val) => {
1419
+ if (val === null || typeof val !== "object") return val;
1420
+ if (seen.has(val)) throw new TypeError("Cannot clone circular structures");
1421
+ if (val instanceof Date) return new Date(val.getTime());
1422
+ if (Array.isArray(val)) {
1423
+ const arr = [];
1424
+ seen.set(val, arr);
1425
+ val.forEach((item) => arr.push(cloneAny(item)));
1426
+ return arr;
1427
+ }
1428
+ const out = {};
1429
+ seen.set(val, out);
1430
+ for (const [k, v] of Object.entries(val)) out[k] = cloneAny(v);
1431
+ return out;
1432
+ };
1433
+ return cloneAny(value);
1434
+ }
1237
1435
  function cmpSemver(a, b) {
1238
1436
  const pa = a.split(".").map((n) => parseInt(n, 10));
1239
1437
  const pb = b.split(".").map((n) => parseInt(n, 10));
@@ -1292,8 +1490,17 @@ function getEnumValues(def) {
1292
1490
  );
1293
1491
  return ok ? src : void 0;
1294
1492
  }
1493
+ function applyDefault(def, current) {
1494
+ if ((current === void 0 || current === null) && typeof def?.getDefault === "function") {
1495
+ const next = def.getDefault();
1496
+ if (next !== void 0) {
1497
+ return { value: next, applied: true };
1498
+ }
1499
+ }
1500
+ return { value: current, applied: false };
1501
+ }
1295
1502
  function isOptional(def) {
1296
- return (def?.isRequired ?? false) === false;
1503
+ return def?.isRequired === false;
1297
1504
  }
1298
1505
  function getValidator(def) {
1299
1506
  return def?._validator ?? void 0;
@@ -1345,10 +1552,18 @@ function validateStringField(parentKey, key, value, def, errors) {
1345
1552
  }
1346
1553
  }
1347
1554
  }
1348
- function validateNumberField(parentKey, key, value, _def, errors) {
1349
- const enumPath = parentKey ? `${parentKey}.${key}` : key;
1555
+ function validateNumberField(parentKey, key, value, def, errors) {
1556
+ const path = parentKey ? `${parentKey}.${key}` : key;
1350
1557
  if (typeof value !== "number") {
1351
- errors.push(`Field ${enumPath} must be number`);
1558
+ errors.push(`Field ${path} must be number`);
1559
+ return;
1560
+ }
1561
+ const enumValues = getEnumValues(def);
1562
+ if (Array.isArray(enumValues)) {
1563
+ const enumError = validateEnum(path, value, enumValues);
1564
+ if (enumError) {
1565
+ errors.push(enumError);
1566
+ }
1352
1567
  }
1353
1568
  }
1354
1569
  function validateBooleanField(parentKey, key, value, _def, errors) {
@@ -1357,9 +1572,15 @@ function validateBooleanField(parentKey, key, value, _def, errors) {
1357
1572
  errors.push(`Field ${path} must be boolean`);
1358
1573
  }
1359
1574
  }
1360
- function validateObjectChildren(parentKey, obj, shape, errors) {
1575
+ function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnforcement, logger) {
1361
1576
  for (const [childKey, childDef] of Object.entries(shape)) {
1362
- const childValue = obj[childKey];
1577
+ let childValue = obj[childKey];
1578
+ const existingChild = existing && typeof existing === "object" ? existing[childKey] : void 0;
1579
+ const { value: withDefault, applied } = applyDefault(childDef, childValue);
1580
+ if (applied) {
1581
+ obj[childKey] = withDefault;
1582
+ childValue = withDefault;
1583
+ }
1363
1584
  const { missing } = checkMissingRequired(
1364
1585
  parentKey,
1365
1586
  childKey,
@@ -1368,6 +1589,25 @@ function validateObjectChildren(parentKey, obj, shape, errors) {
1368
1589
  errors
1369
1590
  );
1370
1591
  if (missing) continue;
1592
+ const { immutableViolation } = checkImmutable(
1593
+ parentKey,
1594
+ childKey,
1595
+ childValue,
1596
+ childDef,
1597
+ existing,
1598
+ errors
1599
+ );
1600
+ if (immutableViolation) continue;
1601
+ const { shortCircuit } = enforcePIIField(
1602
+ parentKey,
1603
+ childKey,
1604
+ childValue,
1605
+ childDef,
1606
+ piiEnforcement,
1607
+ errors,
1608
+ logger
1609
+ );
1610
+ if (shortCircuit) continue;
1371
1611
  const { invalid } = runCustomValidator(
1372
1612
  parentKey,
1373
1613
  childKey,
@@ -1376,17 +1616,27 @@ function validateObjectChildren(parentKey, obj, shape, errors) {
1376
1616
  errors
1377
1617
  );
1378
1618
  if (invalid) continue;
1379
- validateByType(parentKey, childKey, childValue, childDef, errors);
1619
+ validateByType(
1620
+ parentKey,
1621
+ childKey,
1622
+ childValue,
1623
+ childDef,
1624
+ errors,
1625
+ existingChild,
1626
+ piiEnforcement,
1627
+ logger
1628
+ );
1380
1629
  }
1381
1630
  }
1382
- function validateObjectField(parentKey, key, value, def, errors) {
1631
+ function validateObjectField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1383
1632
  const path = parentKey ? `${parentKey}.${key}` : key;
1384
1633
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
1385
1634
  errors.push(`Field ${path} must be object`);
1386
1635
  return;
1387
1636
  }
1388
1637
  const objShape = getShape(def);
1389
- if (objShape) validateObjectChildren(path, value, objShape, errors);
1638
+ if (objShape)
1639
+ validateObjectChildren(path, value, objShape, errors, existing, piiEnforcement, logger);
1390
1640
  }
1391
1641
  function validateArrayOfStrings(parentKey, key, arr, itemDef, errors) {
1392
1642
  const path = parentKey ? `${parentKey}.${key}` : key;
@@ -1401,6 +1651,14 @@ function validateArrayOfStrings(parentKey, key, arr, itemDef, errors) {
1401
1651
  errors.push(enumError);
1402
1652
  }
1403
1653
  }
1654
+ const validator = getValidator(itemDef);
1655
+ if (validator) {
1656
+ arr.forEach((v, idx) => {
1657
+ if (!validator(v)) {
1658
+ errors.push(`Invalid value for field: ${path}[${idx}]`);
1659
+ }
1660
+ });
1661
+ }
1404
1662
  }
1405
1663
  function validateArrayOfNumbers(parentKey, key, arr, itemDef, errors) {
1406
1664
  const path = parentKey ? `${parentKey}.${key}` : key;
@@ -1414,14 +1672,30 @@ function validateArrayOfNumbers(parentKey, key, arr, itemDef, errors) {
1414
1672
  errors.push(enumError);
1415
1673
  }
1416
1674
  }
1675
+ const validator = getValidator(itemDef);
1676
+ if (validator) {
1677
+ arr.forEach((v, idx) => {
1678
+ if (!validator(v)) {
1679
+ errors.push(`Invalid value for field: ${path}[${idx}]`);
1680
+ }
1681
+ });
1682
+ }
1417
1683
  }
1418
1684
  function validateArrayOfBooleans(parentKey, key, arr, _itemDef, errors) {
1419
1685
  const path = parentKey ? `${parentKey}.${key}` : key;
1420
1686
  if (!arr.every((v) => typeof v === "boolean")) {
1421
1687
  errors.push(`Field ${path} must be boolean[]`);
1422
1688
  }
1689
+ const validator = getValidator(_itemDef);
1690
+ if (validator) {
1691
+ arr.forEach((v, idx) => {
1692
+ if (!validator(v)) {
1693
+ errors.push(`Invalid value for field: ${path}[${idx}]`);
1694
+ }
1695
+ });
1696
+ }
1423
1697
  }
1424
- function validateArrayOfObjects(parentKey, key, arr, itemDef, errors) {
1698
+ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing, piiEnforcement, logger) {
1425
1699
  const path = parentKey ? `${parentKey}.${key}` : key;
1426
1700
  if (!Array.isArray(arr) || !arr.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
1427
1701
  errors.push(`Field ${path} must be object[]`);
@@ -1431,8 +1705,18 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors) {
1431
1705
  if (!itemShape) return;
1432
1706
  arr.forEach((item, idx) => {
1433
1707
  const itemParent = `${path}[${idx}]`;
1708
+ const existingItem = Array.isArray(existing) ? existing[idx] : void 0;
1434
1709
  for (const [childKey, childDef] of Object.entries(itemShape)) {
1435
- const childValue = item[childKey];
1710
+ let childValue = item[childKey];
1711
+ const existingChild = existingItem && typeof existingItem === "object" ? existingItem[childKey] : void 0;
1712
+ const { value: withDefault, applied } = applyDefault(
1713
+ childDef,
1714
+ childValue
1715
+ );
1716
+ if (applied) {
1717
+ item[childKey] = withDefault;
1718
+ childValue = withDefault;
1719
+ }
1436
1720
  const { missing } = checkMissingRequired(
1437
1721
  itemParent,
1438
1722
  childKey,
@@ -1441,6 +1725,25 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors) {
1441
1725
  errors
1442
1726
  );
1443
1727
  if (missing) continue;
1728
+ const { immutableViolation } = checkImmutable(
1729
+ itemParent,
1730
+ childKey,
1731
+ childValue,
1732
+ childDef,
1733
+ existingItem,
1734
+ errors
1735
+ );
1736
+ if (immutableViolation) continue;
1737
+ const { shortCircuit } = enforcePIIField(
1738
+ itemParent,
1739
+ childKey,
1740
+ childValue,
1741
+ childDef,
1742
+ piiEnforcement,
1743
+ errors,
1744
+ logger
1745
+ );
1746
+ if (shortCircuit) continue;
1444
1747
  const { invalid } = runCustomValidator(
1445
1748
  itemParent,
1446
1749
  childKey,
@@ -1449,11 +1752,20 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors) {
1449
1752
  errors
1450
1753
  );
1451
1754
  if (invalid) continue;
1452
- validateByType(itemParent, childKey, childValue, childDef, errors);
1755
+ validateByType(
1756
+ itemParent,
1757
+ childKey,
1758
+ childValue,
1759
+ childDef,
1760
+ errors,
1761
+ existingChild,
1762
+ piiEnforcement,
1763
+ logger
1764
+ );
1453
1765
  }
1454
1766
  });
1455
1767
  }
1456
- function validateArrayField(parentKey, key, value, def, errors) {
1768
+ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1457
1769
  const path = parentKey ? `${parentKey}.${key}` : key;
1458
1770
  if (!Array.isArray(value)) {
1459
1771
  errors.push(`Field ${key} must be an array`);
@@ -1467,7 +1779,16 @@ function validateArrayField(parentKey, key, value, def, errors) {
1467
1779
  if (itemType === "boolean")
1468
1780
  return validateArrayOfBooleans(parentKey, key, value, def.itemType, errors);
1469
1781
  if (itemType === "object")
1470
- return validateArrayOfObjects(parentKey, key, value, def.itemType, errors);
1782
+ return validateArrayOfObjects(
1783
+ parentKey,
1784
+ key,
1785
+ value,
1786
+ def.itemType,
1787
+ errors,
1788
+ existing,
1789
+ piiEnforcement,
1790
+ logger
1791
+ );
1471
1792
  if (itemType === "ref") {
1472
1793
  const expectedType = def.itemType.refType;
1473
1794
  value.forEach((ref, idx) => {
@@ -1479,24 +1800,63 @@ function validateArrayField(parentKey, key, value, def, errors) {
1479
1800
  });
1480
1801
  const refShape = getShape(def.itemType);
1481
1802
  if (refShape) {
1803
+ const existingRefs = Array.isArray(existing) ? existing : [];
1482
1804
  value.forEach((ref, idx) => {
1483
1805
  if (ref && typeof ref === "object" && ref !== null) {
1806
+ const existingRef = existingRefs[idx];
1484
1807
  for (const [childKey, childDef] of Object.entries(refShape)) {
1485
- const childValue = ref[childKey];
1808
+ const childPath = `${path}[${idx}].${childKey}`;
1809
+ let childValue = ref[childKey];
1810
+ const existingChild = existingRef && typeof existingRef === "object" ? existingRef[childKey] : void 0;
1811
+ const { value: withDefault, applied } = applyDefault(
1812
+ childDef,
1813
+ childValue
1814
+ );
1815
+ if (applied) {
1816
+ ref[childKey] = withDefault;
1817
+ childValue = withDefault;
1818
+ }
1486
1819
  if ((childValue === void 0 || childValue === null) && !isOptional(childDef)) {
1487
- errors.push(
1488
- `Missing required field: ${path}[${idx}].${childKey}`
1489
- );
1820
+ errors.push(`Missing required field: ${childPath}`);
1490
1821
  continue;
1491
1822
  }
1823
+ const { immutableViolation } = checkImmutable(
1824
+ `${path}[${idx}]`,
1825
+ childKey,
1826
+ childValue,
1827
+ childDef,
1828
+ existingRef,
1829
+ errors
1830
+ );
1831
+ if (immutableViolation) continue;
1832
+ const { shortCircuit } = enforcePIIField(
1833
+ `${path}[${idx}]`,
1834
+ childKey,
1835
+ childValue,
1836
+ childDef,
1837
+ piiEnforcement,
1838
+ errors,
1839
+ logger
1840
+ );
1841
+ if (shortCircuit) continue;
1492
1842
  const childValidator = getValidator(childDef);
1493
1843
  if (childValidator && childValue !== void 0 && childValue !== null) {
1494
1844
  const valid = childValidator(childValue);
1495
- if (!valid)
1496
- errors.push(
1497
- `Invalid value for field: ${path}[${idx}].${childKey}`
1498
- );
1845
+ if (!valid) {
1846
+ errors.push(`Invalid value for field: ${childPath}`);
1847
+ continue;
1848
+ }
1499
1849
  }
1850
+ validateByType(
1851
+ `${path}[${idx}]`,
1852
+ childKey,
1853
+ childValue,
1854
+ childDef,
1855
+ errors,
1856
+ existingChild,
1857
+ piiEnforcement,
1858
+ logger
1859
+ );
1500
1860
  }
1501
1861
  }
1502
1862
  });
@@ -1505,13 +1865,20 @@ function validateArrayField(parentKey, key, value, def, errors) {
1505
1865
  }
1506
1866
  errors.push(`Field ${path} has unsupported array item type`);
1507
1867
  }
1508
- function validateRefField(parentKey, key, value, _def, errors) {
1868
+ function validateRefField(parentKey, key, value, def, errors, _existing, _piiEnforcement, _logger) {
1869
+ const path = parentKey ? `${parentKey}.${key}` : key;
1509
1870
  if (typeof value !== "object" || value === null || typeof value.type !== "string" || typeof value.id !== "string") {
1510
- const path = parentKey ? `${parentKey}.${key}` : key;
1511
1871
  errors.push(`Field ${path} must be { type: string; id: string }`);
1872
+ return;
1873
+ }
1874
+ const expectedType = def.refType;
1875
+ if (expectedType && value.type !== expectedType) {
1876
+ errors.push(
1877
+ `Field ${path} must reference type: ${expectedType} (got ${value.type})`
1878
+ );
1512
1879
  }
1513
1880
  }
1514
- function validateByType(parentKey, key, value, def, errors) {
1881
+ function validateByType(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1515
1882
  const path = parentKey ? `${parentKey}.${key}` : key;
1516
1883
  switch (def.type) {
1517
1884
  case "string":
@@ -1521,11 +1888,29 @@ function validateByType(parentKey, key, value, def, errors) {
1521
1888
  case "boolean":
1522
1889
  return validateBooleanField(parentKey, key, value, def, errors);
1523
1890
  case "object":
1524
- return validateObjectField(parentKey, key, value, def, errors);
1891
+ return validateObjectField(
1892
+ parentKey,
1893
+ key,
1894
+ value,
1895
+ def,
1896
+ errors,
1897
+ existing,
1898
+ piiEnforcement,
1899
+ logger
1900
+ );
1525
1901
  case "array":
1526
- return validateArrayField(parentKey, key, value, def, errors);
1902
+ return validateArrayField(
1903
+ parentKey,
1904
+ key,
1905
+ value,
1906
+ def,
1907
+ errors,
1908
+ existing,
1909
+ piiEnforcement,
1910
+ logger
1911
+ );
1527
1912
  case "ref":
1528
- return validateRefField(parentKey, key, value, def, errors);
1913
+ return validateRefField(parentKey, key, value, def, errors, existing, piiEnforcement, logger);
1529
1914
  default:
1530
1915
  errors.push(`Unknown type for field ${path}: ${def.type}`);
1531
1916
  }
@@ -1556,10 +1941,11 @@ function createSchema(_shape, entityType, options = {
1556
1941
  validate(input, existing) {
1557
1942
  const errors = [];
1558
1943
  const result = {};
1944
+ const piiMode = options.piiEnforcement ?? "none";
1559
1945
  if (typeof input !== "object" || input === null) {
1560
1946
  return { valid: false, errors: ["Input must be an object"] };
1561
1947
  }
1562
- const working = { ...input };
1948
+ const working = typeof input === "object" && input !== null ? deepClone(input) : { ...input };
1563
1949
  if (working.type == null) working.type = entityType;
1564
1950
  if (working.version == null) working.version = version;
1565
1951
  const fromVersion = String(working.version ?? "0.0.0");
@@ -1583,11 +1969,17 @@ function createSchema(_shape, entityType, options = {
1583
1969
  }
1584
1970
  for (const key in schema._shape) {
1585
1971
  const def = schema._shape[key];
1586
- const value = working[key];
1972
+ let value = working[key];
1973
+ const existingField = existing && typeof existing === "object" ? existing[key] : void 0;
1587
1974
  if (!def) {
1588
1975
  errors.push(`Field definition missing for: ${key}`);
1589
1976
  continue;
1590
1977
  }
1978
+ const { value: withDefault, applied } = applyDefault(def, value);
1979
+ if (applied) {
1980
+ working[key] = withDefault;
1981
+ value = withDefault;
1982
+ }
1591
1983
  const { missing } = checkMissingRequired("", key, value, def, errors);
1592
1984
  if (missing) continue;
1593
1985
  const { immutableViolation } = checkImmutable(
@@ -1604,7 +1996,7 @@ function createSchema(_shape, entityType, options = {
1604
1996
  key,
1605
1997
  value,
1606
1998
  def,
1607
- options.piiEnforcement ?? "none",
1999
+ piiMode,
1608
2000
  errors,
1609
2001
  console
1610
2002
  );
@@ -1619,7 +2011,7 @@ function createSchema(_shape, entityType, options = {
1619
2011
  localErrors
1620
2012
  );
1621
2013
  if (!invalid) {
1622
- validateByType("", key, val, def, localErrors);
2014
+ validateByType("", key, val, def, localErrors, existingField, piiMode, console);
1623
2015
  }
1624
2016
  return localErrors;
1625
2017
  };
@@ -1741,13 +2133,20 @@ function createSchema(_shape, entityType, options = {
1741
2133
  log?.(`Skipping field ${key} (not in onlyFields)`);
1742
2134
  continue;
1743
2135
  }
1744
- const refType = def.refType;
1745
2136
  const autoValidate = def.autoValidate !== false;
1746
2137
  const refPolicy = def.refPolicy ?? "eager";
1747
2138
  const value = entity[key];
1748
2139
  if (!value) continue;
1749
2140
  if (def.type === "ref") {
2141
+ const refType = def.refType;
2142
+ if (!refType)
2143
+ throw new Error(`Missing refType for reference field ${key}`);
1750
2144
  const ref = value;
2145
+ if (ref.type && ref.type !== refType) {
2146
+ throw new Error(
2147
+ `Reference type mismatch for field ${key}: expected ${refType}, got ${ref.type}`
2148
+ );
2149
+ }
1751
2150
  const target = await options2.resolveEntity(refType, ref.id);
1752
2151
  if (!target)
1753
2152
  throw new Error(
@@ -1764,8 +2163,16 @@ function createSchema(_shape, entityType, options = {
1764
2163
  }
1765
2164
  }
1766
2165
  } else if (def.type === "array" && def.itemType?.type === "ref") {
2166
+ const refType = def.itemType.refType;
2167
+ if (!refType)
2168
+ throw new Error(`Missing refType for reference array field ${key}`);
1767
2169
  const refs = value;
1768
2170
  for (const ref of refs) {
2171
+ if (ref.type && ref.type !== refType) {
2172
+ throw new Error(
2173
+ `Reference type mismatch for field ${key}: expected ${refType}, got ${ref.type}`
2174
+ );
2175
+ }
1769
2176
  const target = await options2.resolveEntity(refType, ref.id);
1770
2177
  if (!target)
1771
2178
  throw new Error(
@@ -1856,12 +2263,16 @@ function createSchema(_shape, entityType, options = {
1856
2263
  for (const [key, def] of Object.entries(schema._shape)) {
1857
2264
  description[key] = {
1858
2265
  type: def.type,
1859
- optional: !!def.optional,
2266
+ optional: isOptional(def),
2267
+ immutable: !!def.isImmutable,
2268
+ system: !!def.isSystem,
1860
2269
  description: def._description ?? "",
1861
2270
  version: def._version ?? "",
1862
- enum: getEnumValues(def),
1863
- refType: def.refType ?? void 0,
1864
- pii: def._pii ?? void 0
2271
+ deprecated: def.deprecated ?? false,
2272
+ deprecatedVersion: def.deprecatedVersion === void 0 ? null : def.deprecatedVersion,
2273
+ enum: getEnumValues(def) ?? null,
2274
+ refType: def.refType ?? null,
2275
+ pii: !def._pii || def._pii.classification === "none" ? null : def._pii
1865
2276
  };
1866
2277
  }
1867
2278
  return {
@@ -1881,6 +2292,20 @@ function getSchemaForType(type) {
1881
2292
  function getAllSchemas() {
1882
2293
  return Array.from(globalSchemaRegistry.values());
1883
2294
  }
2295
+ function renderSchemaDescription(schema) {
2296
+ const meta = schema.describe();
2297
+ return {
2298
+ title: `${meta.entityType} (v${meta.version})`,
2299
+ fields: Object.entries(meta.shape).map(([name, def]) => ({
2300
+ name,
2301
+ type: def.type,
2302
+ optional: def.optional,
2303
+ description: def.description,
2304
+ deprecated: def.deprecated,
2305
+ pii: def.pii ? def.pii.classification : void 0
2306
+ }))
2307
+ };
2308
+ }
1884
2309
 
1885
2310
  // src/components.ts
1886
2311
  var componentSchemaRegistry = /* @__PURE__ */ new Map();
@@ -1906,6 +2331,7 @@ function getAllComponentSchemas() {
1906
2331
  // Annotate the CommonJS export names for ESM import in node:
1907
2332
  0 && (module.exports = {
1908
2333
  FieldBuilder,
2334
+ IsoLanguageCode,
1909
2335
  createComponentSchema,
1910
2336
  createSchema,
1911
2337
  field,
@@ -1913,13 +2339,23 @@ function getAllComponentSchemas() {
1913
2339
  getAllSchemas,
1914
2340
  getComponentSchema,
1915
2341
  getSchemaForType,
2342
+ isExtensionSingleton,
2343
+ isExtensionSubtag,
2344
+ isIsoLanguageCode,
2345
+ isPrivateUseSingleton,
2346
+ isPrivateUseSubtag,
2347
+ isRegionSubtag,
2348
+ isScriptSubtag,
2349
+ isVariantSubtag,
1916
2350
  isoCountryCodes,
1917
2351
  isoCurrencyCodes,
1918
2352
  registerComponentSchema,
2353
+ renderSchemaDescription,
1919
2354
  validateCountryCode,
1920
2355
  validateCurrencyCode,
1921
2356
  validateDateTimeISO,
1922
2357
  validateEmail,
2358
+ validateLanguage,
1923
2359
  validateName,
1924
2360
  validatePercentage,
1925
2361
  validatePhone,