@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/README.md +42 -0
- package/dist/index.cjs +208 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -4
- package/dist/index.d.ts +32 -4
- package/dist/index.js +208 -28
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -90,6 +90,7 @@ const UserFields = {
|
|
|
90
90
|
Common methods (non‑exhaustive): `.required()`, `.optional()`, `.default(v|fn)`, `.description(text)`, and type‑specific helpers like `.email()`, `.uuid()`, `.min()`, `.max()`, `.enum([...])`.
|
|
91
91
|
Defaults are applied during validation when inputs are missing/`undefined`.
|
|
92
92
|
Fields are required by default; call `.optional()` (or provide `.default()`) to allow omission.
|
|
93
|
+
Use `.internal()` or `.exposure("internal")` on fields that the server may validate/store but must not include in client-facing payloads.
|
|
93
94
|
|
|
94
95
|
### 2) Create a **versioned** schema (enforces `type` + `version`)
|
|
95
96
|
|
|
@@ -144,6 +145,47 @@ if (result.valid && result.errors.length == 0) {
|
|
|
144
145
|
- PII strict/warn enforcement runs on nested fields, preventing empty high-PII subfields from slipping through.
|
|
145
146
|
- Validation deep-clones inputs before applying defaults, so caller-provided objects/arrays aren’t mutated and non-JSON-safe values (e.g., `Date`) are preserved.
|
|
146
147
|
|
|
148
|
+
### 3b) Serialize only client-safe fields
|
|
149
|
+
|
|
150
|
+
Schema serialization is separate from validation. `serialize()` keeps only schema-known fields and drops fields marked `.internal()` unless explicitly requested.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const PersistedUserSchema = createSchema(
|
|
154
|
+
{
|
|
155
|
+
id: field.string(),
|
|
156
|
+
partitionKey: field.string().internal(),
|
|
157
|
+
email: field.email(),
|
|
158
|
+
audit: field.object({
|
|
159
|
+
createdBy: field.string().internal(),
|
|
160
|
+
createdAt: field.dateTimeISO(),
|
|
161
|
+
}),
|
|
162
|
+
},
|
|
163
|
+
"persisted-user",
|
|
164
|
+
{ version: "1.0.0", piiEnforcement: "strict" }
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const publicPayload = PersistedUserSchema.serialize({
|
|
168
|
+
type: "persisted-user",
|
|
169
|
+
version: "1.0.0",
|
|
170
|
+
id: "123",
|
|
171
|
+
partitionKey: "tenant-a",
|
|
172
|
+
email: "alice@example.com",
|
|
173
|
+
audit: {
|
|
174
|
+
createdBy: "admin-1",
|
|
175
|
+
createdAt: "2026-03-09T00:00:00.000Z",
|
|
176
|
+
},
|
|
177
|
+
_rid: "cosmos-only",
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// => {
|
|
181
|
+
// type: "persisted-user",
|
|
182
|
+
// version: "1.0.0",
|
|
183
|
+
// id: "123",
|
|
184
|
+
// email: "alice@example.com",
|
|
185
|
+
// audit: { createdAt: "2026-03-09T00:00:00.000Z" }
|
|
186
|
+
// }
|
|
187
|
+
```
|
|
188
|
+
|
|
147
189
|
### 4) Version enforcement in action
|
|
148
190
|
|
|
149
191
|
If either `type` or `version` doesn’t match the schema, validation fails.
|
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,12 @@ __export(index_exports, {
|
|
|
60
60
|
module.exports = __toCommonJS(index_exports);
|
|
61
61
|
|
|
62
62
|
// src/field.builder.ts
|
|
63
|
+
function mergeValidatorResult(previousResult, nextValid) {
|
|
64
|
+
if (previousResult !== true) {
|
|
65
|
+
return previousResult;
|
|
66
|
+
}
|
|
67
|
+
return nextValid;
|
|
68
|
+
}
|
|
63
69
|
var FieldBuilder = class _FieldBuilder {
|
|
64
70
|
constructor(type, options = {}) {
|
|
65
71
|
this.type = type;
|
|
@@ -86,6 +92,7 @@ var FieldBuilder = class _FieldBuilder {
|
|
|
86
92
|
logHandling: "plain",
|
|
87
93
|
purpose: "an ordinary value"
|
|
88
94
|
};
|
|
95
|
+
_exposure = "public";
|
|
89
96
|
enumValues;
|
|
90
97
|
immutable() {
|
|
91
98
|
this.isImmutable = true;
|
|
@@ -95,6 +102,16 @@ var FieldBuilder = class _FieldBuilder {
|
|
|
95
102
|
this.isSystem = true;
|
|
96
103
|
return this;
|
|
97
104
|
}
|
|
105
|
+
exposure(exposure) {
|
|
106
|
+
this._exposure = exposure;
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
internal() {
|
|
110
|
+
return this.exposure("internal");
|
|
111
|
+
}
|
|
112
|
+
public() {
|
|
113
|
+
return this.exposure("public");
|
|
114
|
+
}
|
|
98
115
|
required() {
|
|
99
116
|
this.isRequired = true;
|
|
100
117
|
return this;
|
|
@@ -144,19 +161,19 @@ var FieldBuilder = class _FieldBuilder {
|
|
|
144
161
|
const prevValidator = this._validator;
|
|
145
162
|
this._validator = (value) => {
|
|
146
163
|
const valid = typeof value === "number" && value >= min;
|
|
147
|
-
return prevValidator ? prevValidator(value)
|
|
164
|
+
return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
|
|
148
165
|
};
|
|
149
166
|
} else if (this.type === "string") {
|
|
150
167
|
const prevValidator = this._validator;
|
|
151
168
|
this._validator = (value) => {
|
|
152
169
|
const valid = typeof value === "string" && value.length >= min;
|
|
153
|
-
return prevValidator ? prevValidator(value)
|
|
170
|
+
return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
|
|
154
171
|
};
|
|
155
172
|
} else if (this.type === "array") {
|
|
156
173
|
const prevValidator = this._validator;
|
|
157
174
|
this._validator = (value) => {
|
|
158
175
|
const valid = Array.isArray(value) && value.length >= min;
|
|
159
|
-
return prevValidator ? prevValidator(value)
|
|
176
|
+
return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
|
|
160
177
|
};
|
|
161
178
|
} else {
|
|
162
179
|
throw new Error(
|
|
@@ -170,19 +187,19 @@ var FieldBuilder = class _FieldBuilder {
|
|
|
170
187
|
const prevValidator = this._validator;
|
|
171
188
|
this._validator = (value) => {
|
|
172
189
|
const valid = typeof value === "number" && value <= max;
|
|
173
|
-
return prevValidator ? prevValidator(value)
|
|
190
|
+
return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
|
|
174
191
|
};
|
|
175
192
|
} else if (this.type === "string") {
|
|
176
193
|
const prevValidator = this._validator;
|
|
177
194
|
this._validator = (value) => {
|
|
178
195
|
const valid = typeof value === "string" && value.length <= max;
|
|
179
|
-
return prevValidator ? prevValidator(value)
|
|
196
|
+
return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
|
|
180
197
|
};
|
|
181
198
|
} else if (this.type === "array") {
|
|
182
199
|
const prevValidator = this._validator;
|
|
183
200
|
this._validator = (value) => {
|
|
184
201
|
const valid = Array.isArray(value) && value.length <= max;
|
|
185
|
-
return prevValidator ? prevValidator(value)
|
|
202
|
+
return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
|
|
186
203
|
};
|
|
187
204
|
} else {
|
|
188
205
|
throw new Error(
|
|
@@ -198,7 +215,7 @@ var FieldBuilder = class _FieldBuilder {
|
|
|
198
215
|
const prevValidator = this._validator;
|
|
199
216
|
this._validator = (value) => {
|
|
200
217
|
const valid = typeof value === "string" && regex.test(value);
|
|
201
|
-
return prevValidator ? prevValidator(value)
|
|
218
|
+
return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
|
|
202
219
|
};
|
|
203
220
|
return this;
|
|
204
221
|
}
|
|
@@ -229,6 +246,7 @@ var FieldBuilder = class _FieldBuilder {
|
|
|
229
246
|
clone._description = this._description;
|
|
230
247
|
clone._version = this._version;
|
|
231
248
|
clone._pii = this._pii;
|
|
249
|
+
clone._exposure = this._exposure;
|
|
232
250
|
clone._validator = this._validator;
|
|
233
251
|
clone._default = this._default;
|
|
234
252
|
clone._upgrade = this._upgrade;
|
|
@@ -1432,6 +1450,94 @@ function deepClone(value) {
|
|
|
1432
1450
|
};
|
|
1433
1451
|
return cloneAny(value);
|
|
1434
1452
|
}
|
|
1453
|
+
function shouldIncludeField(def, options) {
|
|
1454
|
+
return options.includeInternal === true || (def?._exposure ?? "public") !== "internal";
|
|
1455
|
+
}
|
|
1456
|
+
function serializeFieldValue(def, value, options) {
|
|
1457
|
+
if (value === void 0) {
|
|
1458
|
+
return void 0;
|
|
1459
|
+
}
|
|
1460
|
+
if (value === null) {
|
|
1461
|
+
return null;
|
|
1462
|
+
}
|
|
1463
|
+
switch (def?.type) {
|
|
1464
|
+
case "string":
|
|
1465
|
+
case "number":
|
|
1466
|
+
case "boolean":
|
|
1467
|
+
return deepClone(value);
|
|
1468
|
+
case "ref": {
|
|
1469
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1470
|
+
return deepClone(value);
|
|
1471
|
+
}
|
|
1472
|
+
const shape = getShape(def);
|
|
1473
|
+
if (!shape || Object.keys(shape).length === 0) {
|
|
1474
|
+
return deepClone(value);
|
|
1475
|
+
}
|
|
1476
|
+
const serializedRef = {};
|
|
1477
|
+
if (typeof value.type === "string") {
|
|
1478
|
+
serializedRef.type = value.type;
|
|
1479
|
+
}
|
|
1480
|
+
if (typeof value.id === "string") {
|
|
1481
|
+
serializedRef.id = value.id;
|
|
1482
|
+
}
|
|
1483
|
+
for (const [childKey, childDef] of Object.entries(shape)) {
|
|
1484
|
+
if (!shouldIncludeField(childDef, options)) {
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
if (!Object.prototype.hasOwnProperty.call(value, childKey)) {
|
|
1488
|
+
continue;
|
|
1489
|
+
}
|
|
1490
|
+
const serializedChild = serializeFieldValue(
|
|
1491
|
+
childDef,
|
|
1492
|
+
value[childKey],
|
|
1493
|
+
options
|
|
1494
|
+
);
|
|
1495
|
+
if (serializedChild !== void 0) {
|
|
1496
|
+
serializedRef[childKey] = serializedChild;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return serializedRef;
|
|
1500
|
+
}
|
|
1501
|
+
case "object": {
|
|
1502
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1503
|
+
return deepClone(value);
|
|
1504
|
+
}
|
|
1505
|
+
const shape = getShape(def);
|
|
1506
|
+
if (!shape || Object.keys(shape).length === 0) {
|
|
1507
|
+
return deepClone(value);
|
|
1508
|
+
}
|
|
1509
|
+
const serializedObject = {};
|
|
1510
|
+
for (const [childKey, childDef] of Object.entries(shape)) {
|
|
1511
|
+
if (!shouldIncludeField(childDef, options)) {
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1514
|
+
if (!Object.prototype.hasOwnProperty.call(value, childKey)) {
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
const serializedChild = serializeFieldValue(
|
|
1518
|
+
childDef,
|
|
1519
|
+
value[childKey],
|
|
1520
|
+
options
|
|
1521
|
+
);
|
|
1522
|
+
if (serializedChild !== void 0) {
|
|
1523
|
+
serializedObject[childKey] = serializedChild;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return serializedObject;
|
|
1527
|
+
}
|
|
1528
|
+
case "array": {
|
|
1529
|
+
if (!Array.isArray(value)) {
|
|
1530
|
+
return deepClone(value);
|
|
1531
|
+
}
|
|
1532
|
+
if (!def.itemType) {
|
|
1533
|
+
return deepClone(value);
|
|
1534
|
+
}
|
|
1535
|
+
return value.map((item) => serializeFieldValue(def.itemType, item, options));
|
|
1536
|
+
}
|
|
1537
|
+
default:
|
|
1538
|
+
return deepClone(value);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1435
1541
|
function cmpSemver(a, b) {
|
|
1436
1542
|
const pa = a.split(".").map((n) => parseInt(n, 10));
|
|
1437
1543
|
const pb = b.split(".").map((n) => parseInt(n, 10));
|
|
@@ -1508,6 +1614,38 @@ function getValidator(def) {
|
|
|
1508
1614
|
function getShape(def) {
|
|
1509
1615
|
return def?._shape ?? def?.shape ?? void 0;
|
|
1510
1616
|
}
|
|
1617
|
+
function normalizeValidationIssue(path, issue, defaultCode) {
|
|
1618
|
+
if (typeof issue === "string") {
|
|
1619
|
+
return {
|
|
1620
|
+
path,
|
|
1621
|
+
code: defaultCode,
|
|
1622
|
+
message: issue
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
return {
|
|
1626
|
+
path: issue.path ?? path,
|
|
1627
|
+
code: issue.code || defaultCode,
|
|
1628
|
+
message: issue.message
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
function normalizeValidatorIssues(path, result, defaultCode) {
|
|
1632
|
+
if (result === true) {
|
|
1633
|
+
return [];
|
|
1634
|
+
}
|
|
1635
|
+
if (result === false) {
|
|
1636
|
+
return [
|
|
1637
|
+
{
|
|
1638
|
+
path,
|
|
1639
|
+
code: defaultCode,
|
|
1640
|
+
message: `Invalid value for field: ${path}`
|
|
1641
|
+
}
|
|
1642
|
+
];
|
|
1643
|
+
}
|
|
1644
|
+
if (Array.isArray(result)) {
|
|
1645
|
+
return result.map((issue) => normalizeValidationIssue(path, issue, defaultCode));
|
|
1646
|
+
}
|
|
1647
|
+
return [normalizeValidationIssue(path, result, defaultCode)];
|
|
1648
|
+
}
|
|
1511
1649
|
function checkMissingRequired(parentKey, key, value, def, errors) {
|
|
1512
1650
|
if (value === void 0 || value === null) {
|
|
1513
1651
|
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
@@ -1526,13 +1664,16 @@ function checkImmutable(parentKey, key, value, def, existing, errors) {
|
|
|
1526
1664
|
}
|
|
1527
1665
|
return { immutableViolation: false };
|
|
1528
1666
|
}
|
|
1529
|
-
function runCustomValidator(parentKey, key, value, def, errors) {
|
|
1667
|
+
function runCustomValidator(parentKey, key, value, def, errors, issues) {
|
|
1530
1668
|
const validator = getValidator(def);
|
|
1531
1669
|
if (validator && value !== void 0 && value !== null) {
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
errors.push(
|
|
1670
|
+
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
1671
|
+
const normalizedIssues = normalizeValidatorIssues(path, validator(value), "invalid_value");
|
|
1672
|
+
if (normalizedIssues.length > 0) {
|
|
1673
|
+
errors.push(...normalizedIssues.map((issue) => issue.message));
|
|
1674
|
+
if (issues) {
|
|
1675
|
+
issues.push(...normalizedIssues);
|
|
1676
|
+
}
|
|
1536
1677
|
return { invalid: true };
|
|
1537
1678
|
}
|
|
1538
1679
|
}
|
|
@@ -1572,7 +1713,7 @@ function validateBooleanField(parentKey, key, value, _def, errors) {
|
|
|
1572
1713
|
errors.push(`Field ${path} must be boolean`);
|
|
1573
1714
|
}
|
|
1574
1715
|
}
|
|
1575
|
-
function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnforcement, logger) {
|
|
1716
|
+
function validateObjectChildren(parentKey, obj, shape, errors, issues, existing, piiEnforcement, logger) {
|
|
1576
1717
|
for (const [childKey, childDef] of Object.entries(shape)) {
|
|
1577
1718
|
let childValue = obj[childKey];
|
|
1578
1719
|
const existingChild = existing && typeof existing === "object" ? existing[childKey] : void 0;
|
|
@@ -1613,7 +1754,8 @@ function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnfo
|
|
|
1613
1754
|
childKey,
|
|
1614
1755
|
childValue,
|
|
1615
1756
|
childDef,
|
|
1616
|
-
errors
|
|
1757
|
+
errors,
|
|
1758
|
+
issues
|
|
1617
1759
|
);
|
|
1618
1760
|
if (invalid) continue;
|
|
1619
1761
|
validateByType(
|
|
@@ -1622,13 +1764,14 @@ function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnfo
|
|
|
1622
1764
|
childValue,
|
|
1623
1765
|
childDef,
|
|
1624
1766
|
errors,
|
|
1767
|
+
issues,
|
|
1625
1768
|
existingChild,
|
|
1626
1769
|
piiEnforcement,
|
|
1627
1770
|
logger
|
|
1628
1771
|
);
|
|
1629
1772
|
}
|
|
1630
1773
|
}
|
|
1631
|
-
function validateObjectField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
|
|
1774
|
+
function validateObjectField(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
|
|
1632
1775
|
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
1633
1776
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1634
1777
|
errors.push(`Field ${path} must be object`);
|
|
@@ -1636,7 +1779,7 @@ function validateObjectField(parentKey, key, value, def, errors, existing, piiEn
|
|
|
1636
1779
|
}
|
|
1637
1780
|
const objShape = getShape(def);
|
|
1638
1781
|
if (objShape)
|
|
1639
|
-
validateObjectChildren(path, value, objShape, errors, existing, piiEnforcement, logger);
|
|
1782
|
+
validateObjectChildren(path, value, objShape, errors, issues, existing, piiEnforcement, logger);
|
|
1640
1783
|
}
|
|
1641
1784
|
function validateArrayOfStrings(parentKey, key, arr, itemDef, errors) {
|
|
1642
1785
|
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
@@ -1695,7 +1838,7 @@ function validateArrayOfBooleans(parentKey, key, arr, _itemDef, errors) {
|
|
|
1695
1838
|
});
|
|
1696
1839
|
}
|
|
1697
1840
|
}
|
|
1698
|
-
function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing, piiEnforcement, logger) {
|
|
1841
|
+
function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, issues, existing, piiEnforcement, logger) {
|
|
1699
1842
|
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
1700
1843
|
if (!Array.isArray(arr) || !arr.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
|
|
1701
1844
|
errors.push(`Field ${path} must be object[]`);
|
|
@@ -1749,7 +1892,8 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing,
|
|
|
1749
1892
|
childKey,
|
|
1750
1893
|
childValue,
|
|
1751
1894
|
childDef,
|
|
1752
|
-
errors
|
|
1895
|
+
errors,
|
|
1896
|
+
issues
|
|
1753
1897
|
);
|
|
1754
1898
|
if (invalid) continue;
|
|
1755
1899
|
validateByType(
|
|
@@ -1758,6 +1902,7 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing,
|
|
|
1758
1902
|
childValue,
|
|
1759
1903
|
childDef,
|
|
1760
1904
|
errors,
|
|
1905
|
+
issues,
|
|
1761
1906
|
existingChild,
|
|
1762
1907
|
piiEnforcement,
|
|
1763
1908
|
logger
|
|
@@ -1765,7 +1910,7 @@ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing,
|
|
|
1765
1910
|
}
|
|
1766
1911
|
});
|
|
1767
1912
|
}
|
|
1768
|
-
function validateArrayField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
|
|
1913
|
+
function validateArrayField(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
|
|
1769
1914
|
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
1770
1915
|
if (!Array.isArray(value)) {
|
|
1771
1916
|
errors.push(`Field ${key} must be an array`);
|
|
@@ -1785,6 +1930,7 @@ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnf
|
|
|
1785
1930
|
value,
|
|
1786
1931
|
def.itemType,
|
|
1787
1932
|
errors,
|
|
1933
|
+
issues,
|
|
1788
1934
|
existing,
|
|
1789
1935
|
piiEnforcement,
|
|
1790
1936
|
logger
|
|
@@ -1841,9 +1987,14 @@ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnf
|
|
|
1841
1987
|
if (shortCircuit) continue;
|
|
1842
1988
|
const childValidator = getValidator(childDef);
|
|
1843
1989
|
if (childValidator && childValue !== void 0 && childValue !== null) {
|
|
1844
|
-
const
|
|
1845
|
-
|
|
1846
|
-
|
|
1990
|
+
const normalizedIssues = normalizeValidatorIssues(
|
|
1991
|
+
childPath,
|
|
1992
|
+
childValidator(childValue),
|
|
1993
|
+
"invalid_value"
|
|
1994
|
+
);
|
|
1995
|
+
if (normalizedIssues.length > 0) {
|
|
1996
|
+
errors.push(...normalizedIssues.map((issue) => issue.message));
|
|
1997
|
+
issues.push(...normalizedIssues);
|
|
1847
1998
|
continue;
|
|
1848
1999
|
}
|
|
1849
2000
|
}
|
|
@@ -1853,6 +2004,7 @@ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnf
|
|
|
1853
2004
|
childValue,
|
|
1854
2005
|
childDef,
|
|
1855
2006
|
errors,
|
|
2007
|
+
issues,
|
|
1856
2008
|
existingChild,
|
|
1857
2009
|
piiEnforcement,
|
|
1858
2010
|
logger
|
|
@@ -1878,7 +2030,7 @@ function validateRefField(parentKey, key, value, def, errors, _existing, _piiEnf
|
|
|
1878
2030
|
);
|
|
1879
2031
|
}
|
|
1880
2032
|
}
|
|
1881
|
-
function validateByType(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
|
|
2033
|
+
function validateByType(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
|
|
1882
2034
|
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
1883
2035
|
switch (def.type) {
|
|
1884
2036
|
case "string":
|
|
@@ -1894,6 +2046,7 @@ function validateByType(parentKey, key, value, def, errors, existing, piiEnforce
|
|
|
1894
2046
|
value,
|
|
1895
2047
|
def,
|
|
1896
2048
|
errors,
|
|
2049
|
+
issues,
|
|
1897
2050
|
existing,
|
|
1898
2051
|
piiEnforcement,
|
|
1899
2052
|
logger
|
|
@@ -1905,6 +2058,7 @@ function validateByType(parentKey, key, value, def, errors, existing, piiEnforce
|
|
|
1905
2058
|
value,
|
|
1906
2059
|
def,
|
|
1907
2060
|
errors,
|
|
2061
|
+
issues,
|
|
1908
2062
|
existing,
|
|
1909
2063
|
piiEnforcement,
|
|
1910
2064
|
logger
|
|
@@ -1940,6 +2094,7 @@ function createSchema(_shape, entityType, options = {
|
|
|
1940
2094
|
// 🔗 Validate input against the schema
|
|
1941
2095
|
validate(input, existing) {
|
|
1942
2096
|
const errors = [];
|
|
2097
|
+
const issues = [];
|
|
1943
2098
|
const result = {};
|
|
1944
2099
|
const piiMode = options.piiEnforcement ?? "none";
|
|
1945
2100
|
if (typeof input !== "object" || input === null) {
|
|
@@ -2003,16 +2158,19 @@ function createSchema(_shape, entityType, options = {
|
|
|
2003
2158
|
if (shortCircuit) continue;
|
|
2004
2159
|
const validateField = (val) => {
|
|
2005
2160
|
const localErrors = [];
|
|
2161
|
+
const localIssues = [];
|
|
2006
2162
|
const { invalid } = runCustomValidator(
|
|
2007
2163
|
"",
|
|
2008
2164
|
key,
|
|
2009
2165
|
val,
|
|
2010
2166
|
def,
|
|
2011
|
-
localErrors
|
|
2167
|
+
localErrors,
|
|
2168
|
+
localIssues
|
|
2012
2169
|
);
|
|
2013
2170
|
if (!invalid) {
|
|
2014
|
-
validateByType("", key, val, def, localErrors, existingField, piiMode, console);
|
|
2171
|
+
validateByType("", key, val, def, localErrors, localIssues, existingField, piiMode, console);
|
|
2015
2172
|
}
|
|
2173
|
+
issues.push(...localIssues);
|
|
2016
2174
|
return localErrors;
|
|
2017
2175
|
};
|
|
2018
2176
|
let fieldValue = value;
|
|
@@ -2053,7 +2211,8 @@ function createSchema(_shape, entityType, options = {
|
|
|
2053
2211
|
return {
|
|
2054
2212
|
valid: errors.length === 0,
|
|
2055
2213
|
value: result,
|
|
2056
|
-
errors
|
|
2214
|
+
errors,
|
|
2215
|
+
issues
|
|
2057
2216
|
};
|
|
2058
2217
|
},
|
|
2059
2218
|
// specific validator for a schema to allow conditional validation
|
|
@@ -2203,6 +2362,25 @@ function createSchema(_shape, entityType, options = {
|
|
|
2203
2362
|
}
|
|
2204
2363
|
return store;
|
|
2205
2364
|
},
|
|
2365
|
+
serialize(input, options2 = {}) {
|
|
2366
|
+
if (typeof input !== "object" || input === null) {
|
|
2367
|
+
return {};
|
|
2368
|
+
}
|
|
2369
|
+
const serialized = {};
|
|
2370
|
+
for (const [key, def] of Object.entries(schema._shape)) {
|
|
2371
|
+
if (!shouldIncludeField(def, options2)) {
|
|
2372
|
+
continue;
|
|
2373
|
+
}
|
|
2374
|
+
if (!Object.prototype.hasOwnProperty.call(input, key)) {
|
|
2375
|
+
continue;
|
|
2376
|
+
}
|
|
2377
|
+
const value = serializeFieldValue(def, input[key], options2);
|
|
2378
|
+
if (value !== void 0) {
|
|
2379
|
+
serialized[key] = value;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
return serialized;
|
|
2383
|
+
},
|
|
2206
2384
|
/**
|
|
2207
2385
|
* Transforms an input object for persistence by applying PII protection
|
|
2208
2386
|
* according to field annotations (e.g., encryption and hashing).
|
|
@@ -2272,7 +2450,8 @@ function createSchema(_shape, entityType, options = {
|
|
|
2272
2450
|
deprecatedVersion: def.deprecatedVersion === void 0 ? null : def.deprecatedVersion,
|
|
2273
2451
|
enum: getEnumValues(def) ?? null,
|
|
2274
2452
|
refType: def.refType ?? null,
|
|
2275
|
-
pii: !def._pii || def._pii.classification === "none" ? null : def._pii
|
|
2453
|
+
pii: !def._pii || def._pii.classification === "none" ? null : def._pii,
|
|
2454
|
+
exposure: def._exposure ?? "public"
|
|
2276
2455
|
};
|
|
2277
2456
|
}
|
|
2278
2457
|
return {
|
|
@@ -2302,7 +2481,8 @@ function renderSchemaDescription(schema) {
|
|
|
2302
2481
|
optional: def.optional,
|
|
2303
2482
|
description: def.description,
|
|
2304
2483
|
deprecated: def.deprecated,
|
|
2305
|
-
pii: def.pii ? def.pii.classification : void 0
|
|
2484
|
+
pii: def.pii ? def.pii.classification : void 0,
|
|
2485
|
+
exposure: def.exposure
|
|
2306
2486
|
}))
|
|
2307
2487
|
};
|
|
2308
2488
|
}
|