@plasius/schema 1.3.0 → 1.3.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 (36) hide show
  1. package/README.md +188 -1
  2. package/THIRD_PARTY_NOTICES.md +75 -0
  3. package/dist/chunk-6FE4FLKF.js +254 -0
  4. package/dist/chunk-6FE4FLKF.js.map +1 -0
  5. package/dist/chunk-AG2NAPDC.js +117 -0
  6. package/dist/chunk-AG2NAPDC.js.map +1 -0
  7. package/dist/chunk-UQHPDQZX.js +2606 -0
  8. package/dist/chunk-UQHPDQZX.js.map +1 -0
  9. package/dist/feedback-diagnostics-BaniQAFS.d.cts +353 -0
  10. package/dist/feedback-diagnostics-CnsI0B9-.d.ts +353 -0
  11. package/dist/feedback-diagnostics-vocabulary.cjs +156 -0
  12. package/dist/feedback-diagnostics-vocabulary.cjs.map +1 -0
  13. package/dist/feedback-diagnostics-vocabulary.d.cts +70 -0
  14. package/dist/feedback-diagnostics-vocabulary.d.ts +70 -0
  15. package/dist/feedback-diagnostics-vocabulary.js +33 -0
  16. package/dist/feedback-diagnostics-vocabulary.js.map +1 -0
  17. package/dist/feedback-diagnostics.cjs +2696 -0
  18. package/dist/feedback-diagnostics.cjs.map +1 -0
  19. package/dist/feedback-diagnostics.d.cts +2 -0
  20. package/dist/feedback-diagnostics.d.ts +2 -0
  21. package/dist/feedback-diagnostics.js +39 -0
  22. package/dist/feedback-diagnostics.js.map +1 -0
  23. package/dist/feedback-unicode-profile.cjs +283 -0
  24. package/dist/feedback-unicode-profile.cjs.map +1 -0
  25. package/dist/feedback-unicode-profile.d.cts +28 -0
  26. package/dist/feedback-unicode-profile.d.ts +28 -0
  27. package/dist/feedback-unicode-profile.js +13 -0
  28. package/dist/feedback-unicode-profile.js.map +1 -0
  29. package/dist/index.cjs +2133 -14
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +879 -286
  32. package/dist/index.d.ts +879 -286
  33. package/dist/index.js +1516 -2300
  34. package/dist/index.js.map +1 -1
  35. package/package.json +36 -12
  36. package/unicode/feedback-unicode-15.1.0-unassigned.json +13 -0
