@plasius/schema 1.0.18 → 1.1.1

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 (56) hide show
  1. package/README.md +140 -1
  2. package/dist/index.cjs +1934 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +391 -0
  5. package/dist/index.d.ts +391 -0
  6. package/dist/index.js +1883 -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 -186
  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 -86
  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 -119
  28. package/src/field.ts +0 -14
  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 -826
  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 -9
  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/name.OWASP.ts +0 -25
  41. package/src/validation/percentage.ISO80000-1.ts +0 -8
  42. package/src/validation/phone.E.164.ts +0 -9
  43. package/src/validation/richtext.OWASP.ts +0 -34
  44. package/src/validation/url.WHATWG.ts +0 -16
  45. package/src/validation/user.MS-GOOGLE-APPLE.ts +0 -31
  46. package/src/validation/uuid.RFC4122.ts +0 -10
  47. package/src/validation/version.SEMVER2.0.0.ts +0 -8
  48. package/tests/pii.test.ts +0 -139
  49. package/tests/schema.test.ts +0 -501
  50. package/tests/test-utils.ts +0 -97
  51. package/tests/validate.test.ts +0 -97
  52. package/tests/validation.test.ts +0 -98
  53. package/tsconfig.build.json +0 -19
  54. package/tsconfig.json +0 -7
  55. package/tsup.config.ts +0 -10
  56. package/vitest.config.js +0 -20
