@plasius/schema 1.1.0 → 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.
Files changed (59) hide show
  1. package/README.md +31 -18
  2. package/dist/index.cjs +2370 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +627 -0
  5. package/dist/index.d.ts +627 -0
  6. package/dist/index.js +2308 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +18 -6
  9. package/.eslintrc.cjs +0 -7
  10. package/.github/workflows/cd.yml +0 -236
  11. package/.github/workflows/ci.yml +0 -16
  12. package/.nvmrc +0 -1
  13. package/.vscode/launch.json +0 -15
  14. package/CHANGELOG.md +0 -120
  15. package/CODE_OF_CONDUCT.md +0 -79
  16. package/CONTRIBUTING.md +0 -201
  17. package/CONTRIBUTORS.md +0 -27
  18. package/SECURITY.md +0 -17
  19. package/docs/adrs/adr-0001: schema.md +0 -45
  20. package/docs/adrs/adr-template.md +0 -67
  21. package/legal/CLA-REGISTRY.csv +0 -2
  22. package/legal/CLA.md +0 -22
  23. package/legal/CORPORATE_CLA.md +0 -57
  24. package/legal/INDIVIDUAL_CLA.md +0 -91
  25. package/sbom.cdx.json +0 -66
  26. package/src/components.ts +0 -39
  27. package/src/field.builder.ts +0 -239
  28. package/src/field.ts +0 -153
  29. package/src/index.ts +0 -7
  30. package/src/infer.ts +0 -34
  31. package/src/pii.ts +0 -165
  32. package/src/schema.ts +0 -893
  33. package/src/types.ts +0 -156
  34. package/src/validation/countryCode.ISO3166.ts +0 -256
  35. package/src/validation/currencyCode.ISO4217.ts +0 -191
  36. package/src/validation/dateTime.ISO8601.ts +0 -60
  37. package/src/validation/email.RFC5322.ts +0 -9
  38. package/src/validation/generalText.OWASP.ts +0 -39
  39. package/src/validation/index.ts +0 -13
  40. package/src/validation/languageCode.BCP47.ts +0 -299
  41. package/src/validation/name.OWASP.ts +0 -25
  42. package/src/validation/percentage.ISO80000-1.ts +0 -8
  43. package/src/validation/phone.E.164.ts +0 -9
  44. package/src/validation/richtext.OWASP.ts +0 -34
  45. package/src/validation/url.WHATWG.ts +0 -16
  46. package/src/validation/user.MS-GOOGLE-APPLE.ts +0 -31
  47. package/src/validation/uuid.RFC4122.ts +0 -10
  48. package/src/validation/version.SEMVER2.0.0.ts +0 -10
  49. package/tests/field.builder.test.ts +0 -81
  50. package/tests/fields.test.ts +0 -213
  51. package/tests/pii.test.ts +0 -139
  52. package/tests/schema.test.ts +0 -501
  53. package/tests/test-utils.ts +0 -97
  54. package/tests/validate.test.ts +0 -97
  55. package/tests/validation.test.ts +0 -98
  56. package/tsconfig.build.json +0 -19
  57. package/tsconfig.json +0 -7
  58. package/tsup.config.ts +0 -10
  59. package/vitest.config.js +0 -20
