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