package/dist/index.cjs ADDED
@@ -0,0 +1,1934 @@
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
+ createComponentSchema: () => createComponentSchema,
25
+ createSchema: () => createSchema,
26
+ field: () => field,
27
+ getAllComponentSchemas: () => getAllComponentSchemas,
28
+ getAllSchemas: () => getAllSchemas,
29
+ getComponentSchema: () => getComponentSchema,
30
+ getSchemaForType: () => getSchemaForType,
31
+ isoCountryCodes: () => isoCountryCodes,
32
+ isoCurrencyCodes: () => isoCurrencyCodes,
33
+ registerComponentSchema: () => registerComponentSchema,
34
+ validateCountryCode: () => validateCountryCode,
35
+ validateCurrencyCode: () => validateCurrencyCode,
36
+ validateDateTimeISO: () => validateDateTimeISO,
37
+ validateEmail: () => validateEmail,
38
+ validateName: () => validateName,
39
+ validatePercentage: () => validatePercentage,
40
+ validatePhone: () => validatePhone,
41
+ validateRichText: () => validateRichText,
42
+ validateSafeText: () => validateSafeText,
43
+ validateSemVer: () => validateSemVer,
44
+ validateUUID: () => validateUUID,
45
+ validateUrl: () => validateUrl,
46
+ validateUserId: () => validateUserId,
47
+ validateUserIdArray: () => validateUserIdArray
48
+ });
49
+ module.exports = __toCommonJS(index_exports);
50
+
51
+ // src/field.builder.ts
52
+ var FieldBuilder = class _FieldBuilder {
53
+ constructor(type, options = {}) {
54
+ this.type = type;
55
+ this._shape = options.shape;
56
+ this.itemType = options.itemType;
57
+ this.refType = options.refType;
58
+ }
59
+ _type;
60
+ _storageType;
61
+ isSystem = false;
62
+ isImmutable = false;
63
+ isRequired = true;
64
+ _validator;
65
+ _description = "";
66
+ _version = "1.0.0";
67
+ _default;
68
+ _upgrade;
69
+ _shape;
70
+ itemType;
71
+ refType;
72
+ _pii = {
73
+ classification: "none",
74
+ action: "none",
75
+ logHandling: "plain",
76
+ purpose: "an ordinary value"
77
+ };
78
+ enumValues;
79
+ immutable() {
80
+ this.isImmutable = true;
81
+ return this;
82
+ }
83
+ system() {
84
+ this.isSystem = true;
85
+ return this;
86
+ }
87
+ required() {
88
+ this.isRequired = true;
89
+ return this;
90
+ }
91
+ optional() {
92
+ this.isRequired = false;
93
+ return this;
94
+ }
95
+ validator(fn) {
96
+ this._validator = fn;
97
+ return this;
98
+ }
99
+ description(desc) {
100
+ this._description = desc;
101
+ return this;
102
+ }
103
+ default(value) {
104
+ this._default = value;
105
+ this.isRequired = false;
106
+ return this;
107
+ }
108
+ /**
109
+ * Configure an upgrader used when validating older entities against a newer schema.
110
+ * The upgrader receives the current field value and version context, and should
111
+ * return { ok: true, value } with the upgraded value, or { ok: false, error }.
112
+ */
113
+ upgrade(fn) {
114
+ this._upgrade = fn;
115
+ return this;
116
+ }
117
+ getDefault() {
118
+ const v = this._default;
119
+ return typeof v === "function" ? v() : v;
120
+ }
121
+ version(ver) {
122
+ this._version = ver;
123
+ return this;
124
+ }
125
+ /// PID informs the schema PII handling of the manner in
126
+ /// which to handle data relating to this field.
127
+ PID(pii) {
128
+ this._pii = pii;
129
+ return this;
130
+ }
131
+ min(min) {
132
+ if (this.type === "number") {
133
+ const prevValidator = this._validator;
134
+ this._validator = (value) => {
135
+ const valid = typeof value === "number" && value >= min;
136
+ return prevValidator ? prevValidator(value) && valid : valid;
137
+ };
138
+ } else if (this.type === "string") {
139
+ const prevValidator = this._validator;
140
+ this._validator = (value) => {
141
+ const valid = typeof value === "string" && value.length >= min;
142
+ return prevValidator ? prevValidator(value) && valid : valid;
143
+ };
144
+ } else if (this.type === "array") {
145
+ const prevValidator = this._validator;
146
+ this._validator = (value) => {
147
+ const valid = Array.isArray(value) && value.length >= min;
148
+ return prevValidator ? prevValidator(value) && valid : valid;
149
+ };
150
+ } else {
151
+ throw new Error(
152
+ "Min is only supported on number, string, or array fields."
153
+ );
154
+ }
155
+ return this;
156
+ }
157
+ max(max) {
158
+ if (this.type === "number") {
159
+ const prevValidator = this._validator;
160
+ this._validator = (value) => {
161
+ const valid = typeof value === "number" && value <= max;
162
+ return prevValidator ? prevValidator(value) && valid : valid;
163
+ };
164
+ } else if (this.type === "string") {
165
+ const prevValidator = this._validator;
166
+ this._validator = (value) => {
167
+ const valid = typeof value === "string" && value.length <= max;
168
+ return prevValidator ? prevValidator(value) && valid : valid;
169
+ };
170
+ } else if (this.type === "array") {
171
+ const prevValidator = this._validator;
172
+ this._validator = (value) => {
173
+ const valid = Array.isArray(value) && value.length <= max;
174
+ return prevValidator ? prevValidator(value) && valid : valid;
175
+ };
176
+ } else {
177
+ throw new Error(
178
+ "Max is only supported on number, string, or array fields."
179
+ );
180
+ }
181
+ return this;
182
+ }
183
+ pattern(regex) {
184
+ if (this.type !== "string") {
185
+ throw new Error("Pattern is only supported on string fields.");
186
+ }
187
+ const prevValidator = this._validator;
188
+ this._validator = (value) => {
189
+ const valid = typeof value === "string" && regex.test(value);
190
+ return prevValidator ? prevValidator(value) && valid : valid;
191
+ };
192
+ return this;
193
+ }
194
+ enum(values) {
195
+ if (this.type !== "string" && this.type !== "number" && !(this.type === "array" && (this.itemType?.type === "string" || this.itemType?.type === "number"))) {
196
+ throw new Error(
197
+ "Enums are only supported on string or number fields or arrays of strings or numbers."
198
+ );
199
+ }
200
+ this.enumValues = values;
201
+ return this;
202
+ }
203
+ /**
204
+ * Create a shallow clone with a different external type parameter.
205
+ * Note: shape and itemType are passed by reference (shallow). If you need
206
+ * deep isolation of nested FieldBuilders, clone them explicitly.
207
+ */
208
+ as() {
209
+ const clone = new _FieldBuilder(this.type, {
210
+ shape: this._shape,
211
+ itemType: this.itemType,
212
+ refType: this.refType
213
+ });
214
+ clone.enumValues = this.enumValues;
215
+ clone.isImmutable = this.isImmutable;
216
+ clone.isSystem = this.isSystem;
217
+ clone.isRequired = this.isRequired;
218
+ clone._description = this._description;
219
+ clone._version = this._version;
220
+ clone._pii = this._pii;
221
+ clone._validator = this._validator;
222
+ clone._default = this._default;
223
+ clone._upgrade = this._upgrade;
224
+ return clone;
225
+ }
226
+ };
227
+
228
+ // src/validation/email.RFC5322.ts
229
+ var validateEmail = (value) => {
230
+ if (typeof value !== "string") return false;
231
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
232
+ return emailRegex.test(value);
233
+ };
234
+
235
+ // src/validation/phone.E.164.ts
236
+ var validatePhone = (value) => {
237
+ if (typeof value !== "string") return false;
238
+ const phoneRegex = /^\+[1-9]\d{1,14}$/;
239
+ return phoneRegex.test(value);
240
+ };
241
+
242
+ // src/validation/url.WHATWG.ts
243
+ var validateUrl = (value) => {
244
+ if (typeof value !== "string") return false;
245
+ try {
246
+ const url = new URL(value);
247
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
248
+ return true;
249
+ } catch {
250
+ return false;
251
+ }
252
+ };
253
+
254
+ // src/validation/uuid.RFC4122.ts
255
+ var validateUUID = (value) => {
256
+ if (typeof value !== "string") return false;
257
+ 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;
258
+ return uuidRegex.test(value);
259
+ };
260
+
261
+ // src/validation/dateTime.ISO8601.ts
262
+ var validateDateTimeISO = (value, options) => {
263
+ const mode = options?.mode ?? "datetime";
264
+ if (typeof value !== "string") return false;
265
+ if (mode === "datetime") {
266
+ const isoDateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
267
+ if (!isoDateTimeRegex.test(value)) return false;
268
+ const date = new Date(value);
269
+ if (Number.isNaN(date.getTime())) return false;
270
+ return true;
271
+ }
272
+ if (mode === "date") {
273
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
274
+ if (!dateRegex.test(value)) return false;
275
+ const date = new Date(value);
276
+ if (isNaN(date.getTime())) return false;
277
+ const [year, month, day] = value.split("-").map(Number);
278
+ return date.getUTCFullYear() === year && date.getUTCMonth() + 1 === month && date.getUTCDate() === day;
279
+ }
280
+ if (mode === "time") {
281
+ const timeRegex = /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?Z?$/;
282
+ return timeRegex.test(value);
283
+ }
284
+ return false;
285
+ };
286
+
287
+ // src/validation/countryCode.ISO3166.ts
288
+ var isoCountryCodes = /* @__PURE__ */ new Set([
289
+ "AD",
290
+ "AE",
291
+ "AF",
292
+ "AG",
293
+ "AI",
294
+ "AL",
295
+ "AM",
296
+ "AO",
297
+ "AQ",
298
+ "AR",
299
+ "AS",
300
+ "AT",
301
+ "AU",
302
+ "AW",
303
+ "AX",
304
+ "AZ",
305
+ "BA",
306
+ "BB",
307
+ "BD",
308
+ "BE",
309
+ "BF",
310
+ "BG",
311
+ "BH",
312
+ "BI",
313
+ "BJ",
314
+ "BL",
315
+ "BM",
316
+ "BN",
317
+ "BO",
318
+ "BQ",
319
+ "BR",
320
+ "BS",
321
+ "BT",
322
+ "BV",
323
+ "BW",
324
+ "BY",
325
+ "BZ",
326
+ "CA",
327
+ "CC",
328
+ "CD",
329
+ "CF",
330
+ "CG",
331
+ "CH",
332
+ "CI",
333
+ "CK",
334
+ "CL",
335
+ "CM",
336
+ "CN",
337
+ "CO",
338
+ "CR",
339
+ "CU",
340
+ "CV",
341
+ "CW",
342
+ "CX",
343
+ "CY",
344
+ "CZ",
345
+ "DE",
346
+ "DJ",
347
+ "DK",
348
+ "DM",
349
+ "DO",
350
+ "DZ",
351
+ "EC",
352
+ "EE",
353
+ "EG",
354
+ "EH",
355
+ "ER",
356
+ "ES",
357
+ "ET",
358
+ "FI",
359
+ "FJ",
360
+ "FM",
361
+ "FO",
362
+ "FR",
363
+ "GA",
364
+ "GB",
365
+ "GD",
366
+ "GE",
367
+ "GF",
368
+ "GG",
369
+ "GH",
370
+ "GI",
371
+ "GL",
372
+ "GM",
373
+ "GN",
374
+ "GP",
375
+ "GQ",
376
+ "GR",
377
+ "GT",
378
+ "GU",
379
+ "GW",
380
+ "GY",
381
+ "HK",
382
+ "HM",
383
+ "HN",
384
+ "HR",
385
+ "HT",
386
+ "HU",
387
+ "ID",
388
+ "IE",
389
+ "IL",
390
+ "IM",
391
+ "IN",
392
+ "IO",
393
+ "IQ",
394
+ "IR",
395
+ "IS",
396
+ "IT",
397
+ "JE",
398
+ "JM",
399
+ "JO",
400
+ "JP",
401
+ "KE",
402
+ "KG",
403
+ "KH",
404
+ "KI",
405
+ "KM",
406
+ "KN",
407
+ "KP",
408
+ "KR",
409
+ "KW",
410
+ "KY",
411
+ "KZ",
412
+ "LA",
413
+ "LB",
414
+ "LC",
415
+ "LI",
416
+ "LK",
417
+ "LR",
418
+ "LS",
419
+ "LT",
420
+ "LU",
421
+ "LV",
422
+ "LY",
423
+ "MA",
424
+ "MC",
425
+ "MD",
426
+ "ME",
427
+ "MF",
428
+ "MG",
429
+ "MH",
430
+ "MK",
431
+ "ML",
432
+ "MM",
433
+ "MN",
434
+ "MO",
435
+ "MP",
436
+ "MQ",
437
+ "MR",
438
+ "MS",
439
+ "MT",
440
+ "MU",
441
+ "MV",
442
+ "MW",
443
+ "MX",
444
+ "MY",
445
+ "MZ",
446
+ "NA",
447
+ "NC",
448
+ "NE",
449
+ "NF",
450
+ "NG",
451
+ "NI",
452
+ "NL",
453
+ "NO",
454
+ "NP",
455
+ "NR",
456
+ "NU",
457
+ "NZ",
458
+ "OM",
459
+ "PA",
460
+ "PE",
461
+ "PF",
462
+ "PG",
463
+ "PH",
464
+ "PK",
465
+ "PL",
466
+ "PM",
467
+ "PN",
468
+ "PR",
469
+ "PT",
470
+ "PW",
471
+ "PY",
472
+ "QA",
473
+ "RE",
474
+ "RO",
475
+ "RS",
476
+ "RU",
477
+ "RW",
478
+ "SA",
479
+ "SB",
480
+ "SC",
481
+ "SD",
482
+ "SE",
483
+ "SG",
484
+ "SH",
485
+ "SI",
486
+ "SJ",
487
+ "SK",
488
+ "SL",
489
+ "SM",
490
+ "SN",
491
+ "SO",
492
+ "SR",
493
+ "SS",
494
+ "ST",
495
+ "SV",
496
+ "SX",
497
+ "SY",
498
+ "SZ",
499
+ "TC",
500
+ "TD",
501
+ "TF",
502
+ "TG",
503
+ "TH",
504
+ "TJ",
505
+ "TK",
506
+ "TL",
507
+ "TM",
508
+ "TN",
509
+ "TO",
510
+ "TR",
511
+ "TT",
512
+ "TV",
513
+ "TZ",
514
+ "UA",
515
+ "UG",
516
+ "UM",
517
+ "US",
518
+ "UY",
519
+ "UZ",
520
+ "VA",
521
+ "VC",
522
+ "VE",
523
+ "VG",
524
+ "VI",
525
+ "VN",
526
+ "VU",
527
+ "WF",
528
+ "WS",
529
+ "YE",
530
+ "YT",
531
+ "ZA",
532
+ "ZM",
533
+ "ZW"
534
+ ]);
535
+ var validateCountryCode = (value) => {
536
+ if (typeof value !== "string") return false;
537
+ return isoCountryCodes.has(value.toUpperCase());
538
+ };
539
+
540
+ // src/validation/currencyCode.ISO4217.ts
541
+ var isoCurrencyCodes = /* @__PURE__ */ new Set([
542
+ "AED",
543
+ "AFN",
544
+ "ALL",
545
+ "AMD",
546
+ "ANG",
547
+ "AOA",
548
+ "ARS",
549
+ "AUD",
550
+ "AWG",
551
+ "AZN",
552
+ "BAM",
553
+ "BBD",
554
+ "BDT",
555
+ "BGN",
556
+ "BHD",
557
+ "BIF",
558
+ "BMD",
559
+ "BND",
560
+ "BOB",
561
+ "BOV",
562
+ "BRL",
563
+ "BSD",
564
+ "BTN",
565
+ "BWP",
566
+ "BYN",
567
+ "BZD",
568
+ "CAD",
569
+ "CDF",
570
+ "CHE",
571
+ "CHF",
572
+ "CHW",
573
+ "CLF",
574
+ "CLP",
575
+ "CNY",
576
+ "COP",
577
+ "COU",
578
+ "CRC",
579
+ "CUC",
580
+ "CUP",
581
+ "CVE",
582
+ "CZK",
583
+ "DJF",
584
+ "DKK",
585
+ "DOP",
586
+ "DZD",
587
+ "EGP",
588
+ "ERN",
589
+ "ETB",
590
+ "EUR",
591
+ "FJD",
592
+ "FKP",
593
+ "GBP",
594
+ "GEL",
595
+ "GHS",
596
+ "GIP",
597
+ "GMD",
598
+ "GNF",
599
+ "GTQ",
600
+ "GYD",
601
+ "HKD",
602
+ "HNL",
603
+ "HRK",
604
+ "HTG",
605
+ "HUF",
606
+ "IDR",
607
+ "ILS",
608
+ "INR",
609
+ "IQD",
610
+ "IRR",
611
+ "ISK",
612
+ "JMD",
613
+ "JOD",
614
+ "JPY",
615
+ "KES",
616
+ "KGS",
617
+ "KHR",
618
+ "KMF",
619
+ "KPW",
620
+ "KRW",
621
+ "KWD",
622
+ "KYD",
623
+ "KZT",
624
+ "LAK",
625
+ "LBP",
626
+ "LKR",
627
+ "LRD",
628
+ "LSL",
629
+ "LYD",
630
+ "MAD",
631
+ "MDL",
632
+ "MGA",
633
+ "MKD",
634
+ "MMK",
635
+ "MNT",
636
+ "MOP",
637
+ "MRU",
638
+ "MUR",
639
+ "MVR",
640
+ "MWK",
641
+ "MXN",
642
+ "MXV",
643
+ "MYR",
644
+ "MZN",
645
+ "NAD",
646
+ "NGN",
647
+ "NIO",
648
+ "NOK",
649
+ "NPR",
650
+ "NZD",
651
+ "OMR",
652
+ "PAB",
653
+ "PEN",
654
+ "PGK",
655
+ "PHP",
656
+ "PKR",
657
+ "PLN",
658
+ "PYG",
659
+ "QAR",
660
+ "RON",
661
+ "RSD",
662
+ "RUB",
663
+ "RWF",
664
+ "SAR",
665
+ "SBD",
666
+ "SCR",
667
+ "SDG",
668
+ "SEK",
669
+ "SGD",
670
+ "SHP",
671
+ "SLL",
672
+ "SOS",
673
+ "SRD",
674
+ "SSP",
675
+ "STN",
676
+ "SVC",
677
+ "SYP",
678
+ "SZL",
679
+ "THB",
680
+ "TJS",
681
+ "TMT",
682
+ "TND",
683
+ "TOP",
684
+ "TRY",
685
+ "TTD",
686
+ "TWD",
687
+ "TZS",
688
+ "UAH",
689
+ "UGX",
690
+ "USD",
691
+ "USN",
692
+ "UYI",
693
+ "UYU",
694
+ "UYW",
695
+ "UZS",
696
+ "VES",
697
+ "VND",
698
+ "VUV",
699
+ "WST",
700
+ "XAF",
701
+ "XAG",
702
+ "XAU",
703
+ "XBA",
704
+ "XBB",
705
+ "XBC",
706
+ "XBD",
707
+ "XCD",
708
+ "XDR",
709
+ "XOF",
710
+ "XPD",
711
+ "XPF",
712
+ "XPT",
713
+ "XSU",
714
+ "XTS",
715
+ "XUA",
716
+ "XXX",
717
+ "YER",
718
+ "ZAR",
719
+ "ZMW",
720
+ "ZWL"
721
+ ]);
722
+ var validateCurrencyCode = (value) => {
723
+ if (typeof value !== "string") return false;
724
+ return isoCurrencyCodes.has(value.toUpperCase());
725
+ };
726
+
727
+ // src/validation/generalText.OWASP.ts
728
+ function validateSafeText(value) {
729
+ if (typeof value !== "string") return false;
730
+ const trimmed = value.trim();
731
+ if (trimmed.length === 0) return false;
732
+ for (let i = 0; i < trimmed.length; i++) {
733
+ const code = trimmed.codePointAt(i);
734
+ if (code !== void 0 && (code >= 0 && code <= 31 || code === 127)) {
735
+ return false;
736
+ }
737
+ }
738
+ if (/['"<>\\{}();]/.test(trimmed)) return false;
739
+ if (/(--|\b(SELECT|UPDATE|DELETE|INSERT|DROP|ALTER|EXEC|UNION|GRANT|REVOKE)\b|\/\*|\*\/|@@)/i.test(
740
+ trimmed
741
+ ))
742
+ return false;
743
+ if (trimmed.includes("\0")) return false;
744
+ if (trimmed.length > 1024) return false;
745
+ return true;
746
+ }
747
+
748
+ // src/validation/version.SEMVER2.0.0.ts
749
+ function validateSemVer(value) {
750
+ if (typeof value !== "string") return false;
751
+ 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(
752
+ value
753
+ );
754
+ }
755
+
756
+ // src/validation/percentage.ISO80000-1.ts
757
+ function validatePercentage(value) {
758
+ if (typeof value !== "number") return false;
759
+ return value >= 0 && value <= 100;
760
+ }
761
+
762
+ // src/validation/richtext.OWASP.ts
763
+ function validateRichText(value) {
764
+ if (typeof value !== "string") return false;
765
+ const trimmed = value.trim();
766
+ if (trimmed.length === 0) return true;
767
+ if (/<(script|iframe|object|embed|style|link|meta|base|form|input|button|textarea|select)\b/i.test(
768
+ trimmed
769
+ )) {
770
+ return false;
771
+ }
772
+ if (/javascript:/i.test(trimmed)) {
773
+ return false;
774
+ }
775
+ if (/on\w+=["']?/i.test(trimmed)) {
776
+ return false;
777
+ }
778
+ if (trimmed.length > 1e4) return false;
779
+ return true;
780
+ }
781
+
782
+ // src/validation/name.OWASP.ts
783
+ function validateName(value) {
784
+ if (typeof value !== "string") return false;
785
+ const trimmed = value.trim();
786
+ if (trimmed.length === 0) return false;
787
+ if (trimmed.length > 256) return false;
788
+ for (const ch of trimmed) {
789
+ const cp = ch.codePointAt(0);
790
+ if (cp >= 0 && cp <= 31 || cp === 127) return false;
791
+ }
792
+ const namePattern = /^[\p{L}\p{M}'\- ]+$/u;
793
+ if (!namePattern.test(trimmed)) return false;
794
+ return true;
795
+ }
796
+
797
+ // src/validation/user.MS-GOOGLE-APPLE.ts
798
+ function validateUserId(value) {
799
+ if (typeof value !== "string") return false;
800
+ const trimmed = value.trim();
801
+ if (trimmed.length === 0) return false;
802
+ const googlePattern = /^\d{21,22}$/;
803
+ 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;
804
+ const applePattern = /^[\w\-.]{6,255}$/;
805
+ return googlePattern.test(trimmed) || microsoftPattern.test(trimmed) || applePattern.test(trimmed);
806
+ }
807
+ function validateUserIdArray(value) {
808
+ if (!Array.isArray(value)) return false;
809
+ return value.every(validateUserId);
810
+ }
811
+
812
+ // src/validation/languageCode.BCP47.ts
813
+ var IsoLanguageCode = /* @__PURE__ */ ((IsoLanguageCode2) => {
814
+ IsoLanguageCode2["Afar"] = "aa";
815
+ IsoLanguageCode2["Abkhazian"] = "ab";
816
+ IsoLanguageCode2["Afrikaans"] = "af";
817
+ IsoLanguageCode2["Akan"] = "ak";
818
+ IsoLanguageCode2["Albanian"] = "sq";
819
+ IsoLanguageCode2["Amharic"] = "am";
820
+ IsoLanguageCode2["Arabic"] = "ar";
821
+ IsoLanguageCode2["Aragonese"] = "an";
822
+ IsoLanguageCode2["Armenian"] = "hy";
823
+ IsoLanguageCode2["Assamese"] = "as";
824
+ IsoLanguageCode2["Avaric"] = "av";
825
+ IsoLanguageCode2["Aymara"] = "ay";
826
+ IsoLanguageCode2["Azerbaijani"] = "az";
827
+ IsoLanguageCode2["Bashkir"] = "ba";
828
+ IsoLanguageCode2["Bambara"] = "bm";
829
+ IsoLanguageCode2["Basque"] = "eu";
830
+ IsoLanguageCode2["Belarusian"] = "be";
831
+ IsoLanguageCode2["Bengali"] = "bn";
832
+ IsoLanguageCode2["Bislama"] = "bi";
833
+ IsoLanguageCode2["Bosnian"] = "bs";
834
+ IsoLanguageCode2["Breton"] = "br";
835
+ IsoLanguageCode2["Bulgarian"] = "bg";
836
+ IsoLanguageCode2["Burmese"] = "my";
837
+ IsoLanguageCode2["Catalan"] = "ca";
838
+ IsoLanguageCode2["Chamorro"] = "ch";
839
+ IsoLanguageCode2["Chechen"] = "ce";
840
+ IsoLanguageCode2["Chinese"] = "zh";
841
+ IsoLanguageCode2["ChurchSlavic"] = "cu";
842
+ IsoLanguageCode2["Chuvash"] = "cv";
843
+ IsoLanguageCode2["Cornish"] = "kw";
844
+ IsoLanguageCode2["Corsican"] = "co";
845
+ IsoLanguageCode2["Cree"] = "cr";
846
+ IsoLanguageCode2["Croatian"] = "hr";
847
+ IsoLanguageCode2["Czech"] = "cs";
848
+ IsoLanguageCode2["Danish"] = "da";
849
+ IsoLanguageCode2["Divehi"] = "dv";
850
+ IsoLanguageCode2["Dutch"] = "nl";
851
+ IsoLanguageCode2["Dzongkha"] = "dz";
852
+ IsoLanguageCode2["English"] = "en";
853
+ IsoLanguageCode2["Esperanto"] = "eo";
854
+ IsoLanguageCode2["Estonian"] = "et";
855
+ IsoLanguageCode2["Ewe"] = "ee";
856
+ IsoLanguageCode2["Faroese"] = "fo";
857
+ IsoLanguageCode2["Fijian"] = "fj";
858
+ IsoLanguageCode2["Finnish"] = "fi";
859
+ IsoLanguageCode2["French"] = "fr";
860
+ IsoLanguageCode2["WesternFrisian"] = "fy";
861
+ IsoLanguageCode2["Fulah"] = "ff";
862
+ IsoLanguageCode2["Gaelic"] = "gd";
863
+ IsoLanguageCode2["Galician"] = "gl";
864
+ IsoLanguageCode2["Ganda"] = "lg";
865
+ IsoLanguageCode2["Georgian"] = "ka";
866
+ IsoLanguageCode2["German"] = "de";
867
+ IsoLanguageCode2["Greek"] = "el";
868
+ IsoLanguageCode2["Kalaallisut"] = "kl";
869
+ IsoLanguageCode2["Guarani"] = "gn";
870
+ IsoLanguageCode2["Gujarati"] = "gu";
871
+ IsoLanguageCode2["Haitian"] = "ht";
872
+ IsoLanguageCode2["Hausa"] = "ha";
873
+ IsoLanguageCode2["Hebrew"] = "he";
874
+ IsoLanguageCode2["Herero"] = "hz";
875
+ IsoLanguageCode2["Hindi"] = "hi";
876
+ IsoLanguageCode2["HiriMotu"] = "ho";
877
+ IsoLanguageCode2["Hungarian"] = "hu";
878
+ IsoLanguageCode2["Icelandic"] = "is";
879
+ IsoLanguageCode2["Ido"] = "io";
880
+ IsoLanguageCode2["Igbo"] = "ig";
881
+ IsoLanguageCode2["Indonesian"] = "id";
882
+ IsoLanguageCode2["Interlingua"] = "ia";
883
+ IsoLanguageCode2["Interlingue"] = "ie";
884
+ IsoLanguageCode2["Inuktitut"] = "iu";
885
+ IsoLanguageCode2["Inupiaq"] = "ik";
886
+ IsoLanguageCode2["Irish"] = "ga";
887
+ IsoLanguageCode2["Italian"] = "it";
888
+ IsoLanguageCode2["Japanese"] = "ja";
889
+ IsoLanguageCode2["Javanese"] = "jv";
890
+ IsoLanguageCode2["Kannada"] = "kn";
891
+ IsoLanguageCode2["Kanuri"] = "kr";
892
+ IsoLanguageCode2["Kashmiri"] = "ks";
893
+ IsoLanguageCode2["Kazakh"] = "kk";
894
+ IsoLanguageCode2["CentralKhmer"] = "km";
895
+ IsoLanguageCode2["Kikuyu"] = "ki";
896
+ IsoLanguageCode2["Kinyarwanda"] = "rw";
897
+ IsoLanguageCode2["Kyrgyz"] = "ky";
898
+ IsoLanguageCode2["Komi"] = "kv";
899
+ IsoLanguageCode2["Kongo"] = "kg";
900
+ IsoLanguageCode2["Korean"] = "ko";
901
+ IsoLanguageCode2["Kuanyama"] = "kj";
902
+ IsoLanguageCode2["Kurdish"] = "ku";
903
+ IsoLanguageCode2["Lao"] = "lo";
904
+ IsoLanguageCode2["Latin"] = "la";
905
+ IsoLanguageCode2["Latvian"] = "lv";
906
+ IsoLanguageCode2["Limburgan"] = "li";
907
+ IsoLanguageCode2["Lingala"] = "ln";
908
+ IsoLanguageCode2["Lithuanian"] = "lt";
909
+ IsoLanguageCode2["LubaKatanga"] = "lu";
910
+ IsoLanguageCode2["Luxembourgish"] = "lb";
911
+ IsoLanguageCode2["Macedonian"] = "mk";
912
+ IsoLanguageCode2["Malagasy"] = "mg";
913
+ IsoLanguageCode2["Malay"] = "ms";
914
+ IsoLanguageCode2["Malayalam"] = "ml";
915
+ IsoLanguageCode2["Maltese"] = "mt";
916
+ IsoLanguageCode2["Manx"] = "gv";
917
+ IsoLanguageCode2["Maori"] = "mi";
918
+ IsoLanguageCode2["Marathi"] = "mr";
919
+ IsoLanguageCode2["Marshallese"] = "mh";
920
+ IsoLanguageCode2["Mongolian"] = "mn";
921
+ IsoLanguageCode2["Nauru"] = "na";
922
+ IsoLanguageCode2["Navajo"] = "nv";
923
+ IsoLanguageCode2["NorthNdebele"] = "nd";
924
+ IsoLanguageCode2["SouthNdebele"] = "nr";
925
+ IsoLanguageCode2["Ndonga"] = "ng";
926
+ IsoLanguageCode2["Nepali"] = "ne";
927
+ IsoLanguageCode2["Norwegian"] = "no";
928
+ IsoLanguageCode2["NorwegianBokmal"] = "nb";
929
+ IsoLanguageCode2["NorwegianNynorsk"] = "nn";
930
+ IsoLanguageCode2["SichuanYi"] = "ii";
931
+ IsoLanguageCode2["Occitan"] = "oc";
932
+ IsoLanguageCode2["Ojibwa"] = "oj";
933
+ IsoLanguageCode2["Oriya"] = "or";
934
+ IsoLanguageCode2["Oromo"] = "om";
935
+ IsoLanguageCode2["Ossetian"] = "os";
936
+ IsoLanguageCode2["Pali"] = "pi";
937
+ IsoLanguageCode2["Pashto"] = "ps";
938
+ IsoLanguageCode2["Persian"] = "fa";
939
+ IsoLanguageCode2["Polish"] = "pl";
940
+ IsoLanguageCode2["Portuguese"] = "pt";
941
+ IsoLanguageCode2["Punjabi"] = "pa";
942
+ IsoLanguageCode2["Quechua"] = "qu";
943
+ IsoLanguageCode2["Romansh"] = "rm";
944
+ IsoLanguageCode2["Romanian"] = "ro";
945
+ IsoLanguageCode2["Rundi"] = "rn";
946
+ IsoLanguageCode2["Russian"] = "ru";
947
+ IsoLanguageCode2["Samoan"] = "sm";
948
+ IsoLanguageCode2["Sango"] = "sg";
949
+ IsoLanguageCode2["Sanskrit"] = "sa";
950
+ IsoLanguageCode2["Sardinian"] = "sc";
951
+ IsoLanguageCode2["Serbian"] = "sr";
952
+ IsoLanguageCode2["Shona"] = "sn";
953
+ IsoLanguageCode2["Sindhi"] = "sd";
954
+ IsoLanguageCode2["Sinhala"] = "si";
955
+ IsoLanguageCode2["Slovak"] = "sk";
956
+ IsoLanguageCode2["Slovenian"] = "sl";
957
+ IsoLanguageCode2["Somali"] = "so";
958
+ IsoLanguageCode2["SouthernSotho"] = "st";
959
+ IsoLanguageCode2["Spanish"] = "es";
960
+ IsoLanguageCode2["Sundanese"] = "su";
961
+ IsoLanguageCode2["Swahili"] = "sw";
962
+ IsoLanguageCode2["Swati"] = "ss";
963
+ IsoLanguageCode2["Swedish"] = "sv";
964
+ IsoLanguageCode2["Tagalog"] = "tl";
965
+ IsoLanguageCode2["Tahitian"] = "ty";
966
+ IsoLanguageCode2["Tajik"] = "tg";
967
+ IsoLanguageCode2["Tamil"] = "ta";
968
+ IsoLanguageCode2["Tatar"] = "tt";
969
+ IsoLanguageCode2["Telugu"] = "te";
970
+ IsoLanguageCode2["Thai"] = "th";
971
+ IsoLanguageCode2["Tibetan"] = "bo";
972
+ IsoLanguageCode2["Tigrinya"] = "ti";
973
+ IsoLanguageCode2["Tonga"] = "to";
974
+ IsoLanguageCode2["Tsonga"] = "ts";
975
+ IsoLanguageCode2["Tswana"] = "tn";
976
+ IsoLanguageCode2["Turkish"] = "tr";
977
+ IsoLanguageCode2["Turkmen"] = "tk";
978
+ IsoLanguageCode2["Twi"] = "tw";
979
+ IsoLanguageCode2["Uighur"] = "ug";
980
+ IsoLanguageCode2["Ukrainian"] = "uk";
981
+ IsoLanguageCode2["Urdu"] = "ur";
982
+ IsoLanguageCode2["Uzbek"] = "uz";
983
+ IsoLanguageCode2["Venda"] = "ve";
984
+ IsoLanguageCode2["Vietnamese"] = "vi";
985
+ IsoLanguageCode2["Volapuk"] = "vo";
986
+ IsoLanguageCode2["Walloon"] = "wa";
987
+ IsoLanguageCode2["Welsh"] = "cy";
988
+ IsoLanguageCode2["Wolof"] = "wo";
989
+ IsoLanguageCode2["Xhosa"] = "xh";
990
+ IsoLanguageCode2["Yiddish"] = "yi";
991
+ IsoLanguageCode2["Yoruba"] = "yo";
992
+ IsoLanguageCode2["Zhuang"] = "za";
993
+ IsoLanguageCode2["Zulu"] = "zu";
994
+ return IsoLanguageCode2;
995
+ })(IsoLanguageCode || {});
996
+ var ISO_LANGUAGE_SET = new Set(
997
+ Object.values(IsoLanguageCode)
998
+ );
999
+ function isIsoLanguageCode(value) {
1000
+ return typeof value === "string" && ISO_LANGUAGE_SET.has(value.toLowerCase());
1001
+ }
1002
+ function isRegionSubtag(value) {
1003
+ return /^[A-Z]{2}$/.test(value) || /^\d{3}$/.test(value);
1004
+ }
1005
+ function isScriptSubtag(value) {
1006
+ return /^[A-Z][a-z]{3}$/.test(value);
1007
+ }
1008
+ function isVariantSubtag(value) {
1009
+ return /^([0-9][A-Za-z0-9]{3}|[A-Za-z0-9]{5,8})$/.test(value);
1010
+ }
1011
+ function isExtensionSingleton(value) {
1012
+ return /^[0-9A-WY-Za-wy-z]$/.test(value);
1013
+ }
1014
+ function isExtensionSubtag(value) {
1015
+ return /^[A-Za-z0-9]{2,8}$/.test(value);
1016
+ }
1017
+ function isPrivateUseSingleton(value) {
1018
+ return value.toLowerCase() === "x";
1019
+ }
1020
+ function isPrivateUseSubtag(value) {
1021
+ return /^[A-Za-z0-9]{1,8}$/.test(value);
1022
+ }
1023
+ function validateLanguage(value) {
1024
+ if (typeof value !== "string" || value.length === 0) return false;
1025
+ const parts = value.split("-");
1026
+ let i = 0;
1027
+ const lang = parts[i];
1028
+ if (!lang || !isIsoLanguageCode(lang)) return false;
1029
+ i += 1;
1030
+ if (i < parts.length && isScriptSubtag(parts[i])) {
1031
+ i += 1;
1032
+ }
1033
+ if (i < parts.length && isRegionSubtag(parts[i].toUpperCase())) {
1034
+ i += 1;
1035
+ }
1036
+ while (i < parts.length && isVariantSubtag(parts[i])) {
1037
+ i += 1;
1038
+ }
1039
+ while (i < parts.length && isExtensionSingleton(parts[i])) {
1040
+ i += 1;
1041
+ if (!(i < parts.length && isExtensionSubtag(parts[i]))) return false;
1042
+ while (i < parts.length && isExtensionSubtag(parts[i])) {
1043
+ i += 1;
1044
+ }
1045
+ }
1046
+ if (i < parts.length && isPrivateUseSingleton(parts[i])) {
1047
+ i += 1;
1048
+ if (!(i < parts.length && isPrivateUseSubtag(parts[i]))) return false;
1049
+ while (i < parts.length && isPrivateUseSubtag(parts[i])) {
1050
+ i += 1;
1051
+ }
1052
+ }
1053
+ return i === parts.length;
1054
+ }
1055
+
1056
+ // src/field.ts
1057
+ var field = {
1058
+ string: () => new FieldBuilder("string"),
1059
+ number: () => new FieldBuilder("number"),
1060
+ boolean: () => new FieldBuilder("boolean"),
1061
+ object: (fields) => new FieldBuilder("object", { shape: fields }),
1062
+ array: (itemType) => new FieldBuilder("array", { itemType }),
1063
+ ref: (refType) => new FieldBuilder("ref", { refType }),
1064
+ email: () => new FieldBuilder("string").validator(validateEmail).PID({
1065
+ classification: "high",
1066
+ action: "encrypt",
1067
+ logHandling: "redact",
1068
+ purpose: "an email address"
1069
+ }).description("An email address"),
1070
+ phone: () => new FieldBuilder("string").validator(validatePhone).PID({
1071
+ classification: "high",
1072
+ action: "encrypt",
1073
+ logHandling: "redact",
1074
+ purpose: "a phone number"
1075
+ }).description("A phone number"),
1076
+ url: () => new FieldBuilder("string").validator(validateUrl).PID({
1077
+ classification: "low",
1078
+ action: "hash",
1079
+ logHandling: "pseudonym",
1080
+ purpose: "a URL"
1081
+ }).description("A URL"),
1082
+ uuid: () => new FieldBuilder("string").PID({
1083
+ classification: "low",
1084
+ action: "hash",
1085
+ logHandling: "pseudonym",
1086
+ purpose: "a UUID"
1087
+ }).validator(validateUUID).description("A UUID"),
1088
+ dateTimeISO: () => new FieldBuilder("string").PID({
1089
+ classification: "none",
1090
+ action: "none",
1091
+ logHandling: "plain",
1092
+ purpose: "a date string"
1093
+ }).validator(validateDateTimeISO).description("A date string in ISO 8601 format"),
1094
+ dateISO: () => new FieldBuilder("string").PID({
1095
+ classification: "none",
1096
+ action: "none",
1097
+ logHandling: "plain",
1098
+ purpose: "a date string"
1099
+ }).validator((s) => validateDateTimeISO(s, { mode: "date" })).description("A date string in ISO 8601 format (date only)"),
1100
+ timeISO: () => new FieldBuilder("string").PID({
1101
+ classification: "none",
1102
+ action: "none",
1103
+ logHandling: "plain",
1104
+ purpose: "a time string"
1105
+ }).validator((s) => validateDateTimeISO(s, { mode: "time" })).description("A time string in ISO 8601 format (time only)"),
1106
+ richText: () => new FieldBuilder("string").PID({
1107
+ classification: "low",
1108
+ action: "clear",
1109
+ logHandling: "omit",
1110
+ purpose: "rich text content"
1111
+ }).validator(validateRichText).description("Rich text content, may include basic HTML formatting"),
1112
+ generalText: () => new FieldBuilder("string").PID({
1113
+ classification: "none",
1114
+ action: "none",
1115
+ logHandling: "plain",
1116
+ purpose: "Plain text content"
1117
+ }).validator(validateSafeText).description("Standard text content, no HTML allowed"),
1118
+ latitude: () => new FieldBuilder("number").PID({
1119
+ classification: "low",
1120
+ action: "clear",
1121
+ logHandling: "omit",
1122
+ purpose: "Latitude in decimal degrees, WGS 84 (ISO 6709)"
1123
+ }).min(-90).max(90).description("Latitude in decimal degrees, WGS 84 (ISO 6709)"),
1124
+ longitude: () => new FieldBuilder("number").PID({
1125
+ classification: "low",
1126
+ action: "clear",
1127
+ logHandling: "omit",
1128
+ purpose: "Longitude in decimal degrees, WGS 84 (ISO 6709)"
1129
+ }).min(-180).max(180).description("Longitude in decimal degrees, WGS 84 (ISO 6709)"),
1130
+ version: () => new FieldBuilder("string").validator(validateSemVer).description("A semantic version string, e.g. '1.0.0'"),
1131
+ countryCode: () => new FieldBuilder("string").validator(validateCountryCode).description("An ISO 3166 country code, e.g. 'US', 'GB', 'FR'"),
1132
+ languageCode: () => new FieldBuilder("string").validator(validateLanguage).description(
1133
+ "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'"
1134
+ )
1135
+ };
1136
+
1137
+ // src/pii.ts
1138
+ function enforcePIIField(parentKey, key, value, def, enforcement = "none", errors, logger) {
1139
+ const path = parentKey ? `${parentKey}.${key}` : key;
1140
+ if (def?._pii?.classification === "high" && (def?.isRequired ?? true)) {
1141
+ const missing = value === void 0 || value === null || value === "";
1142
+ if (missing) {
1143
+ const msg = `High PII field must not be empty: ${path}`;
1144
+ if (enforcement === "strict") {
1145
+ errors?.push(msg);
1146
+ return { shortCircuit: true };
1147
+ }
1148
+ if (enforcement === "warn") {
1149
+ logger?.warn?.(`WARN (PII Enforcement): ${msg}`);
1150
+ }
1151
+ }
1152
+ }
1153
+ return { shortCircuit: false };
1154
+ }
1155
+ function prepareForStorage(shape, input, encryptFn, hashFn) {
1156
+ const result = {};
1157
+ for (const key in shape) {
1158
+ const def = shape[key];
1159
+ if (!def) continue;
1160
+ const value = input[key];
1161
+ if (def._pii?.action === "encrypt") {
1162
+ result[key + "Encrypted"] = encryptFn(value);
1163
+ } else if (def._pii?.action === "hash") {
1164
+ result[key + "Hash"] = hashFn(value);
1165
+ } else {
1166
+ result[key] = value;
1167
+ }
1168
+ }
1169
+ return result;
1170
+ }
1171
+ function prepareForRead(shape, stored, decryptFn) {
1172
+ const result = {};
1173
+ for (const key in shape) {
1174
+ const def = shape[key];
1175
+ if (!def) continue;
1176
+ if (def._pii?.action === "encrypt") {
1177
+ result[key] = decryptFn(stored[key + "Encrypted"]);
1178
+ } else {
1179
+ result[key] = stored[key];
1180
+ }
1181
+ }
1182
+ return result;
1183
+ }
1184
+ function sanitizeForLog(shape, data, pseudonymFn) {
1185
+ const output = {};
1186
+ for (const key in shape) {
1187
+ const def = shape[key];
1188
+ if (!def) continue;
1189
+ const value = data[key];
1190
+ const handling = def._pii?.logHandling;
1191
+ if (handling === "omit") continue;
1192
+ if (handling === "redact") {
1193
+ output[key] = "[REDACTED]";
1194
+ } else if (handling === "pseudonym") {
1195
+ output[key] = pseudonymFn(value);
1196
+ } else {
1197
+ output[key] = value;
1198
+ }
1199
+ }
1200
+ return output;
1201
+ }
1202
+ function getPiiAudit(shape) {
1203
+ const piiFields = [];
1204
+ for (const key in shape) {
1205
+ const def = shape[key];
1206
+ if (!def) continue;
1207
+ if (def._pii && def._pii.classification !== "none") {
1208
+ piiFields.push({
1209
+ field: key,
1210
+ classification: def._pii.classification,
1211
+ action: def._pii.action,
1212
+ logHandling: def._pii.logHandling,
1213
+ purpose: def._pii.purpose
1214
+ });
1215
+ }
1216
+ }
1217
+ return piiFields;
1218
+ }
1219
+ function scrubPiiForDelete(shape, stored) {
1220
+ const result = { ...stored };
1221
+ for (const key in shape) {
1222
+ const def = shape[key];
1223
+ if (!def) continue;
1224
+ if (def._pii?.action === "encrypt") {
1225
+ result[key + "Encrypted"] = null;
1226
+ } else if (def._pii?.action === "hash") {
1227
+ result[key + "Hash"] = null;
1228
+ } else if (def._pii?.action === "clear") {
1229
+ result[key] = null;
1230
+ }
1231
+ }
1232
+ return result;
1233
+ }
1234
+
1235
+ // src/schema.ts
1236
+ var globalSchemaRegistry = /* @__PURE__ */ new Map();
1237
+ function cmpSemver(a, b) {
1238
+ const pa = a.split(".").map((n) => parseInt(n, 10));
1239
+ const pb = b.split(".").map((n) => parseInt(n, 10));
1240
+ for (let i = 0; i < 3; i++) {
1241
+ const ai = pa[i] ?? 0;
1242
+ const bi = pb[i] ?? 0;
1243
+ if (ai > bi) return 1;
1244
+ if (ai < bi) return -1;
1245
+ }
1246
+ return 0;
1247
+ }
1248
+ function applySchemaUpgrade(spec, input, ctx) {
1249
+ if (typeof spec === "function") {
1250
+ return spec(input, ctx);
1251
+ }
1252
+ const steps = [...spec].sort((s1, s2) => cmpSemver(s1.to, s2.to));
1253
+ let working = { ...input };
1254
+ let fromVersion = ctx.from;
1255
+ for (const step of steps) {
1256
+ if (cmpSemver(fromVersion, step.to) < 0 && cmpSemver(step.to, ctx.to) <= 0) {
1257
+ ctx.log?.(`Upgrading entity from v${fromVersion} \u2192 v${step.to}`);
1258
+ const res = step.run(working, { ...ctx, from: fromVersion, to: step.to });
1259
+ if (!res || res.ok !== true || !res.value) {
1260
+ return {
1261
+ ok: false,
1262
+ errors: res?.errors?.length ? res.errors : [`Failed to upgrade entity from v${fromVersion} to v${step.to}`]
1263
+ };
1264
+ }
1265
+ working = res.value;
1266
+ fromVersion = step.to;
1267
+ }
1268
+ }
1269
+ return { ok: true, value: working };
1270
+ }
1271
+ function validateEnum(parentKey, value, enumValues) {
1272
+ if (!enumValues) return;
1273
+ const values = Array.isArray(enumValues) ? enumValues : Array.from(enumValues);
1274
+ if (Array.isArray(value)) {
1275
+ const invalid = value.filter((v) => !values.includes(v));
1276
+ if (invalid.length > 0) {
1277
+ return `Field ${parentKey} contains invalid enum values: ${invalid.join(
1278
+ ", "
1279
+ )}`;
1280
+ }
1281
+ } else {
1282
+ if (!values.includes(value)) {
1283
+ return `Field ${parentKey} must be one of: ${values.join(", ")}`;
1284
+ }
1285
+ }
1286
+ }
1287
+ function getEnumValues(def) {
1288
+ 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;
1289
+ if (!src) return void 0;
1290
+ const ok = src.every(
1291
+ (v) => typeof v === "string" || typeof v === "number"
1292
+ );
1293
+ return ok ? src : void 0;
1294
+ }
1295
+ function isOptional(def) {
1296
+ return (def?.isRequired ?? false) === false;
1297
+ }
1298
+ function getValidator(def) {
1299
+ return def?._validator ?? void 0;
1300
+ }
1301
+ function getShape(def) {
1302
+ return def?._shape ?? def?.shape ?? void 0;
1303
+ }
1304
+ function checkMissingRequired(parentKey, key, value, def, errors) {
1305
+ if (value === void 0 || value === null) {
1306
+ const path = parentKey ? `${parentKey}.${key}` : key;
1307
+ if (!isOptional(def)) {
1308
+ errors.push(`Missing required field: ${path}`);
1309
+ }
1310
+ return { missing: true };
1311
+ }
1312
+ return { missing: false };
1313
+ }
1314
+ function checkImmutable(parentKey, key, value, def, existing, errors) {
1315
+ if (def.isImmutable && existing && existing[key] !== void 0 && value !== existing[key]) {
1316
+ const path = parentKey ? `${parentKey}.${key}` : key;
1317
+ errors.push(`Field is immutable: ${path}`);
1318
+ return { immutableViolation: true };
1319
+ }
1320
+ return { immutableViolation: false };
1321
+ }
1322
+ function runCustomValidator(parentKey, key, value, def, errors) {
1323
+ const validator = getValidator(def);
1324
+ if (validator && value !== void 0 && value !== null) {
1325
+ const valid = validator(value);
1326
+ if (!valid) {
1327
+ const path = parentKey ? `${parentKey}.${key}` : key;
1328
+ errors.push(`Invalid value for field: ${path}`);
1329
+ return { invalid: true };
1330
+ }
1331
+ }
1332
+ return { invalid: false };
1333
+ }
1334
+ function validateStringField(parentKey, key, value, def, errors) {
1335
+ const path = parentKey ? `${parentKey}.${key}` : key;
1336
+ if (typeof value !== "string") {
1337
+ errors.push(`Field ${path} must be string`);
1338
+ return;
1339
+ }
1340
+ const enumValues = getEnumValues(def);
1341
+ if (Array.isArray(enumValues)) {
1342
+ const enumError = validateEnum(path, value, enumValues);
1343
+ if (enumError) {
1344
+ errors.push(enumError);
1345
+ }
1346
+ }
1347
+ }
1348
+ function validateNumberField(parentKey, key, value, _def, errors) {
1349
+ const enumPath = parentKey ? `${parentKey}.${key}` : key;
1350
+ if (typeof value !== "number") {
1351
+ errors.push(`Field ${enumPath} must be number`);
1352
+ }
1353
+ }
1354
+ function validateBooleanField(parentKey, key, value, _def, errors) {
1355
+ const path = parentKey ? `${parentKey}.${key}` : key;
1356
+ if (typeof value !== "boolean") {
1357
+ errors.push(`Field ${path} must be boolean`);
1358
+ }
1359
+ }
1360
+ function validateObjectChildren(parentKey, obj, shape, errors) {
1361
+ for (const [childKey, childDef] of Object.entries(shape)) {
1362
+ const childValue = obj[childKey];
1363
+ const { missing } = checkMissingRequired(
1364
+ parentKey,
1365
+ childKey,
1366
+ childValue,
1367
+ childDef,
1368
+ errors
1369
+ );
1370
+ if (missing) continue;
1371
+ const { invalid } = runCustomValidator(
1372
+ parentKey,
1373
+ childKey,
1374
+ childValue,
1375
+ childDef,
1376
+ errors
1377
+ );
1378
+ if (invalid) continue;
1379
+ validateByType(parentKey, childKey, childValue, childDef, errors);
1380
+ }
1381
+ }
1382
+ function validateObjectField(parentKey, key, value, def, errors) {
1383
+ const path = parentKey ? `${parentKey}.${key}` : key;
1384
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1385
+ errors.push(`Field ${path} must be object`);
1386
+ return;
1387
+ }
1388
+ const objShape = getShape(def);
1389
+ if (objShape) validateObjectChildren(path, value, objShape, errors);
1390
+ }
1391
+ function validateArrayOfStrings(parentKey, key, arr, itemDef, errors) {
1392
+ const path = parentKey ? `${parentKey}.${key}` : key;
1393
+ if (!arr.every((v) => typeof v === "string")) {
1394
+ errors.push(`Field ${path} must be string[]`);
1395
+ return;
1396
+ }
1397
+ const enumValues = getEnumValues(itemDef);
1398
+ if (Array.isArray(enumValues)) {
1399
+ const enumError = validateEnum(path, arr, enumValues);
1400
+ if (enumError) {
1401
+ errors.push(enumError);
1402
+ }
1403
+ }
1404
+ }
1405
+ function validateArrayOfNumbers(parentKey, key, arr, itemDef, errors) {
1406
+ const path = parentKey ? `${parentKey}.${key}` : key;
1407
+ if (!arr.every((v) => typeof v === "number")) {
1408
+ errors.push(`Field ${path} must be number[]`);
1409
+ }
1410
+ const enumValues = getEnumValues(itemDef);
1411
+ if (Array.isArray(enumValues)) {
1412
+ const enumError = validateEnum(path, arr, enumValues);
1413
+ if (enumError) {
1414
+ errors.push(enumError);
1415
+ }
1416
+ }
1417
+ }
1418
+ function validateArrayOfBooleans(parentKey, key, arr, _itemDef, errors) {
1419
+ const path = parentKey ? `${parentKey}.${key}` : key;
1420
+ if (!arr.every((v) => typeof v === "boolean")) {
1421
+ errors.push(`Field ${path} must be boolean[]`);
1422
+ }
1423
+ }
1424
+ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors) {
1425
+ const path = parentKey ? `${parentKey}.${key}` : key;
1426
+ if (!Array.isArray(arr) || !arr.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
1427
+ errors.push(`Field ${path} must be object[]`);
1428
+ return;
1429
+ }
1430
+ const itemShape = getShape(itemDef);
1431
+ if (!itemShape) return;
1432
+ arr.forEach((item, idx) => {
1433
+ const itemParent = `${path}[${idx}]`;
1434
+ for (const [childKey, childDef] of Object.entries(itemShape)) {
1435
+ const childValue = item[childKey];
1436
+ const { missing } = checkMissingRequired(
1437
+ itemParent,
1438
+ childKey,
1439
+ childValue,
1440
+ childDef,
1441
+ errors
1442
+ );
1443
+ if (missing) continue;
1444
+ const { invalid } = runCustomValidator(
1445
+ itemParent,
1446
+ childKey,
1447
+ childValue,
1448
+ childDef,
1449
+ errors
1450
+ );
1451
+ if (invalid) continue;
1452
+ validateByType(itemParent, childKey, childValue, childDef, errors);
1453
+ }
1454
+ });
1455
+ }
1456
+ function validateArrayField(parentKey, key, value, def, errors) {
1457
+ const path = parentKey ? `${parentKey}.${key}` : key;
1458
+ if (!Array.isArray(value)) {
1459
+ errors.push(`Field ${key} must be an array`);
1460
+ return;
1461
+ }
1462
+ const itemType = def.itemType?.type;
1463
+ if (itemType === "string")
1464
+ return validateArrayOfStrings(parentKey, key, value, def.itemType, errors);
1465
+ if (itemType === "number")
1466
+ return validateArrayOfNumbers(parentKey, key, value, def.itemType, errors);
1467
+ if (itemType === "boolean")
1468
+ return validateArrayOfBooleans(parentKey, key, value, def.itemType, errors);
1469
+ if (itemType === "object")
1470
+ return validateArrayOfObjects(parentKey, key, value, def.itemType, errors);
1471
+ if (itemType === "ref") {
1472
+ const expectedType = def.itemType.refType;
1473
+ value.forEach((ref, idx) => {
1474
+ if (!ref || typeof ref !== "object" || ref === null || typeof ref.type !== "string" || typeof ref.id !== "string" || expectedType && ref.type !== expectedType) {
1475
+ errors.push(
1476
+ `Field ${path}[${idx}] must be a reference object with type: ${expectedType}`
1477
+ );
1478
+ }
1479
+ });
1480
+ const refShape = getShape(def.itemType);
1481
+ if (refShape) {
1482
+ value.forEach((ref, idx) => {
1483
+ if (ref && typeof ref === "object" && ref !== null) {
1484
+ for (const [childKey, childDef] of Object.entries(refShape)) {
1485
+ const childValue = ref[childKey];
1486
+ if ((childValue === void 0 || childValue === null) && !isOptional(childDef)) {
1487
+ errors.push(
1488
+ `Missing required field: ${path}[${idx}].${childKey}`
1489
+ );
1490
+ continue;
1491
+ }
1492
+ const childValidator = getValidator(childDef);
1493
+ if (childValidator && childValue !== void 0 && childValue !== null) {
1494
+ const valid = childValidator(childValue);
1495
+ if (!valid)
1496
+ errors.push(
1497
+ `Invalid value for field: ${path}[${idx}].${childKey}`
1498
+ );
1499
+ }
1500
+ }
1501
+ }
1502
+ });
1503
+ }
1504
+ return;
1505
+ }
1506
+ errors.push(`Field ${path} has unsupported array item type`);
1507
+ }
1508
+ function validateRefField(parentKey, key, value, _def, errors) {
1509
+ if (typeof value !== "object" || value === null || typeof value.type !== "string" || typeof value.id !== "string") {
1510
+ const path = parentKey ? `${parentKey}.${key}` : key;
1511
+ errors.push(`Field ${path} must be { type: string; id: string }`);
1512
+ }
1513
+ }
1514
+ function validateByType(parentKey, key, value, def, errors) {
1515
+ const path = parentKey ? `${parentKey}.${key}` : key;
1516
+ switch (def.type) {
1517
+ case "string":
1518
+ return validateStringField(parentKey, key, value, def, errors);
1519
+ case "number":
1520
+ return validateNumberField(parentKey, key, value, def, errors);
1521
+ case "boolean":
1522
+ return validateBooleanField(parentKey, key, value, def, errors);
1523
+ case "object":
1524
+ return validateObjectField(parentKey, key, value, def, errors);
1525
+ case "array":
1526
+ return validateArrayField(parentKey, key, value, def, errors);
1527
+ case "ref":
1528
+ return validateRefField(parentKey, key, value, def, errors);
1529
+ default:
1530
+ errors.push(`Unknown type for field ${path}: ${def.type}`);
1531
+ }
1532
+ }
1533
+ function createSchema(_shape, entityType, options = {
1534
+ version: "1.0.0",
1535
+ table: "",
1536
+ schemaValidator: () => true,
1537
+ piiEnforcement: "none",
1538
+ schemaUpgrade: void 0
1539
+ }) {
1540
+ const systemFields = {
1541
+ type: field.string().immutable().system(),
1542
+ version: field.string().immutable().system().validator(validateSemVer)
1543
+ };
1544
+ const version = options.version || "1.0.0";
1545
+ const store = options.table || "";
1546
+ const schemaUpgrade = options.schemaUpgrade;
1547
+ const schema = {
1548
+ // 🔗 Define the schema shape
1549
+ _shape: {
1550
+ ...systemFields,
1551
+ ..._shape
1552
+ },
1553
+ // 🔗 Metadata about the schema
1554
+ meta: { entityType, version },
1555
+ // 🔗 Validate input against the schema
1556
+ validate(input, existing) {
1557
+ const errors = [];
1558
+ const result = {};
1559
+ if (typeof input !== "object" || input === null) {
1560
+ return { valid: false, errors: ["Input must be an object"] };
1561
+ }
1562
+ const working = { ...input };
1563
+ if (working.type == null) working.type = entityType;
1564
+ if (working.version == null) working.version = version;
1565
+ const fromVersion = String(working.version ?? "0.0.0");
1566
+ const toVersion = String(version);
1567
+ if (schemaUpgrade && cmpSemver(fromVersion, toVersion) < 0) {
1568
+ const upgradeRes = applySchemaUpgrade(schemaUpgrade, { ...working }, {
1569
+ from: fromVersion,
1570
+ to: toVersion,
1571
+ entityType,
1572
+ describe: () => schema.describe()
1573
+ });
1574
+ if (!upgradeRes || upgradeRes.ok !== true || !upgradeRes.value) {
1575
+ const errs = upgradeRes?.errors?.length ? upgradeRes.errors : [`Failed to upgrade entity from v${fromVersion} to v${toVersion}`];
1576
+ errors.push(...errs);
1577
+ return { valid: false, errors };
1578
+ }
1579
+ for (const k of Object.keys(working)) delete working[k];
1580
+ Object.assign(working, upgradeRes.value);
1581
+ working.type = entityType;
1582
+ working.version = toVersion;
1583
+ }
1584
+ for (const key in schema._shape) {
1585
+ const def = schema._shape[key];
1586
+ const value = working[key];
1587
+ if (!def) {
1588
+ errors.push(`Field definition missing for: ${key}`);
1589
+ continue;
1590
+ }
1591
+ const { missing } = checkMissingRequired("", key, value, def, errors);
1592
+ if (missing) continue;
1593
+ const { immutableViolation } = checkImmutable(
1594
+ "",
1595
+ key,
1596
+ value,
1597
+ def,
1598
+ existing,
1599
+ errors
1600
+ );
1601
+ if (immutableViolation) continue;
1602
+ const { shortCircuit } = enforcePIIField(
1603
+ "",
1604
+ key,
1605
+ value,
1606
+ def,
1607
+ options.piiEnforcement ?? "none",
1608
+ errors,
1609
+ console
1610
+ );
1611
+ if (shortCircuit) continue;
1612
+ const validateField = (val) => {
1613
+ const localErrors = [];
1614
+ const { invalid } = runCustomValidator(
1615
+ "",
1616
+ key,
1617
+ val,
1618
+ def,
1619
+ localErrors
1620
+ );
1621
+ if (!invalid) {
1622
+ validateByType("", key, val, def, localErrors);
1623
+ }
1624
+ return localErrors;
1625
+ };
1626
+ let fieldValue = value;
1627
+ let fieldErrors = validateField(fieldValue);
1628
+ const entityFrom = String(working.version ?? "0.0.0");
1629
+ const entityTo = String(version);
1630
+ const fieldTo = String(def._version ?? entityTo);
1631
+ const hasUpgrader = typeof def._upgrade === "function";
1632
+ if (fieldErrors.length > 0 && hasUpgrader && cmpSemver(entityFrom, entityTo) < 0) {
1633
+ const up = def._upgrade;
1634
+ const res = up(fieldValue, {
1635
+ entityFrom,
1636
+ entityTo,
1637
+ fieldTo,
1638
+ fieldName: key
1639
+ });
1640
+ if (res && res.ok) {
1641
+ fieldValue = res.value;
1642
+ fieldErrors = validateField(fieldValue);
1643
+ } else {
1644
+ fieldErrors.push(
1645
+ res?.error || `Failed to upgrade field ${key} from v${entityFrom} to v${entityTo}`
1646
+ );
1647
+ }
1648
+ }
1649
+ result[key] = fieldValue;
1650
+ if (fieldErrors.length !== 0) {
1651
+ errors.push(...fieldErrors);
1652
+ continue;
1653
+ }
1654
+ }
1655
+ if (errors.length === 0 && options.schemaValidator) {
1656
+ const castValue = result;
1657
+ if (!options.schemaValidator(castValue)) {
1658
+ errors.push("Schema-level validation failed.");
1659
+ }
1660
+ }
1661
+ return {
1662
+ valid: errors.length === 0,
1663
+ value: result,
1664
+ errors
1665
+ };
1666
+ },
1667
+ // specific validator for a schema to allow conditional validation
1668
+ schemaValidator: options.schemaValidator,
1669
+ // <== expose it here!
1670
+ /**
1671
+ * Runs the optional schema-level upgrade function once, without validating.
1672
+ * Useful for offline migrations or testing migration logic.
1673
+ */
1674
+ upgrade(input, log) {
1675
+ const fromVersion = String(input?.version ?? "0.0.0");
1676
+ const toVersion = String(version);
1677
+ if (!schemaUpgrade || cmpSemver(fromVersion, toVersion) >= 0) {
1678
+ return {
1679
+ ok: true,
1680
+ value: { ...input, type: entityType, version: toVersion }
1681
+ };
1682
+ }
1683
+ const res = applySchemaUpgrade(
1684
+ schemaUpgrade,
1685
+ { ...input },
1686
+ {
1687
+ from: fromVersion,
1688
+ to: toVersion,
1689
+ entityType,
1690
+ describe: () => schema.describe(),
1691
+ log
1692
+ }
1693
+ );
1694
+ if (res && res.ok && res.value) {
1695
+ const out = { ...res.value, type: entityType, version: toVersion };
1696
+ return { ok: true, value: out };
1697
+ }
1698
+ return {
1699
+ ok: false,
1700
+ errors: res?.errors?.length ? res.errors : [`Failed to upgrade entity from v${fromVersion} to v${toVersion}`]
1701
+ };
1702
+ },
1703
+ /**
1704
+ * Recursively validates entity references defined in this schema.
1705
+ *
1706
+ * Traverses fields of type `ref` and arrays of `ref` and resolves each target
1707
+ * entity using the provided `resolveEntity` function. When `autoValidate` is
1708
+ * enabled (default) and the field's `refPolicy` is `eager`, the referenced
1709
+ * entity's schema is fetched and validated via `validateComposition` up to
1710
+ * `maxDepth` levels.
1711
+ *
1712
+ * Skips fields not listed in `onlyFields` when provided. Prevents cycles via
1713
+ * a `visited` set in `validatorContext`.
1714
+ *
1715
+ * @param entity The root entity to validate (must include `type` and `id`).
1716
+ * @param options Options controlling traversal and resolution behavior.
1717
+ * @param options.resolveEntity Function to resolve a referenced entity by type and id.
1718
+ * @param options.validatorContext Internal context (visited set) to prevent cycles.
1719
+ * @param options.maxDepth Maximum depth for recursive validation (default: 5).
1720
+ * @param options.onlyFields Optional whitelist of field names to validate.
1721
+ * @param options.log Optional logger for traversal/debug output.
1722
+ *
1723
+ * @throws Error if a broken reference is encountered (target cannot be resolved).
1724
+ */
1725
+ async validateComposition(entity, options2) {
1726
+ const {
1727
+ resolveEntity,
1728
+ validatorContext = { visited: /* @__PURE__ */ new Set() },
1729
+ maxDepth = 5,
1730
+ log
1731
+ } = options2;
1732
+ const entityKey = `${entity.type}:${entity.id}`;
1733
+ if (validatorContext.visited.has(entityKey)) {
1734
+ log?.(`Skipping already visited entity ${entityKey}`);
1735
+ return;
1736
+ }
1737
+ validatorContext.visited.add(entityKey);
1738
+ log?.(`Validating composition for entity ${entityKey}`);
1739
+ for (const [key, def] of Object.entries(schema._shape)) {
1740
+ if (options2.onlyFields && !options2.onlyFields.includes(key)) {
1741
+ log?.(`Skipping field ${key} (not in onlyFields)`);
1742
+ continue;
1743
+ }
1744
+ const refType = def.refType;
1745
+ const autoValidate = def.autoValidate !== false;
1746
+ const refPolicy = def.refPolicy ?? "eager";
1747
+ const value = entity[key];
1748
+ if (!value) continue;
1749
+ if (def.type === "ref") {
1750
+ const ref = value;
1751
+ const target = await options2.resolveEntity(refType, ref.id);
1752
+ if (!target)
1753
+ throw new Error(
1754
+ `Broken reference: ${refType} ${ref.id} in field ${key}`
1755
+ );
1756
+ log?.(`Resolved ${refType} ${ref.id} from field ${key}`);
1757
+ if (autoValidate && refPolicy === "eager") {
1758
+ const targetSchema = getSchemaForType(refType);
1759
+ if (options2.maxDepth > 0 && targetSchema) {
1760
+ await targetSchema.validateComposition(target, {
1761
+ ...options2,
1762
+ maxDepth: options2.maxDepth - 1
1763
+ });
1764
+ }
1765
+ }
1766
+ } else if (def.type === "array" && def.itemType?.type === "ref") {
1767
+ const refs = value;
1768
+ for (const ref of refs) {
1769
+ const target = await options2.resolveEntity(refType, ref.id);
1770
+ if (!target)
1771
+ throw new Error(
1772
+ `Broken reference: ${refType} ${ref.id} in field ${key}`
1773
+ );
1774
+ log?.(`Resolved ${refType} ${ref.id} from field ${key}`);
1775
+ if (autoValidate && refPolicy === "eager") {
1776
+ const targetSchema = getSchemaForType(refType);
1777
+ if (options2.maxDepth > 0 && targetSchema) {
1778
+ await targetSchema.validateComposition(target, {
1779
+ ...options2,
1780
+ maxDepth: options2.maxDepth - 1
1781
+ });
1782
+ }
1783
+ }
1784
+ }
1785
+ }
1786
+ }
1787
+ },
1788
+ /**
1789
+ * Returns the configured table name for this schema.
1790
+ *
1791
+ * @throws Error if no store/table name has been defined for this schema.
1792
+ */
1793
+ tableName() {
1794
+ if (!store || store === "") {
1795
+ throw new Error("Store is not defined for this schema");
1796
+ }
1797
+ return store;
1798
+ },
1799
+ /**
1800
+ * Transforms an input object for persistence by applying PII protection
1801
+ * according to field annotations (e.g., encryption and hashing).
1802
+ *
1803
+ * @param input The raw entity data.
1804
+ * @param encryptFn Function used to encrypt sensitive values.
1805
+ * @param hashFn Function used to hash sensitive values.
1806
+ * @returns A new object safe to store.
1807
+ */
1808
+ prepareForStorage(input, encryptFn, hashFn) {
1809
+ return prepareForStorage(_shape, input, encryptFn, hashFn);
1810
+ },
1811
+ /**
1812
+ * Reverses storage transformations for read paths (e.g., decrypts values)
1813
+ * according to PII annotations, returning a consumer-friendly object.
1814
+ *
1815
+ * @param stored Data retrieved from storage.
1816
+ * @param decryptFn Function used to decrypt values that were encrypted on write.
1817
+ * @returns A new object suitable for application consumption.
1818
+ */
1819
+ prepareForRead(stored, decryptFn) {
1820
+ return prepareForRead(_shape, stored, decryptFn);
1821
+ },
1822
+ /**
1823
+ * Produces a log-safe copy of the provided data by redacting or pseudonymizing
1824
+ * PII fields in accordance with field annotations.
1825
+ *
1826
+ * @param data Arbitrary data to sanitize for logging.
1827
+ * @param pseudonymFn Function producing stable pseudonyms for sensitive values.
1828
+ * @returns A copy safe to emit to logs.
1829
+ */
1830
+ sanitizeForLog(data, pseudonymFn) {
1831
+ return sanitizeForLog(_shape, data, pseudonymFn);
1832
+ },
1833
+ /**
1834
+ * Returns a list of fields annotated with PII metadata for auditing purposes.
1835
+ * Each entry includes classification, required action, and optional log policy.
1836
+ */
1837
+ getPiiAudit() {
1838
+ return getPiiAudit(_shape);
1839
+ },
1840
+ /**
1841
+ * Produces a copy of stored data suitable for data deletion flows by scrubbing
1842
+ * or blanking PII per field annotations.
1843
+ *
1844
+ * @param stored Data as persisted.
1845
+ * @returns A copy with PII removed or neutralized for deletion.
1846
+ */
1847
+ scrubPiiForDelete(stored) {
1848
+ return scrubPiiForDelete(_shape, stored);
1849
+ },
1850
+ /**
1851
+ * Returns a normalized description of the schema suitable for documentation
1852
+ * or UI rendering (type, optionality, enum values, PII flags, etc.).
1853
+ */
1854
+ describe() {
1855
+ const description = {};
1856
+ for (const [key, def] of Object.entries(schema._shape)) {
1857
+ description[key] = {
1858
+ type: def.type,
1859
+ optional: !!def.optional,
1860
+ description: def._description ?? "",
1861
+ version: def._version ?? "",
1862
+ enum: getEnumValues(def),
1863
+ refType: def.refType ?? void 0,
1864
+ pii: def._pii ?? void 0
1865
+ };
1866
+ }
1867
+ return {
1868
+ entityType,
1869
+ version,
1870
+ hasSchemaUpgrade: !!schemaUpgrade,
1871
+ shape: description
1872
+ };
1873
+ }
1874
+ };
1875
+ globalSchemaRegistry.set(entityType, schema);
1876
+ return schema;
1877
+ }
1878
+ function getSchemaForType(type) {
1879
+ return globalSchemaRegistry.get(type);
1880
+ }
1881
+ function getAllSchemas() {
1882
+ return Array.from(globalSchemaRegistry.values());
1883
+ }
1884
+
1885
+ // src/components.ts
1886
+ var componentSchemaRegistry = /* @__PURE__ */ new Map();
1887
+ function createComponentSchema(shape, name, version, tableName = "", schemaValidator = () => true) {
1888
+ const schema = createSchema(shape, name, {
1889
+ version,
1890
+ piiEnforcement: "strict",
1891
+ table: tableName,
1892
+ schemaValidator
1893
+ });
1894
+ registerComponentSchema(name, schema);
1895
+ return schema;
1896
+ }
1897
+ function registerComponentSchema(type, schema) {
1898
+ componentSchemaRegistry.set(type, schema);
1899
+ }
1900
+ function getComponentSchema(type) {
1901
+ return componentSchemaRegistry.get(type);
1902
+ }
1903
+ function getAllComponentSchemas() {
1904
+ return Array.from(componentSchemaRegistry.entries());
1905
+ }
1906
+ // Annotate the CommonJS export names for ESM import in node:
1907
+ 0 && (module.exports = {
1908
+ FieldBuilder,
1909
+ createComponentSchema,
1910
+ createSchema,
1911
+ field,
1912
+ getAllComponentSchemas,
1913
+ getAllSchemas,
1914
+ getComponentSchema,
1915
+ getSchemaForType,
1916
+ isoCountryCodes,
1917
+ isoCurrencyCodes,
1918
+ registerComponentSchema,
1919
+ validateCountryCode,
1920
+ validateCurrencyCode,
1921
+ validateDateTimeISO,
1922
+ validateEmail,
1923
+ validateName,
1924
+ validatePercentage,
1925
+ validatePhone,
1926
+ validateRichText,
1927
+ validateSafeText,
1928
+ validateSemVer,
1929
+ validateUUID,
1930
+ validateUrl,
1931
+ validateUserId,
1932
+ validateUserIdArray
1933
+ });
1934
+ //# sourceMappingURL=index.cjs.map