package/dist/index.cjs ADDED
@@ -0,0 +1,2370 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ FieldBuilder: () => FieldBuilder,
24
+ IsoLanguageCode: () => IsoLanguageCode,
25
+ createComponentSchema: () => createComponentSchema,
26
+ createSchema: () => createSchema,
27
+ field: () => field,
28
+ getAllComponentSchemas: () => getAllComponentSchemas,
29
+ getAllSchemas: () => getAllSchemas,
30
+ getComponentSchema: () => getComponentSchema,
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,
40
+ isoCountryCodes: () => isoCountryCodes,
41
+ isoCurrencyCodes: () => isoCurrencyCodes,
42
+ registerComponentSchema: () => registerComponentSchema,
43
+ renderSchemaDescription: () => renderSchemaDescription,
44
+ validateCountryCode: () => validateCountryCode,
45
+ validateCurrencyCode: () => validateCurrencyCode,
46
+ validateDateTimeISO: () => validateDateTimeISO,
47
+ validateEmail: () => validateEmail,
48
+ validateLanguage: () => validateLanguage,
49
+ validateName: () => validateName,
50
+ validatePercentage: () => validatePercentage,
51
+ validatePhone: () => validatePhone,
52
+ validateRichText: () => validateRichText,
53
+ validateSafeText: () => validateSafeText,
54
+ validateSemVer: () => validateSemVer,
55
+ validateUUID: () => validateUUID,
56
+ validateUrl: () => validateUrl,
57
+ validateUserId: () => validateUserId,
58
+ validateUserIdArray: () => validateUserIdArray
59
+ });
60
+ module.exports = __toCommonJS(index_exports);
61
+
62
+ // src/field.builder.ts
63
+ var FieldBuilder = class _FieldBuilder {
64
+ constructor(type, options = {}) {
65
+ this.type = type;
66
+ this._shape = options.shape;
67
+ this.itemType = options.itemType;
68
+ this.refType = options.refType;
69
+ }
70
+ _type;
71
+ _storageType;
72
+ isSystem = false;
73
+ isImmutable = false;
74
+ isRequired = true;
75
+ _validator;
76
+ _description = "";
77
+ _version = "1.0.0";
78
+ _default;
79
+ _upgrade;
80
+ _shape;
81
+ itemType;
82
+ refType;
83
+ _pii = {
84
+ classification: "none",
85
+ action: "none",
86
+ logHandling: "plain",
87
+ purpose: "an ordinary value"
88
+ };
89
+ enumValues;
90
+ immutable() {
91
+ this.isImmutable = true;
92
+ return this;
93
+ }
94
+ system() {
95
+ this.isSystem = true;
96
+ return this;
97
+ }
98
+ required() {
99
+ this.isRequired = true;
100
+ return this;
101
+ }
102
+ optional() {
103
+ this.isRequired = false;
104
+ return this;
105
+ }
106
+ validator(fn) {
107
+ this._validator = fn;
108
+ return this;
109
+ }
110
+ description(desc) {
111
+ this._description = desc;
112
+ return this;
113
+ }
114
+ default(value) {
115
+ this._default = value;
116
+ this.isRequired = false;
117
+ return this;
118
+ }
119
+ /**
120
+ * Configure an upgrader used when validating older entities against a newer schema.
121
+ * The upgrader receives the current field value and version context, and should
122
+ * return { ok: true, value } with the upgraded value, or { ok: false, error }.
123
+ */
124
+ upgrade(fn) {
125
+ this._upgrade = fn;
126
+ return this;
127
+ }
128
+ getDefault() {
129
+ const v = this._default;
130
+ return typeof v === "function" ? v() : v;
131
+ }
132
+ version(ver) {
133
+ this._version = ver;
134
+ return this;
135
+ }
136
+ /// PID informs the schema PII handling of the manner in
137
+ /// which to handle data relating to this field.
138
+ PID(pii) {
139
+ this._pii = pii;
140
+ return this;
141
+ }
142
+ min(min) {
143
+ if (this.type === "number") {
144
+ const prevValidator = this._validator;
145
+ this._validator = (value) => {
146
+ const valid = typeof value === "number" && value >= min;
147
+ return prevValidator ? prevValidator(value) && valid : valid;
148
+ };
149
+ } else if (this.type === "string") {
150
+ const prevValidator = this._validator;
151
+ this._validator = (value) => {
152
+ const valid = typeof value === "string" && value.length >= min;
153
+ return prevValidator ? prevValidator(value) && valid : valid;
154
+ };
155
+ } else if (this.type === "array") {
156
+ const prevValidator = this._validator;
157
+ this._validator = (value) => {
158
+ const valid = Array.isArray(value) && value.length >= min;
159
+ return prevValidator ? prevValidator(value) && valid : valid;
160
+ };
161
+ } else {
162
+ throw new Error(
163
+ "Min is only supported on number, string, or array fields."
164
+ );
165
+ }
166
+ return this;
167
+ }
168
+ max(max) {
169
+ if (this.type === "number") {
170
+ const prevValidator = this._validator;
171
+ this._validator = (value) => {
172
+ const valid = typeof value === "number" && value <= max;
173
+ return prevValidator ? prevValidator(value) && valid : valid;
174
+ };
175
+ } else if (this.type === "string") {
176
+ const prevValidator = this._validator;
177
+ this._validator = (value) => {
178
+ const valid = typeof value === "string" && value.length <= max;
179
+ return prevValidator ? prevValidator(value) && valid : valid;
180
+ };
181
+ } else if (this.type === "array") {
182
+ const prevValidator = this._validator;
183
+ this._validator = (value) => {
184
+ const valid = Array.isArray(value) && value.length <= max;
185
+ return prevValidator ? prevValidator(value) && valid : valid;
186
+ };
187
+ } else {
188
+ throw new Error(
189
+ "Max is only supported on number, string, or array fields."
190
+ );
191
+ }
192
+ return this;
193
+ }
194
+ pattern(regex) {
195
+ if (this.type !== "string") {
196
+ throw new Error("Pattern is only supported on string fields.");
197
+ }
198
+ const prevValidator = this._validator;
199
+ this._validator = (value) => {
200
+ const valid = typeof value === "string" && regex.test(value);
201
+ return prevValidator ? prevValidator(value) && valid : valid;
202
+ };
203
+ return this;
204
+ }
205
+ enum(values) {
206
+ if (this.type !== "string" && this.type !== "number" && !(this.type === "array" && (this.itemType?.type === "string" || this.itemType?.type === "number"))) {
207
+ throw new Error(
208
+ "Enums are only supported on string or number fields or arrays of strings or numbers."
209
+ );
210
+ }
211
+ this.enumValues = values;
212
+ return this;
213
+ }
214
+ /**
215
+ * Create a shallow clone with a different external type parameter.
216
+ * Note: shape and itemType are passed by reference (shallow). If you need
217
+ * deep isolation of nested FieldBuilders, clone them explicitly.
218
+ */
219
+ as() {
220
+ const clone = new _FieldBuilder(this.type, {
221
+ shape: this._shape,
222
+ itemType: this.itemType,
223
+ refType: this.refType
224
+ });
225
+ clone.enumValues = this.enumValues;
226
+ clone.isImmutable = this.isImmutable;
227
+ clone.isSystem = this.isSystem;
228
+ clone.isRequired = this.isRequired;
229
+ clone._description = this._description;
230
+ clone._version = this._version;
231
+ clone._pii = this._pii;
232
+ clone._validator = this._validator;
233
+ clone._default = this._default;
234
+ clone._upgrade = this._upgrade;
235
+ return clone;
236
+ }
237
+ };
238
+
239
+ // src/validation/email.RFC5322.ts
240
+ var validateEmail = (value) => {
241
+ if (typeof value !== "string") return false;
242
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
243
+ return emailRegex.test(value);
244
+ };
245
+
246
+ // src/validation/phone.E.164.ts
247
+ var validatePhone = (value) => {
248
+ if (typeof value !== "string") return false;
249
+ const phoneRegex = /^\+[1-9]\d{1,14}$/;
250
+ return phoneRegex.test(value);
251
+ };
252
+
253
+ // src/validation/url.WHATWG.ts
254
+ var validateUrl = (value) => {
255
+ if (typeof value !== "string") return false;
256
+ try {
257
+ const url = new URL(value);
258
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
259
+ return true;
260
+ } catch {
261
+ return false;
262
+ }
263
+ };
264
+
265
+ // src/validation/uuid.RFC4122.ts
266
+ var validateUUID = (value) => {
267
+ if (typeof value !== "string") return false;
268
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
269
+ return uuidRegex.test(value);
270
+ };
271
+
272
+ // src/validation/dateTime.ISO8601.ts
273
+ var validateDateTimeISO = (value, options) => {
274
+ const mode = options?.mode ?? "datetime";
275
+ if (typeof value !== "string") return false;
276
+ if (mode === "datetime") {
277
+ const isoDateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
278
+ if (!isoDateTimeRegex.test(value)) return false;
279
+ const date = new Date(value);
280
+ if (Number.isNaN(date.getTime())) return false;
281
+ return true;
282
+ }
283
+ if (mode === "date") {
284
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
285
+ if (!dateRegex.test(value)) return false;
286
+ const date = new Date(value);
287
+ if (isNaN(date.getTime())) return false;
288
+ const [year, month, day] = value.split("-").map(Number);
289
+ return date.getUTCFullYear() === year && date.getUTCMonth() + 1 === month && date.getUTCDate() === day;
290
+ }
291
+ if (mode === "time") {
292
+ const timeRegex = /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?Z?$/;
293
+ return timeRegex.test(value);
294
+ }
295
+ return false;
296
+ };
297
+
298
+ // src/validation/countryCode.ISO3166.ts
299
+ var isoCountryCodes = /* @__PURE__ */ new Set([
300
+ "AD",
301
+ "AE",
302
+ "AF",
303
+ "AG",
304
+ "AI",
305
+ "AL",
306
+ "AM",
307
+ "AO",
308
+ "AQ",
309
+ "AR",
310
+ "AS",
311
+ "AT",
312
+ "AU",
313
+ "AW",
314
+ "AX",
315
+ "AZ",
316
+ "BA",
317
+ "BB",
318
+ "BD",
319
+ "BE",
320
+ "BF",
321
+ "BG",
322
+ "BH",
323
+ "BI",
324
+ "BJ",
325
+ "BL",
326
+ "BM",
327
+ "BN",
328
+ "BO",
329
+ "BQ",
330
+ "BR",
331
+ "BS",
332
+ "BT",
333
+ "BV",
334
+ "BW",
335
+ "BY",
336
+ "BZ",
337
+ "CA",
338
+ "CC",
339
+ "CD",
340
+ "CF",
341
+ "CG",
342
+ "CH",
343
+ "CI",
344
+ "CK",
345
+ "CL",
346
+ "CM",
347
+ "CN",
348
+ "CO",
349
+ "CR",
350
+ "CU",
351
+ "CV",
352
+ "CW",
353
+ "CX",
354
+ "CY",
355
+ "CZ",
356
+ "DE",
357
+ "DJ",
358
+ "DK",
359
+ "DM",
360
+ "DO",
361
+ "DZ",
362
+ "EC",
363
+ "EE",
364
+ "EG",
365
+ "EH",
366
+ "ER",
367
+ "ES",
368
+ "ET",
369
+ "FI",
370
+ "FJ",
371
+ "FM",
372
+ "FO",
373
+ "FR",
374
+ "GA",
375
+ "GB",
376
+ "GD",
377
+ "GE",
378
+ "GF",
379
+ "GG",
380
+ "GH",
381
+ "GI",
382
+ "GL",
383
+ "GM",
384
+ "GN",
385
+ "GP",
386
+ "GQ",
387
+ "GR",
388
+ "GT",
389
+ "GU",
390
+ "GW",
391
+ "GY",
392
+ "HK",
393
+ "HM",
394
+ "HN",
395
+ "HR",
396
+ "HT",
397
+ "HU",
398
+ "ID",
399
+ "IE",
400
+ "IL",
401
+ "IM",
402
+ "IN",
403
+ "IO",
404
+ "IQ",
405
+ "IR",
406
+ "IS",
407
+ "IT",
408
+ "JE",
409
+ "JM",
410
+ "JO",
411
+ "JP",
412
+ "KE",
413
+ "KG",
414
+ "KH",
415
+ "KI",
416
+ "KM",
417
+ "KN",
418
+ "KP",
419
+ "KR",
420
+ "KW",
421
+ "KY",
422
+ "KZ",
423
+ "LA",
424
+ "LB",
425
+ "LC",
426
+ "LI",
427
+ "LK",
428
+ "LR",
429
+ "LS",
430
+ "LT",
431
+ "LU",
432
+ "LV",
433
+ "LY",
434
+ "MA",
435
+ "MC",
436
+ "MD",
437
+ "ME",
438
+ "MF",
439
+ "MG",
440
+ "MH",
441
+ "MK",
442
+ "ML",
443
+ "MM",
444
+ "MN",
445
+ "MO",
446
+ "MP",
447
+ "MQ",
448
+ "MR",
449
+ "MS",
450
+ "MT",
451
+ "MU",
452
+ "MV",
453
+ "MW",
454
+ "MX",
455
+ "MY",
456
+ "MZ",
457
+ "NA",
458
+ "NC",
459
+ "NE",
460
+ "NF",
461
+ "NG",
462
+ "NI",
463
+ "NL",
464
+ "NO",
465
+ "NP",
466
+ "NR",
467
+ "NU",
468
+ "NZ",
469
+ "OM",
470
+ "PA",
471
+ "PE",
472
+ "PF",
473
+ "PG",
474
+ "PH",
475
+ "PK",
476
+ "PL",
477
+ "PM",
478
+ "PN",
479
+ "PR",
480
+ "PS",
481
+ "PT",
482
+ "PW",
483
+ "PY",
484
+ "QA",
485
+ "RE",
486
+ "RO",
487
+ "RS",
488
+ "RU",
489
+ "RW",
490
+ "SA",
491
+ "SB",
492
+ "SC",
493
+ "SD",
494
+ "SE",
495
+ "SG",
496
+ "SH",
497
+ "SI",
498
+ "SJ",
499
+ "SK",
500
+ "SL",
501
+ "SM",
502
+ "SN",
503
+ "SO",
504
+ "SR",
505
+ "SS",
506
+ "ST",
507
+ "SV",
508
+ "SX",
509
+ "SY",
510
+ "SZ",
511
+ "TC",
512
+ "TD",
513
+ "TF",
514
+ "TG",
515
+ "TH",
516
+ "TJ",
517
+ "TK",
518
+ "TL",
519
+ "TM",
520
+ "TN",
521
+ "TO",
522
+ "TR",
523
+ "TT",
524
+ "TV",
525
+ "TZ",
526
+ "UA",
527
+ "UG",
528
+ "UM",
529
+ "US",
530
+ "UY",
531
+ "UZ",
532
+ "VA",
533
+ "VC",
534
+ "VE",
535
+ "VG",
536
+ "VI",
537
+ "VN",
538
+ "VU",
539
+ "WF",
540
+ "WS",
541
+ "YE",
542
+ "YT",
543
+ "ZA",
544
+ "ZM",
545
+ "ZW"
546
+ ]);
547
+ var validateCountryCode = (value) => {
548
+ if (typeof value !== "string") return false;
549
+ return isoCountryCodes.has(value.toUpperCase());
550
+ };
551
+
552
+ // src/validation/currencyCode.ISO4217.ts
553
+ var isoCurrencyCodes = /* @__PURE__ */ new Set([
554
+ "AED",
555
+ "AFN",
556
+ "ALL",
557
+ "AMD",
558
+ "ANG",
559
+ "AOA",
560
+ "ARS",
561
+ "AUD",
562
+ "AWG",
563
+ "AZN",
564
+ "BAM",
565
+ "BBD",
566
+ "BDT",
567
+ "BGN",
568
+ "BHD",
569
+ "BIF",
570
+ "BMD",
571
+ "BND",
572
+ "BOB",
573
+ "BOV",
574
+ "BRL",
575
+ "BSD",
576
+ "BTN",
577
+ "BWP",
578
+ "BYN",
579
+ "BZD",
580
+ "CAD",
581
+ "CDF",
582
+ "CHE",
583
+ "CHF",
584
+ "CHW",
585
+ "CLF",
586
+ "CLP",
587
+ "CNY",
588
+ "COP",
589
+ "COU",
590
+ "CRC",
591
+ "CUC",
592
+ "CUP",
593
+ "CVE",
594
+ "CZK",
595
+ "DJF",
596
+ "DKK",
597
+ "DOP",
598
+ "DZD",
599
+ "EGP",
600
+ "ERN",
601
+ "ETB",
602
+ "EUR",
603
+ "FJD",
604
+ "FKP",
605
+ "GBP",
606
+ "GEL",
607
+ "GHS",
608
+ "GIP",
609
+ "GMD",
610
+ "GNF",
611
+ "GTQ",
612
+ "GYD",
613
+ "HKD",
614
+ "HNL",
615
+ "HRK",
616
+ "HTG",
617
+ "HUF",
618
+ "IDR",
619
+ "ILS",
620
+ "INR",
621
+ "IQD",
622
+ "IRR",
623
+ "ISK",
624
+ "JMD",
625
+ "JOD",
626
+ "JPY",
627
+ "KES",
628
+ "KGS",
629
+ "KHR",
630
+ "KMF",
631
+ "KPW",
632
+ "KRW",
633
+ "KWD",
634
+ "KYD",
635
+ "KZT",
636
+ "LAK",
637
+ "LBP",
638
+ "LKR",
639
+ "LRD",
640
+ "LSL",
641
+ "LYD",
642
+ "MAD",
643
+ "MDL",
644
+ "MGA",
645
+ "MKD",
646
+ "MMK",
647
+ "MNT",
648
+ "MOP",
649
+ "MRU",
650
+ "MUR",
651
+ "MVR",
652
+ "MWK",
653
+ "MXN",
654
+ "MXV",
655
+ "MYR",
656
+ "MZN",
657
+ "NAD",
658
+ "NGN",
659
+ "NIO",
660
+ "NOK",
661
+ "NPR",
662
+ "NZD",
663
+ "OMR",
664
+ "PAB",
665
+ "PEN",
666
+ "PGK",
667
+ "PHP",
668
+ "PKR",
669
+ "PLN",
670
+ "PYG",
671
+ "QAR",
672
+ "RON",
673
+ "RSD",
674
+ "RUB",
675
+ "RWF",
676
+ "SAR",
677
+ "SBD",
678
+ "SCR",
679
+ "SDG",
680
+ "SEK",
681
+ "SGD",
682
+ "SHP",
683
+ "SLE",
684
+ "SLL",
685
+ "SOS",
686
+ "SRD",
687
+ "SSP",
688
+ "STN",
689
+ "SVC",
690
+ "SYP",
691
+ "SZL",
692
+ "THB",
693
+ "TJS",
694
+ "TMT",
695
+ "TND",
696
+ "TOP",
697
+ "TRY",
698
+ "TTD",
699
+ "TWD",
700
+ "TZS",
701
+ "UAH",
702
+ "UGX",
703
+ "USD",
704
+ "USN",
705
+ "UYI",
706
+ "UYU",
707
+ "UYW",
708
+ "UZS",
709
+ "VES",
710
+ "VND",
711
+ "VUV",
712
+ "WST",
713
+ "XAF",
714
+ "XAG",
715
+ "XAU",
716
+ "XBA",
717
+ "XBB",
718
+ "XBC",
719
+ "XBD",
720
+ "XCD",
721
+ "XDR",
722
+ "XOF",
723
+ "XPD",
724
+ "XPF",
725
+ "XPT",
726
+ "XSU",
727
+ "XTS",
728
+ "XUA",
729
+ "XXX",
730
+ "YER",
731
+ "ZAR",
732
+ "ZMW",
733
+ "ZWL"
734
+ ]);
735
+ var validateCurrencyCode = (value) => {
736
+ if (typeof value !== "string") return false;
737
+ return isoCurrencyCodes.has(value.toUpperCase());
738
+ };
739
+
740
+ // src/validation/generalText.OWASP.ts
741
+ function validateSafeText(value) {
742
+ if (typeof value !== "string") return false;
743
+ const trimmed = value.trim();
744
+ if (trimmed.length === 0) return false;
745
+ for (let i = 0; i < trimmed.length; i++) {
746
+ const code = trimmed.codePointAt(i);
747
+ if (code !== void 0 && (code >= 0 && code <= 31 || code === 127)) {
748
+ return false;
749
+ }
750
+ }
751
+ if (/['"<>\\{}();]/.test(trimmed)) return false;
752
+ if (/(--|\b(SELECT|UPDATE|DELETE|INSERT|DROP|ALTER|EXEC|UNION|GRANT|REVOKE)\b|\/\*|\*\/|@@)/i.test(
753
+ trimmed
754
+ ))
755
+ return false;
756
+ if (trimmed.includes("\0")) return false;
757
+ if (trimmed.length > 1024) return false;
758
+ return true;
759
+ }
760
+
761
+ // src/validation/version.SEMVER2.0.0.ts
762
+ function validateSemVer(value) {
763
+ if (typeof value !== "string") return false;
764
+ return /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test(
765
+ value
766
+ );
767
+ }
768
+
769
+ // src/validation/percentage.ISO80000-1.ts
770
+ function validatePercentage(value) {
771
+ if (typeof value !== "number") return false;
772
+ return value >= 0 && value <= 100;
773
+ }
774
+
775
+ // src/validation/richtext.OWASP.ts
776
+ function validateRichText(value) {
777
+ if (typeof value !== "string") return false;
778
+ const trimmed = value.trim();
779
+ if (trimmed.length === 0) return true;
780
+ if (/<(script|iframe|object|embed|style|link|meta|base|form|input|button|textarea|select)\b/i.test(
781
+ trimmed
782
+ )) {
783
+ return false;
784
+ }
785
+ if (/javascript:/i.test(trimmed)) {
786
+ return false;
787
+ }
788
+ if (/on\w+=["']?/i.test(trimmed)) {
789
+ return false;
790
+ }
791
+ if (trimmed.length > 1e4) return false;
792
+ return true;
793
+ }
794
+
795
+ // src/validation/name.OWASP.ts
796
+ function validateName(value) {
797
+ if (typeof value !== "string") return false;
798
+ const trimmed = value.trim();
799
+ if (trimmed.length === 0) return false;
800
+ if (trimmed.length > 256) return false;
801
+ for (const ch of trimmed) {
802
+ const cp = ch.codePointAt(0);
803
+ if (cp >= 0 && cp <= 31 || cp === 127) return false;
804
+ }
805
+ const namePattern = /^[\p{L}\p{M}'\- ]+$/u;
806
+ if (!namePattern.test(trimmed)) return false;
807
+ return true;
808
+ }
809
+
810
+ // src/validation/user.MS-GOOGLE-APPLE.ts
811
+ function validateUserId(value) {
812
+ if (typeof value !== "string") return false;
813
+ const trimmed = value.trim();
814
+ if (trimmed.length === 0) return false;
815
+ const googlePattern = /^\d{21,22}$/;
816
+ const microsoftPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
817
+ const applePattern = /^[\w\-.]{6,255}$/;
818
+ return googlePattern.test(trimmed) || microsoftPattern.test(trimmed) || applePattern.test(trimmed);
819
+ }
820
+ function validateUserIdArray(value) {
821
+ if (!Array.isArray(value)) return false;
822
+ return value.every(validateUserId);
823
+ }
824
+
825
+ // src/validation/languageCode.BCP47.ts
826
+ var IsoLanguageCode = /* @__PURE__ */ ((IsoLanguageCode2) => {
827
+ IsoLanguageCode2["Afar"] = "aa";
828
+ IsoLanguageCode2["Abkhazian"] = "ab";
829
+ IsoLanguageCode2["Afrikaans"] = "af";
830
+ IsoLanguageCode2["Akan"] = "ak";
831
+ IsoLanguageCode2["Albanian"] = "sq";
832
+ IsoLanguageCode2["Amharic"] = "am";
833
+ IsoLanguageCode2["Arabic"] = "ar";
834
+ IsoLanguageCode2["Aragonese"] = "an";
835
+ IsoLanguageCode2["Armenian"] = "hy";
836
+ IsoLanguageCode2["Assamese"] = "as";
837
+ IsoLanguageCode2["Avaric"] = "av";
838
+ IsoLanguageCode2["Aymara"] = "ay";
839
+ IsoLanguageCode2["Azerbaijani"] = "az";
840
+ IsoLanguageCode2["Bashkir"] = "ba";
841
+ IsoLanguageCode2["Bambara"] = "bm";
842
+ IsoLanguageCode2["Basque"] = "eu";
843
+ IsoLanguageCode2["Belarusian"] = "be";
844
+ IsoLanguageCode2["Bengali"] = "bn";
845
+ IsoLanguageCode2["Bislama"] = "bi";
846
+ IsoLanguageCode2["Bosnian"] = "bs";
847
+ IsoLanguageCode2["Breton"] = "br";
848
+ IsoLanguageCode2["Bulgarian"] = "bg";
849
+ IsoLanguageCode2["Burmese"] = "my";
850
+ IsoLanguageCode2["Catalan"] = "ca";
851
+ IsoLanguageCode2["Chamorro"] = "ch";
852
+ IsoLanguageCode2["Chechen"] = "ce";
853
+ IsoLanguageCode2["Chinese"] = "zh";
854
+ IsoLanguageCode2["ChurchSlavic"] = "cu";
855
+ IsoLanguageCode2["Chuvash"] = "cv";
856
+ IsoLanguageCode2["Cornish"] = "kw";
857
+ IsoLanguageCode2["Corsican"] = "co";
858
+ IsoLanguageCode2["Cree"] = "cr";
859
+ IsoLanguageCode2["Croatian"] = "hr";
860
+ IsoLanguageCode2["Czech"] = "cs";
861
+ IsoLanguageCode2["Danish"] = "da";
862
+ IsoLanguageCode2["Divehi"] = "dv";
863
+ IsoLanguageCode2["Dutch"] = "nl";
864
+ IsoLanguageCode2["Dzongkha"] = "dz";
865
+ IsoLanguageCode2["English"] = "en";
866
+ IsoLanguageCode2["Esperanto"] = "eo";
867
+ IsoLanguageCode2["Estonian"] = "et";
868
+ IsoLanguageCode2["Ewe"] = "ee";
869
+ IsoLanguageCode2["Faroese"] = "fo";
870
+ IsoLanguageCode2["Fijian"] = "fj";
871
+ IsoLanguageCode2["Finnish"] = "fi";
872
+ IsoLanguageCode2["French"] = "fr";
873
+ IsoLanguageCode2["WesternFrisian"] = "fy";
874
+ IsoLanguageCode2["Fulah"] = "ff";
875
+ IsoLanguageCode2["Gaelic"] = "gd";
876
+ IsoLanguageCode2["Galician"] = "gl";
877
+ IsoLanguageCode2["Ganda"] = "lg";
878
+ IsoLanguageCode2["Georgian"] = "ka";
879
+ IsoLanguageCode2["German"] = "de";
880
+ IsoLanguageCode2["Greek"] = "el";
881
+ IsoLanguageCode2["Kalaallisut"] = "kl";
882
+ IsoLanguageCode2["Guarani"] = "gn";
883
+ IsoLanguageCode2["Gujarati"] = "gu";
884
+ IsoLanguageCode2["Haitian"] = "ht";
885
+ IsoLanguageCode2["Hausa"] = "ha";
886
+ IsoLanguageCode2["Hebrew"] = "he";
887
+ IsoLanguageCode2["Herero"] = "hz";
888
+ IsoLanguageCode2["Hindi"] = "hi";
889
+ IsoLanguageCode2["HiriMotu"] = "ho";
890
+ IsoLanguageCode2["Hungarian"] = "hu";
891
+ IsoLanguageCode2["Icelandic"] = "is";
892
+ IsoLanguageCode2["Ido"] = "io";
893
+ IsoLanguageCode2["Igbo"] = "ig";
894
+ IsoLanguageCode2["Indonesian"] = "id";
895
+ IsoLanguageCode2["Interlingua"] = "ia";
896
+ IsoLanguageCode2["Interlingue"] = "ie";
897
+ IsoLanguageCode2["Inuktitut"] = "iu";
898
+ IsoLanguageCode2["Inupiaq"] = "ik";
899
+ IsoLanguageCode2["Irish"] = "ga";
900
+ IsoLanguageCode2["Italian"] = "it";
901
+ IsoLanguageCode2["Japanese"] = "ja";
902
+ IsoLanguageCode2["Javanese"] = "jv";
903
+ IsoLanguageCode2["Kannada"] = "kn";
904
+ IsoLanguageCode2["Kanuri"] = "kr";
905
+ IsoLanguageCode2["Kashmiri"] = "ks";
906
+ IsoLanguageCode2["Kazakh"] = "kk";
907
+ IsoLanguageCode2["CentralKhmer"] = "km";
908
+ IsoLanguageCode2["Kikuyu"] = "ki";
909
+ IsoLanguageCode2["Kinyarwanda"] = "rw";
910
+ IsoLanguageCode2["Kyrgyz"] = "ky";
911
+ IsoLanguageCode2["Komi"] = "kv";
912
+ IsoLanguageCode2["Kongo"] = "kg";
913
+ IsoLanguageCode2["Korean"] = "ko";
914
+ IsoLanguageCode2["Kuanyama"] = "kj";
915
+ IsoLanguageCode2["Kurdish"] = "ku";
916
+ IsoLanguageCode2["Lao"] = "lo";
917
+ IsoLanguageCode2["Latin"] = "la";
918
+ IsoLanguageCode2["Latvian"] = "lv";
919
+ IsoLanguageCode2["Limburgan"] = "li";
920
+ IsoLanguageCode2["Lingala"] = "ln";
921
+ IsoLanguageCode2["Lithuanian"] = "lt";
922
+ IsoLanguageCode2["LubaKatanga"] = "lu";
923
+ IsoLanguageCode2["Luxembourgish"] = "lb";
924
+ IsoLanguageCode2["Macedonian"] = "mk";
925
+ IsoLanguageCode2["Malagasy"] = "mg";
926
+ IsoLanguageCode2["Malay"] = "ms";
927
+ IsoLanguageCode2["Malayalam"] = "ml";
928
+ IsoLanguageCode2["Maltese"] = "mt";
929
+ IsoLanguageCode2["Manx"] = "gv";
930
+ IsoLanguageCode2["Maori"] = "mi";
931
+ IsoLanguageCode2["Marathi"] = "mr";
932
+ IsoLanguageCode2["Marshallese"] = "mh";
933
+ IsoLanguageCode2["Mongolian"] = "mn";
934
+ IsoLanguageCode2["Nauru"] = "na";
935
+ IsoLanguageCode2["Navajo"] = "nv";
936
+ IsoLanguageCode2["NorthNdebele"] = "nd";
937
+ IsoLanguageCode2["SouthNdebele"] = "nr";
938
+ IsoLanguageCode2["Ndonga"] = "ng";
939
+ IsoLanguageCode2["Nepali"] = "ne";
940
+ IsoLanguageCode2["Norwegian"] = "no";
941
+ IsoLanguageCode2["NorwegianBokmal"] = "nb";
942
+ IsoLanguageCode2["NorwegianNynorsk"] = "nn";
943
+ IsoLanguageCode2["SichuanYi"] = "ii";
944
+ IsoLanguageCode2["Occitan"] = "oc";
945
+ IsoLanguageCode2["Ojibwa"] = "oj";
946
+ IsoLanguageCode2["Oriya"] = "or";
947
+ IsoLanguageCode2["Oromo"] = "om";
948
+ IsoLanguageCode2["Ossetian"] = "os";
949
+ IsoLanguageCode2["Pali"] = "pi";
950
+ IsoLanguageCode2["Pashto"] = "ps";
951
+ IsoLanguageCode2["Persian"] = "fa";
952
+ IsoLanguageCode2["Polish"] = "pl";
953
+ IsoLanguageCode2["Portuguese"] = "pt";
954
+ IsoLanguageCode2["Punjabi"] = "pa";
955
+ IsoLanguageCode2["Quechua"] = "qu";
956
+ IsoLanguageCode2["Romansh"] = "rm";
957
+ IsoLanguageCode2["Romanian"] = "ro";
958
+ IsoLanguageCode2["Rundi"] = "rn";
959
+ IsoLanguageCode2["Russian"] = "ru";
960
+ IsoLanguageCode2["Samoan"] = "sm";
961
+ IsoLanguageCode2["Sango"] = "sg";
962
+ IsoLanguageCode2["Sanskrit"] = "sa";
963
+ IsoLanguageCode2["Sardinian"] = "sc";
964
+ IsoLanguageCode2["Serbian"] = "sr";
965
+ IsoLanguageCode2["Shona"] = "sn";
966
+ IsoLanguageCode2["Sindhi"] = "sd";
967
+ IsoLanguageCode2["Sinhala"] = "si";
968
+ IsoLanguageCode2["Slovak"] = "sk";
969
+ IsoLanguageCode2["Slovenian"] = "sl";
970
+ IsoLanguageCode2["Somali"] = "so";
971
+ IsoLanguageCode2["SouthernSotho"] = "st";
972
+ IsoLanguageCode2["Spanish"] = "es";
973
+ IsoLanguageCode2["Sundanese"] = "su";
974
+ IsoLanguageCode2["Swahili"] = "sw";
975
+ IsoLanguageCode2["Swati"] = "ss";
976
+ IsoLanguageCode2["Swedish"] = "sv";
977
+ IsoLanguageCode2["Tagalog"] = "tl";
978
+ IsoLanguageCode2["Tahitian"] = "ty";
979
+ IsoLanguageCode2["Tajik"] = "tg";
980
+ IsoLanguageCode2["Tamil"] = "ta";
981
+ IsoLanguageCode2["Tatar"] = "tt";
982
+ IsoLanguageCode2["Telugu"] = "te";
983
+ IsoLanguageCode2["Thai"] = "th";
984
+ IsoLanguageCode2["Tibetan"] = "bo";
985
+ IsoLanguageCode2["Tigrinya"] = "ti";
986
+ IsoLanguageCode2["Tonga"] = "to";
987
+ IsoLanguageCode2["Tsonga"] = "ts";
988
+ IsoLanguageCode2["Tswana"] = "tn";
989
+ IsoLanguageCode2["Turkish"] = "tr";
990
+ IsoLanguageCode2["Turkmen"] = "tk";
991
+ IsoLanguageCode2["Twi"] = "tw";
992
+ IsoLanguageCode2["Uighur"] = "ug";
993
+ IsoLanguageCode2["Ukrainian"] = "uk";
994
+ IsoLanguageCode2["Urdu"] = "ur";
995
+ IsoLanguageCode2["Uzbek"] = "uz";
996
+ IsoLanguageCode2["Venda"] = "ve";
997
+ IsoLanguageCode2["Vietnamese"] = "vi";
998
+ IsoLanguageCode2["Volapuk"] = "vo";
999
+ IsoLanguageCode2["Walloon"] = "wa";
1000
+ IsoLanguageCode2["Welsh"] = "cy";
1001
+ IsoLanguageCode2["Wolof"] = "wo";
1002
+ IsoLanguageCode2["Xhosa"] = "xh";
1003
+ IsoLanguageCode2["Yiddish"] = "yi";
1004
+ IsoLanguageCode2["Yoruba"] = "yo";
1005
+ IsoLanguageCode2["Zhuang"] = "za";
1006
+ IsoLanguageCode2["Zulu"] = "zu";
1007
+ return IsoLanguageCode2;
1008
+ })(IsoLanguageCode || {});
1009
+ var ISO_LANGUAGE_SET = new Set(
1010
+ Object.values(IsoLanguageCode)
1011
+ );
1012
+ function isIsoLanguageCode(value) {
1013
+ return typeof value === "string" && ISO_LANGUAGE_SET.has(value.toLowerCase());
1014
+ }
1015
+ function isRegionSubtag(value) {
1016
+ return /^[A-Z]{2}$/.test(value) || /^\d{3}$/.test(value);
1017
+ }
1018
+ function isScriptSubtag(value) {
1019
+ return /^[A-Z][a-z]{3}$/.test(value);
1020
+ }
1021
+ function isVariantSubtag(value) {
1022
+ return /^([0-9][A-Za-z0-9]{3}|[A-Za-z0-9]{5,8})$/.test(value);
1023
+ }
1024
+ function isExtensionSingleton(value) {
1025
+ return /^[0-9A-WY-Za-wy-z]$/.test(value);
1026
+ }
1027
+ function isExtensionSubtag(value) {
1028
+ return /^[A-Za-z0-9]{2,8}$/.test(value);
1029
+ }
1030
+ function isPrivateUseSingleton(value) {
1031
+ return value.toLowerCase() === "x";
1032
+ }
1033
+ function isPrivateUseSubtag(value) {
1034
+ return /^[A-Za-z0-9]{1,8}$/.test(value);
1035
+ }
1036
+ function validateLanguage(value) {
1037
+ if (typeof value !== "string" || value.length === 0) return false;
1038
+ const parts = value.split("-");
1039
+ let i = 0;
1040
+ const lang = parts[i];
1041
+ if (!lang || !isIsoLanguageCode(lang)) return false;
1042
+ i += 1;
1043
+ if (i < parts.length && isScriptSubtag(parts[i])) {
1044
+ i += 1;
1045
+ }
1046
+ if (i < parts.length && isRegionSubtag(parts[i].toUpperCase())) {
1047
+ i += 1;
1048
+ }
1049
+ while (i < parts.length && isVariantSubtag(parts[i])) {
1050
+ i += 1;
1051
+ }
1052
+ while (i < parts.length && isExtensionSingleton(parts[i])) {
1053
+ i += 1;
1054
+ if (!(i < parts.length && isExtensionSubtag(parts[i]))) return false;
1055
+ while (i < parts.length && isExtensionSubtag(parts[i])) {
1056
+ i += 1;
1057
+ }
1058
+ }
1059
+ if (i < parts.length && isPrivateUseSingleton(parts[i])) {
1060
+ i += 1;
1061
+ if (!(i < parts.length && isPrivateUseSubtag(parts[i]))) return false;
1062
+ while (i < parts.length && isPrivateUseSubtag(parts[i])) {
1063
+ i += 1;
1064
+ }
1065
+ }
1066
+ return i === parts.length;
1067
+ }
1068
+
1069
+ // src/field.ts
1070
+ var field = {
1071
+ string: () => new FieldBuilder("string"),
1072
+ number: () => new FieldBuilder("number"),
1073
+ boolean: () => new FieldBuilder("boolean"),
1074
+ object: (fields) => new FieldBuilder("object", { shape: fields }),
1075
+ array: (itemType) => new FieldBuilder("array", { itemType }),
1076
+ ref: (refType) => new FieldBuilder("ref", { refType }),
1077
+ email: () => new FieldBuilder("string").validator(validateEmail).PID({
1078
+ classification: "high",
1079
+ action: "encrypt",
1080
+ logHandling: "redact",
1081
+ purpose: "an email address"
1082
+ }).description("An email address"),
1083
+ phone: () => new FieldBuilder("string").validator(validatePhone).PID({
1084
+ classification: "high",
1085
+ action: "encrypt",
1086
+ logHandling: "redact",
1087
+ purpose: "a phone number"
1088
+ }).description("A phone number"),
1089
+ url: () => new FieldBuilder("string").validator(validateUrl).PID({
1090
+ classification: "low",
1091
+ action: "hash",
1092
+ logHandling: "pseudonym",
1093
+ purpose: "a URL"
1094
+ }).description("A URL"),
1095
+ uuid: () => new FieldBuilder("string").PID({
1096
+ classification: "low",
1097
+ action: "hash",
1098
+ logHandling: "pseudonym",
1099
+ purpose: "a UUID"
1100
+ }).validator(validateUUID).description("A UUID"),
1101
+ dateTimeISO: () => new FieldBuilder("string").PID({
1102
+ classification: "none",
1103
+ action: "none",
1104
+ logHandling: "plain",
1105
+ purpose: "a date string"
1106
+ }).validator(validateDateTimeISO).description("A date string in ISO 8601 format"),
1107
+ dateISO: () => new FieldBuilder("string").PID({
1108
+ classification: "none",
1109
+ action: "none",
1110
+ logHandling: "plain",
1111
+ purpose: "a date string"
1112
+ }).validator((s) => validateDateTimeISO(s, { mode: "date" })).description("A date string in ISO 8601 format (date only)"),
1113
+ timeISO: () => new FieldBuilder("string").PID({
1114
+ classification: "none",
1115
+ action: "none",
1116
+ logHandling: "plain",
1117
+ purpose: "a time string"
1118
+ }).validator((s) => validateDateTimeISO(s, { mode: "time" })).description("A time string in ISO 8601 format (time only)"),
1119
+ richText: () => new FieldBuilder("string").PID({
1120
+ classification: "low",
1121
+ action: "clear",
1122
+ logHandling: "omit",
1123
+ purpose: "rich text content"
1124
+ }).validator(validateRichText).description("Rich text content, may include basic HTML formatting"),
1125
+ generalText: () => new FieldBuilder("string").PID({
1126
+ classification: "none",
1127
+ action: "none",
1128
+ logHandling: "plain",
1129
+ purpose: "Plain text content"
1130
+ }).validator(validateSafeText).description("Standard text content, no HTML allowed"),
1131
+ latitude: () => new FieldBuilder("number").PID({
1132
+ classification: "low",
1133
+ action: "clear",
1134
+ logHandling: "omit",
1135
+ purpose: "Latitude in decimal degrees, WGS 84 (ISO 6709)"
1136
+ }).min(-90).max(90).description("Latitude in decimal degrees, WGS 84 (ISO 6709)"),
1137
+ longitude: () => new FieldBuilder("number").PID({
1138
+ classification: "low",
1139
+ action: "clear",
1140
+ logHandling: "omit",
1141
+ purpose: "Longitude in decimal degrees, WGS 84 (ISO 6709)"
1142
+ }).min(-180).max(180).description("Longitude in decimal degrees, WGS 84 (ISO 6709)"),
1143
+ version: () => new FieldBuilder("string").validator(validateSemVer).description("A semantic version string, e.g. '1.0.0'"),
1144
+ countryCode: () => new FieldBuilder("string").validator(validateCountryCode).description("An ISO 3166 country code, e.g. 'US', 'GB', 'FR'"),
1145
+ languageCode: () => new FieldBuilder("string").validator(validateLanguage).description(
1146
+ "An BCP 47 structured language code, primarily ISO 639-1 and optionally with ISO 3166-1 alpha-2 country code, e.g. 'en', 'en-US', 'fr', 'fr-FR'"
1147
+ )
1148
+ };
1149
+
1150
+ // src/pii.ts
1151
+ function enforcePIIField(parentKey, key, value, def, enforcement = "none", errors, logger) {
1152
+ const path = parentKey ? `${parentKey}.${key}` : key;
1153
+ if (def?._pii?.classification === "high" && (def?.isRequired ?? true)) {
1154
+ const missing = value === void 0 || value === null || value === "";
1155
+ if (missing) {
1156
+ const msg = `High PII field must not be empty: ${path}`;
1157
+ if (enforcement === "strict") {
1158
+ errors?.push(msg);
1159
+ return { shortCircuit: true };
1160
+ }
1161
+ if (enforcement === "warn") {
1162
+ logger?.warn?.(`WARN (PII Enforcement): ${msg}`);
1163
+ }
1164
+ }
1165
+ }
1166
+ return { shortCircuit: false };
1167
+ }
1168
+ function prepareForStorage(shape, input, encryptFn, hashFn) {
1169
+ const build = (def, value, key, out, ctx = {}) => {
1170
+ if (!def) return;
1171
+ const isMissing = value === void 0 || value === null;
1172
+ if (def._pii?.action === "encrypt") {
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;
1216
+ }
1217
+ out[key] = value;
1218
+ };
1219
+ const result = {};
1220
+ for (const key in shape) {
1221
+ build(shape[key], input?.[key], key, result);
1222
+ }
1223
+ return result;
1224
+ }
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
+ };
1266
+ const result = {};
1267
+ for (const key in shape) {
1268
+ result[key] = readValue(shape[key], key, stored);
1269
+ }
1270
+ return result;
1271
+ }
1272
+ function sanitizeForLog(shape, data, pseudonymFn) {
1273
+ const visit = (def, value, ctx = {}) => {
1274
+ if (!def) return void 0;
1275
+ const handling = def._pii?.logHandling;
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;
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;
1314
+ }
1315
+ return output;
1316
+ }
1317
+ function getPiiAudit(shape) {
1318
+ const piiFields = [];
1319
+ for (const key in shape) {
1320
+ const def = shape[key];
1321
+ if (!def) continue;
1322
+ if (def._pii && def._pii.classification !== "none") {
1323
+ piiFields.push({
1324
+ field: key,
1325
+ classification: def._pii.classification,
1326
+ action: def._pii.action,
1327
+ logHandling: def._pii.logHandling,
1328
+ purpose: def._pii.purpose
1329
+ });
1330
+ }
1331
+ }
1332
+ return piiFields;
1333
+ }
1334
+ function scrubPiiForDelete(shape, stored) {
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
+ };
1362
+ if (def._pii?.action === "encrypt") {
1363
+ const targetKey = `${keyName}Encrypted`;
1364
+ if (host && Object.prototype.hasOwnProperty.call(host, targetKey)) {
1365
+ applyPath(targetKey);
1366
+ }
1367
+ return;
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);
1410
+ }
1411
+ return result;
1412
+ }
1413
+
1414
+ // src/schema.ts
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
+ }
1435
+ function cmpSemver(a, b) {
1436
+ const pa = a.split(".").map((n) => parseInt(n, 10));
1437
+ const pb = b.split(".").map((n) => parseInt(n, 10));
1438
+ for (let i = 0; i < 3; i++) {
1439
+ const ai = pa[i] ?? 0;
1440
+ const bi = pb[i] ?? 0;
1441
+ if (ai > bi) return 1;
1442
+ if (ai < bi) return -1;
1443
+ }
1444
+ return 0;
1445
+ }
1446
+ function applySchemaUpgrade(spec, input, ctx) {
1447
+ if (typeof spec === "function") {
1448
+ return spec(input, ctx);
1449
+ }
1450
+ const steps = [...spec].sort((s1, s2) => cmpSemver(s1.to, s2.to));
1451
+ let working = { ...input };
1452
+ let fromVersion = ctx.from;
1453
+ for (const step of steps) {
1454
+ if (cmpSemver(fromVersion, step.to) < 0 && cmpSemver(step.to, ctx.to) <= 0) {
1455
+ ctx.log?.(`Upgrading entity from v${fromVersion} \u2192 v${step.to}`);
1456
+ const res = step.run(working, { ...ctx, from: fromVersion, to: step.to });
1457
+ if (!res || res.ok !== true || !res.value) {
1458
+ return {
1459
+ ok: false,
1460
+ errors: res?.errors?.length ? res.errors : [`Failed to upgrade entity from v${fromVersion} to v${step.to}`]
1461
+ };
1462
+ }
1463
+ working = res.value;
1464
+ fromVersion = step.to;
1465
+ }
1466
+ }
1467
+ return { ok: true, value: working };
1468
+ }
1469
+ function validateEnum(parentKey, value, enumValues) {
1470
+ if (!enumValues) return;
1471
+ const values = Array.isArray(enumValues) ? enumValues : Array.from(enumValues);
1472
+ if (Array.isArray(value)) {
1473
+ const invalid = value.filter((v) => !values.includes(v));
1474
+ if (invalid.length > 0) {
1475
+ return `Field ${parentKey} contains invalid enum values: ${invalid.join(
1476
+ ", "
1477
+ )}`;
1478
+ }
1479
+ } else {
1480
+ if (!values.includes(value)) {
1481
+ return `Field ${parentKey} must be one of: ${values.join(", ")}`;
1482
+ }
1483
+ }
1484
+ }
1485
+ function getEnumValues(def) {
1486
+ const src = Array.isArray(def?.enum) ? def.enum : Array.isArray(def?.enumValues) ? def.enumValues : Array.isArray(def?._enum) ? def._enum : Array.isArray(def?._enumValues) ? def._enumValues : def?._enumSet instanceof Set ? Array.from(def._enumSet) : void 0;
1487
+ if (!src) return void 0;
1488
+ const ok = src.every(
1489
+ (v) => typeof v === "string" || typeof v === "number"
1490
+ );
1491
+ return ok ? src : void 0;
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
+ }
1502
+ function isOptional(def) {
1503
+ return def?.isRequired === false;
1504
+ }
1505
+ function getValidator(def) {
1506
+ return def?._validator ?? void 0;
1507
+ }
1508
+ function getShape(def) {
1509
+ return def?._shape ?? def?.shape ?? void 0;
1510
+ }
1511
+ function checkMissingRequired(parentKey, key, value, def, errors) {
1512
+ if (value === void 0 || value === null) {
1513
+ const path = parentKey ? `${parentKey}.${key}` : key;
1514
+ if (!isOptional(def)) {
1515
+ errors.push(`Missing required field: ${path}`);
1516
+ }
1517
+ return { missing: true };
1518
+ }
1519
+ return { missing: false };
1520
+ }
1521
+ function checkImmutable(parentKey, key, value, def, existing, errors) {
1522
+ if (def.isImmutable && existing && existing[key] !== void 0 && value !== existing[key]) {
1523
+ const path = parentKey ? `${parentKey}.${key}` : key;
1524
+ errors.push(`Field is immutable: ${path}`);
1525
+ return { immutableViolation: true };
1526
+ }
1527
+ return { immutableViolation: false };
1528
+ }
1529
+ function runCustomValidator(parentKey, key, value, def, errors) {
1530
+ const validator = getValidator(def);
1531
+ if (validator && value !== void 0 && value !== null) {
1532
+ const valid = validator(value);
1533
+ if (!valid) {
1534
+ const path = parentKey ? `${parentKey}.${key}` : key;
1535
+ errors.push(`Invalid value for field: ${path}`);
1536
+ return { invalid: true };
1537
+ }
1538
+ }
1539
+ return { invalid: false };
1540
+ }
1541
+ function validateStringField(parentKey, key, value, def, errors) {
1542
+ const path = parentKey ? `${parentKey}.${key}` : key;
1543
+ if (typeof value !== "string") {
1544
+ errors.push(`Field ${path} must be string`);
1545
+ return;
1546
+ }
1547
+ const enumValues = getEnumValues(def);
1548
+ if (Array.isArray(enumValues)) {
1549
+ const enumError = validateEnum(path, value, enumValues);
1550
+ if (enumError) {
1551
+ errors.push(enumError);
1552
+ }
1553
+ }
1554
+ }
1555
+ function validateNumberField(parentKey, key, value, def, errors) {
1556
+ const path = parentKey ? `${parentKey}.${key}` : key;
1557
+ if (typeof value !== "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
+ }
1567
+ }
1568
+ }
1569
+ function validateBooleanField(parentKey, key, value, _def, errors) {
1570
+ const path = parentKey ? `${parentKey}.${key}` : key;
1571
+ if (typeof value !== "boolean") {
1572
+ errors.push(`Field ${path} must be boolean`);
1573
+ }
1574
+ }
1575
+ function validateObjectChildren(parentKey, obj, shape, errors, existing, piiEnforcement, logger) {
1576
+ for (const [childKey, childDef] of Object.entries(shape)) {
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
+ }
1584
+ const { missing } = checkMissingRequired(
1585
+ parentKey,
1586
+ childKey,
1587
+ childValue,
1588
+ childDef,
1589
+ errors
1590
+ );
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;
1611
+ const { invalid } = runCustomValidator(
1612
+ parentKey,
1613
+ childKey,
1614
+ childValue,
1615
+ childDef,
1616
+ errors
1617
+ );
1618
+ if (invalid) continue;
1619
+ validateByType(
1620
+ parentKey,
1621
+ childKey,
1622
+ childValue,
1623
+ childDef,
1624
+ errors,
1625
+ existingChild,
1626
+ piiEnforcement,
1627
+ logger
1628
+ );
1629
+ }
1630
+ }
1631
+ function validateObjectField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1632
+ const path = parentKey ? `${parentKey}.${key}` : key;
1633
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1634
+ errors.push(`Field ${path} must be object`);
1635
+ return;
1636
+ }
1637
+ const objShape = getShape(def);
1638
+ if (objShape)
1639
+ validateObjectChildren(path, value, objShape, errors, existing, piiEnforcement, logger);
1640
+ }
1641
+ function validateArrayOfStrings(parentKey, key, arr, itemDef, errors) {
1642
+ const path = parentKey ? `${parentKey}.${key}` : key;
1643
+ if (!arr.every((v) => typeof v === "string")) {
1644
+ errors.push(`Field ${path} must be string[]`);
1645
+ return;
1646
+ }
1647
+ const enumValues = getEnumValues(itemDef);
1648
+ if (Array.isArray(enumValues)) {
1649
+ const enumError = validateEnum(path, arr, enumValues);
1650
+ if (enumError) {
1651
+ errors.push(enumError);
1652
+ }
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
+ }
1662
+ }
1663
+ function validateArrayOfNumbers(parentKey, key, arr, itemDef, errors) {
1664
+ const path = parentKey ? `${parentKey}.${key}` : key;
1665
+ if (!arr.every((v) => typeof v === "number")) {
1666
+ errors.push(`Field ${path} must be number[]`);
1667
+ }
1668
+ const enumValues = getEnumValues(itemDef);
1669
+ if (Array.isArray(enumValues)) {
1670
+ const enumError = validateEnum(path, arr, enumValues);
1671
+ if (enumError) {
1672
+ errors.push(enumError);
1673
+ }
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
+ }
1683
+ }
1684
+ function validateArrayOfBooleans(parentKey, key, arr, _itemDef, errors) {
1685
+ const path = parentKey ? `${parentKey}.${key}` : key;
1686
+ if (!arr.every((v) => typeof v === "boolean")) {
1687
+ errors.push(`Field ${path} must be boolean[]`);
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
+ }
1697
+ }
1698
+ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, existing, piiEnforcement, logger) {
1699
+ const path = parentKey ? `${parentKey}.${key}` : key;
1700
+ if (!Array.isArray(arr) || !arr.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
1701
+ errors.push(`Field ${path} must be object[]`);
1702
+ return;
1703
+ }
1704
+ const itemShape = getShape(itemDef);
1705
+ if (!itemShape) return;
1706
+ arr.forEach((item, idx) => {
1707
+ const itemParent = `${path}[${idx}]`;
1708
+ const existingItem = Array.isArray(existing) ? existing[idx] : void 0;
1709
+ for (const [childKey, childDef] of Object.entries(itemShape)) {
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
+ }
1720
+ const { missing } = checkMissingRequired(
1721
+ itemParent,
1722
+ childKey,
1723
+ childValue,
1724
+ childDef,
1725
+ errors
1726
+ );
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;
1747
+ const { invalid } = runCustomValidator(
1748
+ itemParent,
1749
+ childKey,
1750
+ childValue,
1751
+ childDef,
1752
+ errors
1753
+ );
1754
+ if (invalid) continue;
1755
+ validateByType(
1756
+ itemParent,
1757
+ childKey,
1758
+ childValue,
1759
+ childDef,
1760
+ errors,
1761
+ existingChild,
1762
+ piiEnforcement,
1763
+ logger
1764
+ );
1765
+ }
1766
+ });
1767
+ }
1768
+ function validateArrayField(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1769
+ const path = parentKey ? `${parentKey}.${key}` : key;
1770
+ if (!Array.isArray(value)) {
1771
+ errors.push(`Field ${key} must be an array`);
1772
+ return;
1773
+ }
1774
+ const itemType = def.itemType?.type;
1775
+ if (itemType === "string")
1776
+ return validateArrayOfStrings(parentKey, key, value, def.itemType, errors);
1777
+ if (itemType === "number")
1778
+ return validateArrayOfNumbers(parentKey, key, value, def.itemType, errors);
1779
+ if (itemType === "boolean")
1780
+ return validateArrayOfBooleans(parentKey, key, value, def.itemType, errors);
1781
+ if (itemType === "object")
1782
+ return validateArrayOfObjects(
1783
+ parentKey,
1784
+ key,
1785
+ value,
1786
+ def.itemType,
1787
+ errors,
1788
+ existing,
1789
+ piiEnforcement,
1790
+ logger
1791
+ );
1792
+ if (itemType === "ref") {
1793
+ const expectedType = def.itemType.refType;
1794
+ value.forEach((ref, idx) => {
1795
+ if (!ref || typeof ref !== "object" || ref === null || typeof ref.type !== "string" || typeof ref.id !== "string" || expectedType && ref.type !== expectedType) {
1796
+ errors.push(
1797
+ `Field ${path}[${idx}] must be a reference object with type: ${expectedType}`
1798
+ );
1799
+ }
1800
+ });
1801
+ const refShape = getShape(def.itemType);
1802
+ if (refShape) {
1803
+ const existingRefs = Array.isArray(existing) ? existing : [];
1804
+ value.forEach((ref, idx) => {
1805
+ if (ref && typeof ref === "object" && ref !== null) {
1806
+ const existingRef = existingRefs[idx];
1807
+ for (const [childKey, childDef] of Object.entries(refShape)) {
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
+ }
1819
+ if ((childValue === void 0 || childValue === null) && !isOptional(childDef)) {
1820
+ errors.push(`Missing required field: ${childPath}`);
1821
+ continue;
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;
1842
+ const childValidator = getValidator(childDef);
1843
+ if (childValidator && childValue !== void 0 && childValue !== null) {
1844
+ const valid = childValidator(childValue);
1845
+ if (!valid) {
1846
+ errors.push(`Invalid value for field: ${childPath}`);
1847
+ continue;
1848
+ }
1849
+ }
1850
+ validateByType(
1851
+ `${path}[${idx}]`,
1852
+ childKey,
1853
+ childValue,
1854
+ childDef,
1855
+ errors,
1856
+ existingChild,
1857
+ piiEnforcement,
1858
+ logger
1859
+ );
1860
+ }
1861
+ }
1862
+ });
1863
+ }
1864
+ return;
1865
+ }
1866
+ errors.push(`Field ${path} has unsupported array item type`);
1867
+ }
1868
+ function validateRefField(parentKey, key, value, def, errors, _existing, _piiEnforcement, _logger) {
1869
+ const path = parentKey ? `${parentKey}.${key}` : key;
1870
+ if (typeof value !== "object" || value === null || typeof value.type !== "string" || typeof value.id !== "string") {
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
+ );
1879
+ }
1880
+ }
1881
+ function validateByType(parentKey, key, value, def, errors, existing, piiEnforcement, logger) {
1882
+ const path = parentKey ? `${parentKey}.${key}` : key;
1883
+ switch (def.type) {
1884
+ case "string":
1885
+ return validateStringField(parentKey, key, value, def, errors);
1886
+ case "number":
1887
+ return validateNumberField(parentKey, key, value, def, errors);
1888
+ case "boolean":
1889
+ return validateBooleanField(parentKey, key, value, def, errors);
1890
+ case "object":
1891
+ return validateObjectField(
1892
+ parentKey,
1893
+ key,
1894
+ value,
1895
+ def,
1896
+ errors,
1897
+ existing,
1898
+ piiEnforcement,
1899
+ logger
1900
+ );
1901
+ case "array":
1902
+ return validateArrayField(
1903
+ parentKey,
1904
+ key,
1905
+ value,
1906
+ def,
1907
+ errors,
1908
+ existing,
1909
+ piiEnforcement,
1910
+ logger
1911
+ );
1912
+ case "ref":
1913
+ return validateRefField(parentKey, key, value, def, errors, existing, piiEnforcement, logger);
1914
+ default:
1915
+ errors.push(`Unknown type for field ${path}: ${def.type}`);
1916
+ }
1917
+ }
1918
+ function createSchema(_shape, entityType, options = {
1919
+ version: "1.0.0",
1920
+ table: "",
1921
+ schemaValidator: () => true,
1922
+ piiEnforcement: "none",
1923
+ schemaUpgrade: void 0
1924
+ }) {
1925
+ const systemFields = {
1926
+ type: field.string().immutable().system(),
1927
+ version: field.string().immutable().system().validator(validateSemVer)
1928
+ };
1929
+ const version = options.version || "1.0.0";
1930
+ const store = options.table || "";
1931
+ const schemaUpgrade = options.schemaUpgrade;
1932
+ const schema = {
1933
+ // 🔗 Define the schema shape
1934
+ _shape: {
1935
+ ...systemFields,
1936
+ ..._shape
1937
+ },
1938
+ // 🔗 Metadata about the schema
1939
+ meta: { entityType, version },
1940
+ // 🔗 Validate input against the schema
1941
+ validate(input, existing) {
1942
+ const errors = [];
1943
+ const result = {};
1944
+ const piiMode = options.piiEnforcement ?? "none";
1945
+ if (typeof input !== "object" || input === null) {
1946
+ return { valid: false, errors: ["Input must be an object"] };
1947
+ }
1948
+ const working = typeof input === "object" && input !== null ? deepClone(input) : { ...input };
1949
+ if (working.type == null) working.type = entityType;
1950
+ if (working.version == null) working.version = version;
1951
+ const fromVersion = String(working.version ?? "0.0.0");
1952
+ const toVersion = String(version);
1953
+ if (schemaUpgrade && cmpSemver(fromVersion, toVersion) < 0) {
1954
+ const upgradeRes = applySchemaUpgrade(schemaUpgrade, { ...working }, {
1955
+ from: fromVersion,
1956
+ to: toVersion,
1957
+ entityType,
1958
+ describe: () => schema.describe()
1959
+ });
1960
+ if (!upgradeRes || upgradeRes.ok !== true || !upgradeRes.value) {
1961
+ const errs = upgradeRes?.errors?.length ? upgradeRes.errors : [`Failed to upgrade entity from v${fromVersion} to v${toVersion}`];
1962
+ errors.push(...errs);
1963
+ return { valid: false, errors };
1964
+ }
1965
+ for (const k of Object.keys(working)) delete working[k];
1966
+ Object.assign(working, upgradeRes.value);
1967
+ working.type = entityType;
1968
+ working.version = toVersion;
1969
+ }
1970
+ for (const key in schema._shape) {
1971
+ const def = schema._shape[key];
1972
+ let value = working[key];
1973
+ const existingField = existing && typeof existing === "object" ? existing[key] : void 0;
1974
+ if (!def) {
1975
+ errors.push(`Field definition missing for: ${key}`);
1976
+ continue;
1977
+ }
1978
+ const { value: withDefault, applied } = applyDefault(def, value);
1979
+ if (applied) {
1980
+ working[key] = withDefault;
1981
+ value = withDefault;
1982
+ }
1983
+ const { missing } = checkMissingRequired("", key, value, def, errors);
1984
+ if (missing) continue;
1985
+ const { immutableViolation } = checkImmutable(
1986
+ "",
1987
+ key,
1988
+ value,
1989
+ def,
1990
+ existing,
1991
+ errors
1992
+ );
1993
+ if (immutableViolation) continue;
1994
+ const { shortCircuit } = enforcePIIField(
1995
+ "",
1996
+ key,
1997
+ value,
1998
+ def,
1999
+ piiMode,
2000
+ errors,
2001
+ console
2002
+ );
2003
+ if (shortCircuit) continue;
2004
+ const validateField = (val) => {
2005
+ const localErrors = [];
2006
+ const { invalid } = runCustomValidator(
2007
+ "",
2008
+ key,
2009
+ val,
2010
+ def,
2011
+ localErrors
2012
+ );
2013
+ if (!invalid) {
2014
+ validateByType("", key, val, def, localErrors, existingField, piiMode, console);
2015
+ }
2016
+ return localErrors;
2017
+ };
2018
+ let fieldValue = value;
2019
+ let fieldErrors = validateField(fieldValue);
2020
+ const entityFrom = String(working.version ?? "0.0.0");
2021
+ const entityTo = String(version);
2022
+ const fieldTo = String(def._version ?? entityTo);
2023
+ const hasUpgrader = typeof def._upgrade === "function";
2024
+ if (fieldErrors.length > 0 && hasUpgrader && cmpSemver(entityFrom, entityTo) < 0) {
2025
+ const up = def._upgrade;
2026
+ const res = up(fieldValue, {
2027
+ entityFrom,
2028
+ entityTo,
2029
+ fieldTo,
2030
+ fieldName: key
2031
+ });
2032
+ if (res && res.ok) {
2033
+ fieldValue = res.value;
2034
+ fieldErrors = validateField(fieldValue);
2035
+ } else {
2036
+ fieldErrors.push(
2037
+ res?.error || `Failed to upgrade field ${key} from v${entityFrom} to v${entityTo}`
2038
+ );
2039
+ }
2040
+ }
2041
+ result[key] = fieldValue;
2042
+ if (fieldErrors.length !== 0) {
2043
+ errors.push(...fieldErrors);
2044
+ continue;
2045
+ }
2046
+ }
2047
+ if (errors.length === 0 && options.schemaValidator) {
2048
+ const castValue = result;
2049
+ if (!options.schemaValidator(castValue)) {
2050
+ errors.push("Schema-level validation failed.");
2051
+ }
2052
+ }
2053
+ return {
2054
+ valid: errors.length === 0,
2055
+ value: result,
2056
+ errors
2057
+ };
2058
+ },
2059
+ // specific validator for a schema to allow conditional validation
2060
+ schemaValidator: options.schemaValidator,
2061
+ // <== expose it here!
2062
+ /**
2063
+ * Runs the optional schema-level upgrade function once, without validating.
2064
+ * Useful for offline migrations or testing migration logic.
2065
+ */
2066
+ upgrade(input, log) {
2067
+ const fromVersion = String(input?.version ?? "0.0.0");
2068
+ const toVersion = String(version);
2069
+ if (!schemaUpgrade || cmpSemver(fromVersion, toVersion) >= 0) {
2070
+ return {
2071
+ ok: true,
2072
+ value: { ...input, type: entityType, version: toVersion }
2073
+ };
2074
+ }
2075
+ const res = applySchemaUpgrade(
2076
+ schemaUpgrade,
2077
+ { ...input },
2078
+ {
2079
+ from: fromVersion,
2080
+ to: toVersion,
2081
+ entityType,
2082
+ describe: () => schema.describe(),
2083
+ log
2084
+ }
2085
+ );
2086
+ if (res && res.ok && res.value) {
2087
+ const out = { ...res.value, type: entityType, version: toVersion };
2088
+ return { ok: true, value: out };
2089
+ }
2090
+ return {
2091
+ ok: false,
2092
+ errors: res?.errors?.length ? res.errors : [`Failed to upgrade entity from v${fromVersion} to v${toVersion}`]
2093
+ };
2094
+ },
2095
+ /**
2096
+ * Recursively validates entity references defined in this schema.
2097
+ *
2098
+ * Traverses fields of type `ref` and arrays of `ref` and resolves each target
2099
+ * entity using the provided `resolveEntity` function. When `autoValidate` is
2100
+ * enabled (default) and the field's `refPolicy` is `eager`, the referenced
2101
+ * entity's schema is fetched and validated via `validateComposition` up to
2102
+ * `maxDepth` levels.
2103
+ *
2104
+ * Skips fields not listed in `onlyFields` when provided. Prevents cycles via
2105
+ * a `visited` set in `validatorContext`.
2106
+ *
2107
+ * @param entity The root entity to validate (must include `type` and `id`).
2108
+ * @param options Options controlling traversal and resolution behavior.
2109
+ * @param options.resolveEntity Function to resolve a referenced entity by type and id.
2110
+ * @param options.validatorContext Internal context (visited set) to prevent cycles.
2111
+ * @param options.maxDepth Maximum depth for recursive validation (default: 5).
2112
+ * @param options.onlyFields Optional whitelist of field names to validate.
2113
+ * @param options.log Optional logger for traversal/debug output.
2114
+ *
2115
+ * @throws Error if a broken reference is encountered (target cannot be resolved).
2116
+ */
2117
+ async validateComposition(entity, options2) {
2118
+ const {
2119
+ resolveEntity,
2120
+ validatorContext = { visited: /* @__PURE__ */ new Set() },
2121
+ maxDepth = 5,
2122
+ log
2123
+ } = options2;
2124
+ const entityKey = `${entity.type}:${entity.id}`;
2125
+ if (validatorContext.visited.has(entityKey)) {
2126
+ log?.(`Skipping already visited entity ${entityKey}`);
2127
+ return;
2128
+ }
2129
+ validatorContext.visited.add(entityKey);
2130
+ log?.(`Validating composition for entity ${entityKey}`);
2131
+ for (const [key, def] of Object.entries(schema._shape)) {
2132
+ if (options2.onlyFields && !options2.onlyFields.includes(key)) {
2133
+ log?.(`Skipping field ${key} (not in onlyFields)`);
2134
+ continue;
2135
+ }
2136
+ const autoValidate = def.autoValidate !== false;
2137
+ const refPolicy = def.refPolicy ?? "eager";
2138
+ const value = entity[key];
2139
+ if (!value) continue;
2140
+ if (def.type === "ref") {
2141
+ const refType = def.refType;
2142
+ if (!refType)
2143
+ throw new Error(`Missing refType for reference field ${key}`);
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
+ }
2150
+ const target = await options2.resolveEntity(refType, ref.id);
2151
+ if (!target)
2152
+ throw new Error(
2153
+ `Broken reference: ${refType} ${ref.id} in field ${key}`
2154
+ );
2155
+ log?.(`Resolved ${refType} ${ref.id} from field ${key}`);
2156
+ if (autoValidate && refPolicy === "eager") {
2157
+ const targetSchema = getSchemaForType(refType);
2158
+ if (options2.maxDepth > 0 && targetSchema) {
2159
+ await targetSchema.validateComposition(target, {
2160
+ ...options2,
2161
+ maxDepth: options2.maxDepth - 1
2162
+ });
2163
+ }
2164
+ }
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}`);
2169
+ const refs = value;
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
+ }
2176
+ const target = await options2.resolveEntity(refType, ref.id);
2177
+ if (!target)
2178
+ throw new Error(
2179
+ `Broken reference: ${refType} ${ref.id} in field ${key}`
2180
+ );
2181
+ log?.(`Resolved ${refType} ${ref.id} from field ${key}`);
2182
+ if (autoValidate && refPolicy === "eager") {
2183
+ const targetSchema = getSchemaForType(refType);
2184
+ if (options2.maxDepth > 0 && targetSchema) {
2185
+ await targetSchema.validateComposition(target, {
2186
+ ...options2,
2187
+ maxDepth: options2.maxDepth - 1
2188
+ });
2189
+ }
2190
+ }
2191
+ }
2192
+ }
2193
+ }
2194
+ },
2195
+ /**
2196
+ * Returns the configured table name for this schema.
2197
+ *
2198
+ * @throws Error if no store/table name has been defined for this schema.
2199
+ */
2200
+ tableName() {
2201
+ if (!store || store === "") {
2202
+ throw new Error("Store is not defined for this schema");
2203
+ }
2204
+ return store;
2205
+ },
2206
+ /**
2207
+ * Transforms an input object for persistence by applying PII protection
2208
+ * according to field annotations (e.g., encryption and hashing).
2209
+ *
2210
+ * @param input The raw entity data.
2211
+ * @param encryptFn Function used to encrypt sensitive values.
2212
+ * @param hashFn Function used to hash sensitive values.
2213
+ * @returns A new object safe to store.
2214
+ */
2215
+ prepareForStorage(input, encryptFn, hashFn) {
2216
+ return prepareForStorage(_shape, input, encryptFn, hashFn);
2217
+ },
2218
+ /**
2219
+ * Reverses storage transformations for read paths (e.g., decrypts values)
2220
+ * according to PII annotations, returning a consumer-friendly object.
2221
+ *
2222
+ * @param stored Data retrieved from storage.
2223
+ * @param decryptFn Function used to decrypt values that were encrypted on write.
2224
+ * @returns A new object suitable for application consumption.
2225
+ */
2226
+ prepareForRead(stored, decryptFn) {
2227
+ return prepareForRead(_shape, stored, decryptFn);
2228
+ },
2229
+ /**
2230
+ * Produces a log-safe copy of the provided data by redacting or pseudonymizing
2231
+ * PII fields in accordance with field annotations.
2232
+ *
2233
+ * @param data Arbitrary data to sanitize for logging.
2234
+ * @param pseudonymFn Function producing stable pseudonyms for sensitive values.
2235
+ * @returns A copy safe to emit to logs.
2236
+ */
2237
+ sanitizeForLog(data, pseudonymFn) {
2238
+ return sanitizeForLog(_shape, data, pseudonymFn);
2239
+ },
2240
+ /**
2241
+ * Returns a list of fields annotated with PII metadata for auditing purposes.
2242
+ * Each entry includes classification, required action, and optional log policy.
2243
+ */
2244
+ getPiiAudit() {
2245
+ return getPiiAudit(_shape);
2246
+ },
2247
+ /**
2248
+ * Produces a copy of stored data suitable for data deletion flows by scrubbing
2249
+ * or blanking PII per field annotations.
2250
+ *
2251
+ * @param stored Data as persisted.
2252
+ * @returns A copy with PII removed or neutralized for deletion.
2253
+ */
2254
+ scrubPiiForDelete(stored) {
2255
+ return scrubPiiForDelete(_shape, stored);
2256
+ },
2257
+ /**
2258
+ * Returns a normalized description of the schema suitable for documentation
2259
+ * or UI rendering (type, optionality, enum values, PII flags, etc.).
2260
+ */
2261
+ describe() {
2262
+ const description = {};
2263
+ for (const [key, def] of Object.entries(schema._shape)) {
2264
+ description[key] = {
2265
+ type: def.type,
2266
+ optional: isOptional(def),
2267
+ immutable: !!def.isImmutable,
2268
+ system: !!def.isSystem,
2269
+ description: def._description ?? "",
2270
+ version: def._version ?? "",
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
2276
+ };
2277
+ }
2278
+ return {
2279
+ entityType,
2280
+ version,
2281
+ hasSchemaUpgrade: !!schemaUpgrade,
2282
+ shape: description
2283
+ };
2284
+ }
2285
+ };
2286
+ globalSchemaRegistry.set(entityType, schema);
2287
+ return schema;
2288
+ }
2289
+ function getSchemaForType(type) {
2290
+ return globalSchemaRegistry.get(type);
2291
+ }
2292
+ function getAllSchemas() {
2293
+ return Array.from(globalSchemaRegistry.values());
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
+ }
2309
+
2310
+ // src/components.ts
2311
+ var componentSchemaRegistry = /* @__PURE__ */ new Map();
2312
+ function createComponentSchema(shape, name, version, tableName = "", schemaValidator = () => true) {
2313
+ const schema = createSchema(shape, name, {
2314
+ version,
2315
+ piiEnforcement: "strict",
2316
+ table: tableName,
2317
+ schemaValidator
2318
+ });
2319
+ registerComponentSchema(name, schema);
2320
+ return schema;
2321
+ }
2322
+ function registerComponentSchema(type, schema) {
2323
+ componentSchemaRegistry.set(type, schema);
2324
+ }
2325
+ function getComponentSchema(type) {
2326
+ return componentSchemaRegistry.get(type);
2327
+ }
2328
+ function getAllComponentSchemas() {
2329
+ return Array.from(componentSchemaRegistry.entries());
2330
+ }
2331
+ // Annotate the CommonJS export names for ESM import in node:
2332
+ 0 && (module.exports = {
2333
+ FieldBuilder,
2334
+ IsoLanguageCode,
2335
+ createComponentSchema,
2336
+ createSchema,
2337
+ field,
2338
+ getAllComponentSchemas,
2339
+ getAllSchemas,
2340
+ getComponentSchema,
2341
+ getSchemaForType,
2342
+ isExtensionSingleton,
2343
+ isExtensionSubtag,
2344
+ isIsoLanguageCode,
2345
+ isPrivateUseSingleton,
2346
+ isPrivateUseSubtag,
2347
+ isRegionSubtag,
2348
+ isScriptSubtag,
2349
+ isVariantSubtag,
2350
+ isoCountryCodes,
2351
+ isoCurrencyCodes,
2352
+ registerComponentSchema,
2353
+ renderSchemaDescription,
2354
+ validateCountryCode,
2355
+ validateCurrencyCode,
2356
+ validateDateTimeISO,
2357
+ validateEmail,
2358
+ validateLanguage,
2359
+ validateName,
2360
+ validatePercentage,
2361
+ validatePhone,
2362
+ validateRichText,
2363
+ validateSafeText,
2364
+ validateSemVer,
2365
+ validateUUID,
2366
+ validateUrl,
2367
+ validateUserId,
2368
+ validateUserIdArray
2369
+ });
2370
+ //# sourceMappingURL=index.cjs.map