@@ -0,0 +1,2696 @@
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/feedback-diagnostics.ts
21
+ var feedback_diagnostics_exports = {};
22
+ __export(feedback_diagnostics_exports, {
23
+ FEEDBACK_BACKEND_BUCKETS: () => FEEDBACK_BACKEND_BUCKETS,
24
+ FEEDBACK_CONTRACT_VERSION: () => FEEDBACK_CONTRACT_VERSION,
25
+ FEEDBACK_FRAME_RATE_BUCKETS: () => FEEDBACK_FRAME_RATE_BUCKETS,
26
+ FEEDBACK_FRAME_TIME_BUCKETS: () => FEEDBACK_FRAME_TIME_BUCKETS,
27
+ FEEDBACK_GAME_COUNTER_CODES: () => FEEDBACK_GAME_COUNTER_CODES,
28
+ FEEDBACK_GAME_COUNTER_MAX_COUNT: () => FEEDBACK_GAME_COUNTER_MAX_COUNT,
29
+ FEEDBACK_GAME_COUNTER_MAX_ITEMS: () => FEEDBACK_GAME_COUNTER_MAX_ITEMS,
30
+ FEEDBACK_GAME_ERROR_CODES: () => FEEDBACK_GAME_ERROR_CODES,
31
+ FEEDBACK_GAME_FEATURE_IDS: () => FEEDBACK_GAME_FEATURE_IDS,
32
+ FEEDBACK_GAME_PROVENANCE_CONTRACTS: () => FEEDBACK_GAME_PROVENANCE_CONTRACTS,
33
+ FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS: () => FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS,
34
+ FEEDBACK_GAME_SURFACE_IDS: () => FEEDBACK_GAME_SURFACE_IDS,
35
+ FEEDBACK_RENDERER_BUCKETS: () => FEEDBACK_RENDERER_BUCKETS,
36
+ FEEDBACK_VIEWPORT_BUCKETS: () => FEEDBACK_VIEWPORT_BUCKETS,
37
+ FeedbackGameDiagnosticsSchema: () => FeedbackGameDiagnosticsSchema,
38
+ validateFeedbackGameDiagnostics: () => validateFeedbackGameDiagnostics
39
+ });
40
+ module.exports = __toCommonJS(feedback_diagnostics_exports);
41
+
42
+ // src/field.builder.ts
43
+ function mergeValidatorResult(previousResult, nextValid) {
44
+ if (previousResult !== true) {
45
+ return previousResult;
46
+ }
47
+ return nextValid;
48
+ }
49
+ var FieldBuilder = class _FieldBuilder {
50
+ constructor(type, options = {}) {
51
+ this.type = type;
52
+ this._shape = options.shape;
53
+ this.itemType = options.itemType;
54
+ this.refType = options.refType;
55
+ }
56
+ type;
57
+ _type;
58
+ _storageType;
59
+ isSystem = false;
60
+ isImmutable = false;
61
+ isRequired = true;
62
+ _validator;
63
+ _description = "";
64
+ _version = "1.0.0";
65
+ _default;
66
+ _upgrade;
67
+ _shape;
68
+ itemType;
69
+ refType;
70
+ _pii = {
71
+ classification: "none",
72
+ action: "none",
73
+ logHandling: "plain",
74
+ purpose: "an ordinary value"
75
+ };
76
+ _exposure = "public";
77
+ enumValues;
78
+ immutable() {
79
+ this.isImmutable = true;
80
+ return this;
81
+ }
82
+ system() {
83
+ this.isSystem = true;
84
+ return this;
85
+ }
86
+ exposure(exposure) {
87
+ this._exposure = exposure;
88
+ return this;
89
+ }
90
+ internal() {
91
+ return this.exposure("internal");
92
+ }
93
+ public() {
94
+ return this.exposure("public");
95
+ }
96
+ required() {
97
+ this.isRequired = true;
98
+ return this;
99
+ }
100
+ optional() {
101
+ this.isRequired = false;
102
+ return this;
103
+ }
104
+ validator(fn) {
105
+ this._validator = fn;
106
+ return this;
107
+ }
108
+ description(desc) {
109
+ this._description = desc;
110
+ return this;
111
+ }
112
+ default(value) {
113
+ this._default = value;
114
+ this.isRequired = false;
115
+ return this;
116
+ }
117
+ /**
118
+ * Configure an upgrader used when validating older entities against a newer schema.
119
+ * The upgrader receives the current field value and version context, and should
120
+ * return { ok: true, value } with the upgraded value, or { ok: false, error }.
121
+ */
122
+ upgrade(fn) {
123
+ this._upgrade = fn;
124
+ return this;
125
+ }
126
+ getDefault() {
127
+ const v = this._default;
128
+ return typeof v === "function" ? v() : v;
129
+ }
130
+ version(ver) {
131
+ this._version = ver;
132
+ return this;
133
+ }
134
+ /// PID informs the schema PII handling of the manner in
135
+ /// which to handle data relating to this field.
136
+ PID(pii) {
137
+ this._pii = pii;
138
+ return this;
139
+ }
140
+ min(min) {
141
+ if (this.type === "number") {
142
+ const prevValidator = this._validator;
143
+ this._validator = (value) => {
144
+ const valid = typeof value === "number" && value >= min;
145
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
146
+ };
147
+ } else if (this.type === "string") {
148
+ const prevValidator = this._validator;
149
+ this._validator = (value) => {
150
+ const valid = typeof value === "string" && value.length >= min;
151
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
152
+ };
153
+ } else if (this.type === "array") {
154
+ const prevValidator = this._validator;
155
+ this._validator = (value) => {
156
+ const valid = Array.isArray(value) && value.length >= min;
157
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
158
+ };
159
+ } else {
160
+ throw new Error(
161
+ "Min is only supported on number, string, or array fields."
162
+ );
163
+ }
164
+ return this;
165
+ }
166
+ max(max) {
167
+ if (this.type === "number") {
168
+ const prevValidator = this._validator;
169
+ this._validator = (value) => {
170
+ const valid = typeof value === "number" && value <= max;
171
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
172
+ };
173
+ } else if (this.type === "string") {
174
+ const prevValidator = this._validator;
175
+ this._validator = (value) => {
176
+ const valid = typeof value === "string" && value.length <= max;
177
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
178
+ };
179
+ } else if (this.type === "array") {
180
+ const prevValidator = this._validator;
181
+ this._validator = (value) => {
182
+ const valid = Array.isArray(value) && value.length <= max;
183
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
184
+ };
185
+ } else {
186
+ throw new Error(
187
+ "Max is only supported on number, string, or array fields."
188
+ );
189
+ }
190
+ return this;
191
+ }
192
+ pattern(regex) {
193
+ if (this.type !== "string") {
194
+ throw new Error("Pattern is only supported on string fields.");
195
+ }
196
+ const prevValidator = this._validator;
197
+ this._validator = (value) => {
198
+ const valid = typeof value === "string" && regex.test(value);
199
+ return prevValidator ? mergeValidatorResult(prevValidator(value), valid) : valid;
200
+ };
201
+ return this;
202
+ }
203
+ enum(values) {
204
+ if (this.type !== "string" && this.type !== "number" && !(this.type === "array" && (this.itemType?.type === "string" || this.itemType?.type === "number"))) {
205
+ throw new Error(
206
+ "Enums are only supported on string or number fields or arrays of strings or numbers."
207
+ );
208
+ }
209
+ this.enumValues = values;
210
+ return this;
211
+ }
212
+ /**
213
+ * Create a shallow clone with a different external type parameter.
214
+ * Note: shape and itemType are passed by reference (shallow). If you need
215
+ * deep isolation of nested FieldBuilders, clone them explicitly.
216
+ */
217
+ as() {
218
+ const clone = new _FieldBuilder(this.type, {
219
+ shape: this._shape,
220
+ itemType: this.itemType,
221
+ refType: this.refType
222
+ });
223
+ clone.enumValues = this.enumValues;
224
+ clone.isImmutable = this.isImmutable;
225
+ clone.isSystem = this.isSystem;
226
+ clone.isRequired = this.isRequired;
227
+ clone._description = this._description;
228
+ clone._version = this._version;
229
+ clone._pii = this._pii;
230
+ clone._exposure = this._exposure;
231
+ clone._validator = this._validator;
232
+ clone._default = this._default;
233
+ clone._upgrade = this._upgrade;
234
+ return clone;
235
+ }
236
+ };
237
+
238
+ // src/validation/email.RFC5322.ts
239
+ var validateEmail = (value) => {
240
+ if (typeof value !== "string") return false;
241
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
242
+ return emailRegex.test(value);
243
+ };
244
+
245
+ // src/validation/phone.E.164.ts
246
+ var validatePhone = (value) => {
247
+ if (typeof value !== "string") return false;
248
+ const phoneRegex = /^\+[1-9]\d{1,14}$/;
249
+ return phoneRegex.test(value);
250
+ };
251
+
252
+ // src/validation/url.WHATWG.ts
253
+ var validateUrl = (value) => {
254
+ if (typeof value !== "string") return false;
255
+ try {
256
+ const url = new URL(value);
257
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
258
+ return true;
259
+ } catch {
260
+ return false;
261
+ }
262
+ };
263
+
264
+ // src/validation/uuid.RFC4122.ts
265
+ var validateUUID = (value) => {
266
+ if (typeof value !== "string") return false;
267
+ const standard = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
268
+ return standard.test(value) || value.toLowerCase() === "00000000-0000-0000-0000-000000000000" || value.toLowerCase() === "ffffffff-ffff-ffff-ffff-ffffffffffff";
269
+ };
270
+
271
+ // src/validation/dateTime.ISO8601.ts
272
+ var validateDateTimeISO = (value, options) => {
273
+ const mode = options?.mode ?? "datetime";
274
+ if (typeof value !== "string") return false;
275
+ if (mode === "datetime") {
276
+ const isoDateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
277
+ if (!isoDateTimeRegex.test(value)) return false;
278
+ const date = new Date(value);
279
+ if (Number.isNaN(date.getTime())) return false;
280
+ return true;
281
+ }
282
+ if (mode === "date") {
283
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
284
+ if (!dateRegex.test(value)) return false;
285
+ const date = new Date(value);
286
+ if (isNaN(date.getTime())) return false;
287
+ const [year, month, day] = value.split("-").map(Number);
288
+ return date.getUTCFullYear() === year && date.getUTCMonth() + 1 === month && date.getUTCDate() === day;
289
+ }
290
+ if (mode === "time") {
291
+ const timeRegex = /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?Z?$/;
292
+ return timeRegex.test(value);
293
+ }
294
+ return false;
295
+ };
296
+
297
+ // src/validation/countryCode.ISO3166.ts
298
+ var isoCountryCodes = /* @__PURE__ */ new Set([
299
+ "AD",
300
+ "AE",
301
+ "AF",
302
+ "AG",
303
+ "AI",
304
+ "AL",
305
+ "AM",
306
+ "AO",
307
+ "AQ",
308
+ "AR",
309
+ "AS",
310
+ "AT",
311
+ "AU",
312
+ "AW",
313
+ "AX",
314
+ "AZ",
315
+ "BA",
316
+ "BB",
317
+ "BD",
318
+ "BE",
319
+ "BF",
320
+ "BG",
321
+ "BH",
322
+ "BI",
323
+ "BJ",
324
+ "BL",
325
+ "BM",
326
+ "BN",
327
+ "BO",
328
+ "BQ",
329
+ "BR",
330
+ "BS",
331
+ "BT",
332
+ "BV",
333
+ "BW",
334
+ "BY",
335
+ "BZ",
336
+ "CA",
337
+ "CC",
338
+ "CD",
339
+ "CF",
340
+ "CG",
341
+ "CH",
342
+ "CI",
343
+ "CK",
344
+ "CL",
345
+ "CM",
346
+ "CN",
347
+ "CO",
348
+ "CR",
349
+ "CU",
350
+ "CV",
351
+ "CW",
352
+ "CX",
353
+ "CY",
354
+ "CZ",
355
+ "DE",
356
+ "DJ",
357
+ "DK",
358
+ "DM",
359
+ "DO",
360
+ "DZ",
361
+ "EC",
362
+ "EE",
363
+ "EG",
364
+ "EH",
365
+ "ER",
366
+ "ES",
367
+ "ET",
368
+ "FI",
369
+ "FJ",
370
+ "FM",
371
+ "FO",
372
+ "FR",
373
+ "GA",
374
+ "GB",
375
+ "GD",
376
+ "GE",
377
+ "GF",
378
+ "GG",
379
+ "GH",
380
+ "GI",
381
+ "GL",
382
+ "GM",
383
+ "GN",
384
+ "GP",
385
+ "GQ",
386
+ "GR",
387
+ "GT",
388
+ "GU",
389
+ "GW",
390
+ "GY",
391
+ "HK",
392
+ "HM",
393
+ "HN",
394
+ "HR",
395
+ "HT",
396
+ "HU",
397
+ "ID",
398
+ "IE",
399
+ "IL",
400
+ "IM",
401
+ "IN",
402
+ "IO",
403
+ "IQ",
404
+ "IR",
405
+ "IS",
406
+ "IT",
407
+ "JE",
408
+ "JM",
409
+ "JO",
410
+ "JP",
411
+ "KE",
412
+ "KG",
413
+ "KH",
414
+ "KI",
415
+ "KM",
416
+ "KN",
417
+ "KP",
418
+ "KR",
419
+ "KW",
420
+ "KY",
421
+ "KZ",
422
+ "LA",
423
+ "LB",
424
+ "LC",
425
+ "LI",
426
+ "LK",
427
+ "LR",
428
+ "LS",
429
+ "LT",
430
+ "LU",
431
+ "LV",
432
+ "LY",
433
+ "MA",
434
+ "MC",
435
+ "MD",
436
+ "ME",
437
+ "MF",
438
+ "MG",
439
+ "MH",
440
+ "MK",
441
+ "ML",
442
+ "MM",
443
+ "MN",
444
+ "MO",
445
+ "MP",
446
+ "MQ",
447
+ "MR",
448
+ "MS",
449
+ "MT",
450
+ "MU",
451
+ "MV",
452
+ "MW",
453
+ "MX",
454
+ "MY",
455
+ "MZ",
456
+ "NA",
457
+ "NC",
458
+ "NE",
459
+ "NF",
460
+ "NG",
461
+ "NI",
462
+ "NL",
463
+ "NO",
464
+ "NP",
465
+ "NR",
466
+ "NU",
467
+ "NZ",
468
+ "OM",
469
+ "PA",
470
+ "PE",
471
+ "PF",
472
+ "PG",
473
+ "PH",
474
+ "PK",
475
+ "PL",
476
+ "PM",
477
+ "PN",
478
+ "PR",
479
+ "PS",
480
+ "PT",
481
+ "PW",
482
+ "PY",
483
+ "QA",
484
+ "RE",
485
+ "RO",
486
+ "RS",
487
+ "RU",
488
+ "RW",
489
+ "SA",
490
+ "SB",
491
+ "SC",
492
+ "SD",
493
+ "SE",
494
+ "SG",
495
+ "SH",
496
+ "SI",
497
+ "SJ",
498
+ "SK",
499
+ "SL",
500
+ "SM",
501
+ "SN",
502
+ "SO",
503
+ "SR",
504
+ "SS",
505
+ "ST",
506
+ "SV",
507
+ "SX",
508
+ "SY",
509
+ "SZ",
510
+ "TC",
511
+ "TD",
512
+ "TF",
513
+ "TG",
514
+ "TH",
515
+ "TJ",
516
+ "TK",
517
+ "TL",
518
+ "TM",
519
+ "TN",
520
+ "TO",
521
+ "TR",
522
+ "TT",
523
+ "TV",
524
+ "TZ",
525
+ "UA",
526
+ "UG",
527
+ "UM",
528
+ "US",
529
+ "UY",
530
+ "UZ",
531
+ "VA",
532
+ "VC",
533
+ "VE",
534
+ "VG",
535
+ "VI",
536
+ "VN",
537
+ "VU",
538
+ "WF",
539
+ "WS",
540
+ "YE",
541
+ "YT",
542
+ "ZA",
543
+ "ZM",
544
+ "ZW"
545
+ ]);
546
+ var validateCountryCode = (value) => {
547
+ if (typeof value !== "string") return false;
548
+ return isoCountryCodes.has(value.toUpperCase());
549
+ };
550
+
551
+ // src/validation/generalText.OWASP.ts
552
+ function validateSafeText(value) {
553
+ if (typeof value !== "string") return false;
554
+ const trimmed = value.trim();
555
+ if (trimmed.length === 0) return false;
556
+ for (let i = 0; i < trimmed.length; i++) {
557
+ const code = trimmed.codePointAt(i);
558
+ if (code !== void 0 && (code >= 0 && code <= 31 || code === 127)) {
559
+ return false;
560
+ }
561
+ }
562
+ if (/['"<>\\{}();]/.test(trimmed)) return false;
563
+ if (/(--|\b(SELECT|UPDATE|DELETE|INSERT|DROP|ALTER|EXEC|UNION|GRANT|REVOKE)\b|\/\*|\*\/|@@)/i.test(
564
+ trimmed
565
+ ))
566
+ return false;
567
+ if (trimmed.includes("\0")) return false;
568
+ if (trimmed.length > 1024) return false;
569
+ return true;
570
+ }
571
+
572
+ // src/validation/version.SEMVER2.0.0.ts
573
+ function validateSemVer(value) {
574
+ if (typeof value !== "string") return false;
575
+ 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(
576
+ value
577
+ );
578
+ }
579
+
580
+ // src/validation/richtext.OWASP.ts
581
+ function validateRichText(value) {
582
+ if (typeof value !== "string") return false;
583
+ const trimmed = value.trim();
584
+ if (trimmed.length === 0) return true;
585
+ if (/<(script|iframe|object|embed|style|link|meta|base|form|input|button|textarea|select)\b/i.test(
586
+ trimmed
587
+ )) {
588
+ return false;
589
+ }
590
+ if (/javascript:/i.test(trimmed)) {
591
+ return false;
592
+ }
593
+ if (/on\w+=["']?/i.test(trimmed)) {
594
+ return false;
595
+ }
596
+ if (trimmed.length > 1e4) return false;
597
+ return true;
598
+ }
599
+
600
+ // src/validation/languageCode.BCP47.ts
601
+ var IsoLanguageCode = /* @__PURE__ */ ((IsoLanguageCode2) => {
602
+ IsoLanguageCode2["Afar"] = "aa";
603
+ IsoLanguageCode2["Abkhazian"] = "ab";
604
+ IsoLanguageCode2["Afrikaans"] = "af";
605
+ IsoLanguageCode2["Akan"] = "ak";
606
+ IsoLanguageCode2["Albanian"] = "sq";
607
+ IsoLanguageCode2["Amharic"] = "am";
608
+ IsoLanguageCode2["Arabic"] = "ar";
609
+ IsoLanguageCode2["Aragonese"] = "an";
610
+ IsoLanguageCode2["Armenian"] = "hy";
611
+ IsoLanguageCode2["Assamese"] = "as";
612
+ IsoLanguageCode2["Avaric"] = "av";
613
+ IsoLanguageCode2["Aymara"] = "ay";
614
+ IsoLanguageCode2["Azerbaijani"] = "az";
615
+ IsoLanguageCode2["Bashkir"] = "ba";
616
+ IsoLanguageCode2["Bambara"] = "bm";
617
+ IsoLanguageCode2["Basque"] = "eu";
618
+ IsoLanguageCode2["Belarusian"] = "be";
619
+ IsoLanguageCode2["Bengali"] = "bn";
620
+ IsoLanguageCode2["Bislama"] = "bi";
621
+ IsoLanguageCode2["Bosnian"] = "bs";
622
+ IsoLanguageCode2["Breton"] = "br";
623
+ IsoLanguageCode2["Bulgarian"] = "bg";
624
+ IsoLanguageCode2["Burmese"] = "my";
625
+ IsoLanguageCode2["Catalan"] = "ca";
626
+ IsoLanguageCode2["Chamorro"] = "ch";
627
+ IsoLanguageCode2["Chechen"] = "ce";
628
+ IsoLanguageCode2["Chinese"] = "zh";
629
+ IsoLanguageCode2["ChurchSlavic"] = "cu";
630
+ IsoLanguageCode2["Chuvash"] = "cv";
631
+ IsoLanguageCode2["Cornish"] = "kw";
632
+ IsoLanguageCode2["Corsican"] = "co";
633
+ IsoLanguageCode2["Cree"] = "cr";
634
+ IsoLanguageCode2["Croatian"] = "hr";
635
+ IsoLanguageCode2["Czech"] = "cs";
636
+ IsoLanguageCode2["Danish"] = "da";
637
+ IsoLanguageCode2["Divehi"] = "dv";
638
+ IsoLanguageCode2["Dutch"] = "nl";
639
+ IsoLanguageCode2["Dzongkha"] = "dz";
640
+ IsoLanguageCode2["English"] = "en";
641
+ IsoLanguageCode2["Esperanto"] = "eo";
642
+ IsoLanguageCode2["Estonian"] = "et";
643
+ IsoLanguageCode2["Ewe"] = "ee";
644
+ IsoLanguageCode2["Faroese"] = "fo";
645
+ IsoLanguageCode2["Fijian"] = "fj";
646
+ IsoLanguageCode2["Finnish"] = "fi";
647
+ IsoLanguageCode2["French"] = "fr";
648
+ IsoLanguageCode2["WesternFrisian"] = "fy";
649
+ IsoLanguageCode2["Fulah"] = "ff";
650
+ IsoLanguageCode2["Gaelic"] = "gd";
651
+ IsoLanguageCode2["Galician"] = "gl";
652
+ IsoLanguageCode2["Ganda"] = "lg";
653
+ IsoLanguageCode2["Georgian"] = "ka";
654
+ IsoLanguageCode2["German"] = "de";
655
+ IsoLanguageCode2["Greek"] = "el";
656
+ IsoLanguageCode2["Kalaallisut"] = "kl";
657
+ IsoLanguageCode2["Guarani"] = "gn";
658
+ IsoLanguageCode2["Gujarati"] = "gu";
659
+ IsoLanguageCode2["Haitian"] = "ht";
660
+ IsoLanguageCode2["Hausa"] = "ha";
661
+ IsoLanguageCode2["Hebrew"] = "he";
662
+ IsoLanguageCode2["Herero"] = "hz";
663
+ IsoLanguageCode2["Hindi"] = "hi";
664
+ IsoLanguageCode2["HiriMotu"] = "ho";
665
+ IsoLanguageCode2["Hungarian"] = "hu";
666
+ IsoLanguageCode2["Icelandic"] = "is";
667
+ IsoLanguageCode2["Ido"] = "io";
668
+ IsoLanguageCode2["Igbo"] = "ig";
669
+ IsoLanguageCode2["Indonesian"] = "id";
670
+ IsoLanguageCode2["Interlingua"] = "ia";
671
+ IsoLanguageCode2["Interlingue"] = "ie";
672
+ IsoLanguageCode2["Inuktitut"] = "iu";
673
+ IsoLanguageCode2["Inupiaq"] = "ik";
674
+ IsoLanguageCode2["Irish"] = "ga";
675
+ IsoLanguageCode2["Italian"] = "it";
676
+ IsoLanguageCode2["Japanese"] = "ja";
677
+ IsoLanguageCode2["Javanese"] = "jv";
678
+ IsoLanguageCode2["Kannada"] = "kn";
679
+ IsoLanguageCode2["Kanuri"] = "kr";
680
+ IsoLanguageCode2["Kashmiri"] = "ks";
681
+ IsoLanguageCode2["Kazakh"] = "kk";
682
+ IsoLanguageCode2["CentralKhmer"] = "km";
683
+ IsoLanguageCode2["Kikuyu"] = "ki";
684
+ IsoLanguageCode2["Kinyarwanda"] = "rw";
685
+ IsoLanguageCode2["Kyrgyz"] = "ky";
686
+ IsoLanguageCode2["Komi"] = "kv";
687
+ IsoLanguageCode2["Kongo"] = "kg";
688
+ IsoLanguageCode2["Korean"] = "ko";
689
+ IsoLanguageCode2["Kuanyama"] = "kj";
690
+ IsoLanguageCode2["Kurdish"] = "ku";
691
+ IsoLanguageCode2["Lao"] = "lo";
692
+ IsoLanguageCode2["Latin"] = "la";
693
+ IsoLanguageCode2["Latvian"] = "lv";
694
+ IsoLanguageCode2["Limburgan"] = "li";
695
+ IsoLanguageCode2["Lingala"] = "ln";
696
+ IsoLanguageCode2["Lithuanian"] = "lt";
697
+ IsoLanguageCode2["LubaKatanga"] = "lu";
698
+ IsoLanguageCode2["Luxembourgish"] = "lb";
699
+ IsoLanguageCode2["Macedonian"] = "mk";
700
+ IsoLanguageCode2["Malagasy"] = "mg";
701
+ IsoLanguageCode2["Malay"] = "ms";
702
+ IsoLanguageCode2["Malayalam"] = "ml";
703
+ IsoLanguageCode2["Maltese"] = "mt";
704
+ IsoLanguageCode2["Manx"] = "gv";
705
+ IsoLanguageCode2["Maori"] = "mi";
706
+ IsoLanguageCode2["Marathi"] = "mr";
707
+ IsoLanguageCode2["Marshallese"] = "mh";
708
+ IsoLanguageCode2["Mongolian"] = "mn";
709
+ IsoLanguageCode2["Nauru"] = "na";
710
+ IsoLanguageCode2["Navajo"] = "nv";
711
+ IsoLanguageCode2["NorthNdebele"] = "nd";
712
+ IsoLanguageCode2["SouthNdebele"] = "nr";
713
+ IsoLanguageCode2["Ndonga"] = "ng";
714
+ IsoLanguageCode2["Nepali"] = "ne";
715
+ IsoLanguageCode2["Norwegian"] = "no";
716
+ IsoLanguageCode2["NorwegianBokmal"] = "nb";
717
+ IsoLanguageCode2["NorwegianNynorsk"] = "nn";
718
+ IsoLanguageCode2["SichuanYi"] = "ii";
719
+ IsoLanguageCode2["Occitan"] = "oc";
720
+ IsoLanguageCode2["Ojibwa"] = "oj";
721
+ IsoLanguageCode2["Oriya"] = "or";
722
+ IsoLanguageCode2["Oromo"] = "om";
723
+ IsoLanguageCode2["Ossetian"] = "os";
724
+ IsoLanguageCode2["Pali"] = "pi";
725
+ IsoLanguageCode2["Pashto"] = "ps";
726
+ IsoLanguageCode2["Persian"] = "fa";
727
+ IsoLanguageCode2["Polish"] = "pl";
728
+ IsoLanguageCode2["Portuguese"] = "pt";
729
+ IsoLanguageCode2["Punjabi"] = "pa";
730
+ IsoLanguageCode2["Quechua"] = "qu";
731
+ IsoLanguageCode2["Romansh"] = "rm";
732
+ IsoLanguageCode2["Romanian"] = "ro";
733
+ IsoLanguageCode2["Rundi"] = "rn";
734
+ IsoLanguageCode2["Russian"] = "ru";
735
+ IsoLanguageCode2["Samoan"] = "sm";
736
+ IsoLanguageCode2["Sango"] = "sg";
737
+ IsoLanguageCode2["Sanskrit"] = "sa";
738
+ IsoLanguageCode2["Sardinian"] = "sc";
739
+ IsoLanguageCode2["Serbian"] = "sr";
740
+ IsoLanguageCode2["Shona"] = "sn";
741
+ IsoLanguageCode2["Sindhi"] = "sd";
742
+ IsoLanguageCode2["Sinhala"] = "si";
743
+ IsoLanguageCode2["Slovak"] = "sk";
744
+ IsoLanguageCode2["Slovenian"] = "sl";
745
+ IsoLanguageCode2["Somali"] = "so";
746
+ IsoLanguageCode2["SouthernSotho"] = "st";
747
+ IsoLanguageCode2["Spanish"] = "es";
748
+ IsoLanguageCode2["Sundanese"] = "su";
749
+ IsoLanguageCode2["Swahili"] = "sw";
750
+ IsoLanguageCode2["Swati"] = "ss";
751
+ IsoLanguageCode2["Swedish"] = "sv";
752
+ IsoLanguageCode2["Tagalog"] = "tl";
753
+ IsoLanguageCode2["Tahitian"] = "ty";
754
+ IsoLanguageCode2["Tajik"] = "tg";
755
+ IsoLanguageCode2["Tamil"] = "ta";
756
+ IsoLanguageCode2["Tatar"] = "tt";
757
+ IsoLanguageCode2["Telugu"] = "te";
758
+ IsoLanguageCode2["Thai"] = "th";
759
+ IsoLanguageCode2["Tibetan"] = "bo";
760
+ IsoLanguageCode2["Tigrinya"] = "ti";
761
+ IsoLanguageCode2["Tonga"] = "to";
762
+ IsoLanguageCode2["Tsonga"] = "ts";
763
+ IsoLanguageCode2["Tswana"] = "tn";
764
+ IsoLanguageCode2["Turkish"] = "tr";
765
+ IsoLanguageCode2["Turkmen"] = "tk";
766
+ IsoLanguageCode2["Twi"] = "tw";
767
+ IsoLanguageCode2["Uighur"] = "ug";
768
+ IsoLanguageCode2["Ukrainian"] = "uk";
769
+ IsoLanguageCode2["Urdu"] = "ur";
770
+ IsoLanguageCode2["Uzbek"] = "uz";
771
+ IsoLanguageCode2["Venda"] = "ve";
772
+ IsoLanguageCode2["Vietnamese"] = "vi";
773
+ IsoLanguageCode2["Volapuk"] = "vo";
774
+ IsoLanguageCode2["Walloon"] = "wa";
775
+ IsoLanguageCode2["Welsh"] = "cy";
776
+ IsoLanguageCode2["Wolof"] = "wo";
777
+ IsoLanguageCode2["Xhosa"] = "xh";
778
+ IsoLanguageCode2["Yiddish"] = "yi";
779
+ IsoLanguageCode2["Yoruba"] = "yo";
780
+ IsoLanguageCode2["Zhuang"] = "za";
781
+ IsoLanguageCode2["Zulu"] = "zu";
782
+ return IsoLanguageCode2;
783
+ })(IsoLanguageCode || {});
784
+ var ISO_LANGUAGE_SET = new Set(
785
+ Object.values(IsoLanguageCode)
786
+ );
787
+ function isIsoLanguageCode(value) {
788
+ return typeof value === "string" && ISO_LANGUAGE_SET.has(value.toLowerCase());
789
+ }
790
+ function isRegionSubtag(value) {
791
+ return /^[A-Za-z]{2}$/.test(value) || /^\d{3}$/.test(value);
792
+ }
793
+ function isScriptSubtag(value) {
794
+ return /^[A-Za-z]{4}$/.test(value);
795
+ }
796
+ function isVariantSubtag(value) {
797
+ return /^([0-9][A-Za-z0-9]{3}|[A-Za-z0-9]{5,8})$/.test(value);
798
+ }
799
+ function isExtensionSingleton(value) {
800
+ return /^[0-9A-WY-Za-wy-z]$/.test(value);
801
+ }
802
+ function isExtensionSubtag(value) {
803
+ return /^[A-Za-z0-9]{2,8}$/.test(value);
804
+ }
805
+ function isPrivateUseSingleton(value) {
806
+ return value.toLowerCase() === "x";
807
+ }
808
+ function isPrivateUseSubtag(value) {
809
+ return /^[A-Za-z0-9]{1,8}$/.test(value);
810
+ }
811
+ function validateLanguage(value) {
812
+ if (typeof value !== "string" || value.length === 0) return false;
813
+ const parts = value.split("-");
814
+ if (parts.some((part) => part.length === 0)) return false;
815
+ const lowerTag = value.toLowerCase();
816
+ if (GRANDFATHERED_TAGS.has(lowerTag)) return true;
817
+ if (parts[0]?.toLowerCase() === "x") {
818
+ return parts.length > 1 && parts.slice(1).every(isPrivateUseSubtag);
819
+ }
820
+ let i = 0;
821
+ const lang = parts[i];
822
+ if (!lang || !/^[A-Za-z]{2,3}$/.test(lang)) return false;
823
+ if (lang.length === 2 && !isIsoLanguageCode(lang)) return false;
824
+ i += 1;
825
+ if (lang.length <= 3) {
826
+ let extlangCount = 0;
827
+ while (i < parts.length && extlangCount < 3 && /^[A-Za-z]{3}$/.test(parts[i])) {
828
+ i += 1;
829
+ extlangCount += 1;
830
+ }
831
+ }
832
+ if (i < parts.length && isScriptSubtag(parts[i])) {
833
+ i += 1;
834
+ }
835
+ if (i < parts.length && isRegionSubtag(parts[i].toUpperCase())) {
836
+ i += 1;
837
+ }
838
+ const variants = /* @__PURE__ */ new Set();
839
+ while (i < parts.length && isVariantSubtag(parts[i])) {
840
+ const variant = parts[i].toLowerCase();
841
+ if (variants.has(variant)) return false;
842
+ variants.add(variant);
843
+ i += 1;
844
+ }
845
+ const extensionSingletons = /* @__PURE__ */ new Set();
846
+ while (i < parts.length && isExtensionSingleton(parts[i])) {
847
+ const singleton = parts[i].toLowerCase();
848
+ if (extensionSingletons.has(singleton)) return false;
849
+ extensionSingletons.add(singleton);
850
+ i += 1;
851
+ if (!(i < parts.length && isExtensionSubtag(parts[i]))) return false;
852
+ while (i < parts.length && isExtensionSubtag(parts[i])) {
853
+ i += 1;
854
+ }
855
+ }
856
+ if (i < parts.length && isPrivateUseSingleton(parts[i])) {
857
+ i += 1;
858
+ if (!(i < parts.length && isPrivateUseSubtag(parts[i]))) return false;
859
+ while (i < parts.length && isPrivateUseSubtag(parts[i])) {
860
+ i += 1;
861
+ }
862
+ }
863
+ return i === parts.length;
864
+ }
865
+ var GRANDFATHERED_TAGS = /* @__PURE__ */ new Set([
866
+ "art-lojban",
867
+ "cel-gaulish",
868
+ "en-gb-oed",
869
+ "i-ami",
870
+ "i-bnn",
871
+ "i-default",
872
+ "i-enochian",
873
+ "i-hak",
874
+ "i-klingon",
875
+ "i-lux",
876
+ "i-mingo",
877
+ "i-navajo",
878
+ "i-pwn",
879
+ "i-tao",
880
+ "i-tay",
881
+ "i-tsu",
882
+ "no-bok",
883
+ "no-nyn",
884
+ "sgn-be-fr",
885
+ "sgn-be-nl",
886
+ "sgn-ch-de",
887
+ "zh-guoyu",
888
+ "zh-hakka",
889
+ "zh-min",
890
+ "zh-min-nan",
891
+ "zh-xiang"
892
+ ]);
893
+
894
+ // src/field.ts
895
+ var field = {
896
+ string: () => new FieldBuilder("string"),
897
+ number: () => new FieldBuilder("number"),
898
+ boolean: () => new FieldBuilder("boolean"),
899
+ object: (fields) => new FieldBuilder("object", { shape: fields }),
900
+ array: (itemType) => new FieldBuilder("array", { itemType }),
901
+ ref: (refType) => new FieldBuilder("ref", { refType }),
902
+ email: () => new FieldBuilder("string").validator(validateEmail).PID({
903
+ classification: "high",
904
+ action: "encrypt",
905
+ logHandling: "redact",
906
+ purpose: "an email address"
907
+ }).description("An email address"),
908
+ phone: () => new FieldBuilder("string").validator(validatePhone).PID({
909
+ classification: "high",
910
+ action: "encrypt",
911
+ logHandling: "redact",
912
+ purpose: "a phone number"
913
+ }).description("A phone number"),
914
+ url: () => new FieldBuilder("string").validator(validateUrl).PID({
915
+ classification: "low",
916
+ action: "hash",
917
+ logHandling: "pseudonym",
918
+ purpose: "a URL"
919
+ }).description("A URL"),
920
+ uuid: () => new FieldBuilder("string").PID({
921
+ classification: "low",
922
+ action: "hash",
923
+ logHandling: "pseudonym",
924
+ purpose: "a UUID"
925
+ }).validator(validateUUID).description("A UUID"),
926
+ dateTimeISO: () => new FieldBuilder("string").PID({
927
+ classification: "none",
928
+ action: "none",
929
+ logHandling: "plain",
930
+ purpose: "a date string"
931
+ }).validator(validateDateTimeISO).description("A date string in ISO 8601 format"),
932
+ dateISO: () => new FieldBuilder("string").PID({
933
+ classification: "none",
934
+ action: "none",
935
+ logHandling: "plain",
936
+ purpose: "a date string"
937
+ }).validator((s) => validateDateTimeISO(s, { mode: "date" })).description("A date string in ISO 8601 format (date only)"),
938
+ timeISO: () => new FieldBuilder("string").PID({
939
+ classification: "none",
940
+ action: "none",
941
+ logHandling: "plain",
942
+ purpose: "a time string"
943
+ }).validator((s) => validateDateTimeISO(s, { mode: "time" })).description("A time string in ISO 8601 format (time only)"),
944
+ richText: () => new FieldBuilder("string").PID({
945
+ classification: "low",
946
+ action: "clear",
947
+ logHandling: "omit",
948
+ purpose: "rich text content"
949
+ }).validator(validateRichText).description("Rich text content, may include basic HTML formatting"),
950
+ generalText: () => new FieldBuilder("string").PID({
951
+ classification: "none",
952
+ action: "none",
953
+ logHandling: "plain",
954
+ purpose: "Plain text content"
955
+ }).validator(validateSafeText).description("Standard text content, no HTML allowed"),
956
+ latitude: () => new FieldBuilder("number").PID({
957
+ classification: "low",
958
+ action: "clear",
959
+ logHandling: "omit",
960
+ purpose: "Latitude in decimal degrees, WGS 84 (ISO 6709)"
961
+ }).min(-90).max(90).description("Latitude in decimal degrees, WGS 84 (ISO 6709)"),
962
+ longitude: () => new FieldBuilder("number").PID({
963
+ classification: "low",
964
+ action: "clear",
965
+ logHandling: "omit",
966
+ purpose: "Longitude in decimal degrees, WGS 84 (ISO 6709)"
967
+ }).min(-180).max(180).description("Longitude in decimal degrees, WGS 84 (ISO 6709)"),
968
+ version: () => new FieldBuilder("string").validator(validateSemVer).description("A semantic version string, e.g. '1.0.0'"),
969
+ countryCode: () => new FieldBuilder("string").validator(validateCountryCode).description("An ISO 3166 country code, e.g. 'US', 'GB', 'FR'"),
970
+ languageCode: () => new FieldBuilder("string").validator(validateLanguage).description(
971
+ "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'"
972
+ )
973
+ };
974
+
975
+ // src/feedback-contract-version.ts
976
+ var FEEDBACK_CONTRACT_VERSION = "1.0.0";
977
+
978
+ // src/feedback-diagnostics-vocabulary.ts
979
+ var FEEDBACK_RENDERER_BUCKETS = [
980
+ "webgl2",
981
+ "webgpu",
982
+ "canvas2d",
983
+ "unknown"
984
+ ];
985
+ var FEEDBACK_BACKEND_BUCKETS = [
986
+ "browser",
987
+ "worker",
988
+ "unknown"
989
+ ];
990
+ var FEEDBACK_GAME_SURFACE_IDS = [
991
+ "site.generator",
992
+ "site.gpu-demo"
993
+ ];
994
+ var FEEDBACK_GAME_PROVENANCE_CONTRACTS = [
995
+ {
996
+ surfaceId: "site.generator",
997
+ contractId: "generator.renderer-diagnostics.v1"
998
+ },
999
+ {
1000
+ surfaceId: "site.gpu-demo",
1001
+ contractId: "gpu-demo.renderer-diagnostics.v1"
1002
+ }
1003
+ ];
1004
+ var FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS = [
1005
+ "generator.renderer-diagnostics.v1",
1006
+ "gpu-demo.renderer-diagnostics.v1"
1007
+ ];
1008
+ var FEEDBACK_GAME_FEATURE_IDS = [
1009
+ "renderer.initialisation",
1010
+ "renderer.frame-loop",
1011
+ "renderer.asset-loading",
1012
+ "renderer.input",
1013
+ "renderer.scene-generation",
1014
+ "renderer.post-processing"
1015
+ ];
1016
+ var FEEDBACK_GAME_COUNTER_CODES = [
1017
+ "frame-drop",
1018
+ "device-loss",
1019
+ "asset-load-failure",
1020
+ "shader-failure",
1021
+ "fallback-activation"
1022
+ ];
1023
+ var FEEDBACK_GAME_COUNTER_MAX_ITEMS = 32;
1024
+ var FEEDBACK_GAME_COUNTER_MAX_COUNT = 1e4;
1025
+ var FEEDBACK_GAME_ERROR_CODES = [
1026
+ "renderer.initialisation-failed",
1027
+ "renderer.device-lost",
1028
+ "renderer.asset-load-failed",
1029
+ "renderer.frame-budget-exceeded",
1030
+ "renderer.shader-failed",
1031
+ "renderer.unknown"
1032
+ ];
1033
+ var FEEDBACK_VIEWPORT_BUCKETS = [
1034
+ "small-portrait",
1035
+ "small-landscape",
1036
+ "medium-portrait",
1037
+ "medium-landscape",
1038
+ "large-portrait",
1039
+ "large-landscape",
1040
+ "unknown"
1041
+ ];
1042
+ var FEEDBACK_FRAME_RATE_BUCKETS = [
1043
+ "under-15",
1044
+ "15-29",
1045
+ "30-59",
1046
+ "60-plus",
1047
+ "unknown"
1048
+ ];
1049
+ var FEEDBACK_FRAME_TIME_BUCKETS = [
1050
+ "under-17ms",
1051
+ "17-33ms",
1052
+ "34-66ms",
1053
+ "over-66ms",
1054
+ "unknown"
1055
+ ];
1056
+ for (const vocabulary of [
1057
+ FEEDBACK_RENDERER_BUCKETS,
1058
+ FEEDBACK_BACKEND_BUCKETS,
1059
+ FEEDBACK_GAME_SURFACE_IDS,
1060
+ FEEDBACK_GAME_PROVENANCE_CONTRACTS,
1061
+ FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS,
1062
+ FEEDBACK_GAME_FEATURE_IDS,
1063
+ FEEDBACK_GAME_COUNTER_CODES,
1064
+ FEEDBACK_GAME_ERROR_CODES,
1065
+ FEEDBACK_VIEWPORT_BUCKETS,
1066
+ FEEDBACK_FRAME_RATE_BUCKETS,
1067
+ FEEDBACK_FRAME_TIME_BUCKETS
1068
+ ]) {
1069
+ Object.freeze(vocabulary);
1070
+ }
1071
+ for (const entry of FEEDBACK_GAME_PROVENANCE_CONTRACTS) {
1072
+ Object.freeze(entry);
1073
+ }
1074
+
1075
+ // src/types.ts
1076
+ var SCHEMA_UNKNOWN_FIELDS_POLICIES = Object.freeze([
1077
+ "strip",
1078
+ "reject"
1079
+ ]);
1080
+ var SCHEMA_IDENTITY_POLICIES = Object.freeze([
1081
+ "compatible",
1082
+ "exact"
1083
+ ]);
1084
+
1085
+ // src/pii.ts
1086
+ function enforcePIIField(parentKey, key, value, def, enforcement = "none", errors, logger) {
1087
+ const path = parentKey ? `${parentKey}.${key}` : key;
1088
+ if (def?._pii?.classification === "high" && (def?.isRequired ?? true)) {
1089
+ const missing = value === void 0 || value === null || value === "";
1090
+ if (missing) {
1091
+ const msg = `High PII field must not be empty: ${path}`;
1092
+ if (enforcement === "strict") {
1093
+ errors?.push(msg);
1094
+ return { shortCircuit: true };
1095
+ }
1096
+ if (enforcement === "warn") {
1097
+ logger?.warn?.(`WARN (PII Enforcement): ${msg}`);
1098
+ }
1099
+ }
1100
+ }
1101
+ return { shortCircuit: false };
1102
+ }
1103
+ function prepareForStorage(shape, input, encryptFn, hashFn) {
1104
+ const build = (def, value, key, out, ctx = {}) => {
1105
+ if (!def) return;
1106
+ const isMissing = value === void 0 || value === null;
1107
+ if (def._pii?.action === "encrypt") {
1108
+ if (!isMissing) out[key + "Encrypted"] = encryptFn(value);
1109
+ return;
1110
+ }
1111
+ if (def._pii?.action === "hash") {
1112
+ if (!isMissing) out[key + "Hash"] = hashFn(value);
1113
+ return;
1114
+ }
1115
+ if (def._pii?.action === "clear") {
1116
+ if (!isMissing) out[key] = null;
1117
+ return;
1118
+ }
1119
+ if (isMissing && def.isRequired === false) {
1120
+ return;
1121
+ }
1122
+ if (def.type === "object" && def._shape) {
1123
+ const obj = {};
1124
+ for (const [childKey, childDef] of Object.entries(def._shape)) {
1125
+ build(childDef, value?.[childKey], childKey, obj, { parentKey: key });
1126
+ }
1127
+ out[key] = obj;
1128
+ return;
1129
+ }
1130
+ if (def.type === "array" && def.itemType && Array.isArray(value)) {
1131
+ out[key] = value.map((item) => {
1132
+ const obj = {};
1133
+ build(def.itemType, item, key, obj, {
1134
+ parentKey: key,
1135
+ isArrayItem: true
1136
+ });
1137
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1138
+ return obj[key];
1139
+ }
1140
+ return Object.keys(obj).length > 0 ? obj : item;
1141
+ });
1142
+ return;
1143
+ }
1144
+ if (def.type === "ref") {
1145
+ const ref = { ...value };
1146
+ const refShape = def._shape;
1147
+ if (refShape && value) {
1148
+ for (const [childKey, childDef] of Object.entries(refShape)) {
1149
+ build(childDef, value[childKey], childKey, ref, { parentKey: key });
1150
+ }
1151
+ }
1152
+ out[key] = ref;
1153
+ return;
1154
+ }
1155
+ out[key] = value;
1156
+ };
1157
+ const result = {};
1158
+ for (const key in shape) {
1159
+ build(shape[key], input?.[key], key, result);
1160
+ }
1161
+ return result;
1162
+ }
1163
+ function prepareForRead(shape, stored, decryptFn) {
1164
+ const readValue = (def, key, container, ctx = {}) => {
1165
+ if (!def) return container?.[key];
1166
+ const action = def._pii?.action;
1167
+ const hasValue = container !== void 0 && container !== null && Object.prototype.hasOwnProperty.call(container, key);
1168
+ if (action === "encrypt") {
1169
+ const enc = container?.[key + "Encrypted"] ?? container?.[`${ctx.parentKey ?? key}Encrypted`];
1170
+ return enc === void 0 ? void 0 : decryptFn(enc);
1171
+ }
1172
+ if (action === "hash") {
1173
+ const h = container?.[key + "Hash"] ?? container?.[`${ctx.parentKey ?? key}Hash`];
1174
+ return h === void 0 ? void 0 : h;
1175
+ }
1176
+ if (action === "clear")
1177
+ return container?.hasOwnProperty(key) ? null : void 0;
1178
+ if (!hasValue && def.isRequired === false) {
1179
+ return void 0;
1180
+ }
1181
+ if (def.type === "object" && def._shape) {
1182
+ const obj = {};
1183
+ const source = container && Object.prototype.hasOwnProperty.call(container, key) ? container[key] : container ?? {};
1184
+ for (const [childKey, childDef] of Object.entries(def._shape)) {
1185
+ const child = readValue(childDef, childKey, source);
1186
+ if (child !== void 0) {
1187
+ obj[childKey] = child;
1188
+ }
1189
+ }
1190
+ return obj;
1191
+ }
1192
+ if (def.type === "array" && def.itemType) {
1193
+ const arr = container?.[key];
1194
+ if (!Array.isArray(arr)) return arr;
1195
+ return arr.map(
1196
+ (item) => readValue(def.itemType, key, item, { parentKey: key, isArrayItem: true })
1197
+ );
1198
+ }
1199
+ if (def.type === "ref") {
1200
+ const ref = { ...container?.[key] ?? {} };
1201
+ const refShape = def._shape;
1202
+ if (refShape) {
1203
+ for (const [childKey, childDef] of Object.entries(refShape)) {
1204
+ const child = readValue(childDef, childKey, ref);
1205
+ if (child !== void 0) {
1206
+ ref[childKey] = child;
1207
+ }
1208
+ }
1209
+ }
1210
+ return ref;
1211
+ }
1212
+ return container?.[key];
1213
+ };
1214
+ const result = {};
1215
+ for (const key in shape) {
1216
+ const value = readValue(shape[key], key, stored);
1217
+ if (value !== void 0) {
1218
+ result[key] = value;
1219
+ }
1220
+ }
1221
+ return result;
1222
+ }
1223
+ function sanitizeForLog(shape, data, pseudonymFn) {
1224
+ const visit = (def, value, ctx = {}) => {
1225
+ if (!def) return void 0;
1226
+ if ((value === void 0 || value === null) && def.isRequired === false) {
1227
+ return void 0;
1228
+ }
1229
+ const handling = def._pii?.logHandling;
1230
+ if (handling === "omit") return void 0;
1231
+ if (handling === "redact") return "[REDACTED]";
1232
+ if (handling === "pseudonym") return pseudonymFn(value);
1233
+ if (def.type === "object" && def._shape) {
1234
+ const obj = {};
1235
+ const src = value ?? {};
1236
+ for (const [k, childDef] of Object.entries(def._shape)) {
1237
+ const child = visit(childDef, src[k], { parentKey: k });
1238
+ if (child !== void 0) obj[k] = child;
1239
+ }
1240
+ return obj;
1241
+ }
1242
+ if (def.type === "array" && def.itemType) {
1243
+ if (!Array.isArray(value)) return void 0;
1244
+ const arr = value.map((v) => visit(def.itemType, v, { parentKey: ctx.parentKey })).filter((v) => v !== void 0);
1245
+ return arr;
1246
+ }
1247
+ if (def.type === "ref") {
1248
+ const ref = value ? { type: value.type, id: value.id } : {};
1249
+ const refShape = def._shape;
1250
+ const src = value ?? {};
1251
+ if (refShape) {
1252
+ for (const [k, childDef] of Object.entries(refShape)) {
1253
+ const child = visit(childDef, src[k]);
1254
+ if (child !== void 0) ref[k] = child;
1255
+ }
1256
+ } else if (value) {
1257
+ ref.type = value.type;
1258
+ ref.id = value.id;
1259
+ }
1260
+ return ref;
1261
+ }
1262
+ return value;
1263
+ };
1264
+ const output = {};
1265
+ for (const key in shape) {
1266
+ const child = visit(shape[key], data?.[key]);
1267
+ if (child !== void 0) output[key] = child;
1268
+ }
1269
+ return output;
1270
+ }
1271
+ function getPiiAudit(shape) {
1272
+ const piiFields = [];
1273
+ const visit = (def, path) => {
1274
+ if (!def) return;
1275
+ if (def._pii && def._pii.classification !== "none") {
1276
+ piiFields.push({
1277
+ field: path,
1278
+ classification: def._pii.classification,
1279
+ action: def._pii.action,
1280
+ logHandling: def._pii.logHandling,
1281
+ purpose: def._pii.purpose
1282
+ });
1283
+ }
1284
+ if ((def.type === "object" || def.type === "ref") && def._shape) {
1285
+ for (const [childKey, childDef] of Object.entries(def._shape)) {
1286
+ visit(childDef, `${path}.${childKey}`);
1287
+ }
1288
+ }
1289
+ if (def.type === "array" && def.itemType) {
1290
+ const itemPath = `${path}[]`;
1291
+ if ((def.itemType.type === "object" || def.itemType.type === "ref") && def.itemType._shape) {
1292
+ for (const [childKey, childDef] of Object.entries(
1293
+ def.itemType._shape
1294
+ )) {
1295
+ visit(childDef, `${itemPath}.${childKey}`);
1296
+ }
1297
+ } else {
1298
+ visit(def.itemType, itemPath);
1299
+ }
1300
+ }
1301
+ };
1302
+ for (const key in shape) {
1303
+ visit(shape[key], key);
1304
+ }
1305
+ return piiFields;
1306
+ }
1307
+ function scrubPiiForDelete(shape, stored) {
1308
+ const result = Array.isArray(stored) ? [...stored] : { ...stored };
1309
+ const setAtPath = (target, path, val) => {
1310
+ let cur = target;
1311
+ for (let i = 0; i < path.length - 1; i++) {
1312
+ const p = path[i];
1313
+ if (p === void 0) return;
1314
+ if (cur[p] === void 0) {
1315
+ const next = path[i + 1];
1316
+ cur[p] = typeof next === "number" ? [] : {};
1317
+ }
1318
+ cur = cur[p];
1319
+ }
1320
+ const last = path[path.length - 1];
1321
+ if (last === void 0) return;
1322
+ cur[last] = val;
1323
+ };
1324
+ const visit = (def, host, path, keyName) => {
1325
+ if (!def) return;
1326
+ const applyPath = (targetKey) => {
1327
+ const last = path[path.length - 1];
1328
+ if (last === void 0) return;
1329
+ if (typeof last === "number") {
1330
+ setAtPath(result, [...path, targetKey], null);
1331
+ } else {
1332
+ setAtPath(result, [...path.slice(0, -1), targetKey], null);
1333
+ }
1334
+ };
1335
+ if (def._pii?.action === "encrypt") {
1336
+ const targetKey = `${keyName}Encrypted`;
1337
+ if (host && Object.prototype.hasOwnProperty.call(host, targetKey)) {
1338
+ applyPath(targetKey);
1339
+ }
1340
+ return;
1341
+ }
1342
+ if (def._pii?.action === "hash") {
1343
+ const targetKey = `${keyName}Hash`;
1344
+ if (host && Object.prototype.hasOwnProperty.call(host, targetKey)) {
1345
+ applyPath(targetKey);
1346
+ }
1347
+ return;
1348
+ }
1349
+ if (def._pii?.action === "clear") {
1350
+ if (host && Object.prototype.hasOwnProperty.call(host, keyName)) {
1351
+ setAtPath(result, path, null);
1352
+ }
1353
+ return;
1354
+ }
1355
+ if (def.type === "object" && def._shape) {
1356
+ const obj = host && Object.prototype.hasOwnProperty.call(host, keyName) ? host[keyName] : host;
1357
+ for (const [k, childDef] of Object.entries(def._shape)) {
1358
+ visit(childDef, obj, [...path, k], k);
1359
+ }
1360
+ return;
1361
+ }
1362
+ if (def.type === "array" && def.itemType) {
1363
+ const arr = host?.[keyName];
1364
+ if (Array.isArray(arr)) {
1365
+ arr.forEach(
1366
+ (item, idx) => visit(def.itemType, item, [...path, idx], keyName)
1367
+ );
1368
+ }
1369
+ return;
1370
+ }
1371
+ if (def.type === "ref") {
1372
+ const refShape = def._shape;
1373
+ const ref = host?.[keyName];
1374
+ if (refShape && ref) {
1375
+ for (const [k, childDef] of Object.entries(refShape)) {
1376
+ visit(childDef, ref, [...path, k], k);
1377
+ }
1378
+ }
1379
+ }
1380
+ };
1381
+ for (const key in shape) {
1382
+ visit(shape[key], stored, [key], key);
1383
+ }
1384
+ return result;
1385
+ }
1386
+
1387
+ // src/schema.ts
1388
+ var globalSchemaRegistry = /* @__PURE__ */ new Map();
1389
+ function deepClone(value) {
1390
+ const seen = /* @__PURE__ */ new WeakMap();
1391
+ const cloneAny = (val) => {
1392
+ if (val === null || typeof val !== "object") return val;
1393
+ if (seen.has(val)) throw new TypeError("Cannot clone circular structures");
1394
+ if (val instanceof Date) return new Date(val.getTime());
1395
+ if (Array.isArray(val)) {
1396
+ const arr = [];
1397
+ seen.set(val, arr);
1398
+ val.forEach((item) => arr.push(cloneAny(item)));
1399
+ return arr;
1400
+ }
1401
+ const out = {};
1402
+ seen.set(val, out);
1403
+ for (const [k, v] of Object.entries(val)) out[k] = cloneAny(v);
1404
+ return out;
1405
+ };
1406
+ return cloneAny(value);
1407
+ }
1408
+ function shouldIncludeField(def, options) {
1409
+ return options.includeInternal === true || (def?._exposure ?? "public") !== "internal";
1410
+ }
1411
+ function serializeFieldValue(def, value, options) {
1412
+ if (value === void 0) {
1413
+ return void 0;
1414
+ }
1415
+ if (value === null) {
1416
+ return null;
1417
+ }
1418
+ switch (def?.type) {
1419
+ case "string":
1420
+ case "number":
1421
+ case "boolean":
1422
+ return deepClone(value);
1423
+ case "ref": {
1424
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1425
+ return deepClone(value);
1426
+ }
1427
+ const shape = getShape(def);
1428
+ if (!shape || Object.keys(shape).length === 0) {
1429
+ return deepClone(value);
1430
+ }
1431
+ const serializedRef = {};
1432
+ if (typeof value.type === "string") {
1433
+ serializedRef.type = value.type;
1434
+ }
1435
+ if (typeof value.id === "string") {
1436
+ serializedRef.id = value.id;
1437
+ }
1438
+ for (const [childKey, childDef] of Object.entries(shape)) {
1439
+ if (!shouldIncludeField(childDef, options)) {
1440
+ continue;
1441
+ }
1442
+ if (!Object.prototype.hasOwnProperty.call(value, childKey)) {
1443
+ continue;
1444
+ }
1445
+ const serializedChild = serializeFieldValue(
1446
+ childDef,
1447
+ value[childKey],
1448
+ options
1449
+ );
1450
+ if (serializedChild !== void 0) {
1451
+ serializedRef[childKey] = serializedChild;
1452
+ }
1453
+ }
1454
+ return serializedRef;
1455
+ }
1456
+ case "object": {
1457
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1458
+ return deepClone(value);
1459
+ }
1460
+ const shape = getShape(def);
1461
+ if (!shape || Object.keys(shape).length === 0) {
1462
+ return deepClone(value);
1463
+ }
1464
+ const serializedObject = {};
1465
+ for (const [childKey, childDef] of Object.entries(shape)) {
1466
+ if (!shouldIncludeField(childDef, options)) {
1467
+ continue;
1468
+ }
1469
+ if (!Object.prototype.hasOwnProperty.call(value, childKey)) {
1470
+ continue;
1471
+ }
1472
+ const serializedChild = serializeFieldValue(
1473
+ childDef,
1474
+ value[childKey],
1475
+ options
1476
+ );
1477
+ if (serializedChild !== void 0) {
1478
+ serializedObject[childKey] = serializedChild;
1479
+ }
1480
+ }
1481
+ return serializedObject;
1482
+ }
1483
+ case "array": {
1484
+ if (!Array.isArray(value)) {
1485
+ return deepClone(value);
1486
+ }
1487
+ if (!def.itemType) {
1488
+ return deepClone(value);
1489
+ }
1490
+ return value.map((item) => serializeFieldValue(def.itemType, item, options));
1491
+ }
1492
+ default:
1493
+ return deepClone(value);
1494
+ }
1495
+ }
1496
+ function cmpSemver(a, b) {
1497
+ const pa = a.split(".").map((n) => parseInt(n, 10));
1498
+ const pb = b.split(".").map((n) => parseInt(n, 10));
1499
+ for (let i = 0; i < 3; i++) {
1500
+ const ai = pa[i] ?? 0;
1501
+ const bi = pb[i] ?? 0;
1502
+ if (ai > bi) return 1;
1503
+ if (ai < bi) return -1;
1504
+ }
1505
+ return 0;
1506
+ }
1507
+ function applySchemaUpgrade(spec, input, ctx) {
1508
+ if (typeof spec === "function") {
1509
+ return spec(input, ctx);
1510
+ }
1511
+ const steps = [...spec].sort((s1, s2) => cmpSemver(s1.to, s2.to));
1512
+ let working = { ...input };
1513
+ let fromVersion = ctx.from;
1514
+ for (const step of steps) {
1515
+ if (cmpSemver(fromVersion, step.to) < 0 && cmpSemver(step.to, ctx.to) <= 0) {
1516
+ ctx.log?.(`Upgrading entity from v${fromVersion} \u2192 v${step.to}`);
1517
+ const res = step.run(working, { ...ctx, from: fromVersion, to: step.to });
1518
+ if (!res || res.ok !== true || !res.value) {
1519
+ return {
1520
+ ok: false,
1521
+ errors: res?.errors?.length ? res.errors : [`Failed to upgrade entity from v${fromVersion} to v${step.to}`]
1522
+ };
1523
+ }
1524
+ working = res.value;
1525
+ fromVersion = step.to;
1526
+ }
1527
+ }
1528
+ return { ok: true, value: working };
1529
+ }
1530
+ function validateEnum(parentKey, value, enumValues) {
1531
+ if (!enumValues) return;
1532
+ const values = Array.isArray(enumValues) ? enumValues : Array.from(enumValues);
1533
+ if (Array.isArray(value)) {
1534
+ const invalid = value.filter((v) => !values.includes(v));
1535
+ if (invalid.length > 0) {
1536
+ return `Field ${parentKey} must contain only: ${values.join(", ")}`;
1537
+ }
1538
+ } else {
1539
+ if (!values.includes(value)) {
1540
+ return `Field ${parentKey} must be one of: ${values.join(", ")}`;
1541
+ }
1542
+ }
1543
+ }
1544
+ function getEnumValues(def) {
1545
+ 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;
1546
+ if (!src) return void 0;
1547
+ const ok = src.every(
1548
+ (v) => typeof v === "string" || typeof v === "number"
1549
+ );
1550
+ return ok ? src : void 0;
1551
+ }
1552
+ function applyDefault(def, current) {
1553
+ if ((current === void 0 || current === null) && typeof def?.getDefault === "function") {
1554
+ const next = def.getDefault();
1555
+ if (next !== void 0) {
1556
+ return { value: next, applied: true };
1557
+ }
1558
+ }
1559
+ return { value: current, applied: false };
1560
+ }
1561
+ function isOptional(def) {
1562
+ return def?.isRequired === false;
1563
+ }
1564
+ function getValidator(def) {
1565
+ return def?._validator ?? void 0;
1566
+ }
1567
+ function getShape(def) {
1568
+ return def?._shape ?? def?.shape ?? void 0;
1569
+ }
1570
+ var MAX_STRICT_VALIDATION_NODES = 1e4;
1571
+ var MAX_STRICT_VALIDATION_DEPTH = 256;
1572
+ var MAX_STRICT_VALIDATION_OBJECTS = MAX_STRICT_VALIDATION_NODES;
1573
+ function strictInputIsWithinComplexityLimit(input) {
1574
+ const pending = [
1575
+ { value: input, depth: 0 }
1576
+ ];
1577
+ const seen = /* @__PURE__ */ new WeakSet();
1578
+ let visitedNodes = 0;
1579
+ try {
1580
+ while (pending.length > 0) {
1581
+ const current = pending.pop();
1582
+ if (!current) {
1583
+ continue;
1584
+ }
1585
+ visitedNodes += 1;
1586
+ if (visitedNodes > MAX_STRICT_VALIDATION_NODES || current.depth > MAX_STRICT_VALIDATION_DEPTH) {
1587
+ return false;
1588
+ }
1589
+ if (typeof current.value !== "object" || current.value === null) {
1590
+ continue;
1591
+ }
1592
+ if (seen.has(current.value)) {
1593
+ return false;
1594
+ }
1595
+ seen.add(current.value);
1596
+ const keys = Object.keys(current.value);
1597
+ if (visitedNodes + pending.length + keys.length > MAX_STRICT_VALIDATION_NODES) {
1598
+ return false;
1599
+ }
1600
+ const record = current.value;
1601
+ for (const key of keys) {
1602
+ pending.push({
1603
+ value: record[key],
1604
+ depth: current.depth + 1
1605
+ });
1606
+ }
1607
+ }
1608
+ } catch {
1609
+ return false;
1610
+ }
1611
+ return true;
1612
+ }
1613
+ function strictReferenceShape(def) {
1614
+ return {
1615
+ type: { type: "string" },
1616
+ id: { type: "string" },
1617
+ ...getShape(def) ?? {}
1618
+ };
1619
+ }
1620
+ function unknownFieldLocations(value, shape, location = "root", state = {
1621
+ remainingObjects: MAX_STRICT_VALIDATION_OBJECTS,
1622
+ limitExceeded: false
1623
+ }) {
1624
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1625
+ return [];
1626
+ }
1627
+ if (state.remainingObjects <= 0) {
1628
+ state.limitExceeded = true;
1629
+ return [];
1630
+ }
1631
+ state.remainingObjects -= 1;
1632
+ const record = value;
1633
+ const knownKeys = new Set(Object.keys(shape));
1634
+ const locations = /* @__PURE__ */ new Set();
1635
+ if (Object.keys(record).some((key) => !knownKeys.has(key))) {
1636
+ locations.add(location);
1637
+ }
1638
+ for (const [key, def] of Object.entries(shape)) {
1639
+ const child = record[key];
1640
+ if (child === void 0 || child === null) {
1641
+ continue;
1642
+ }
1643
+ const childLocation = location === "root" ? key : `${location}.${key}`;
1644
+ const childShape = getShape(def);
1645
+ if (def?.type === "object" && childShape) {
1646
+ for (const nestedLocation of unknownFieldLocations(
1647
+ child,
1648
+ childShape,
1649
+ childLocation,
1650
+ state
1651
+ )) {
1652
+ locations.add(nestedLocation);
1653
+ }
1654
+ continue;
1655
+ }
1656
+ const itemType = def?.itemType;
1657
+ if (def?.type === "array" && (itemType?.type === "object" || itemType?.type === "ref") && Array.isArray(child)) {
1658
+ const itemShape = itemType.type === "ref" ? strictReferenceShape(itemType) : getShape(itemType);
1659
+ if (!itemShape) {
1660
+ continue;
1661
+ }
1662
+ for (const [index, item] of child.entries()) {
1663
+ if (state.limitExceeded) {
1664
+ break;
1665
+ }
1666
+ for (const nestedLocation of unknownFieldLocations(
1667
+ item,
1668
+ itemShape,
1669
+ `${childLocation}[${index}]`,
1670
+ state
1671
+ )) {
1672
+ locations.add(nestedLocation);
1673
+ }
1674
+ }
1675
+ continue;
1676
+ }
1677
+ if (def?.type === "ref") {
1678
+ for (const nestedLocation of unknownFieldLocations(
1679
+ child,
1680
+ strictReferenceShape(def),
1681
+ childLocation,
1682
+ state
1683
+ )) {
1684
+ locations.add(nestedLocation);
1685
+ }
1686
+ }
1687
+ }
1688
+ return [...locations];
1689
+ }
1690
+ function normalizeValidationIssue(path, issue, defaultCode) {
1691
+ if (typeof issue === "string") {
1692
+ return {
1693
+ path,
1694
+ code: defaultCode,
1695
+ message: issue
1696
+ };
1697
+ }
1698
+ return {
1699
+ path: issue.path ?? path,
1700
+ code: issue.code || defaultCode,
1701
+ message: issue.message
1702
+ };
1703
+ }
1704
+ function normalizeValidatorIssues(path, result, defaultCode) {
1705
+ if (result === true) {
1706
+ return [];
1707
+ }
1708
+ if (result === false) {
1709
+ return [
1710
+ {
1711
+ path,
1712
+ code: defaultCode,
1713
+ message: `Invalid value for field: ${path}`
1714
+ }
1715
+ ];
1716
+ }
1717
+ if (Array.isArray(result)) {
1718
+ return result.map((issue) => normalizeValidationIssue(path, issue, defaultCode));
1719
+ }
1720
+ return [normalizeValidationIssue(path, result, defaultCode)];
1721
+ }
1722
+ function checkMissingRequired(parentKey, key, value, def, errors) {
1723
+ if (value === void 0 || value === null) {
1724
+ const path = parentKey ? `${parentKey}.${key}` : key;
1725
+ if (!isOptional(def)) {
1726
+ errors.push(`Missing required field: ${path}`);
1727
+ }
1728
+ return { missing: true };
1729
+ }
1730
+ return { missing: false };
1731
+ }
1732
+ function checkImmutable(parentKey, key, value, def, existing, errors) {
1733
+ if (def.isImmutable && existing && existing[key] !== void 0 && value !== existing[key]) {
1734
+ const path = parentKey ? `${parentKey}.${key}` : key;
1735
+ errors.push(`Field is immutable: ${path}`);
1736
+ return { immutableViolation: true };
1737
+ }
1738
+ return { immutableViolation: false };
1739
+ }
1740
+ function runCustomValidator(parentKey, key, value, def, errors, issues) {
1741
+ const validator = getValidator(def);
1742
+ if (validator && value !== void 0 && value !== null) {
1743
+ const path = parentKey ? `${parentKey}.${key}` : key;
1744
+ const normalizedIssues = normalizeValidatorIssues(path, validator(value), "invalid_value");
1745
+ if (normalizedIssues.length > 0) {
1746
+ errors.push(...normalizedIssues.map((issue) => issue.message));
1747
+ if (issues) {
1748
+ issues.push(...normalizedIssues);
1749
+ }
1750
+ return { invalid: true };
1751
+ }
1752
+ }
1753
+ return { invalid: false };
1754
+ }
1755
+ function validateStringField(parentKey, key, value, def, errors) {
1756
+ const path = parentKey ? `${parentKey}.${key}` : key;
1757
+ if (typeof value !== "string") {
1758
+ errors.push(`Field ${path} must be string`);
1759
+ return;
1760
+ }
1761
+ const enumValues = getEnumValues(def);
1762
+ if (Array.isArray(enumValues)) {
1763
+ const enumError = validateEnum(path, value, enumValues);
1764
+ if (enumError) {
1765
+ errors.push(enumError);
1766
+ }
1767
+ }
1768
+ }
1769
+ function validateNumberField(parentKey, key, value, def, errors) {
1770
+ const path = parentKey ? `${parentKey}.${key}` : key;
1771
+ if (typeof value !== "number") {
1772
+ errors.push(`Field ${path} must be number`);
1773
+ return;
1774
+ }
1775
+ const enumValues = getEnumValues(def);
1776
+ if (Array.isArray(enumValues)) {
1777
+ const enumError = validateEnum(path, value, enumValues);
1778
+ if (enumError) {
1779
+ errors.push(enumError);
1780
+ }
1781
+ }
1782
+ }
1783
+ function validateBooleanField(parentKey, key, value, _def, errors) {
1784
+ const path = parentKey ? `${parentKey}.${key}` : key;
1785
+ if (typeof value !== "boolean") {
1786
+ errors.push(`Field ${path} must be boolean`);
1787
+ }
1788
+ }
1789
+ function validateObjectChildren(parentKey, obj, shape, errors, issues, existing, piiEnforcement, logger) {
1790
+ for (const [childKey, childDef] of Object.entries(shape)) {
1791
+ let childValue = obj[childKey];
1792
+ const existingChild = existing && typeof existing === "object" ? existing[childKey] : void 0;
1793
+ const { value: withDefault, applied } = applyDefault(childDef, childValue);
1794
+ if (applied) {
1795
+ obj[childKey] = withDefault;
1796
+ childValue = withDefault;
1797
+ }
1798
+ const { missing } = checkMissingRequired(
1799
+ parentKey,
1800
+ childKey,
1801
+ childValue,
1802
+ childDef,
1803
+ errors
1804
+ );
1805
+ if (missing) continue;
1806
+ const { immutableViolation } = checkImmutable(
1807
+ parentKey,
1808
+ childKey,
1809
+ childValue,
1810
+ childDef,
1811
+ existing,
1812
+ errors
1813
+ );
1814
+ if (immutableViolation) continue;
1815
+ const { shortCircuit } = enforcePIIField(
1816
+ parentKey,
1817
+ childKey,
1818
+ childValue,
1819
+ childDef,
1820
+ piiEnforcement,
1821
+ errors,
1822
+ logger
1823
+ );
1824
+ if (shortCircuit) continue;
1825
+ const { invalid } = runCustomValidator(
1826
+ parentKey,
1827
+ childKey,
1828
+ childValue,
1829
+ childDef,
1830
+ errors,
1831
+ issues
1832
+ );
1833
+ if (invalid) continue;
1834
+ validateByType(
1835
+ parentKey,
1836
+ childKey,
1837
+ childValue,
1838
+ childDef,
1839
+ errors,
1840
+ issues,
1841
+ existingChild,
1842
+ piiEnforcement,
1843
+ logger
1844
+ );
1845
+ }
1846
+ }
1847
+ function validateObjectField(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
1848
+ const path = parentKey ? `${parentKey}.${key}` : key;
1849
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1850
+ errors.push(`Field ${path} must be object`);
1851
+ return;
1852
+ }
1853
+ const objShape = getShape(def);
1854
+ if (objShape)
1855
+ validateObjectChildren(path, value, objShape, errors, issues, existing, piiEnforcement, logger);
1856
+ }
1857
+ function validateArrayOfStrings(parentKey, key, arr, itemDef, errors) {
1858
+ const path = parentKey ? `${parentKey}.${key}` : key;
1859
+ if (!arr.every((v) => typeof v === "string")) {
1860
+ errors.push(`Field ${path} must be string[]`);
1861
+ return;
1862
+ }
1863
+ const enumValues = getEnumValues(itemDef);
1864
+ if (Array.isArray(enumValues)) {
1865
+ const enumError = validateEnum(path, arr, enumValues);
1866
+ if (enumError) {
1867
+ errors.push(enumError);
1868
+ }
1869
+ }
1870
+ const validator = getValidator(itemDef);
1871
+ if (validator) {
1872
+ arr.forEach((v, idx) => {
1873
+ if (!validator(v)) {
1874
+ errors.push(`Invalid value for field: ${path}[${idx}]`);
1875
+ }
1876
+ });
1877
+ }
1878
+ }
1879
+ function validateArrayOfNumbers(parentKey, key, arr, itemDef, errors) {
1880
+ const path = parentKey ? `${parentKey}.${key}` : key;
1881
+ if (!arr.every((v) => typeof v === "number")) {
1882
+ errors.push(`Field ${path} must be number[]`);
1883
+ }
1884
+ const enumValues = getEnumValues(itemDef);
1885
+ if (Array.isArray(enumValues)) {
1886
+ const enumError = validateEnum(path, arr, enumValues);
1887
+ if (enumError) {
1888
+ errors.push(enumError);
1889
+ }
1890
+ }
1891
+ const validator = getValidator(itemDef);
1892
+ if (validator) {
1893
+ arr.forEach((v, idx) => {
1894
+ if (!validator(v)) {
1895
+ errors.push(`Invalid value for field: ${path}[${idx}]`);
1896
+ }
1897
+ });
1898
+ }
1899
+ }
1900
+ function validateArrayOfBooleans(parentKey, key, arr, _itemDef, errors) {
1901
+ const path = parentKey ? `${parentKey}.${key}` : key;
1902
+ if (!arr.every((v) => typeof v === "boolean")) {
1903
+ errors.push(`Field ${path} must be boolean[]`);
1904
+ }
1905
+ const validator = getValidator(_itemDef);
1906
+ if (validator) {
1907
+ arr.forEach((v, idx) => {
1908
+ if (!validator(v)) {
1909
+ errors.push(`Invalid value for field: ${path}[${idx}]`);
1910
+ }
1911
+ });
1912
+ }
1913
+ }
1914
+ function validateArrayOfObjects(parentKey, key, arr, itemDef, errors, issues, existing, piiEnforcement, logger) {
1915
+ const path = parentKey ? `${parentKey}.${key}` : key;
1916
+ if (!Array.isArray(arr) || !arr.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
1917
+ errors.push(`Field ${path} must be object[]`);
1918
+ return;
1919
+ }
1920
+ const itemShape = getShape(itemDef);
1921
+ if (!itemShape) return;
1922
+ arr.forEach((item, idx) => {
1923
+ const itemParent = `${path}[${idx}]`;
1924
+ const existingItem = Array.isArray(existing) ? existing[idx] : void 0;
1925
+ for (const [childKey, childDef] of Object.entries(itemShape)) {
1926
+ let childValue = item[childKey];
1927
+ const existingChild = existingItem && typeof existingItem === "object" ? existingItem[childKey] : void 0;
1928
+ const { value: withDefault, applied } = applyDefault(
1929
+ childDef,
1930
+ childValue
1931
+ );
1932
+ if (applied) {
1933
+ item[childKey] = withDefault;
1934
+ childValue = withDefault;
1935
+ }
1936
+ const { missing } = checkMissingRequired(
1937
+ itemParent,
1938
+ childKey,
1939
+ childValue,
1940
+ childDef,
1941
+ errors
1942
+ );
1943
+ if (missing) continue;
1944
+ const { immutableViolation } = checkImmutable(
1945
+ itemParent,
1946
+ childKey,
1947
+ childValue,
1948
+ childDef,
1949
+ existingItem,
1950
+ errors
1951
+ );
1952
+ if (immutableViolation) continue;
1953
+ const { shortCircuit } = enforcePIIField(
1954
+ itemParent,
1955
+ childKey,
1956
+ childValue,
1957
+ childDef,
1958
+ piiEnforcement,
1959
+ errors,
1960
+ logger
1961
+ );
1962
+ if (shortCircuit) continue;
1963
+ const { invalid } = runCustomValidator(
1964
+ itemParent,
1965
+ childKey,
1966
+ childValue,
1967
+ childDef,
1968
+ errors,
1969
+ issues
1970
+ );
1971
+ if (invalid) continue;
1972
+ validateByType(
1973
+ itemParent,
1974
+ childKey,
1975
+ childValue,
1976
+ childDef,
1977
+ errors,
1978
+ issues,
1979
+ existingChild,
1980
+ piiEnforcement,
1981
+ logger
1982
+ );
1983
+ }
1984
+ });
1985
+ }
1986
+ function validateArrayField(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
1987
+ const path = parentKey ? `${parentKey}.${key}` : key;
1988
+ if (!Array.isArray(value)) {
1989
+ errors.push(`Field ${key} must be an array`);
1990
+ return;
1991
+ }
1992
+ const itemType = def.itemType?.type;
1993
+ if (itemType === "string")
1994
+ return validateArrayOfStrings(parentKey, key, value, def.itemType, errors);
1995
+ if (itemType === "number")
1996
+ return validateArrayOfNumbers(parentKey, key, value, def.itemType, errors);
1997
+ if (itemType === "boolean")
1998
+ return validateArrayOfBooleans(parentKey, key, value, def.itemType, errors);
1999
+ if (itemType === "object")
2000
+ return validateArrayOfObjects(
2001
+ parentKey,
2002
+ key,
2003
+ value,
2004
+ def.itemType,
2005
+ errors,
2006
+ issues,
2007
+ existing,
2008
+ piiEnforcement,
2009
+ logger
2010
+ );
2011
+ if (itemType === "ref") {
2012
+ const expectedType = def.itemType.refType;
2013
+ value.forEach((ref, idx) => {
2014
+ if (!ref || typeof ref !== "object" || ref === null || typeof ref.type !== "string" || typeof ref.id !== "string" || expectedType && ref.type !== expectedType) {
2015
+ errors.push(
2016
+ `Field ${path}[${idx}] must be a reference object with type: ${expectedType}`
2017
+ );
2018
+ }
2019
+ });
2020
+ const refShape = getShape(def.itemType);
2021
+ if (refShape) {
2022
+ const existingRefs = Array.isArray(existing) ? existing : [];
2023
+ value.forEach((ref, idx) => {
2024
+ if (ref && typeof ref === "object" && ref !== null) {
2025
+ const existingRef = existingRefs[idx];
2026
+ for (const [childKey, childDef] of Object.entries(refShape)) {
2027
+ const childPath = `${path}[${idx}].${childKey}`;
2028
+ let childValue = ref[childKey];
2029
+ const existingChild = existingRef && typeof existingRef === "object" ? existingRef[childKey] : void 0;
2030
+ const { value: withDefault, applied } = applyDefault(
2031
+ childDef,
2032
+ childValue
2033
+ );
2034
+ if (applied) {
2035
+ ref[childKey] = withDefault;
2036
+ childValue = withDefault;
2037
+ }
2038
+ if ((childValue === void 0 || childValue === null) && !isOptional(childDef)) {
2039
+ errors.push(`Missing required field: ${childPath}`);
2040
+ continue;
2041
+ }
2042
+ const { immutableViolation } = checkImmutable(
2043
+ `${path}[${idx}]`,
2044
+ childKey,
2045
+ childValue,
2046
+ childDef,
2047
+ existingRef,
2048
+ errors
2049
+ );
2050
+ if (immutableViolation) continue;
2051
+ const { shortCircuit } = enforcePIIField(
2052
+ `${path}[${idx}]`,
2053
+ childKey,
2054
+ childValue,
2055
+ childDef,
2056
+ piiEnforcement,
2057
+ errors,
2058
+ logger
2059
+ );
2060
+ if (shortCircuit) continue;
2061
+ const childValidator = getValidator(childDef);
2062
+ if (childValidator && childValue !== void 0 && childValue !== null) {
2063
+ const normalizedIssues = normalizeValidatorIssues(
2064
+ childPath,
2065
+ childValidator(childValue),
2066
+ "invalid_value"
2067
+ );
2068
+ if (normalizedIssues.length > 0) {
2069
+ errors.push(...normalizedIssues.map((issue) => issue.message));
2070
+ issues.push(...normalizedIssues);
2071
+ continue;
2072
+ }
2073
+ }
2074
+ validateByType(
2075
+ `${path}[${idx}]`,
2076
+ childKey,
2077
+ childValue,
2078
+ childDef,
2079
+ errors,
2080
+ issues,
2081
+ existingChild,
2082
+ piiEnforcement,
2083
+ logger
2084
+ );
2085
+ }
2086
+ }
2087
+ });
2088
+ }
2089
+ return;
2090
+ }
2091
+ errors.push(`Field ${path} has unsupported array item type`);
2092
+ }
2093
+ function validateRefField(parentKey, key, value, def, errors, _existing, _piiEnforcement, _logger) {
2094
+ const path = parentKey ? `${parentKey}.${key}` : key;
2095
+ if (typeof value !== "object" || value === null || typeof value.type !== "string" || typeof value.id !== "string") {
2096
+ errors.push(`Field ${path} must be { type: string; id: string }`);
2097
+ return;
2098
+ }
2099
+ const expectedType = def.refType;
2100
+ if (expectedType && value.type !== expectedType) {
2101
+ errors.push(
2102
+ `Field ${path} must reference type: ${expectedType} (got ${value.type})`
2103
+ );
2104
+ }
2105
+ }
2106
+ function validateByType(parentKey, key, value, def, errors, issues, existing, piiEnforcement, logger) {
2107
+ const path = parentKey ? `${parentKey}.${key}` : key;
2108
+ switch (def.type) {
2109
+ case "string":
2110
+ return validateStringField(parentKey, key, value, def, errors);
2111
+ case "number":
2112
+ return validateNumberField(parentKey, key, value, def, errors);
2113
+ case "boolean":
2114
+ return validateBooleanField(parentKey, key, value, def, errors);
2115
+ case "object":
2116
+ return validateObjectField(
2117
+ parentKey,
2118
+ key,
2119
+ value,
2120
+ def,
2121
+ errors,
2122
+ issues,
2123
+ existing,
2124
+ piiEnforcement,
2125
+ logger
2126
+ );
2127
+ case "array":
2128
+ return validateArrayField(
2129
+ parentKey,
2130
+ key,
2131
+ value,
2132
+ def,
2133
+ errors,
2134
+ issues,
2135
+ existing,
2136
+ piiEnforcement,
2137
+ logger
2138
+ );
2139
+ case "ref":
2140
+ return validateRefField(parentKey, key, value, def, errors, existing, piiEnforcement, logger);
2141
+ default:
2142
+ errors.push(`Unknown type for field ${path}: ${def.type}`);
2143
+ }
2144
+ }
2145
+ function createSchema(_shape, entityType, options = {
2146
+ version: "1.0.0",
2147
+ table: "",
2148
+ schemaValidator: () => true,
2149
+ piiEnforcement: "none",
2150
+ unknownFields: SCHEMA_UNKNOWN_FIELDS_POLICIES[0],
2151
+ identity: SCHEMA_IDENTITY_POLICIES[0],
2152
+ schemaUpgrade: void 0
2153
+ }) {
2154
+ const systemFields = {
2155
+ type: field.string().immutable().system(),
2156
+ version: field.string().immutable().system().validator(validateSemVer)
2157
+ };
2158
+ const version = options.version || "1.0.0";
2159
+ const store = options.table || "";
2160
+ const schemaUpgrade = options.schemaUpgrade;
2161
+ const schema = {
2162
+ // 🔗 Define the schema shape
2163
+ _shape: {
2164
+ ...systemFields,
2165
+ ..._shape
2166
+ },
2167
+ // 🔗 Metadata about the schema
2168
+ meta: { entityType, version },
2169
+ // 🔗 Validate input against the schema
2170
+ validate(input, existing) {
2171
+ const errors = [];
2172
+ const issues = [];
2173
+ const result = {};
2174
+ const piiMode = options.piiEnforcement ?? "none";
2175
+ if (typeof input !== "object" || input === null) {
2176
+ return { valid: false, errors: ["Input must be an object"] };
2177
+ }
2178
+ if (options.unknownFields === "reject") {
2179
+ if (!strictInputIsWithinComplexityLimit(input)) {
2180
+ return {
2181
+ valid: false,
2182
+ errors: ["Strict validation complexity limit exceeded."],
2183
+ issues: [
2184
+ {
2185
+ path: "root",
2186
+ code: "validation_complexity_limit",
2187
+ message: "Strict validation complexity limit exceeded."
2188
+ }
2189
+ ]
2190
+ };
2191
+ }
2192
+ const scanState = {
2193
+ remainingObjects: MAX_STRICT_VALIDATION_OBJECTS,
2194
+ limitExceeded: false
2195
+ };
2196
+ const locations = unknownFieldLocations(
2197
+ input,
2198
+ schema._shape,
2199
+ "root",
2200
+ scanState
2201
+ );
2202
+ if (scanState.limitExceeded) {
2203
+ return {
2204
+ valid: false,
2205
+ errors: ["Strict validation complexity limit exceeded."],
2206
+ issues: [
2207
+ {
2208
+ path: "root",
2209
+ code: "validation_complexity_limit",
2210
+ message: "Strict validation complexity limit exceeded."
2211
+ }
2212
+ ]
2213
+ };
2214
+ }
2215
+ if (locations.length > 0) {
2216
+ return {
2217
+ valid: false,
2218
+ errors: locations.map(
2219
+ (location) => `Unknown fields are not allowed at ${location}.`
2220
+ ),
2221
+ issues: locations.map((location) => ({
2222
+ path: location,
2223
+ code: "unknown_fields",
2224
+ message: `Unknown fields are not allowed at ${location}.`
2225
+ }))
2226
+ };
2227
+ }
2228
+ }
2229
+ const working = typeof input === "object" && input !== null ? deepClone(input) : { ...input };
2230
+ if (working.type == null) working.type = entityType;
2231
+ if (working.version == null) working.version = version;
2232
+ const fromVersion = String(working.version ?? "0.0.0");
2233
+ const toVersion = String(version);
2234
+ if (schemaUpgrade && cmpSemver(fromVersion, toVersion) < 0) {
2235
+ const upgradeRes = applySchemaUpgrade(schemaUpgrade, { ...working }, {
2236
+ from: fromVersion,
2237
+ to: toVersion,
2238
+ entityType,
2239
+ describe: () => schema.describe()
2240
+ });
2241
+ if (!upgradeRes || upgradeRes.ok !== true || !upgradeRes.value) {
2242
+ const errs = upgradeRes?.errors?.length ? upgradeRes.errors : [`Failed to upgrade entity from v${fromVersion} to v${toVersion}`];
2243
+ errors.push(...errs);
2244
+ return { valid: false, errors };
2245
+ }
2246
+ for (const k of Object.keys(working)) delete working[k];
2247
+ Object.assign(working, upgradeRes.value);
2248
+ working.type = entityType;
2249
+ working.version = toVersion;
2250
+ }
2251
+ if (options.identity === "exact" && (working.type !== entityType || working.version !== version)) {
2252
+ return {
2253
+ valid: false,
2254
+ errors: ["Schema type or version does not match the contract."],
2255
+ issues: [
2256
+ {
2257
+ path: "root",
2258
+ code: "schema_identity_mismatch",
2259
+ message: "Schema type or version does not match the contract."
2260
+ }
2261
+ ]
2262
+ };
2263
+ }
2264
+ for (const key in schema._shape) {
2265
+ const def = schema._shape[key];
2266
+ let value = working[key];
2267
+ const existingField = existing && typeof existing === "object" ? existing[key] : void 0;
2268
+ if (!def) {
2269
+ errors.push(`Field definition missing for: ${key}`);
2270
+ continue;
2271
+ }
2272
+ const { value: withDefault, applied } = applyDefault(def, value);
2273
+ if (applied) {
2274
+ working[key] = withDefault;
2275
+ value = withDefault;
2276
+ }
2277
+ const { missing } = checkMissingRequired("", key, value, def, errors);
2278
+ if (missing) continue;
2279
+ const { immutableViolation } = checkImmutable(
2280
+ "",
2281
+ key,
2282
+ value,
2283
+ def,
2284
+ existing,
2285
+ errors
2286
+ );
2287
+ if (immutableViolation) continue;
2288
+ const { shortCircuit } = enforcePIIField(
2289
+ "",
2290
+ key,
2291
+ value,
2292
+ def,
2293
+ piiMode,
2294
+ errors,
2295
+ console
2296
+ );
2297
+ if (shortCircuit) continue;
2298
+ const validateField = (val) => {
2299
+ const localErrors = [];
2300
+ const localIssues = [];
2301
+ const { invalid } = runCustomValidator(
2302
+ "",
2303
+ key,
2304
+ val,
2305
+ def,
2306
+ localErrors,
2307
+ localIssues
2308
+ );
2309
+ if (!invalid) {
2310
+ validateByType("", key, val, def, localErrors, localIssues, existingField, piiMode, console);
2311
+ }
2312
+ issues.push(...localIssues);
2313
+ return localErrors;
2314
+ };
2315
+ let fieldValue = value;
2316
+ let fieldErrors = validateField(fieldValue);
2317
+ const entityFrom = String(working.version ?? "0.0.0");
2318
+ const entityTo = String(version);
2319
+ const fieldTo = String(def._version ?? entityTo);
2320
+ const hasUpgrader = typeof def._upgrade === "function";
2321
+ if (fieldErrors.length > 0 && hasUpgrader && cmpSemver(entityFrom, entityTo) < 0) {
2322
+ const up = def._upgrade;
2323
+ const res = up(fieldValue, {
2324
+ entityFrom,
2325
+ entityTo,
2326
+ fieldTo,
2327
+ fieldName: key
2328
+ });
2329
+ if (res && res.ok) {
2330
+ fieldValue = res.value;
2331
+ fieldErrors = validateField(fieldValue);
2332
+ } else {
2333
+ fieldErrors.push(
2334
+ res?.error || `Failed to upgrade field ${key} from v${entityFrom} to v${entityTo}`
2335
+ );
2336
+ }
2337
+ }
2338
+ result[key] = fieldValue;
2339
+ if (fieldErrors.length !== 0) {
2340
+ errors.push(...fieldErrors);
2341
+ continue;
2342
+ }
2343
+ }
2344
+ if (errors.length === 0 && options.schemaValidator) {
2345
+ const castValue = result;
2346
+ const source = input;
2347
+ const validationContext = Object.freeze({
2348
+ wasProvided: (fieldName) => Object.prototype.hasOwnProperty.call(source, fieldName)
2349
+ });
2350
+ if (!options.schemaValidator(castValue, validationContext)) {
2351
+ errors.push("Schema-level validation failed.");
2352
+ }
2353
+ }
2354
+ return {
2355
+ valid: errors.length === 0,
2356
+ value: result,
2357
+ errors,
2358
+ issues
2359
+ };
2360
+ },
2361
+ // specific validator for a schema to allow conditional validation
2362
+ schemaValidator: options.schemaValidator,
2363
+ // <== expose it here!
2364
+ /**
2365
+ * Runs the optional schema-level upgrade function once, without validating.
2366
+ * Useful for offline migrations or testing migration logic.
2367
+ */
2368
+ upgrade(input, log) {
2369
+ const fromVersion = String(input?.version ?? "0.0.0");
2370
+ const toVersion = String(version);
2371
+ if (!schemaUpgrade || cmpSemver(fromVersion, toVersion) >= 0) {
2372
+ return {
2373
+ ok: true,
2374
+ value: { ...input, type: entityType, version: toVersion }
2375
+ };
2376
+ }
2377
+ const res = applySchemaUpgrade(
2378
+ schemaUpgrade,
2379
+ { ...input },
2380
+ {
2381
+ from: fromVersion,
2382
+ to: toVersion,
2383
+ entityType,
2384
+ describe: () => schema.describe(),
2385
+ log
2386
+ }
2387
+ );
2388
+ if (res && res.ok && res.value) {
2389
+ const out = { ...res.value, type: entityType, version: toVersion };
2390
+ return { ok: true, value: out };
2391
+ }
2392
+ return {
2393
+ ok: false,
2394
+ errors: res?.errors?.length ? res.errors : [`Failed to upgrade entity from v${fromVersion} to v${toVersion}`]
2395
+ };
2396
+ },
2397
+ /**
2398
+ * Recursively validates entity references defined in this schema.
2399
+ *
2400
+ * Traverses fields of type `ref` and arrays of `ref` and resolves each target
2401
+ * entity using the provided `resolveEntity` function. When `autoValidate` is
2402
+ * enabled (default) and the field's `refPolicy` is `eager`, the referenced
2403
+ * entity's schema is fetched and validated via `validateComposition` up to
2404
+ * `maxDepth` levels.
2405
+ *
2406
+ * Skips fields not listed in `onlyFields` when provided. Prevents cycles via
2407
+ * a `visited` set in `validatorContext`.
2408
+ *
2409
+ * @param entity The root entity to validate (must include `type` and `id`).
2410
+ * @param options Options controlling traversal and resolution behavior.
2411
+ * @param options.resolveEntity Function to resolve a referenced entity by type and id.
2412
+ * @param options.validatorContext Internal context (visited set) to prevent cycles.
2413
+ * @param options.maxDepth Maximum depth for recursive validation (default: 5).
2414
+ * @param options.onlyFields Optional whitelist of field names to validate.
2415
+ * @param options.log Optional logger for traversal/debug output.
2416
+ *
2417
+ * @throws Error if a broken reference is encountered (target cannot be resolved).
2418
+ */
2419
+ async validateComposition(entity, options2) {
2420
+ const {
2421
+ resolveEntity,
2422
+ validatorContext = { visited: /* @__PURE__ */ new Set() },
2423
+ maxDepth = 5,
2424
+ log
2425
+ } = options2;
2426
+ const entityKey = `${entity.type}:${entity.id}`;
2427
+ if (validatorContext.visited.has(entityKey)) {
2428
+ log?.(`Skipping already visited entity ${entityKey}`);
2429
+ return;
2430
+ }
2431
+ validatorContext.visited.add(entityKey);
2432
+ log?.(`Validating composition for entity ${entityKey}`);
2433
+ for (const [key, def] of Object.entries(schema._shape)) {
2434
+ if (options2.onlyFields && !options2.onlyFields.includes(key)) {
2435
+ log?.(`Skipping field ${key} (not in onlyFields)`);
2436
+ continue;
2437
+ }
2438
+ const autoValidate = def.autoValidate !== false;
2439
+ const refPolicy = def.refPolicy ?? "eager";
2440
+ const value = entity[key];
2441
+ if (!value) continue;
2442
+ if (def.type === "ref") {
2443
+ const refType = def.refType;
2444
+ if (!refType)
2445
+ throw new Error(`Missing refType for reference field ${key}`);
2446
+ const ref = value;
2447
+ if (ref.type && ref.type !== refType) {
2448
+ throw new Error(
2449
+ `Reference type mismatch for field ${key}: expected ${refType}, got ${ref.type}`
2450
+ );
2451
+ }
2452
+ const target = await options2.resolveEntity(refType, ref.id);
2453
+ if (!target)
2454
+ throw new Error(
2455
+ `Broken reference: ${refType} ${ref.id} in field ${key}`
2456
+ );
2457
+ log?.(`Resolved ${refType} ${ref.id} from field ${key}`);
2458
+ if (autoValidate && refPolicy === "eager") {
2459
+ const targetSchema = getSchemaForType(refType);
2460
+ if (options2.maxDepth > 0 && targetSchema) {
2461
+ await targetSchema.validateComposition(target, {
2462
+ ...options2,
2463
+ maxDepth: options2.maxDepth - 1
2464
+ });
2465
+ }
2466
+ }
2467
+ } else if (def.type === "array" && def.itemType?.type === "ref") {
2468
+ const refType = def.itemType.refType;
2469
+ if (!refType)
2470
+ throw new Error(`Missing refType for reference array field ${key}`);
2471
+ const refs = value;
2472
+ for (const ref of refs) {
2473
+ if (ref.type && ref.type !== refType) {
2474
+ throw new Error(
2475
+ `Reference type mismatch for field ${key}: expected ${refType}, got ${ref.type}`
2476
+ );
2477
+ }
2478
+ const target = await options2.resolveEntity(refType, ref.id);
2479
+ if (!target)
2480
+ throw new Error(
2481
+ `Broken reference: ${refType} ${ref.id} in field ${key}`
2482
+ );
2483
+ log?.(`Resolved ${refType} ${ref.id} from field ${key}`);
2484
+ if (autoValidate && refPolicy === "eager") {
2485
+ const targetSchema = getSchemaForType(refType);
2486
+ if (options2.maxDepth > 0 && targetSchema) {
2487
+ await targetSchema.validateComposition(target, {
2488
+ ...options2,
2489
+ maxDepth: options2.maxDepth - 1
2490
+ });
2491
+ }
2492
+ }
2493
+ }
2494
+ }
2495
+ }
2496
+ },
2497
+ /**
2498
+ * Returns the configured table name for this schema.
2499
+ *
2500
+ * @throws Error if no store/table name has been defined for this schema.
2501
+ */
2502
+ tableName() {
2503
+ if (!store || store === "") {
2504
+ throw new Error("Store is not defined for this schema");
2505
+ }
2506
+ return store;
2507
+ },
2508
+ serialize(input, options2 = {}) {
2509
+ if (typeof input !== "object" || input === null) {
2510
+ return {};
2511
+ }
2512
+ const serialized = {};
2513
+ for (const [key, def] of Object.entries(schema._shape)) {
2514
+ if (!shouldIncludeField(def, options2)) {
2515
+ continue;
2516
+ }
2517
+ if (!Object.prototype.hasOwnProperty.call(input, key)) {
2518
+ continue;
2519
+ }
2520
+ const value = serializeFieldValue(def, input[key], options2);
2521
+ if (value !== void 0) {
2522
+ serialized[key] = value;
2523
+ }
2524
+ }
2525
+ return serialized;
2526
+ },
2527
+ /**
2528
+ * Transforms an input object for persistence by applying PII protection
2529
+ * according to field annotations (e.g., encryption and hashing).
2530
+ *
2531
+ * @param input The raw entity data.
2532
+ * @param encryptFn Function used to encrypt sensitive values.
2533
+ * @param hashFn Function used to hash sensitive values.
2534
+ * @returns A new object safe to store.
2535
+ */
2536
+ prepareForStorage(input, encryptFn, hashFn) {
2537
+ return prepareForStorage(_shape, input, encryptFn, hashFn);
2538
+ },
2539
+ /**
2540
+ * Reverses storage transformations for read paths (e.g., decrypts values)
2541
+ * according to PII annotations, returning a consumer-friendly object.
2542
+ *
2543
+ * @param stored Data retrieved from storage.
2544
+ * @param decryptFn Function used to decrypt values that were encrypted on write.
2545
+ * @returns A new object suitable for application consumption.
2546
+ */
2547
+ prepareForRead(stored, decryptFn) {
2548
+ return prepareForRead(_shape, stored, decryptFn);
2549
+ },
2550
+ /**
2551
+ * Produces a log-safe copy of the provided data by redacting or pseudonymizing
2552
+ * PII fields in accordance with field annotations.
2553
+ *
2554
+ * @param data Arbitrary data to sanitize for logging.
2555
+ * @param pseudonymFn Function producing stable pseudonyms for sensitive values.
2556
+ * @returns A copy safe to emit to logs.
2557
+ */
2558
+ sanitizeForLog(data, pseudonymFn) {
2559
+ return sanitizeForLog(_shape, data, pseudonymFn);
2560
+ },
2561
+ /**
2562
+ * Returns a list of fields annotated with PII metadata for auditing purposes.
2563
+ * Each entry includes classification, required action, and optional log policy.
2564
+ */
2565
+ getPiiAudit() {
2566
+ return getPiiAudit(_shape);
2567
+ },
2568
+ /**
2569
+ * Produces a copy of stored data suitable for data deletion flows by scrubbing
2570
+ * or blanking PII per field annotations.
2571
+ *
2572
+ * @param stored Data as persisted.
2573
+ * @returns A copy with PII removed or neutralized for deletion.
2574
+ */
2575
+ scrubPiiForDelete(stored) {
2576
+ return scrubPiiForDelete(_shape, stored);
2577
+ },
2578
+ /**
2579
+ * Returns a normalized description of the schema suitable for documentation
2580
+ * or UI rendering (type, optionality, enum values, PII flags, etc.).
2581
+ */
2582
+ describe() {
2583
+ const description = {};
2584
+ for (const [key, def] of Object.entries(schema._shape)) {
2585
+ description[key] = {
2586
+ type: def.type,
2587
+ optional: isOptional(def),
2588
+ immutable: !!def.isImmutable,
2589
+ system: !!def.isSystem,
2590
+ description: def._description ?? "",
2591
+ version: def._version ?? "",
2592
+ deprecated: def.deprecated ?? false,
2593
+ deprecatedVersion: def.deprecatedVersion === void 0 ? null : def.deprecatedVersion,
2594
+ enum: getEnumValues(def) ?? null,
2595
+ refType: def.refType ?? null,
2596
+ pii: !def._pii || def._pii.classification === "none" ? null : def._pii,
2597
+ exposure: def._exposure ?? "public"
2598
+ };
2599
+ }
2600
+ return {
2601
+ entityType,
2602
+ version,
2603
+ hasSchemaUpgrade: !!schemaUpgrade,
2604
+ shape: description
2605
+ };
2606
+ }
2607
+ };
2608
+ globalSchemaRegistry.set(entityType, schema);
2609
+ return schema;
2610
+ }
2611
+ function getSchemaForType(type) {
2612
+ return globalSchemaRegistry.get(type);
2613
+ }
2614
+
2615
+ // src/feedback-diagnostics.contract.ts
2616
+ var positiveCountField = (maximum) => field.number().validator((value) => Number.isSafeInteger(value)).min(1).max(maximum);
2617
+ var nestedIdentityShape = () => ({
2618
+ type: field.string().enum(["feedback-game-diagnostics"]),
2619
+ version: field.string().enum([FEEDBACK_CONTRACT_VERSION])
2620
+ });
2621
+ var gameCounterShape = () => ({
2622
+ code: field.string().enum(FEEDBACK_GAME_COUNTER_CODES),
2623
+ count: positiveCountField(FEEDBACK_GAME_COUNTER_MAX_COUNT)
2624
+ });
2625
+ var feedbackGameDiagnosticsShape = (includeIdentity = false) => ({
2626
+ ...includeIdentity ? nestedIdentityShape() : {},
2627
+ surfaceId: field.string().enum(FEEDBACK_GAME_SURFACE_IDS),
2628
+ consentConfirmed: field.boolean(),
2629
+ provenanceContractId: field.string().enum(FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS),
2630
+ renderer: field.string().enum(FEEDBACK_RENDERER_BUCKETS),
2631
+ backend: field.string().enum(FEEDBACK_BACKEND_BUCKETS),
2632
+ viewportBucket: field.string().enum(FEEDBACK_VIEWPORT_BUCKETS),
2633
+ frameRateBucket: field.string().enum(FEEDBACK_FRAME_RATE_BUCKETS),
2634
+ frameTimeBucket: field.string().enum(FEEDBACK_FRAME_TIME_BUCKETS),
2635
+ featureIds: field.array(field.string().enum(FEEDBACK_GAME_FEATURE_IDS)).max(FEEDBACK_GAME_FEATURE_IDS.length),
2636
+ counters: field.array(field.object(gameCounterShape())).max(FEEDBACK_GAME_COUNTER_MAX_ITEMS),
2637
+ errorCodes: field.array(field.string().enum(FEEDBACK_GAME_ERROR_CODES)).max(FEEDBACK_GAME_ERROR_CODES.length)
2638
+ });
2639
+ var isFeedbackGameDiagnosticsConsistent = (value) => {
2640
+ const featureIds = Array.isArray(value.featureIds) ? value.featureIds : [];
2641
+ const errorCodes = Array.isArray(value.errorCodes) ? value.errorCodes : [];
2642
+ const counters = Array.isArray(value.counters) ? value.counters : [];
2643
+ const provenance = FEEDBACK_GAME_PROVENANCE_CONTRACTS.find(
2644
+ ({ surfaceId }) => surfaceId === value.surfaceId
2645
+ );
2646
+ return value.consentConfirmed === true && provenance !== void 0 && provenance.contractId === value.provenanceContractId && new Set(featureIds).size === featureIds.length && new Set(errorCodes).size === errorCodes.length && new Set(
2647
+ counters.map(
2648
+ (counter) => typeof counter === "object" && counter !== null ? counter.code : void 0
2649
+ )
2650
+ ).size === counters.length;
2651
+ };
2652
+ var FeedbackGameDiagnosticsSchema = createSchema(
2653
+ feedbackGameDiagnosticsShape(),
2654
+ "feedback-game-diagnostics",
2655
+ {
2656
+ version: FEEDBACK_CONTRACT_VERSION,
2657
+ piiEnforcement: "strict",
2658
+ unknownFields: "reject",
2659
+ identity: "exact",
2660
+ schemaValidator: isFeedbackGameDiagnosticsConsistent
2661
+ }
2662
+ );
2663
+ var validateFeedbackGameDiagnostics = (input) => {
2664
+ const result = FeedbackGameDiagnosticsSchema.validate(input);
2665
+ if (!result.valid || result.value === void 0) {
2666
+ return {
2667
+ valid: false,
2668
+ errors: result.errors ?? ["Game diagnostics validation failed."],
2669
+ ...result.issues === void 0 ? {} : { issues: result.issues }
2670
+ };
2671
+ }
2672
+ return {
2673
+ valid: true,
2674
+ value: result.value
2675
+ };
2676
+ };
2677
+ // Annotate the CommonJS export names for ESM import in node:
2678
+ 0 && (module.exports = {
2679
+ FEEDBACK_BACKEND_BUCKETS,
2680
+ FEEDBACK_CONTRACT_VERSION,
2681
+ FEEDBACK_FRAME_RATE_BUCKETS,
2682
+ FEEDBACK_FRAME_TIME_BUCKETS,
2683
+ FEEDBACK_GAME_COUNTER_CODES,
2684
+ FEEDBACK_GAME_COUNTER_MAX_COUNT,
2685
+ FEEDBACK_GAME_COUNTER_MAX_ITEMS,
2686
+ FEEDBACK_GAME_ERROR_CODES,
2687
+ FEEDBACK_GAME_FEATURE_IDS,
2688
+ FEEDBACK_GAME_PROVENANCE_CONTRACTS,
2689
+ FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS,
2690
+ FEEDBACK_GAME_SURFACE_IDS,
2691
+ FEEDBACK_RENDERER_BUCKETS,
2692
+ FEEDBACK_VIEWPORT_BUCKETS,
2693
+ FeedbackGameDiagnosticsSchema,
2694
+ validateFeedbackGameDiagnostics
2695
+ });
2696
+ //# sourceMappingURL=feedback-diagnostics.cjs.map