@plasius/schema 1.2.5 → 1.2.7

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.js CHANGED
@@ -1,4 +1,10 @@
1
1
  // src/field.builder.ts
2
+ function mergeValidatorResult(previousResult, nextValid) {
3
+ if (previousResult !== true) {
4
+ return previousResult;
5
+ }
6
+ return nextValid;
7
+ }
2
8
  var FieldBuilder = class _FieldBuilder {
3
9
  constructor(type, options = {}) {
4
10
  this.type = type;
@@ -25,6 +31,7 @@ var FieldBuilder = class _FieldBuilder {
25
31
  logHandling: "plain",
26
32
  purpose: "an ordinary value"
27
33
  };
34
+ _exposure = "public";
28
35
  enumValues;
29
36
  immutable() {
30
37
  this.isImmutable = true;
@@ -34,6 +41,16 @@ var FieldBuilder = class _FieldBuilder {
34
41
  this.isSystem = true;
35
42
  return this;
36
43
  }
44
+ exposure(exposure) {
45
+ this._exposure = exposure;
46
+ return this;
47
+ }
48
+ internal() {
49
+ return this.exposure("internal");
50
+ }
51
+ public() {
52
+ return this.exposure("public");
53
+ }
37
54
  required() {
38
55
  this.isRequired = true;
39
56
  return this;
@@ -83,19 +100,19 @@ var FieldBuilder = class _FieldBuilder {
83
100
  const prevValidator = this._validator;
84
101
  this._validator = (value) => {
85
102
  const valid = typeof value === "number" && value >= min;
86
- return prevValidator ? prevValidator(value) && valid : valid;
103
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
87
104
  };
88
105
  } else if (this.type === "string") {
89
106
  const prevValidator = this._validator;
90
107
  this._validator = (value) => {
91
108
  const valid = typeof value === "string" && value.length >= min;
92
- return prevValidator ? prevValidator(value) && valid : valid;
109
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
93
110
  };
94
111
  } else if (this.type === "array") {
95
112
  const prevValidator = this._validator;
96
113
  this._validator = (value) => {
97
114
  const valid = Array.isArray(value) && value.length >= min;
98
- return prevValidator ? prevValidator(value) && valid : valid;
115
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
99
116
  };
100
117
  } else {
101
118
  throw new Error(
@@ -109,19 +126,19 @@ var FieldBuilder = class _FieldBuilder {
109
126
  const prevValidator = this._validator;
110
127
  this._validator = (value) => {
111
128
  const valid = typeof value === "number" && value <= max;
112
- return prevValidator ? prevValidator(value) && valid : valid;
129
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
113
130
  };
114
131
  } else if (this.type === "string") {
115
132
  const prevValidator = this._validator;
116
133
  this._validator = (value) => {
117
134
  const valid = typeof value === "string" && value.length <= max;
118
- return prevValidator ? prevValidator(value) && valid : valid;
135
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
119
136
  };
120
137
  } else if (this.type === "array") {
121
138
  const prevValidator = this._validator;
122
139
  this._validator = (value) => {
123
140
  const valid = Array.isArray(value) && value.length <= max;
124
- return prevValidator ? prevValidator(value) && valid : valid;
141
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
125
142
  };
126
143
  } else {
127
144
  throw new Error(
@@ -137,7 +154,7 @@ var FieldBuilder = class _FieldBuilder {
137
154
  const prevValidator = this._validator;
138
155
  this._validator = (value) => {
139
156
  const valid = typeof value === "string" && regex.test(value);
140
- return prevValidator ? prevValidator(value) && valid : valid;
157
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
141
158
  };
142
159
  return this;
143
160
  }
@@ -168,6 +185,7 @@ var FieldBuilder = class _FieldBuilder {
168
185
  clone._description = this._description;
169
186
  clone._version = this._version;
170
187
  clone._pii = this._pii;
188
+ clone._exposure = this._exposure;
171
189
  clone._validator = this._validator;
172
190
  clone._default = this._default;
173
191
  clone._upgrade = this._upgrade;
@@ -1371,6 +1389,94 @@ function deepClone(value) {
1371
1389
  };
1372
1390
  return cloneAny(value);
1373
1391
  }
1392
+ function shouldIncludeField(def, options) {
1393
+ return options.includeInternal === true || (def?._exposure ?? "public") !== "internal";
1394
+ }
1395
+ function serializeFieldValue(def, value, options) {
1396
+ if (value === void 0) {
1397
+ return void 0;
1398
+ }
1399
+ if (value === null) {
1400
+ return null;
1401
+ }
1402
+ switch (def?.type) {
1403
+ case "string":
1404
+ case "number":
1405
+ case "boolean":
1406
+ return deepClone(value);
1407
+ case "ref": {
1408
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1409
+ return deepClone(value);
1410
+ }
1411
+ const shape = getShape(def);
1412
+ if (!shape || Object.keys(shape).length === 0) {
1413
+ return deepClone(value);
1414
+ }
1415
+ const serializedRef = {};
1416
+ if (typeof value.type === "string") {
1417
+ serializedRef.type = value.type;
1418
+ }
1419
+ if (typeof value.id === "string") {
1420
+ serializedRef.id = value.id;
1421
+ }
1422
+ for (const [childKey, childDef] of Object.entries(shape)) {
1423
+ if (!shouldIncludeField(childDef, options)) {
1424
+ continue;
1425
+ }
1426
+ if (!Object.prototype.hasOwnProperty.call(value, childKey)) {
1427
+ continue;
1428
+ }
1429
+ const serializedChild = serializeFieldValue(
1430
+ childDef,
1431
+ value[childKey],
1432
+ options
1433
+ );
1434
+ if (serializedChild !== void 0) {
1435
+ serializedRef[childKey] = serializedChild;
1436
+ }
1437
+ }
1438
+ return serializedRef;
1439
+ }
1440
+ case "object": {
1441
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1442
+ return deepClone(value);
1443
+ }
1444
+ const shape = getShape(def);
1445
+ if (!shape || Object.keys(shape).length === 0) {
1446
+ return deepClone(value);
1447
+ }
1448
+ const serializedObject = {};
1449
+ for (const [childKey, childDef] of Object.entries(shape)) {
1450
+ if (!shouldIncludeField(childDef, options)) {
1451
+ continue;
1452
+ }
1453
+ if (!Object.prototype.hasOwnProperty.call(value, childKey)) {
1454
+ continue;
1455
+ }
1456
+ const serializedChild = serializeFieldValue(
1457
+ childDef,
1458
+ value[childKey],
1459
+ options
1460
+ );
1461
+ if (serializedChild !== void 0) {
1462
+ serializedObject[childKey] = serializedChild;
1463
+ }
1464
+ }
1465
+ return serializedObject;
1466
+ }
1467
+ case "array": {
1468
+ if (!Array.isArray(value)) {
1469
+ return deepClone(value);
1470
+ }
1471
+ if (!def.itemType) {
1472
+ return deepClone(value);
1473
+ }
1474
+ return value.map((item) => serializeFieldValue(def.itemType, item, options));
1475
+ }
1476
+ default:
1477
+ return deepClone(value);
1478
+ }
1479
+ }
1374
1480
  function cmpSemver(a, b) {
1375
1481
  const pa = a.split(".").map((n) => parseInt(n, 10));
1376
1482
  const pb = b.split(".").map((n) => parseInt(n, 10));
@@ -1447,6 +1553,38 @@ function getValidator(def) {
1447
1553
  function getShape(def) {
1448
1554
  return def?._shape ?? def?.shape ?? void 0;
1449
1555
  }
1556
+ function normalizeValidationIssue(path, issue, defaultCode) {
1557
+ if (typeof issue === "string") {
1558
+ return {
1559
+ path,
1560
+ code: defaultCode,
1561
+ message: issue
1562
+ };
1563
+ }
1564
+ return {
1565
+ path: issue.path ?? path,
1566
+ code: issue.code || defaultCode,
1567
+ message: issue.message
1568
+ };
1569
+ }
1570
+ function normalizeValidatorIssues(path, result, defaultCode) {
1571
+ if (result === true) {
1572
+ return [];
1573
+ }
1574
+ if (result === false) {
1575
+ return [
1576
+ {
1577
+ path,
1578
+ code: defaultCode,
1579
+ message: `Invalid value for field: ${path}`
1580
+ }
1581
+ ];
1582
+ }
1583
+ if (Array.isArray(result)) {
1584
+ return result.map((issue) => normalizeValidationIssue(path, issue, defaultCode));
1585
+ }
1586
+ return [normalizeValidationIssue(path, result, defaultCode)];
1587
+ }
1450
1588
  function checkMissingRequired(parentKey, key, value, def, errors) {
1451
1589
  if (value === void 0 || value === null) {
1452
1590
  const path = parentKey ? `${parentKey}.${key}` : key;
@@ -1465,13 +1603,16 @@ function checkImmutable(parentKey, key, value, def, existing, errors) {
1465
1603
  }
1466
1604
  return { immutableViolation: false };
1467
1605
  }
1468
- function runCustomValidator(parentKey, key, value, def, errors) {
1606
+ function runCustomValidator(parentKey, key, value, def, errors, issues) {
1469
1607
  const validator = getValidator(def);
1470
1608
  if (validator && value !== void 0 && value !== null) {
1471
- const valid = validator(value);
1472
- if (!valid) {
1473
- const path = parentKey ? `${parentKey}.${key}` : key;
1474
- errors.push(`Invalid value for field: ${path}`);
1609
+ const path = parentKey ? `${parentKey}.${key}` : key;
1610
+ const normalizedIssues = normalizeValidatorIssues(path, validator(value), "invalid_value");
1611
+ if (normalizedIssues.length > 0) {
1612
+ errors.push(...normalizedIssues.map((issue) => issue.message));
1613
+ if (issues) {
1614
+ issues.push(...normalizedIssues);
1615
+ }
1475
1616
  return { invalid: true };
1476
1617
  }
1477
1618
  }
@@ -1511,7 +1652,7 @@ function validateBooleanField(parentKey, key, value, _def, errors) {
1511
1652
  errors.push(`Field ${path} must be boolean`);
1512
1653
  }
1513
1654
  }
1514
- function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnforcement, logger) {
1655
+ function validateObjectChildren(parentKey, obj, shape, errors, issues, existing, piiEnforcement, logger) {
1515
1656
  for (const [childKey, childDef] of Object.entries(shape)) {
1516
1657
  let childValue = obj[childKey];
1517
1658
  const existingChild = existing && typeof existing === "object" ? existing[childKey] : void 0;
@@ -1552,7 +1693,8 @@ function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnfo
1552
1693
  childKey,
1553
1694
  childValue,
1554
1695
  childDef,
1555
- errors
1696
+ errors,
1697
+ issues
1556
1698
  );
1557
1699
  if (invalid) continue;
1558
1700
  validateByType(
@@ -1561,13 +1703,14 @@ function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnfo
1561
1703
  childValue,
1562
1704
  childDef,
1563
1705
  errors,
1706
+ issues,
1564
1707
  existingChild,
1565
1708
  piiEnforcement,
1566
1709
  logger
1567
1710
  );
1568
1711
  }
1569
1712
  }
1570
- function validateObjectField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1713
+ function validateObjectField(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
1571
1714
  const path = parentKey ? `${parentKey}.${key}` : key;
1572
1715
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
1573
1716
  errors.push(`Field ${path} must be object`);
@@ -1575,7 +1718,7 @@ function validateObjectField(parentKey, key, value, def, errors, existing, piiEn
1575
1718
  }
1576
1719
  const objShape = getShape(def);
1577
1720
  if (objShape)
1578
- validateObjectChildren(path, value, objShape, errors, existing, piiEnforcement, logger);
1721
+ validateObjectChildren(path, value, objShape, errors, issues, existing, piiEnforcement, logger);
1579
1722
  }
1580
1723
  function validateArrayOfStrings(parentKey, key, arr, itemDef, errors) {
1581
1724
  const path = parentKey ? `${parentKey}.${key}` : key;
@@ -1634,7 +1777,7 @@ function validateArrayOfBooleans(parentKey, key, arr, _itemDef, errors) {
1634
1777
  });
1635
1778
  }
1636
1779
  }
1637
- function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing, piiEnforcement, logger) {
1780
+ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, issues, existing, piiEnforcement, logger) {
1638
1781
  const path = parentKey ? `${parentKey}.${key}` : key;
1639
1782
  if (!Array.isArray(arr) || !arr.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
1640
1783
  errors.push(`Field ${path} must be object[]`);
@@ -1688,7 +1831,8 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing,
1688
1831
  childKey,
1689
1832
  childValue,
1690
1833
  childDef,
1691
- errors
1834
+ errors,
1835
+ issues
1692
1836
  );
1693
1837
  if (invalid) continue;
1694
1838
  validateByType(
@@ -1697,6 +1841,7 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing,
1697
1841
  childValue,
1698
1842
  childDef,
1699
1843
  errors,
1844
+ issues,
1700
1845
  existingChild,
1701
1846
  piiEnforcement,
1702
1847
  logger
@@ -1704,7 +1849,7 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing,
1704
1849
  }
1705
1850
  });
1706
1851
  }
1707
- function validateArrayField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1852
+ function validateArrayField(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
1708
1853
  const path = parentKey ? `${parentKey}.${key}` : key;
1709
1854
  if (!Array.isArray(value)) {
1710
1855
  errors.push(`Field ${key} must be an array`);
@@ -1724,6 +1869,7 @@ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnf
1724
1869
  value,
1725
1870
  def.itemType,
1726
1871
  errors,
1872
+ issues,
1727
1873
  existing,
1728
1874
  piiEnforcement,
1729
1875
  logger
@@ -1780,9 +1926,14 @@ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnf
1780
1926
  if (shortCircuit) continue;
1781
1927
  const childValidator = getValidator(childDef);
1782
1928
  if (childValidator && childValue !== void 0 && childValue !== null) {
1783
- const valid = childValidator(childValue);
1784
- if (!valid) {
1785
- errors.push(`Invalid value for field: ${childPath}`);
1929
+ const normalizedIssues = normalizeValidatorIssues(
1930
+ childPath,
1931
+ childValidator(childValue),
1932
+ "invalid_value"
1933
+ );
1934
+ if (normalizedIssues.length > 0) {
1935
+ errors.push(...normalizedIssues.map((issue) => issue.message));
1936
+ issues.push(...normalizedIssues);
1786
1937
  continue;
1787
1938
  }
1788
1939
  }
@@ -1792,6 +1943,7 @@ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnf
1792
1943
  childValue,
1793
1944
  childDef,
1794
1945
  errors,
1946
+ issues,
1795
1947
  existingChild,
1796
1948
  piiEnforcement,
1797
1949
  logger
@@ -1817,7 +1969,7 @@ function validateRefField(parentKey, key, value, def, errors, _existing, _piiEnf
1817
1969
  );
1818
1970
  }
1819
1971
  }
1820
- function validateByType(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1972
+ function validateByType(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
1821
1973
  const path = parentKey ? `${parentKey}.${key}` : key;
1822
1974
  switch (def.type) {
1823
1975
  case "string":
@@ -1833,6 +1985,7 @@ function validateByType(parentKey, key, value, def, errors, existing, piiEnforce
1833
1985
  value,
1834
1986
  def,
1835
1987
  errors,
1988
+ issues,
1836
1989
  existing,
1837
1990
  piiEnforcement,
1838
1991
  logger
@@ -1844,6 +1997,7 @@ function validateByType(parentKey, key, value, def, errors, existing, piiEnforce
1844
1997
  value,
1845
1998
  def,
1846
1999
  errors,
2000
+ issues,
1847
2001
  existing,
1848
2002
  piiEnforcement,
1849
2003
  logger
@@ -1879,6 +2033,7 @@ function createSchema(_shape, entityType, options = {
1879
2033
  // 🔗 Validate input against the schema
1880
2034
  validate(input, existing) {
1881
2035
  const errors = [];
2036
+ const issues = [];
1882
2037
  const result = {};
1883
2038
  const piiMode = options.piiEnforcement ?? "none";
1884
2039
  if (typeof input !== "object" || input === null) {
@@ -1942,16 +2097,19 @@ function createSchema(_shape, entityType, options = {
1942
2097
  if (shortCircuit) continue;
1943
2098
  const validateField = (val) => {
1944
2099
  const localErrors = [];
2100
+ const localIssues = [];
1945
2101
  const { invalid } = runCustomValidator(
1946
2102
  "",
1947
2103
  key,
1948
2104
  val,
1949
2105
  def,
1950
- localErrors
2106
+ localErrors,
2107
+ localIssues
1951
2108
  );
1952
2109
  if (!invalid) {
1953
- validateByType("", key, val, def, localErrors, existingField, piiMode, console);
2110
+ validateByType("", key, val, def, localErrors, localIssues, existingField, piiMode, console);
1954
2111
  }
2112
+ issues.push(...localIssues);
1955
2113
  return localErrors;
1956
2114
  };
1957
2115
  let fieldValue = value;
@@ -1992,7 +2150,8 @@ function createSchema(_shape, entityType, options = {
1992
2150
  return {
1993
2151
  valid: errors.length === 0,
1994
2152
  value: result,
1995
- errors
2153
+ errors,
2154
+ issues
1996
2155
  };
1997
2156
  },
1998
2157
  // specific validator for a schema to allow conditional validation
@@ -2142,6 +2301,25 @@ function createSchema(_shape, entityType, options = {
2142
2301
  }
2143
2302
  return store;
2144
2303
  },
2304
+ serialize(input, options2 = {}) {
2305
+ if (typeof input !== "object" || input === null) {
2306
+ return {};
2307
+ }
2308
+ const serialized = {};
2309
+ for (const [key, def] of Object.entries(schema._shape)) {
2310
+ if (!shouldIncludeField(def, options2)) {
2311
+ continue;
2312
+ }
2313
+ if (!Object.prototype.hasOwnProperty.call(input, key)) {
2314
+ continue;
2315
+ }
2316
+ const value = serializeFieldValue(def, input[key], options2);
2317
+ if (value !== void 0) {
2318
+ serialized[key] = value;
2319
+ }
2320
+ }
2321
+ return serialized;
2322
+ },
2145
2323
  /**
2146
2324
  * Transforms an input object for persistence by applying PII protection
2147
2325
  * according to field annotations (e.g., encryption and hashing).
@@ -2211,7 +2389,8 @@ function createSchema(_shape, entityType, options = {
2211
2389
  deprecatedVersion: def.deprecatedVersion === void 0 ? null : def.deprecatedVersion,
2212
2390
  enum: getEnumValues(def) ?? null,
2213
2391
  refType: def.refType ?? null,
2214
- pii: !def._pii || def._pii.classification === "none" ? null : def._pii
2392
+ pii: !def._pii || def._pii.classification === "none" ? null : def._pii,
2393
+ exposure: def._exposure ?? "public"
2215
2394
  };
2216
2395
  }
2217
2396
  return {
@@ -2241,7 +2420,8 @@ function renderSchemaDescription(schema) {
2241
2420
  optional: def.optional,
2242
2421
  description: def.description,
2243
2422
  deprecated: def.deprecated,
2244
- pii: def.pii ? def.pii.classification : void 0
2423
+ pii: def.pii ? def.pii.classification : void 0,
2424
+ exposure: def.exposure
2245
2425
  }))
2246
2426
  };
2247
2427
  }