@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.
- package/README.md +207 -1
- package/THIRD_PARTY_NOTICES.md +75 -0
- package/dist/chunk-6FE4FLKF.js +254 -0
- package/dist/chunk-6FE4FLKF.js.map +1 -0
- package/dist/chunk-AG2NAPDC.js +117 -0
- package/dist/chunk-AG2NAPDC.js.map +1 -0
- package/dist/chunk-UQHPDQZX.js +2606 -0
- package/dist/chunk-UQHPDQZX.js.map +1 -0
- package/dist/feedback-diagnostics-BaniQAFS.d.cts +353 -0
- package/dist/feedback-diagnostics-CnsI0B9-.d.ts +353 -0
- package/dist/feedback-diagnostics-vocabulary.cjs +156 -0
- package/dist/feedback-diagnostics-vocabulary.cjs.map +1 -0
- package/dist/feedback-diagnostics-vocabulary.d.cts +70 -0
- package/dist/feedback-diagnostics-vocabulary.d.ts +70 -0
- package/dist/feedback-diagnostics-vocabulary.js +33 -0
- package/dist/feedback-diagnostics-vocabulary.js.map +1 -0
- package/dist/feedback-diagnostics.cjs +2696 -0
- package/dist/feedback-diagnostics.cjs.map +1 -0
- package/dist/feedback-diagnostics.d.cts +2 -0
- package/dist/feedback-diagnostics.d.ts +2 -0
- package/dist/feedback-diagnostics.js +39 -0
- package/dist/feedback-diagnostics.js.map +1 -0
- package/dist/feedback-unicode-profile.cjs +283 -0
- package/dist/feedback-unicode-profile.cjs.map +1 -0
- package/dist/feedback-unicode-profile.d.cts +28 -0
- package/dist/feedback-unicode-profile.d.ts +28 -0
- package/dist/feedback-unicode-profile.js +13 -0
- package/dist/feedback-unicode-profile.js.map +1 -0
- package/dist/index.cjs +2258 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +987 -286
- package/dist/index.d.ts +987 -286
- package/dist/index.js +1532 -2197
- package/dist/index.js.map +1 -1
- package/package.json +36 -12
- package/unicode/feedback-unicode-15.1.0-unassigned.json +13 -0
package/dist/index.cjs
CHANGED
|
@@ -20,8 +20,68 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
FEEDBACK_ANALYSIS_MODEL_VERSIONS: () => FEEDBACK_ANALYSIS_MODEL_VERSIONS,
|
|
24
|
+
FEEDBACK_BACKEND_BUCKETS: () => FEEDBACK_BACKEND_BUCKETS,
|
|
25
|
+
FEEDBACK_BUG_COOLDOWN_SECONDS: () => FEEDBACK_BUG_COOLDOWN_SECONDS,
|
|
26
|
+
FEEDBACK_CONTRACT_VERSION: () => FEEDBACK_CONTRACT_VERSION,
|
|
27
|
+
FEEDBACK_FRAME_RATE_BUCKETS: () => FEEDBACK_FRAME_RATE_BUCKETS,
|
|
28
|
+
FEEDBACK_FRAME_TIME_BUCKETS: () => FEEDBACK_FRAME_TIME_BUCKETS,
|
|
29
|
+
FEEDBACK_GAME_COUNTER_CODES: () => FEEDBACK_GAME_COUNTER_CODES,
|
|
30
|
+
FEEDBACK_GAME_COUNTER_MAX_COUNT: () => FEEDBACK_GAME_COUNTER_MAX_COUNT,
|
|
31
|
+
FEEDBACK_GAME_COUNTER_MAX_ITEMS: () => FEEDBACK_GAME_COUNTER_MAX_ITEMS,
|
|
32
|
+
FEEDBACK_GAME_ERROR_CODES: () => FEEDBACK_GAME_ERROR_CODES,
|
|
33
|
+
FEEDBACK_GAME_FEATURE_IDS: () => FEEDBACK_GAME_FEATURE_IDS,
|
|
34
|
+
FEEDBACK_GAME_PROVENANCE_CONTRACTS: () => FEEDBACK_GAME_PROVENANCE_CONTRACTS,
|
|
35
|
+
FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS: () => FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS,
|
|
36
|
+
FEEDBACK_GAME_SURFACE_IDS: () => FEEDBACK_GAME_SURFACE_IDS,
|
|
37
|
+
FEEDBACK_INTENT_IDS: () => FEEDBACK_INTENT_IDS,
|
|
38
|
+
FEEDBACK_ISSUE_TYPES: () => FEEDBACK_ISSUE_TYPES,
|
|
39
|
+
FEEDBACK_PERSISTABLE_ISSUE_TYPES: () => FEEDBACK_PERSISTABLE_ISSUE_TYPES,
|
|
40
|
+
FEEDBACK_PRIVACY_POLICY_VERSIONS: () => FEEDBACK_PRIVACY_POLICY_VERSIONS,
|
|
41
|
+
FEEDBACK_RENDERER_BUCKETS: () => FEEDBACK_RENDERER_BUCKETS,
|
|
42
|
+
FEEDBACK_REVIEW_COOLDOWN_SECONDS: () => FEEDBACK_REVIEW_COOLDOWN_SECONDS,
|
|
43
|
+
FEEDBACK_SATISFACTION_LEVELS: () => FEEDBACK_SATISFACTION_LEVELS,
|
|
44
|
+
FEEDBACK_SENTIMENT_BUCKETS: () => FEEDBACK_SENTIMENT_BUCKETS,
|
|
45
|
+
FEEDBACK_SEVERITY_LEVELS: () => FEEDBACK_SEVERITY_LEVELS,
|
|
46
|
+
FEEDBACK_STRUCTURED_ONLY_REASON_CODES: () => FEEDBACK_STRUCTURED_ONLY_REASON_CODES,
|
|
47
|
+
FEEDBACK_SURFACE_DEFINITIONS: () => FEEDBACK_SURFACE_DEFINITIONS,
|
|
48
|
+
FEEDBACK_SURFACE_IDS: () => FEEDBACK_SURFACE_IDS,
|
|
49
|
+
FEEDBACK_THEME_IDS: () => FEEDBACK_THEME_IDS,
|
|
50
|
+
FEEDBACK_UNICODE_NORMALIZATION_PROFILE: () => FEEDBACK_UNICODE_NORMALIZATION_PROFILE,
|
|
51
|
+
FEEDBACK_VIEWPORT_BUCKETS: () => FEEDBACK_VIEWPORT_BUCKETS,
|
|
52
|
+
FeedbackAcceptanceReceiptSchema: () => FeedbackAcceptanceReceiptSchema,
|
|
53
|
+
FeedbackAnalysisReceiptSchema: () => FeedbackAnalysisReceiptSchema,
|
|
54
|
+
FeedbackBugPacketSchema: () => FeedbackBugPacketSchema,
|
|
55
|
+
FeedbackBugSubmissionRequestSchema: () => FeedbackBugSubmissionRequestSchema,
|
|
56
|
+
FeedbackContextSchema: () => FeedbackContextSchema,
|
|
57
|
+
FeedbackDailySatisfactionReportSchema: () => FeedbackDailySatisfactionReportSchema,
|
|
58
|
+
FeedbackDerivedAnalysisSchema: () => FeedbackDerivedAnalysisSchema,
|
|
59
|
+
FeedbackDraftPacketSchema: () => FeedbackDraftPacketSchema,
|
|
60
|
+
FeedbackDraftReceiptSchema: () => FeedbackDraftReceiptSchema,
|
|
61
|
+
FeedbackDraftUpsertRequestSchema: () => FeedbackDraftUpsertRequestSchema,
|
|
62
|
+
FeedbackEncryptedNarrativeEnvelopeSchema: () => FeedbackEncryptedNarrativeEnvelopeSchema,
|
|
63
|
+
FeedbackGameDiagnosticsSchema: () => FeedbackGameDiagnosticsSchema,
|
|
64
|
+
FeedbackGameReconstructionManifestSchema: () => FeedbackGameReconstructionManifestSchema,
|
|
65
|
+
FeedbackHourlyBugReportSchema: () => FeedbackHourlyBugReportSchema,
|
|
66
|
+
FeedbackMaterializationManifestSchema: () => FeedbackMaterializationManifestSchema,
|
|
67
|
+
FeedbackProcessorCheckpointSchema: () => FeedbackProcessorCheckpointSchema,
|
|
68
|
+
FeedbackPublicSummarySchema: () => FeedbackPublicSummarySchema,
|
|
69
|
+
FeedbackReviewPacketSchema: () => FeedbackReviewPacketSchema,
|
|
70
|
+
FeedbackReviewSubmissionRequestSchema: () => FeedbackReviewSubmissionRequestSchema,
|
|
71
|
+
FeedbackRichTextAstSchema: () => FeedbackRichTextAstSchema,
|
|
72
|
+
FeedbackStructuredOnlyAnalysisResultSchema: () => FeedbackStructuredOnlyAnalysisResultSchema,
|
|
73
|
+
FeedbackSurfaceCatalogSchema: () => FeedbackSurfaceCatalogSchema,
|
|
74
|
+
FeedbackTransientAnalysisRequestSchema: () => FeedbackTransientAnalysisRequestSchema,
|
|
23
75
|
FieldBuilder: () => FieldBuilder,
|
|
24
76
|
IsoLanguageCode: () => IsoLanguageCode,
|
|
77
|
+
PLAYER_SYSTEM_ACHIEVEMENT_TIERS: () => PLAYER_SYSTEM_ACHIEVEMENT_TIERS,
|
|
78
|
+
PLAYER_SYSTEM_ACHIEVEMENT_TRACK_STATUSES: () => PLAYER_SYSTEM_ACHIEVEMENT_TRACK_STATUSES,
|
|
79
|
+
PLAYER_SYSTEM_EVENT_KINDS: () => PLAYER_SYSTEM_EVENT_KINDS,
|
|
80
|
+
PlayerSystemCuratedSnapshotSchema: () => PlayerSystemCuratedSnapshotSchema,
|
|
81
|
+
PlayerSystemNormalizedEventBatchSchema: () => PlayerSystemNormalizedEventBatchSchema,
|
|
82
|
+
PlayerSystemObservedEventBatchSchema: () => PlayerSystemObservedEventBatchSchema,
|
|
83
|
+
SCHEMA_IDENTITY_POLICIES: () => SCHEMA_IDENTITY_POLICIES,
|
|
84
|
+
SCHEMA_UNKNOWN_FIELDS_POLICIES: () => SCHEMA_UNKNOWN_FIELDS_POLICIES,
|
|
25
85
|
createComponentSchema: () => createComponentSchema,
|
|
26
86
|
createSchema: () => createSchema,
|
|
27
87
|
field: () => field,
|
|
@@ -44,7 +104,10 @@ __export(index_exports, {
|
|
|
44
104
|
validateCountryCode: () => validateCountryCode,
|
|
45
105
|
validateCurrencyCode: () => validateCurrencyCode,
|
|
46
106
|
validateDateTimeISO: () => validateDateTimeISO,
|
|
107
|
+
validateDisplayName: () => validateDisplayName,
|
|
47
108
|
validateEmail: () => validateEmail,
|
|
109
|
+
validateFeedbackGameDiagnostics: () => validateFeedbackGameDiagnostics,
|
|
110
|
+
validateFeedbackTransientAnalysisRequest: () => validateFeedbackTransientAnalysisRequest,
|
|
48
111
|
validateLanguage: () => validateLanguage,
|
|
49
112
|
validateName: () => validateName,
|
|
50
113
|
validatePercentage: () => validatePercentage,
|
|
@@ -812,7 +875,7 @@ function validateRichText(value) {
|
|
|
812
875
|
}
|
|
813
876
|
|
|
814
877
|
// src/validation/name.OWASP.ts
|
|
815
|
-
function
|
|
878
|
+
function validateNameLike(value, pattern) {
|
|
816
879
|
if (typeof value !== "string") return false;
|
|
817
880
|
const trimmed = value.trim();
|
|
818
881
|
if (trimmed.length === 0) return false;
|
|
@@ -821,10 +884,15 @@ function validateName(value) {
|
|
|
821
884
|
const cp = ch.codePointAt(0);
|
|
822
885
|
if (cp >= 0 && cp <= 31 || cp === 127) return false;
|
|
823
886
|
}
|
|
824
|
-
|
|
825
|
-
if (!namePattern.test(trimmed)) return false;
|
|
887
|
+
if (!pattern.test(trimmed)) return false;
|
|
826
888
|
return true;
|
|
827
889
|
}
|
|
890
|
+
function validateName(value) {
|
|
891
|
+
return validateNameLike(value, /^[\p{L}\p{M}'\-. ]+$/u);
|
|
892
|
+
}
|
|
893
|
+
function validateDisplayName(value) {
|
|
894
|
+
return validateNameLike(value, /^[\p{L}\p{M}\p{Nd}'\-. ]+$/u);
|
|
895
|
+
}
|
|
828
896
|
|
|
829
897
|
// src/validation/user.MS-GOOGLE-APPLE.ts
|
|
830
898
|
function validateUserId(value) {
|
|
@@ -1216,6 +1284,16 @@ var field = {
|
|
|
1216
1284
|
)
|
|
1217
1285
|
};
|
|
1218
1286
|
|
|
1287
|
+
// src/types.ts
|
|
1288
|
+
var SCHEMA_UNKNOWN_FIELDS_POLICIES = Object.freeze([
|
|
1289
|
+
"strip",
|
|
1290
|
+
"reject"
|
|
1291
|
+
]);
|
|
1292
|
+
var SCHEMA_IDENTITY_POLICIES = Object.freeze([
|
|
1293
|
+
"compatible",
|
|
1294
|
+
"exact"
|
|
1295
|
+
]);
|
|
1296
|
+
|
|
1219
1297
|
// src/pii.ts
|
|
1220
1298
|
function enforcePIIField(parentKey, key, value, def, enforcement = "none", errors, logger) {
|
|
1221
1299
|
const path = parentKey ? `${parentKey}.${key}` : key;
|
|
@@ -1250,6 +1328,9 @@ function prepareForStorage(shape, input, encryptFn, hashFn) {
|
|
|
1250
1328
|
if (!isMissing) out[key] = null;
|
|
1251
1329
|
return;
|
|
1252
1330
|
}
|
|
1331
|
+
if (isMissing && def.isRequired === false) {
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1253
1334
|
if (def.type === "object" && def._shape) {
|
|
1254
1335
|
const obj = {};
|
|
1255
1336
|
for (const [childKey, childDef] of Object.entries(def._shape)) {
|
|
@@ -1295,6 +1376,7 @@ function prepareForRead(shape, stored, decryptFn) {
|
|
|
1295
1376
|
const readValue = (def, key, container, ctx = {}) => {
|
|
1296
1377
|
if (!def) return container?.[key];
|
|
1297
1378
|
const action = def._pii?.action;
|
|
1379
|
+
const hasValue = container !== void 0 && container !== null && Object.prototype.hasOwnProperty.call(container, key);
|
|
1298
1380
|
if (action === "encrypt") {
|
|
1299
1381
|
const enc = container?.[key + "Encrypted"] ?? container?.[`${ctx.parentKey ?? key}Encrypted`];
|
|
1300
1382
|
return enc === void 0 ? void 0 : decryptFn(enc);
|
|
@@ -1305,11 +1387,17 @@ function prepareForRead(shape, stored, decryptFn) {
|
|
|
1305
1387
|
}
|
|
1306
1388
|
if (action === "clear")
|
|
1307
1389
|
return container?.hasOwnProperty(key) ? null : void 0;
|
|
1390
|
+
if (!hasValue && def.isRequired === false) {
|
|
1391
|
+
return void 0;
|
|
1392
|
+
}
|
|
1308
1393
|
if (def.type === "object" && def._shape) {
|
|
1309
1394
|
const obj = {};
|
|
1310
1395
|
const source = container && Object.prototype.hasOwnProperty.call(container, key) ? container[key] : container ?? {};
|
|
1311
1396
|
for (const [childKey, childDef] of Object.entries(def._shape)) {
|
|
1312
|
-
|
|
1397
|
+
const child = readValue(childDef, childKey, source);
|
|
1398
|
+
if (child !== void 0) {
|
|
1399
|
+
obj[childKey] = child;
|
|
1400
|
+
}
|
|
1313
1401
|
}
|
|
1314
1402
|
return obj;
|
|
1315
1403
|
}
|
|
@@ -1325,7 +1413,10 @@ function prepareForRead(shape, stored, decryptFn) {
|
|
|
1325
1413
|
const refShape = def._shape;
|
|
1326
1414
|
if (refShape) {
|
|
1327
1415
|
for (const [childKey, childDef] of Object.entries(refShape)) {
|
|
1328
|
-
|
|
1416
|
+
const child = readValue(childDef, childKey, ref);
|
|
1417
|
+
if (child !== void 0) {
|
|
1418
|
+
ref[childKey] = child;
|
|
1419
|
+
}
|
|
1329
1420
|
}
|
|
1330
1421
|
}
|
|
1331
1422
|
return ref;
|
|
@@ -1334,13 +1425,19 @@ function prepareForRead(shape, stored, decryptFn) {
|
|
|
1334
1425
|
};
|
|
1335
1426
|
const result = {};
|
|
1336
1427
|
for (const key in shape) {
|
|
1337
|
-
|
|
1428
|
+
const value = readValue(shape[key], key, stored);
|
|
1429
|
+
if (value !== void 0) {
|
|
1430
|
+
result[key] = value;
|
|
1431
|
+
}
|
|
1338
1432
|
}
|
|
1339
1433
|
return result;
|
|
1340
1434
|
}
|
|
1341
1435
|
function sanitizeForLog(shape, data, pseudonymFn) {
|
|
1342
1436
|
const visit = (def, value, ctx = {}) => {
|
|
1343
1437
|
if (!def) return void 0;
|
|
1438
|
+
if ((value === void 0 || value === null) && def.isRequired === false) {
|
|
1439
|
+
return void 0;
|
|
1440
|
+
}
|
|
1344
1441
|
const handling = def._pii?.logHandling;
|
|
1345
1442
|
if (handling === "omit") return void 0;
|
|
1346
1443
|
if (handling === "redact") return "[REDACTED]";
|
|
@@ -1385,18 +1482,37 @@ function sanitizeForLog(shape, data, pseudonymFn) {
|
|
|
1385
1482
|
}
|
|
1386
1483
|
function getPiiAudit(shape) {
|
|
1387
1484
|
const piiFields = [];
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
if (!def) continue;
|
|
1485
|
+
const visit = (def, path) => {
|
|
1486
|
+
if (!def) return;
|
|
1391
1487
|
if (def._pii && def._pii.classification !== "none") {
|
|
1392
1488
|
piiFields.push({
|
|
1393
|
-
field:
|
|
1489
|
+
field: path,
|
|
1394
1490
|
classification: def._pii.classification,
|
|
1395
1491
|
action: def._pii.action,
|
|
1396
1492
|
logHandling: def._pii.logHandling,
|
|
1397
1493
|
purpose: def._pii.purpose
|
|
1398
1494
|
});
|
|
1399
1495
|
}
|
|
1496
|
+
if ((def.type === "object" || def.type === "ref") && def._shape) {
|
|
1497
|
+
for (const [childKey, childDef] of Object.entries(def._shape)) {
|
|
1498
|
+
visit(childDef, `${path}.${childKey}`);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
if (def.type === "array" && def.itemType) {
|
|
1502
|
+
const itemPath = `${path}[]`;
|
|
1503
|
+
if ((def.itemType.type === "object" || def.itemType.type === "ref") && def.itemType._shape) {
|
|
1504
|
+
for (const [childKey, childDef] of Object.entries(
|
|
1505
|
+
def.itemType._shape
|
|
1506
|
+
)) {
|
|
1507
|
+
visit(childDef, `${itemPath}.${childKey}`);
|
|
1508
|
+
}
|
|
1509
|
+
} else {
|
|
1510
|
+
visit(def.itemType, itemPath);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
for (const key in shape) {
|
|
1515
|
+
visit(shape[key], key);
|
|
1400
1516
|
}
|
|
1401
1517
|
return piiFields;
|
|
1402
1518
|
}
|
|
@@ -1629,9 +1745,7 @@ function validateEnum(parentKey, value, enumValues) {
|
|
|
1629
1745
|
if (Array.isArray(value)) {
|
|
1630
1746
|
const invalid = value.filter((v) => !values.includes(v));
|
|
1631
1747
|
if (invalid.length > 0) {
|
|
1632
|
-
return `Field ${parentKey}
|
|
1633
|
-
", "
|
|
1634
|
-
)}`;
|
|
1748
|
+
return `Field ${parentKey} must contain only: ${values.join(", ")}`;
|
|
1635
1749
|
}
|
|
1636
1750
|
} else {
|
|
1637
1751
|
if (!values.includes(value)) {
|
|
@@ -1665,6 +1779,126 @@ function getValidator(def) {
|
|
|
1665
1779
|
function getShape(def) {
|
|
1666
1780
|
return def?._shape ?? def?.shape ?? void 0;
|
|
1667
1781
|
}
|
|
1782
|
+
var MAX_STRICT_VALIDATION_NODES = 1e4;
|
|
1783
|
+
var MAX_STRICT_VALIDATION_DEPTH = 256;
|
|
1784
|
+
var MAX_STRICT_VALIDATION_OBJECTS = MAX_STRICT_VALIDATION_NODES;
|
|
1785
|
+
function strictInputIsWithinComplexityLimit(input) {
|
|
1786
|
+
const pending = [
|
|
1787
|
+
{ value: input, depth: 0 }
|
|
1788
|
+
];
|
|
1789
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1790
|
+
let visitedNodes = 0;
|
|
1791
|
+
try {
|
|
1792
|
+
while (pending.length > 0) {
|
|
1793
|
+
const current = pending.pop();
|
|
1794
|
+
if (!current) {
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1797
|
+
visitedNodes += 1;
|
|
1798
|
+
if (visitedNodes > MAX_STRICT_VALIDATION_NODES || current.depth > MAX_STRICT_VALIDATION_DEPTH) {
|
|
1799
|
+
return false;
|
|
1800
|
+
}
|
|
1801
|
+
if (typeof current.value !== "object" || current.value === null) {
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
if (seen.has(current.value)) {
|
|
1805
|
+
return false;
|
|
1806
|
+
}
|
|
1807
|
+
seen.add(current.value);
|
|
1808
|
+
const keys = Object.keys(current.value);
|
|
1809
|
+
if (visitedNodes + pending.length + keys.length > MAX_STRICT_VALIDATION_NODES) {
|
|
1810
|
+
return false;
|
|
1811
|
+
}
|
|
1812
|
+
const record = current.value;
|
|
1813
|
+
for (const key of keys) {
|
|
1814
|
+
pending.push({
|
|
1815
|
+
value: record[key],
|
|
1816
|
+
depth: current.depth + 1
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
} catch {
|
|
1821
|
+
return false;
|
|
1822
|
+
}
|
|
1823
|
+
return true;
|
|
1824
|
+
}
|
|
1825
|
+
function strictReferenceShape(def) {
|
|
1826
|
+
return {
|
|
1827
|
+
type: { type: "string" },
|
|
1828
|
+
id: { type: "string" },
|
|
1829
|
+
...getShape(def) ?? {}
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
function unknownFieldLocations(value, shape, location = "root", state = {
|
|
1833
|
+
remainingObjects: MAX_STRICT_VALIDATION_OBJECTS,
|
|
1834
|
+
limitExceeded: false
|
|
1835
|
+
}) {
|
|
1836
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1837
|
+
return [];
|
|
1838
|
+
}
|
|
1839
|
+
if (state.remainingObjects <= 0) {
|
|
1840
|
+
state.limitExceeded = true;
|
|
1841
|
+
return [];
|
|
1842
|
+
}
|
|
1843
|
+
state.remainingObjects -= 1;
|
|
1844
|
+
const record = value;
|
|
1845
|
+
const knownKeys = new Set(Object.keys(shape));
|
|
1846
|
+
const locations = /* @__PURE__ */ new Set();
|
|
1847
|
+
if (Object.keys(record).some((key) => !knownKeys.has(key))) {
|
|
1848
|
+
locations.add(location);
|
|
1849
|
+
}
|
|
1850
|
+
for (const [key, def] of Object.entries(shape)) {
|
|
1851
|
+
const child = record[key];
|
|
1852
|
+
if (child === void 0 || child === null) {
|
|
1853
|
+
continue;
|
|
1854
|
+
}
|
|
1855
|
+
const childLocation = location === "root" ? key : `${location}.${key}`;
|
|
1856
|
+
const childShape = getShape(def);
|
|
1857
|
+
if (def?.type === "object" && childShape) {
|
|
1858
|
+
for (const nestedLocation of unknownFieldLocations(
|
|
1859
|
+
child,
|
|
1860
|
+
childShape,
|
|
1861
|
+
childLocation,
|
|
1862
|
+
state
|
|
1863
|
+
)) {
|
|
1864
|
+
locations.add(nestedLocation);
|
|
1865
|
+
}
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
const itemType = def?.itemType;
|
|
1869
|
+
if (def?.type === "array" && (itemType?.type === "object" || itemType?.type === "ref") && Array.isArray(child)) {
|
|
1870
|
+
const itemShape = itemType.type === "ref" ? strictReferenceShape(itemType) : getShape(itemType);
|
|
1871
|
+
if (!itemShape) {
|
|
1872
|
+
continue;
|
|
1873
|
+
}
|
|
1874
|
+
for (const [index, item] of child.entries()) {
|
|
1875
|
+
if (state.limitExceeded) {
|
|
1876
|
+
break;
|
|
1877
|
+
}
|
|
1878
|
+
for (const nestedLocation of unknownFieldLocations(
|
|
1879
|
+
item,
|
|
1880
|
+
itemShape,
|
|
1881
|
+
`${childLocation}[${index}]`,
|
|
1882
|
+
state
|
|
1883
|
+
)) {
|
|
1884
|
+
locations.add(nestedLocation);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
1889
|
+
if (def?.type === "ref") {
|
|
1890
|
+
for (const nestedLocation of unknownFieldLocations(
|
|
1891
|
+
child,
|
|
1892
|
+
strictReferenceShape(def),
|
|
1893
|
+
childLocation,
|
|
1894
|
+
state
|
|
1895
|
+
)) {
|
|
1896
|
+
locations.add(nestedLocation);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
return [...locations];
|
|
1901
|
+
}
|
|
1668
1902
|
function normalizeValidationIssue(path, issue, defaultCode) {
|
|
1669
1903
|
if (typeof issue === "string") {
|
|
1670
1904
|
return {
|
|
@@ -2125,6 +2359,8 @@ function createSchema(_shape, entityType, options = {
|
|
|
2125
2359
|
table: "",
|
|
2126
2360
|
schemaValidator: () => true,
|
|
2127
2361
|
piiEnforcement: "none",
|
|
2362
|
+
unknownFields: SCHEMA_UNKNOWN_FIELDS_POLICIES[0],
|
|
2363
|
+
identity: SCHEMA_IDENTITY_POLICIES[0],
|
|
2128
2364
|
schemaUpgrade: void 0
|
|
2129
2365
|
}) {
|
|
2130
2366
|
const systemFields = {
|
|
@@ -2151,6 +2387,57 @@ function createSchema(_shape, entityType, options = {
|
|
|
2151
2387
|
if (typeof input !== "object" || input === null) {
|
|
2152
2388
|
return { valid: false, errors: ["Input must be an object"] };
|
|
2153
2389
|
}
|
|
2390
|
+
if (options.unknownFields === "reject") {
|
|
2391
|
+
if (!strictInputIsWithinComplexityLimit(input)) {
|
|
2392
|
+
return {
|
|
2393
|
+
valid: false,
|
|
2394
|
+
errors: ["Strict validation complexity limit exceeded."],
|
|
2395
|
+
issues: [
|
|
2396
|
+
{
|
|
2397
|
+
path: "root",
|
|
2398
|
+
code: "validation_complexity_limit",
|
|
2399
|
+
message: "Strict validation complexity limit exceeded."
|
|
2400
|
+
}
|
|
2401
|
+
]
|
|
2402
|
+
};
|
|
2403
|
+
}
|
|
2404
|
+
const scanState = {
|
|
2405
|
+
remainingObjects: MAX_STRICT_VALIDATION_OBJECTS,
|
|
2406
|
+
limitExceeded: false
|
|
2407
|
+
};
|
|
2408
|
+
const locations = unknownFieldLocations(
|
|
2409
|
+
input,
|
|
2410
|
+
schema._shape,
|
|
2411
|
+
"root",
|
|
2412
|
+
scanState
|
|
2413
|
+
);
|
|
2414
|
+
if (scanState.limitExceeded) {
|
|
2415
|
+
return {
|
|
2416
|
+
valid: false,
|
|
2417
|
+
errors: ["Strict validation complexity limit exceeded."],
|
|
2418
|
+
issues: [
|
|
2419
|
+
{
|
|
2420
|
+
path: "root",
|
|
2421
|
+
code: "validation_complexity_limit",
|
|
2422
|
+
message: "Strict validation complexity limit exceeded."
|
|
2423
|
+
}
|
|
2424
|
+
]
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
if (locations.length > 0) {
|
|
2428
|
+
return {
|
|
2429
|
+
valid: false,
|
|
2430
|
+
errors: locations.map(
|
|
2431
|
+
(location) => `Unknown fields are not allowed at ${location}.`
|
|
2432
|
+
),
|
|
2433
|
+
issues: locations.map((location) => ({
|
|
2434
|
+
path: location,
|
|
2435
|
+
code: "unknown_fields",
|
|
2436
|
+
message: `Unknown fields are not allowed at ${location}.`
|
|
2437
|
+
}))
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2154
2441
|
const working = typeof input === "object" && input !== null ? deepClone(input) : { ...input };
|
|
2155
2442
|
if (working.type == null) working.type = entityType;
|
|
2156
2443
|
if (working.version == null) working.version = version;
|
|
@@ -2173,6 +2460,19 @@ function createSchema(_shape, entityType, options = {
|
|
|
2173
2460
|
working.type = entityType;
|
|
2174
2461
|
working.version = toVersion;
|
|
2175
2462
|
}
|
|
2463
|
+
if (options.identity === "exact" && (working.type !== entityType || working.version !== version)) {
|
|
2464
|
+
return {
|
|
2465
|
+
valid: false,
|
|
2466
|
+
errors: ["Schema type or version does not match the contract."],
|
|
2467
|
+
issues: [
|
|
2468
|
+
{
|
|
2469
|
+
path: "root",
|
|
2470
|
+
code: "schema_identity_mismatch",
|
|
2471
|
+
message: "Schema type or version does not match the contract."
|
|
2472
|
+
}
|
|
2473
|
+
]
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2176
2476
|
for (const key in schema._shape) {
|
|
2177
2477
|
const def = schema._shape[key];
|
|
2178
2478
|
let value = working[key];
|
|
@@ -2255,7 +2555,11 @@ function createSchema(_shape, entityType, options = {
|
|
|
2255
2555
|
}
|
|
2256
2556
|
if (errors.length === 0 && options.schemaValidator) {
|
|
2257
2557
|
const castValue = result;
|
|
2258
|
-
|
|
2558
|
+
const source = input;
|
|
2559
|
+
const validationContext = Object.freeze({
|
|
2560
|
+
wasProvided: (fieldName) => Object.prototype.hasOwnProperty.call(source, fieldName)
|
|
2561
|
+
});
|
|
2562
|
+
if (!options.schemaValidator(castValue, validationContext)) {
|
|
2259
2563
|
errors.push("Schema-level validation failed.");
|
|
2260
2564
|
}
|
|
2261
2565
|
}
|
|
@@ -2559,10 +2863,1947 @@ function getComponentSchema(type) {
|
|
|
2559
2863
|
function getAllComponentSchemas() {
|
|
2560
2864
|
return Array.from(componentSchemaRegistry.entries());
|
|
2561
2865
|
}
|
|
2866
|
+
|
|
2867
|
+
// src/player-system-events.ts
|
|
2868
|
+
var PLAYER_SYSTEM_EVENT_KINDS = [
|
|
2869
|
+
"combat",
|
|
2870
|
+
"social",
|
|
2871
|
+
"mission",
|
|
2872
|
+
"crafting",
|
|
2873
|
+
"world"
|
|
2874
|
+
];
|
|
2875
|
+
var PLAYER_SYSTEM_ACHIEVEMENT_TIERS = [
|
|
2876
|
+
"bronze",
|
|
2877
|
+
"silver",
|
|
2878
|
+
"gold"
|
|
2879
|
+
];
|
|
2880
|
+
var PLAYER_SYSTEM_ACHIEVEMENT_TRACK_STATUSES = [
|
|
2881
|
+
"tracking",
|
|
2882
|
+
"earned"
|
|
2883
|
+
];
|
|
2884
|
+
var playerIdField = () => field.string().min(1).max(128).PID({
|
|
2885
|
+
classification: "low",
|
|
2886
|
+
action: "none",
|
|
2887
|
+
logHandling: "pseudonym",
|
|
2888
|
+
purpose: "a stable player-scoped storage identifier"
|
|
2889
|
+
}).description(
|
|
2890
|
+
"Stable player identifier; the only personal identifier retained in event projections"
|
|
2891
|
+
);
|
|
2892
|
+
var eventIdField = () => field.string().min(1).max(128);
|
|
2893
|
+
var eventKindField = () => field.string().enum(PLAYER_SYSTEM_EVENT_KINDS);
|
|
2894
|
+
var eventTitleField = () => field.generalText().min(1).max(120).description("Player-visible event title");
|
|
2895
|
+
var eventSummaryField = () => field.generalText().min(1).max(320).description("Player-visible event summary");
|
|
2896
|
+
var observedEventFields = (defaultHighlighted = true) => ({
|
|
2897
|
+
id: eventIdField(),
|
|
2898
|
+
playerId: playerIdField(),
|
|
2899
|
+
occurredAt: field.dateTimeISO(),
|
|
2900
|
+
kind: eventKindField(),
|
|
2901
|
+
title: eventTitleField(),
|
|
2902
|
+
summary: eventSummaryField(),
|
|
2903
|
+
highlighted: defaultHighlighted ? field.boolean().default(false) : field.boolean()
|
|
2904
|
+
});
|
|
2905
|
+
var eventHighlightFields = () => ({
|
|
2906
|
+
id: eventIdField(),
|
|
2907
|
+
title: eventTitleField(),
|
|
2908
|
+
summary: eventSummaryField(),
|
|
2909
|
+
occurredAt: field.dateTimeISO(),
|
|
2910
|
+
kind: eventKindField()
|
|
2911
|
+
});
|
|
2912
|
+
var eventEntryFields = () => ({
|
|
2913
|
+
id: eventIdField(),
|
|
2914
|
+
title: eventTitleField(),
|
|
2915
|
+
summary: eventSummaryField(),
|
|
2916
|
+
occurredAt: field.dateTimeISO(),
|
|
2917
|
+
kind: eventKindField(),
|
|
2918
|
+
highlighted: field.boolean()
|
|
2919
|
+
});
|
|
2920
|
+
var achievementFields = () => ({
|
|
2921
|
+
id: eventIdField(),
|
|
2922
|
+
title: eventTitleField(),
|
|
2923
|
+
tier: field.string().enum(PLAYER_SYSTEM_ACHIEVEMENT_TIERS),
|
|
2924
|
+
progressLabel: eventSummaryField(),
|
|
2925
|
+
earnedAt: field.dateTimeISO().optional()
|
|
2926
|
+
});
|
|
2927
|
+
var achievementTrackFields = () => ({
|
|
2928
|
+
id: eventIdField(),
|
|
2929
|
+
title: eventTitleField(),
|
|
2930
|
+
current: field.number().min(0).max(1e3),
|
|
2931
|
+
target: field.number().min(1).max(1e3),
|
|
2932
|
+
status: field.string().enum(PLAYER_SYSTEM_ACHIEVEMENT_TRACK_STATUSES)
|
|
2933
|
+
});
|
|
2934
|
+
var PlayerSystemObservedEventBatchSchema = createSchema(
|
|
2935
|
+
{
|
|
2936
|
+
source: field.string().min(1).max(120),
|
|
2937
|
+
emittedAt: field.dateTimeISO(),
|
|
2938
|
+
events: field.array(field.object(observedEventFields()))
|
|
2939
|
+
},
|
|
2940
|
+
"player-system-observed-event-batch",
|
|
2941
|
+
{
|
|
2942
|
+
version: "1.0.0",
|
|
2943
|
+
piiEnforcement: "strict"
|
|
2944
|
+
}
|
|
2945
|
+
);
|
|
2946
|
+
var PlayerSystemNormalizedEventBatchSchema = createSchema(
|
|
2947
|
+
{
|
|
2948
|
+
sourceBlobName: field.string().min(1).max(512),
|
|
2949
|
+
processedAt: field.dateTimeISO(),
|
|
2950
|
+
events: field.array(
|
|
2951
|
+
field.object({
|
|
2952
|
+
...observedEventFields(false),
|
|
2953
|
+
deterministicKey: field.string().min(1).max(256)
|
|
2954
|
+
})
|
|
2955
|
+
)
|
|
2956
|
+
},
|
|
2957
|
+
"player-system-normalized-event-batch",
|
|
2958
|
+
{
|
|
2959
|
+
version: "1.0.0",
|
|
2960
|
+
piiEnforcement: "strict"
|
|
2961
|
+
}
|
|
2962
|
+
);
|
|
2963
|
+
var PlayerSystemCuratedSnapshotSchema = createSchema(
|
|
2964
|
+
{
|
|
2965
|
+
playerId: playerIdField(),
|
|
2966
|
+
projectedAt: field.dateTimeISO(),
|
|
2967
|
+
lastObservedAt: field.dateTimeISO(),
|
|
2968
|
+
highlights: field.array(field.object(eventHighlightFields())),
|
|
2969
|
+
events: field.array(field.object(eventEntryFields())),
|
|
2970
|
+
achievements: field.array(field.object(achievementFields())),
|
|
2971
|
+
tracks: field.array(field.object(achievementTrackFields()))
|
|
2972
|
+
},
|
|
2973
|
+
"player-system-curated-snapshot",
|
|
2974
|
+
{
|
|
2975
|
+
version: "1.0.0",
|
|
2976
|
+
piiEnforcement: "strict"
|
|
2977
|
+
}
|
|
2978
|
+
);
|
|
2979
|
+
|
|
2980
|
+
// src/feedback-contract-version.ts
|
|
2981
|
+
var FEEDBACK_CONTRACT_VERSION = "1.0.0";
|
|
2982
|
+
|
|
2983
|
+
// src/feedback-diagnostics-vocabulary.ts
|
|
2984
|
+
var FEEDBACK_RENDERER_BUCKETS = [
|
|
2985
|
+
"webgl2",
|
|
2986
|
+
"webgpu",
|
|
2987
|
+
"canvas2d",
|
|
2988
|
+
"unknown"
|
|
2989
|
+
];
|
|
2990
|
+
var FEEDBACK_BACKEND_BUCKETS = [
|
|
2991
|
+
"browser",
|
|
2992
|
+
"worker",
|
|
2993
|
+
"unknown"
|
|
2994
|
+
];
|
|
2995
|
+
var FEEDBACK_GAME_SURFACE_IDS = [
|
|
2996
|
+
"site.generator",
|
|
2997
|
+
"site.gpu-demo"
|
|
2998
|
+
];
|
|
2999
|
+
var FEEDBACK_GAME_PROVENANCE_CONTRACTS = [
|
|
3000
|
+
{
|
|
3001
|
+
surfaceId: "site.generator",
|
|
3002
|
+
contractId: "generator.renderer-diagnostics.v1"
|
|
3003
|
+
},
|
|
3004
|
+
{
|
|
3005
|
+
surfaceId: "site.gpu-demo",
|
|
3006
|
+
contractId: "gpu-demo.renderer-diagnostics.v1"
|
|
3007
|
+
}
|
|
3008
|
+
];
|
|
3009
|
+
var FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS = [
|
|
3010
|
+
"generator.renderer-diagnostics.v1",
|
|
3011
|
+
"gpu-demo.renderer-diagnostics.v1"
|
|
3012
|
+
];
|
|
3013
|
+
var FEEDBACK_GAME_FEATURE_IDS = [
|
|
3014
|
+
"renderer.initialisation",
|
|
3015
|
+
"renderer.frame-loop",
|
|
3016
|
+
"renderer.asset-loading",
|
|
3017
|
+
"renderer.input",
|
|
3018
|
+
"renderer.scene-generation",
|
|
3019
|
+
"renderer.post-processing"
|
|
3020
|
+
];
|
|
3021
|
+
var FEEDBACK_GAME_COUNTER_CODES = [
|
|
3022
|
+
"frame-drop",
|
|
3023
|
+
"device-loss",
|
|
3024
|
+
"asset-load-failure",
|
|
3025
|
+
"shader-failure",
|
|
3026
|
+
"fallback-activation"
|
|
3027
|
+
];
|
|
3028
|
+
var FEEDBACK_GAME_COUNTER_MAX_ITEMS = 32;
|
|
3029
|
+
var FEEDBACK_GAME_COUNTER_MAX_COUNT = 1e4;
|
|
3030
|
+
var FEEDBACK_GAME_ERROR_CODES = [
|
|
3031
|
+
"renderer.initialisation-failed",
|
|
3032
|
+
"renderer.device-lost",
|
|
3033
|
+
"renderer.asset-load-failed",
|
|
3034
|
+
"renderer.frame-budget-exceeded",
|
|
3035
|
+
"renderer.shader-failed",
|
|
3036
|
+
"renderer.unknown"
|
|
3037
|
+
];
|
|
3038
|
+
var FEEDBACK_VIEWPORT_BUCKETS = [
|
|
3039
|
+
"small-portrait",
|
|
3040
|
+
"small-landscape",
|
|
3041
|
+
"medium-portrait",
|
|
3042
|
+
"medium-landscape",
|
|
3043
|
+
"large-portrait",
|
|
3044
|
+
"large-landscape",
|
|
3045
|
+
"unknown"
|
|
3046
|
+
];
|
|
3047
|
+
var FEEDBACK_FRAME_RATE_BUCKETS = [
|
|
3048
|
+
"under-15",
|
|
3049
|
+
"15-29",
|
|
3050
|
+
"30-59",
|
|
3051
|
+
"60-plus",
|
|
3052
|
+
"unknown"
|
|
3053
|
+
];
|
|
3054
|
+
var FEEDBACK_FRAME_TIME_BUCKETS = [
|
|
3055
|
+
"under-17ms",
|
|
3056
|
+
"17-33ms",
|
|
3057
|
+
"34-66ms",
|
|
3058
|
+
"over-66ms",
|
|
3059
|
+
"unknown"
|
|
3060
|
+
];
|
|
3061
|
+
for (const vocabulary of [
|
|
3062
|
+
FEEDBACK_RENDERER_BUCKETS,
|
|
3063
|
+
FEEDBACK_BACKEND_BUCKETS,
|
|
3064
|
+
FEEDBACK_GAME_SURFACE_IDS,
|
|
3065
|
+
FEEDBACK_GAME_PROVENANCE_CONTRACTS,
|
|
3066
|
+
FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS,
|
|
3067
|
+
FEEDBACK_GAME_FEATURE_IDS,
|
|
3068
|
+
FEEDBACK_GAME_COUNTER_CODES,
|
|
3069
|
+
FEEDBACK_GAME_ERROR_CODES,
|
|
3070
|
+
FEEDBACK_VIEWPORT_BUCKETS,
|
|
3071
|
+
FEEDBACK_FRAME_RATE_BUCKETS,
|
|
3072
|
+
FEEDBACK_FRAME_TIME_BUCKETS
|
|
3073
|
+
]) {
|
|
3074
|
+
Object.freeze(vocabulary);
|
|
3075
|
+
}
|
|
3076
|
+
for (const entry of FEEDBACK_GAME_PROVENANCE_CONTRACTS) {
|
|
3077
|
+
Object.freeze(entry);
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
// src/feedback-diagnostics.contract.ts
|
|
3081
|
+
var positiveCountField = (maximum) => field.number().validator((value) => Number.isSafeInteger(value)).min(1).max(maximum);
|
|
3082
|
+
var nestedIdentityShape = () => ({
|
|
3083
|
+
type: field.string().enum(["feedback-game-diagnostics"]),
|
|
3084
|
+
version: field.string().enum([FEEDBACK_CONTRACT_VERSION])
|
|
3085
|
+
});
|
|
3086
|
+
var gameCounterShape = () => ({
|
|
3087
|
+
code: field.string().enum(FEEDBACK_GAME_COUNTER_CODES),
|
|
3088
|
+
count: positiveCountField(FEEDBACK_GAME_COUNTER_MAX_COUNT)
|
|
3089
|
+
});
|
|
3090
|
+
var feedbackGameDiagnosticsShape = (includeIdentity = false) => ({
|
|
3091
|
+
...includeIdentity ? nestedIdentityShape() : {},
|
|
3092
|
+
surfaceId: field.string().enum(FEEDBACK_GAME_SURFACE_IDS),
|
|
3093
|
+
consentConfirmed: field.boolean(),
|
|
3094
|
+
provenanceContractId: field.string().enum(FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS),
|
|
3095
|
+
renderer: field.string().enum(FEEDBACK_RENDERER_BUCKETS),
|
|
3096
|
+
backend: field.string().enum(FEEDBACK_BACKEND_BUCKETS),
|
|
3097
|
+
viewportBucket: field.string().enum(FEEDBACK_VIEWPORT_BUCKETS),
|
|
3098
|
+
frameRateBucket: field.string().enum(FEEDBACK_FRAME_RATE_BUCKETS),
|
|
3099
|
+
frameTimeBucket: field.string().enum(FEEDBACK_FRAME_TIME_BUCKETS),
|
|
3100
|
+
featureIds: field.array(field.string().enum(FEEDBACK_GAME_FEATURE_IDS)).max(FEEDBACK_GAME_FEATURE_IDS.length),
|
|
3101
|
+
counters: field.array(field.object(gameCounterShape())).max(FEEDBACK_GAME_COUNTER_MAX_ITEMS),
|
|
3102
|
+
errorCodes: field.array(field.string().enum(FEEDBACK_GAME_ERROR_CODES)).max(FEEDBACK_GAME_ERROR_CODES.length)
|
|
3103
|
+
});
|
|
3104
|
+
var isFeedbackGameDiagnosticsConsistent = (value) => {
|
|
3105
|
+
const featureIds = Array.isArray(value.featureIds) ? value.featureIds : [];
|
|
3106
|
+
const errorCodes = Array.isArray(value.errorCodes) ? value.errorCodes : [];
|
|
3107
|
+
const counters = Array.isArray(value.counters) ? value.counters : [];
|
|
3108
|
+
const provenance = FEEDBACK_GAME_PROVENANCE_CONTRACTS.find(
|
|
3109
|
+
({ surfaceId }) => surfaceId === value.surfaceId
|
|
3110
|
+
);
|
|
3111
|
+
return value.consentConfirmed === true && provenance !== void 0 && provenance.contractId === value.provenanceContractId && new Set(featureIds).size === featureIds.length && new Set(errorCodes).size === errorCodes.length && new Set(
|
|
3112
|
+
counters.map(
|
|
3113
|
+
(counter) => typeof counter === "object" && counter !== null ? counter.code : void 0
|
|
3114
|
+
)
|
|
3115
|
+
).size === counters.length;
|
|
3116
|
+
};
|
|
3117
|
+
var FeedbackGameDiagnosticsSchema = createSchema(
|
|
3118
|
+
feedbackGameDiagnosticsShape(),
|
|
3119
|
+
"feedback-game-diagnostics",
|
|
3120
|
+
{
|
|
3121
|
+
version: FEEDBACK_CONTRACT_VERSION,
|
|
3122
|
+
piiEnforcement: "strict",
|
|
3123
|
+
unknownFields: "reject",
|
|
3124
|
+
identity: "exact",
|
|
3125
|
+
schemaValidator: isFeedbackGameDiagnosticsConsistent
|
|
3126
|
+
}
|
|
3127
|
+
);
|
|
3128
|
+
var validateFeedbackGameDiagnostics = (input) => {
|
|
3129
|
+
const result = FeedbackGameDiagnosticsSchema.validate(input);
|
|
3130
|
+
if (!result.valid || result.value === void 0) {
|
|
3131
|
+
return {
|
|
3132
|
+
valid: false,
|
|
3133
|
+
errors: result.errors ?? ["Game diagnostics validation failed."],
|
|
3134
|
+
...result.issues === void 0 ? {} : { issues: result.issues }
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
3137
|
+
return {
|
|
3138
|
+
valid: true,
|
|
3139
|
+
value: result.value
|
|
3140
|
+
};
|
|
3141
|
+
};
|
|
3142
|
+
|
|
3143
|
+
// unicode/feedback-unicode-15.1.0-unassigned.json
|
|
3144
|
+
var feedback_unicode_15_1_0_unassigned_default = {
|
|
3145
|
+
profileId: "unicode-15.1.0-nfkc-v1",
|
|
3146
|
+
unicodeVersion: "15.1.0",
|
|
3147
|
+
sourcePackage: "@unicode/unicode-15.1.0",
|
|
3148
|
+
sourcePackageVersion: "1.6.17",
|
|
3149
|
+
sourceProperty: "General_Category/Unassigned",
|
|
3150
|
+
rangeEncoding: "flat-half-open-delta-uleb128-base64url-v1",
|
|
3151
|
+
rangeCount: 707,
|
|
3152
|
+
endpointCount: 1414,
|
|
3153
|
+
codePointCount: 824718,
|
|
3154
|
+
endpointSha256: "591e457524d6b4b988aa7c7687e76c95a78370fea38bbd1b09085be7fd935ef3",
|
|
3155
|
+
payload: "-AYCBgQHAQEBFAGNAwEmAjICAwE3CBsEBguOAgE8AmUOOwIxAg8BHAIBAQsFHwECBuwBAQgCAgIWAQcBAQMEAgkCAgIECAEEAgEFAhkCAwEGBAICFgEHAQIBAgECAgEBBQQCAgMDAQcEAQEHEQoDAQkBAwEWAQcBAgEFAgoBAwEDAgEPBAIMBwcBAwEIAgICFgEHAQIBBQIJAgICAwcDBAIBBQISCgIBBgMDAQQDAgEBAQIDAgMDAwwEBQMDAQQCAQYBDhUFDQEDARcBEAIJAQMBBAcCAQMCAQIEAgoHFgEDARcBCgEFAgkBAwEEBwIGAgEEAgoBAwwNAQMBMwEDAQYEEAIaAQMBEgMYAQkBAQIHAwEEBgEBAQgGCgIDDDoEHSUCAQEBBQEYAQEBFwIFAQEBBwEKAgQgSAEkBCcBJAEPAQ0lxgEBAQUBAvkCAQQCBwEBAQQCKQEEAiEBBAIHAQEBBAIPATkBBAJDAiADGgZWAgYCnQUDWQcWCRgJFAwNAQMBAgxeAgoGCgYaBlkHKwVGCh8BDAQMBAEDKgIFCywEGgYLAz4CQQEdAgsGCgYOAh8xTQMvAXQIPAMPAzwHKwILCCsFlgQCBgImAgYCCAEBAQEBAQEfAjUBDwEOAgYBEwIDAQkBZQEMAhsBDQMhDyEPjAEElwUZCxWUDgIgAd0CBS0BAQUBAjgHAg4YCQcBBwEHAQcBBwEHAQcBBwF-IhoBWQzWARpQAVYCZwUrAV4BVAswAe3kAQM3CdwCFLgBCMsBBQIBAQEFGDsDCgY4CEYIDAZ0Cx4DTgELBCEBNwkOAgoCZxgcCgYCBgIGCQcBBwE8BH4CCgakVwwXBDEE7kQCaiYHDAUFGgEFAQEBAgECAX0QvQMCNgcBICoGMwETAQQEBQGHAQIBAb4BAwYCBgIGAgMDBwEHCgUCDAEaARMBAgEPAg4iewUDBC0DWAENAwEvLoIBHQMxDxwEJAkeBSsFHgElBA4qngECCgYkBCQEKAg0CwwBDwEHAQIBCwEPAQcBAkO3AgkWCggYBgEqAQlFBgIBASwBAgMBAhcBSAgJMBMBAgUhAxsFAUA4BBQCMgECBQgBAwEdAgMECgcJB0AgJwQMCTYDHQIbBRoHBAwHUEk3Mw0zBy4ICqYCHwEqAQMCAksrCCoWGiYcFBcJTgQkCUQKAQIZBwoGNQESCCcJYAEUCxIBLz4HAQEBBAEPAQsGOwUKBgQBCAICAhYBBwECAQUBCgICAgMCAQYBBQcCBwMFiwFcAQUeSAgKpgE2AiYiRQsKBg0TOgYKNhsCDwQXuQE8ZFMMCAIBAggBAgEeAQICDAkKRggCLgILG0gIUw1JBwr2AQkBLQEOCh0DIAIWAQ5JBwECASwDAQECAQkICgYGAQIBJQECAQYHCrYCGQcRASkDHFYBDzINmwdmbwEFC8QBzBRjDdYIqh_HBLlDuQQHHwEKBFEBCgYeAgYKRgoKAQcBFQUTsAVbZUsEOQcRQAULAg74LwjWCSoJ50UEAQcBAgGjAg8BHQMCAQ4ECIwDhBJrBQ0DCQcKAgjcJC4CFwl0PPYBCicCwgEVRnoUDBQMVwkZhwFVAUcBAgIBAgICBAEMAQEBBwFBAQQCCAEHARwBBAEFAQEDBwHUAgKkAgK-BQ8FAQ_QCB8GBtUBBwERAgcBAgEFBT4hAXAtAw4CCgQCwAIfEToFAdADKuYFBwEEAQIBDwHFAQIQKUwECgQCkQZETD3CAQQBGwECAQECAQEKAQQBAQEBBgEEAQEBAQEBAwECAQECAQEBAQEBAQEBAQIBAQIEAQcBBAEEAQEBCgERBQMBBQERNAKOAiwEZAwPAg8BDwElCq4BOB0NLAQJBwIOBpoB2AcEEQMNA3cEXwYMBAEPDAQ4CAoGKAgeAgJO1AIMDgINAwkHLgEHCA4ECQcJB5MBATclCoYI4M0CILogBt4BAoItDrE6D-4EohOeBOILyyYF4CDRuCsBHmCAAfABkPwD_v8DAv7_AwI"
|
|
3156
|
+
};
|
|
3157
|
+
|
|
3158
|
+
// src/feedback-unicode-profile.codec.ts
|
|
3159
|
+
var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
3160
|
+
var BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
3161
|
+
var MAX_UNICODE_ENDPOINT = 1114112;
|
|
3162
|
+
var MAX_ENCODED_PAYLOAD_CHARACTERS = 8192;
|
|
3163
|
+
var MAX_ULEB128_BYTES = 5;
|
|
3164
|
+
var MAX_PROFILE_RANGE_COUNT = 707;
|
|
3165
|
+
var MAX_PROFILE_ENDPOINT_COUNT = MAX_PROFILE_RANGE_COUNT * 2;
|
|
3166
|
+
var MAX_PROFILE_CODE_POINT_COUNT = MAX_UNICODE_ENDPOINT;
|
|
3167
|
+
var ARTIFACT_KEYS = /* @__PURE__ */ new Set([
|
|
3168
|
+
"profileId",
|
|
3169
|
+
"unicodeVersion",
|
|
3170
|
+
"sourcePackage",
|
|
3171
|
+
"sourcePackageVersion",
|
|
3172
|
+
"sourceProperty",
|
|
3173
|
+
"rangeEncoding",
|
|
3174
|
+
"rangeCount",
|
|
3175
|
+
"endpointCount",
|
|
3176
|
+
"codePointCount",
|
|
3177
|
+
"endpointSha256",
|
|
3178
|
+
"payload"
|
|
3179
|
+
]);
|
|
3180
|
+
function isRecord(value) {
|
|
3181
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3182
|
+
}
|
|
3183
|
+
function hasExpectedMetadata(artifact, expected) {
|
|
3184
|
+
return Object.keys(artifact).length === ARTIFACT_KEYS.size && Object.keys(artifact).every((key) => ARTIFACT_KEYS.has(key)) && artifact.profileId === expected.profileId && artifact.unicodeVersion === expected.unicodeVersion && artifact.sourcePackage === expected.sourcePackage && artifact.sourcePackageVersion === expected.sourcePackageVersion && artifact.sourceProperty === expected.sourceProperty && artifact.rangeEncoding === expected.rangeEncoding && artifact.rangeCount === expected.rangeCount && artifact.endpointCount === expected.endpointCount && artifact.codePointCount === expected.codePointCount && artifact.endpointSha256 === expected.endpointSha256;
|
|
3185
|
+
}
|
|
3186
|
+
function isBoundedPositiveInteger(value, maximum) {
|
|
3187
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= maximum;
|
|
3188
|
+
}
|
|
3189
|
+
function hasBoundedCounts(artifact, expected) {
|
|
3190
|
+
return isBoundedPositiveInteger(
|
|
3191
|
+
artifact.rangeCount,
|
|
3192
|
+
MAX_PROFILE_RANGE_COUNT
|
|
3193
|
+
) && isBoundedPositiveInteger(
|
|
3194
|
+
expected.rangeCount,
|
|
3195
|
+
MAX_PROFILE_RANGE_COUNT
|
|
3196
|
+
) && isBoundedPositiveInteger(
|
|
3197
|
+
artifact.endpointCount,
|
|
3198
|
+
MAX_PROFILE_ENDPOINT_COUNT
|
|
3199
|
+
) && isBoundedPositiveInteger(
|
|
3200
|
+
expected.endpointCount,
|
|
3201
|
+
MAX_PROFILE_ENDPOINT_COUNT
|
|
3202
|
+
) && artifact.endpointCount === artifact.rangeCount * 2 && expected.endpointCount === expected.rangeCount * 2 && isBoundedPositiveInteger(
|
|
3203
|
+
artifact.codePointCount,
|
|
3204
|
+
MAX_PROFILE_CODE_POINT_COUNT
|
|
3205
|
+
) && isBoundedPositiveInteger(
|
|
3206
|
+
expected.codePointCount,
|
|
3207
|
+
MAX_PROFILE_CODE_POINT_COUNT
|
|
3208
|
+
);
|
|
3209
|
+
}
|
|
3210
|
+
function decodeCanonicalBase64Url(value) {
|
|
3211
|
+
if (value.length === 0 || value.length > MAX_ENCODED_PAYLOAD_CHARACTERS || value.length % 4 === 1 || !BASE64URL_PATTERN.test(value)) {
|
|
3212
|
+
return void 0;
|
|
3213
|
+
}
|
|
3214
|
+
const finalSextet = BASE64URL_ALPHABET.indexOf(value.at(-1) ?? "");
|
|
3215
|
+
if (finalSextet < 0 || value.length % 4 === 2 && (finalSextet & 15) !== 0 || value.length % 4 === 3 && (finalSextet & 3) !== 0) {
|
|
3216
|
+
return void 0;
|
|
3217
|
+
}
|
|
3218
|
+
const expectedByteCount = Math.floor(value.length * 6 / 8);
|
|
3219
|
+
const output = new Uint8Array(expectedByteCount);
|
|
3220
|
+
let outputIndex = 0;
|
|
3221
|
+
let accumulator = 0;
|
|
3222
|
+
let bitCount = 0;
|
|
3223
|
+
for (const character of value) {
|
|
3224
|
+
const sextet = BASE64URL_ALPHABET.indexOf(character);
|
|
3225
|
+
if (sextet < 0) {
|
|
3226
|
+
return void 0;
|
|
3227
|
+
}
|
|
3228
|
+
accumulator = accumulator << 6 | sextet;
|
|
3229
|
+
bitCount += 6;
|
|
3230
|
+
while (bitCount >= 8) {
|
|
3231
|
+
bitCount -= 8;
|
|
3232
|
+
output[outputIndex] = accumulator >> bitCount & 255;
|
|
3233
|
+
outputIndex += 1;
|
|
3234
|
+
accumulator &= (1 << bitCount) - 1;
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
if (outputIndex !== expectedByteCount || bitCount > 0 && accumulator !== 0) {
|
|
3238
|
+
return void 0;
|
|
3239
|
+
}
|
|
3240
|
+
return output;
|
|
3241
|
+
}
|
|
3242
|
+
function decodeCanonicalEndpointDeltas(bytes, endpointCount) {
|
|
3243
|
+
if (!isBoundedPositiveInteger(
|
|
3244
|
+
endpointCount,
|
|
3245
|
+
MAX_PROFILE_ENDPOINT_COUNT
|
|
3246
|
+
) || endpointCount % 2 !== 0) {
|
|
3247
|
+
return void 0;
|
|
3248
|
+
}
|
|
3249
|
+
const endpoints = new Uint32Array(endpointCount);
|
|
3250
|
+
let byteOffset = 0;
|
|
3251
|
+
let previousEndpoint = 0;
|
|
3252
|
+
for (let endpointIndex = 0; endpointIndex < endpointCount; endpointIndex += 1) {
|
|
3253
|
+
let delta = 0;
|
|
3254
|
+
let multiplier = 1;
|
|
3255
|
+
let byteCount = 0;
|
|
3256
|
+
while (true) {
|
|
3257
|
+
if (byteOffset >= bytes.length || byteCount >= MAX_ULEB128_BYTES) {
|
|
3258
|
+
return void 0;
|
|
3259
|
+
}
|
|
3260
|
+
const byte = bytes[byteOffset];
|
|
3261
|
+
if (byte === void 0) {
|
|
3262
|
+
return void 0;
|
|
3263
|
+
}
|
|
3264
|
+
byteOffset += 1;
|
|
3265
|
+
byteCount += 1;
|
|
3266
|
+
const payload = byte & 127;
|
|
3267
|
+
delta += payload * multiplier;
|
|
3268
|
+
if (!Number.isSafeInteger(delta)) {
|
|
3269
|
+
return void 0;
|
|
3270
|
+
}
|
|
3271
|
+
if ((byte & 128) === 0) {
|
|
3272
|
+
if (byteCount > 1 && payload === 0) {
|
|
3273
|
+
return void 0;
|
|
3274
|
+
}
|
|
3275
|
+
break;
|
|
3276
|
+
}
|
|
3277
|
+
multiplier *= 128;
|
|
3278
|
+
}
|
|
3279
|
+
const endpoint = previousEndpoint + delta;
|
|
3280
|
+
if (delta <= 0 || !Number.isSafeInteger(endpoint) || endpoint > MAX_UNICODE_ENDPOINT) {
|
|
3281
|
+
return void 0;
|
|
3282
|
+
}
|
|
3283
|
+
endpoints[endpointIndex] = endpoint;
|
|
3284
|
+
previousEndpoint = endpoint;
|
|
3285
|
+
}
|
|
3286
|
+
if (byteOffset !== bytes.length) {
|
|
3287
|
+
return void 0;
|
|
3288
|
+
}
|
|
3289
|
+
return endpoints;
|
|
3290
|
+
}
|
|
3291
|
+
function hasExpectedRanges(endpoints, expected) {
|
|
3292
|
+
let previousEnd = -1;
|
|
3293
|
+
let codePointCount = 0;
|
|
3294
|
+
for (let index = 0; index < endpoints.length; index += 2) {
|
|
3295
|
+
const start = endpoints[index];
|
|
3296
|
+
const end = endpoints[index + 1];
|
|
3297
|
+
if (start === void 0 || end === void 0 || start <= previousEnd || end <= start || end > MAX_UNICODE_ENDPOINT) {
|
|
3298
|
+
return false;
|
|
3299
|
+
}
|
|
3300
|
+
codePointCount += end - start;
|
|
3301
|
+
previousEnd = end;
|
|
3302
|
+
}
|
|
3303
|
+
return codePointCount === expected.codePointCount;
|
|
3304
|
+
}
|
|
3305
|
+
function decodeFeedbackUnicodeProfileArtifact(value, expected) {
|
|
3306
|
+
if (!isRecord(value) || !hasBoundedCounts(value, expected) || !hasExpectedMetadata(value, expected) || typeof value.payload !== "string") {
|
|
3307
|
+
return void 0;
|
|
3308
|
+
}
|
|
3309
|
+
const bytes = decodeCanonicalBase64Url(value.payload);
|
|
3310
|
+
if (bytes === void 0) {
|
|
3311
|
+
return void 0;
|
|
3312
|
+
}
|
|
3313
|
+
const endpoints = decodeCanonicalEndpointDeltas(
|
|
3314
|
+
bytes,
|
|
3315
|
+
expected.endpointCount
|
|
3316
|
+
);
|
|
3317
|
+
return endpoints !== void 0 && hasExpectedRanges(endpoints, expected) ? endpoints : void 0;
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
// src/feedback-unicode-profile.ts
|
|
3321
|
+
var FEEDBACK_UNICODE_PROFILE_ID = "unicode-15.1.0-nfkc-v1";
|
|
3322
|
+
var FEEDBACK_UNICODE_PROFILE_ENDPOINT_SHA256 = "591e457524d6b4b988aa7c7687e76c95a78370fea38bbd1b09085be7fd935ef3";
|
|
3323
|
+
var MAX_UNICODE_CODE_POINT = 1114111;
|
|
3324
|
+
var HIGH_SURROGATE_START = 55296;
|
|
3325
|
+
var HIGH_SURROGATE_END = 56319;
|
|
3326
|
+
var LOW_SURROGATE_START = 56320;
|
|
3327
|
+
var LOW_SURROGATE_END = 57343;
|
|
3328
|
+
var UNASSIGNED_RANGE_ENDPOINTS = decodeFeedbackUnicodeProfileArtifact(
|
|
3329
|
+
feedback_unicode_15_1_0_unassigned_default,
|
|
3330
|
+
{
|
|
3331
|
+
profileId: FEEDBACK_UNICODE_PROFILE_ID,
|
|
3332
|
+
unicodeVersion: "15.1.0",
|
|
3333
|
+
sourcePackage: "@unicode/unicode-15.1.0",
|
|
3334
|
+
sourcePackageVersion: "1.6.17",
|
|
3335
|
+
sourceProperty: "General_Category/Unassigned",
|
|
3336
|
+
rangeEncoding: "flat-half-open-delta-uleb128-base64url-v1",
|
|
3337
|
+
rangeCount: 707,
|
|
3338
|
+
endpointCount: 1414,
|
|
3339
|
+
codePointCount: 824718,
|
|
3340
|
+
endpointSha256: FEEDBACK_UNICODE_PROFILE_ENDPOINT_SHA256
|
|
3341
|
+
}
|
|
3342
|
+
);
|
|
3343
|
+
function isFeedbackUnicodeProfileUnassigned(codePoint) {
|
|
3344
|
+
if (UNASSIGNED_RANGE_ENDPOINTS === void 0 || !Number.isInteger(codePoint) || codePoint < 0 || codePoint > MAX_UNICODE_CODE_POINT || codePoint >= HIGH_SURROGATE_START && codePoint <= LOW_SURROGATE_END) {
|
|
3345
|
+
return true;
|
|
3346
|
+
}
|
|
3347
|
+
let lowerRange = 0;
|
|
3348
|
+
let upperRange = UNASSIGNED_RANGE_ENDPOINTS.length / 2 - 1;
|
|
3349
|
+
while (lowerRange <= upperRange) {
|
|
3350
|
+
const rangeIndex = Math.floor((lowerRange + upperRange) / 2);
|
|
3351
|
+
const start = UNASSIGNED_RANGE_ENDPOINTS[rangeIndex * 2];
|
|
3352
|
+
const end = UNASSIGNED_RANGE_ENDPOINTS[rangeIndex * 2 + 1];
|
|
3353
|
+
if (start === void 0 || end === void 0) {
|
|
3354
|
+
return true;
|
|
3355
|
+
}
|
|
3356
|
+
if (codePoint < start) {
|
|
3357
|
+
upperRange = rangeIndex - 1;
|
|
3358
|
+
} else if (codePoint >= end) {
|
|
3359
|
+
lowerRange = rangeIndex + 1;
|
|
3360
|
+
} else {
|
|
3361
|
+
return true;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
return false;
|
|
3365
|
+
}
|
|
3366
|
+
function containsFeedbackUnicodeProfileUnsupportedText(text) {
|
|
3367
|
+
if (typeof text !== "string") {
|
|
3368
|
+
return true;
|
|
3369
|
+
}
|
|
3370
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
3371
|
+
const first = text.charCodeAt(index);
|
|
3372
|
+
let codePoint = first;
|
|
3373
|
+
if (first >= HIGH_SURROGATE_START && first <= HIGH_SURROGATE_END) {
|
|
3374
|
+
const second = text.charCodeAt(index + 1);
|
|
3375
|
+
if (!Number.isInteger(second) || second < LOW_SURROGATE_START || second > LOW_SURROGATE_END) {
|
|
3376
|
+
return true;
|
|
3377
|
+
}
|
|
3378
|
+
codePoint = 65536 + (first - HIGH_SURROGATE_START << 10) + (second - LOW_SURROGATE_START);
|
|
3379
|
+
index += 1;
|
|
3380
|
+
} else if (first >= LOW_SURROGATE_START && first <= LOW_SURROGATE_END) {
|
|
3381
|
+
return true;
|
|
3382
|
+
}
|
|
3383
|
+
if (isFeedbackUnicodeProfileUnassigned(codePoint)) {
|
|
3384
|
+
return true;
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
return false;
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
// src/feedback.ts
|
|
3391
|
+
var FEEDBACK_UNICODE_NORMALIZATION_PROFILE = FEEDBACK_UNICODE_PROFILE_ID;
|
|
3392
|
+
var FEEDBACK_BUG_COOLDOWN_SECONDS = Object.freeze([
|
|
3393
|
+
5 * 60,
|
|
3394
|
+
15 * 60,
|
|
3395
|
+
60 * 60,
|
|
3396
|
+
6 * 60 * 60,
|
|
3397
|
+
24 * 60 * 60
|
|
3398
|
+
]);
|
|
3399
|
+
var FEEDBACK_REVIEW_COOLDOWN_SECONDS = 30 * 24 * 60 * 60;
|
|
3400
|
+
var FEEDBACK_ISSUE_TYPES = [
|
|
3401
|
+
"visual-layout",
|
|
3402
|
+
"functionality",
|
|
3403
|
+
"gameplay",
|
|
3404
|
+
"performance-stability",
|
|
3405
|
+
"accessibility",
|
|
3406
|
+
"content-localisation",
|
|
3407
|
+
"account-access",
|
|
3408
|
+
"privacy-security",
|
|
3409
|
+
"other"
|
|
3410
|
+
];
|
|
3411
|
+
var FEEDBACK_PERSISTABLE_ISSUE_TYPES = FEEDBACK_ISSUE_TYPES.filter(
|
|
3412
|
+
(issueType) => issueType !== "privacy-security"
|
|
3413
|
+
);
|
|
3414
|
+
var FEEDBACK_SEVERITY_LEVELS = [
|
|
3415
|
+
{ value: 1, id: "cosmetic", labelKey: "feedback.severity.cosmetic" },
|
|
3416
|
+
{ value: 2, id: "minor", labelKey: "feedback.severity.minor" },
|
|
3417
|
+
{ value: 3, id: "disruptive", labelKey: "feedback.severity.disruptive" },
|
|
3418
|
+
{ value: 4, id: "blocking", labelKey: "feedback.severity.blocking" },
|
|
3419
|
+
{ value: 5, id: "critical", labelKey: "feedback.severity.critical" }
|
|
3420
|
+
];
|
|
3421
|
+
var FEEDBACK_SATISFACTION_LEVELS = [
|
|
3422
|
+
{ value: 1, id: "very-poor", labelKey: "feedback.satisfaction.very-poor" },
|
|
3423
|
+
{ value: 2, id: "poor", labelKey: "feedback.satisfaction.poor" },
|
|
3424
|
+
{ value: 3, id: "fair", labelKey: "feedback.satisfaction.fair" },
|
|
3425
|
+
{ value: 4, id: "good", labelKey: "feedback.satisfaction.good" },
|
|
3426
|
+
{ value: 5, id: "excellent", labelKey: "feedback.satisfaction.excellent" }
|
|
3427
|
+
];
|
|
3428
|
+
var FEEDBACK_SENTIMENT_BUCKETS = [
|
|
3429
|
+
"very-negative",
|
|
3430
|
+
"negative",
|
|
3431
|
+
"mixed",
|
|
3432
|
+
"neutral",
|
|
3433
|
+
"positive",
|
|
3434
|
+
"very-positive",
|
|
3435
|
+
"not-detected"
|
|
3436
|
+
];
|
|
3437
|
+
var FEEDBACK_INTENT_IDS = [
|
|
3438
|
+
"visual-layout",
|
|
3439
|
+
"functionality",
|
|
3440
|
+
"gameplay",
|
|
3441
|
+
"performance-stability",
|
|
3442
|
+
"accessibility",
|
|
3443
|
+
"content-localisation",
|
|
3444
|
+
"account-access",
|
|
3445
|
+
"other",
|
|
3446
|
+
"praise",
|
|
3447
|
+
"suggestion",
|
|
3448
|
+
"confusion",
|
|
3449
|
+
"frustration"
|
|
3450
|
+
];
|
|
3451
|
+
var FEEDBACK_THEME_IDS = [
|
|
3452
|
+
"navigation",
|
|
3453
|
+
"visual-design",
|
|
3454
|
+
"game-controls",
|
|
3455
|
+
"rendering",
|
|
3456
|
+
"responsiveness",
|
|
3457
|
+
"stability",
|
|
3458
|
+
"accessibility",
|
|
3459
|
+
"localisation",
|
|
3460
|
+
"account-access",
|
|
3461
|
+
"overall-experience",
|
|
3462
|
+
"other"
|
|
3463
|
+
];
|
|
3464
|
+
var FEEDBACK_SURFACE_IDS = [
|
|
3465
|
+
"site.about",
|
|
3466
|
+
"admin.analytics",
|
|
3467
|
+
"admin.capabilities",
|
|
3468
|
+
"admin.feature-flags",
|
|
3469
|
+
"admin.feedback",
|
|
3470
|
+
"admin.moderation",
|
|
3471
|
+
"admin.users",
|
|
3472
|
+
"site.chatbot",
|
|
3473
|
+
"site.generator",
|
|
3474
|
+
"site.gpu-demo",
|
|
3475
|
+
"site.home",
|
|
3476
|
+
"game.player-system",
|
|
3477
|
+
"account.profile",
|
|
3478
|
+
"site.video"
|
|
3479
|
+
];
|
|
3480
|
+
var FEEDBACK_SURFACE_DEFINITIONS = [
|
|
3481
|
+
{
|
|
3482
|
+
id: "site.about",
|
|
3483
|
+
labelKey: "feedback.surface.about",
|
|
3484
|
+
kind: "public",
|
|
3485
|
+
gameDiagnosticsEligible: false
|
|
3486
|
+
},
|
|
3487
|
+
{
|
|
3488
|
+
id: "admin.analytics",
|
|
3489
|
+
labelKey: "feedback.surface.adminAnalytics",
|
|
3490
|
+
kind: "admin",
|
|
3491
|
+
gameDiagnosticsEligible: false
|
|
3492
|
+
},
|
|
3493
|
+
{
|
|
3494
|
+
id: "admin.capabilities",
|
|
3495
|
+
labelKey: "feedback.surface.adminCapabilities",
|
|
3496
|
+
kind: "admin",
|
|
3497
|
+
gameDiagnosticsEligible: false
|
|
3498
|
+
},
|
|
3499
|
+
{
|
|
3500
|
+
id: "admin.feature-flags",
|
|
3501
|
+
labelKey: "feedback.surface.adminFeatureFlags",
|
|
3502
|
+
kind: "admin",
|
|
3503
|
+
gameDiagnosticsEligible: false
|
|
3504
|
+
},
|
|
3505
|
+
{
|
|
3506
|
+
id: "admin.feedback",
|
|
3507
|
+
labelKey: "feedback.surface.adminFeedback",
|
|
3508
|
+
kind: "admin",
|
|
3509
|
+
gameDiagnosticsEligible: false
|
|
3510
|
+
},
|
|
3511
|
+
{
|
|
3512
|
+
id: "admin.moderation",
|
|
3513
|
+
labelKey: "feedback.surface.adminModeration",
|
|
3514
|
+
kind: "admin",
|
|
3515
|
+
gameDiagnosticsEligible: false
|
|
3516
|
+
},
|
|
3517
|
+
{
|
|
3518
|
+
id: "admin.users",
|
|
3519
|
+
labelKey: "feedback.surface.adminUsers",
|
|
3520
|
+
kind: "admin",
|
|
3521
|
+
gameDiagnosticsEligible: false
|
|
3522
|
+
},
|
|
3523
|
+
{
|
|
3524
|
+
id: "site.chatbot",
|
|
3525
|
+
labelKey: "feedback.surface.chatbot",
|
|
3526
|
+
kind: "public",
|
|
3527
|
+
gameDiagnosticsEligible: false
|
|
3528
|
+
},
|
|
3529
|
+
{
|
|
3530
|
+
id: "site.generator",
|
|
3531
|
+
labelKey: "feedback.surface.generator",
|
|
3532
|
+
kind: "game",
|
|
3533
|
+
gameDiagnosticsEligible: true
|
|
3534
|
+
},
|
|
3535
|
+
{
|
|
3536
|
+
id: "site.gpu-demo",
|
|
3537
|
+
labelKey: "feedback.surface.gpuDemo",
|
|
3538
|
+
kind: "game",
|
|
3539
|
+
gameDiagnosticsEligible: true
|
|
3540
|
+
},
|
|
3541
|
+
{
|
|
3542
|
+
id: "site.home",
|
|
3543
|
+
labelKey: "feedback.surface.home",
|
|
3544
|
+
kind: "public",
|
|
3545
|
+
gameDiagnosticsEligible: false
|
|
3546
|
+
},
|
|
3547
|
+
{
|
|
3548
|
+
id: "game.player-system",
|
|
3549
|
+
labelKey: "feedback.surface.playerSystem",
|
|
3550
|
+
kind: "game",
|
|
3551
|
+
gameDiagnosticsEligible: false
|
|
3552
|
+
},
|
|
3553
|
+
{
|
|
3554
|
+
id: "account.profile",
|
|
3555
|
+
labelKey: "feedback.surface.profile",
|
|
3556
|
+
kind: "authenticated",
|
|
3557
|
+
gameDiagnosticsEligible: false
|
|
3558
|
+
},
|
|
3559
|
+
{
|
|
3560
|
+
id: "site.video",
|
|
3561
|
+
labelKey: "feedback.surface.video",
|
|
3562
|
+
kind: "public",
|
|
3563
|
+
gameDiagnosticsEligible: false
|
|
3564
|
+
}
|
|
3565
|
+
];
|
|
3566
|
+
var FEEDBACK_STRUCTURED_ONLY_REASON_CODES = [
|
|
3567
|
+
"analysis-unavailable",
|
|
3568
|
+
"invalid-envelope",
|
|
3569
|
+
"invalid-sensitive-payload",
|
|
3570
|
+
"language-not-enabled",
|
|
3571
|
+
"request-too-large",
|
|
3572
|
+
"length-required"
|
|
3573
|
+
];
|
|
3574
|
+
var FEEDBACK_PRIVACY_POLICY_VERSIONS = [
|
|
3575
|
+
"feedback-privacy-en-v1"
|
|
3576
|
+
];
|
|
3577
|
+
var FEEDBACK_ANALYSIS_MODEL_VERSIONS = [
|
|
3578
|
+
"feedback-en-rules-v1"
|
|
3579
|
+
];
|
|
3580
|
+
for (const vocabulary of [
|
|
3581
|
+
FEEDBACK_BUG_COOLDOWN_SECONDS,
|
|
3582
|
+
FEEDBACK_ISSUE_TYPES,
|
|
3583
|
+
FEEDBACK_PERSISTABLE_ISSUE_TYPES,
|
|
3584
|
+
FEEDBACK_SEVERITY_LEVELS,
|
|
3585
|
+
FEEDBACK_SATISFACTION_LEVELS,
|
|
3586
|
+
FEEDBACK_SENTIMENT_BUCKETS,
|
|
3587
|
+
FEEDBACK_INTENT_IDS,
|
|
3588
|
+
FEEDBACK_THEME_IDS,
|
|
3589
|
+
FEEDBACK_SURFACE_IDS,
|
|
3590
|
+
FEEDBACK_SURFACE_DEFINITIONS,
|
|
3591
|
+
FEEDBACK_STRUCTURED_ONLY_REASON_CODES,
|
|
3592
|
+
FEEDBACK_PRIVACY_POLICY_VERSIONS,
|
|
3593
|
+
FEEDBACK_ANALYSIS_MODEL_VERSIONS
|
|
3594
|
+
]) {
|
|
3595
|
+
Object.freeze(vocabulary);
|
|
3596
|
+
}
|
|
3597
|
+
for (const entry of [
|
|
3598
|
+
...FEEDBACK_SEVERITY_LEVELS,
|
|
3599
|
+
...FEEDBACK_SATISFACTION_LEVELS,
|
|
3600
|
+
...FEEDBACK_SURFACE_DEFINITIONS
|
|
3601
|
+
]) {
|
|
3602
|
+
Object.freeze(entry);
|
|
3603
|
+
}
|
|
3604
|
+
var SAFE_ID_PATTERN = /^[a-z0-9][a-z0-9._:-]*$/i;
|
|
3605
|
+
var INGESTION_KEY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
3606
|
+
var BASE64URL_PATTERN2 = /^[A-Za-z0-9_-]+$/;
|
|
3607
|
+
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}$/;
|
|
3608
|
+
var DISALLOWED_NARRATIVE_SYNTAX_PATTERN = /<|>|(?:https?|ftp|mailto|javascript|data|blob|file):|\/\/|www\.|\]\s*\(/i;
|
|
3609
|
+
var DISALLOWED_UNICODE_FORMAT_PATTERN = /\p{Cf}/u;
|
|
3610
|
+
var MAX_NARRATIVE_CHARACTERS = 4e3;
|
|
3611
|
+
var MAX_NARRATIVE_UTF16_CODE_UNITS = 8e3;
|
|
3612
|
+
var MAX_COUNT = 1e9;
|
|
3613
|
+
var BASE64URL_ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
3614
|
+
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])$/;
|
|
3615
|
+
var REVIEW_DAY_WINDOW_PATTERN = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;
|
|
3616
|
+
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)$/;
|
|
3617
|
+
var strictOptions = {
|
|
3618
|
+
version: FEEDBACK_CONTRACT_VERSION,
|
|
3619
|
+
piiEnforcement: "strict",
|
|
3620
|
+
unknownFields: "reject",
|
|
3621
|
+
identity: "exact"
|
|
3622
|
+
};
|
|
3623
|
+
var uuidField = () => field.string().validator((value) => OPAQUE_UUID_V4_PATTERN.test(value)).description("Opaque UUID generated for the feedback workflow");
|
|
3624
|
+
var draftIdField = () => field.string().validator((value) => OPAQUE_UUID_V4_PATTERN.test(value)).description("Opaque identifier for one short-lived structured draft").PID({
|
|
3625
|
+
classification: "low",
|
|
3626
|
+
action: "none",
|
|
3627
|
+
logHandling: "omit",
|
|
3628
|
+
purpose: "short-lived feedback draft lookup that must never enter logs"
|
|
3629
|
+
});
|
|
3630
|
+
var safeIdField = (maximum = 128) => field.string().min(1).max(maximum).pattern(SAFE_ID_PATTERN);
|
|
3631
|
+
var ingestionKeyIdField = () => field.string().min(1).max(80).pattern(INGESTION_KEY_ID_PATTERN);
|
|
3632
|
+
var decodeCanonicalBase64Url2 = (value) => {
|
|
3633
|
+
if (!BASE64URL_PATTERN2.test(value) || value.length % 4 === 1) {
|
|
3634
|
+
return void 0;
|
|
3635
|
+
}
|
|
3636
|
+
const finalValue = BASE64URL_ALPHABET2.indexOf(value.at(-1) ?? "");
|
|
3637
|
+
if (finalValue < 0 || value.length % 4 === 2 && (finalValue & 15) !== 0 || value.length % 4 === 3 && (finalValue & 3) !== 0) {
|
|
3638
|
+
return void 0;
|
|
3639
|
+
}
|
|
3640
|
+
const output = [];
|
|
3641
|
+
let accumulator = 0;
|
|
3642
|
+
let bitCount = 0;
|
|
3643
|
+
for (const character of value) {
|
|
3644
|
+
const sextet = BASE64URL_ALPHABET2.indexOf(character);
|
|
3645
|
+
if (sextet < 0) {
|
|
3646
|
+
return void 0;
|
|
3647
|
+
}
|
|
3648
|
+
accumulator = accumulator << 6 | sextet;
|
|
3649
|
+
bitCount += 6;
|
|
3650
|
+
while (bitCount >= 8) {
|
|
3651
|
+
bitCount -= 8;
|
|
3652
|
+
output.push(accumulator >> bitCount & 255);
|
|
3653
|
+
accumulator &= (1 << bitCount) - 1;
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
return Uint8Array.from(output);
|
|
3657
|
+
};
|
|
3658
|
+
var isCanonicalRsaModulus = (value) => {
|
|
3659
|
+
const bytes = decodeCanonicalBase64Url2(value);
|
|
3660
|
+
return bytes !== void 0 && (bytes.length === 256 || bytes.length === 384 || bytes.length === 512) && (bytes[0] ?? 0) >= 128 && ((bytes.at(-1) ?? 0) & 1) === 1;
|
|
3661
|
+
};
|
|
3662
|
+
var isCanonicalRsaExponent = (value) => {
|
|
3663
|
+
const bytes = decodeCanonicalBase64Url2(value);
|
|
3664
|
+
if (bytes === void 0 || bytes.length < 1 || bytes.length > 4 || bytes[0] === 0) {
|
|
3665
|
+
return false;
|
|
3666
|
+
}
|
|
3667
|
+
const exponent = bytes.reduce(
|
|
3668
|
+
(total, byte) => total * 256 + byte,
|
|
3669
|
+
0
|
|
3670
|
+
);
|
|
3671
|
+
return exponent >= 3 && exponent % 2 === 1;
|
|
3672
|
+
};
|
|
3673
|
+
var surfaceIdField = () => field.string().enum(FEEDBACK_SURFACE_IDS);
|
|
3674
|
+
var countField = () => field.number().validator((value) => Number.isSafeInteger(value)).min(0).max(MAX_COUNT);
|
|
3675
|
+
var positiveCountField2 = (maximum = MAX_COUNT) => field.number().validator((value) => Number.isSafeInteger(value)).min(1).max(maximum);
|
|
3676
|
+
var redactionCountField = () => field.number().validator((value) => Number.isSafeInteger(value)).min(0).max(MAX_NARRATIVE_CHARACTERS);
|
|
3677
|
+
var revisionField = () => field.number().validator((value) => Number.isSafeInteger(value)).min(0).max(1e6);
|
|
3678
|
+
var ratingField = () => field.number().validator((value) => Number.isInteger(value)).min(1).max(5);
|
|
3679
|
+
var narrativeTextField = () => field.string().validator(
|
|
3680
|
+
(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) => {
|
|
3681
|
+
const codePoint = character.codePointAt(0);
|
|
3682
|
+
return codePoint !== void 0 && (codePoint < 32 || codePoint === 127);
|
|
3683
|
+
})
|
|
3684
|
+
).min(1).max(MAX_NARRATIVE_UTF16_CODE_UNITS).PID({
|
|
3685
|
+
classification: "high",
|
|
3686
|
+
action: "clear",
|
|
3687
|
+
logHandling: "omit",
|
|
3688
|
+
purpose: "transient feedback narrative that must be discarded after classification"
|
|
3689
|
+
});
|
|
3690
|
+
var transientCorrelationIdField = () => field.string().validator((value) => OPAQUE_UUID_V4_PATTERN.test(value)).PID({
|
|
3691
|
+
classification: "low",
|
|
3692
|
+
action: "clear",
|
|
3693
|
+
logHandling: "omit",
|
|
3694
|
+
purpose: "one-use transient feedback correlation"
|
|
3695
|
+
});
|
|
3696
|
+
var transientEncryptedField = (minimum, maximum, allowedLengths) => field.string().min(minimum).max(maximum).pattern(BASE64URL_PATTERN2).validator(
|
|
3697
|
+
(value) => {
|
|
3698
|
+
const decoded = decodeCanonicalBase64Url2(value);
|
|
3699
|
+
return decoded !== void 0 && decoded.length > 0 && (allowedLengths === void 0 || allowedLengths.includes(value.length));
|
|
3700
|
+
}
|
|
3701
|
+
).PID({
|
|
3702
|
+
classification: "high",
|
|
3703
|
+
action: "clear",
|
|
3704
|
+
logHandling: "omit",
|
|
3705
|
+
purpose: "encrypted transient feedback narrative material"
|
|
3706
|
+
});
|
|
3707
|
+
var richTextNodeShape = () => ({
|
|
3708
|
+
type: field.string().enum(["text"]),
|
|
3709
|
+
text: narrativeTextField(),
|
|
3710
|
+
marks: field.array(
|
|
3711
|
+
field.string().enum(["bold", "italic", "underline"])
|
|
3712
|
+
).max(3).optional()
|
|
3713
|
+
});
|
|
3714
|
+
var richTextBlockShape = () => ({
|
|
3715
|
+
type: field.string().enum(["paragraph", "listItem"]),
|
|
3716
|
+
depth: field.number().validator((value) => Number.isInteger(value)).min(0).max(4),
|
|
3717
|
+
listType: field.string().enum(["bullet"]).optional(),
|
|
3718
|
+
children: field.array(field.object(richTextNodeShape())).min(1).max(128)
|
|
3719
|
+
});
|
|
3720
|
+
var nestedIdentityShape2 = (type) => ({
|
|
3721
|
+
type: field.string().enum([type]),
|
|
3722
|
+
version: field.string().enum([FEEDBACK_CONTRACT_VERSION])
|
|
3723
|
+
});
|
|
3724
|
+
var envelopeShape = (includeIdentity = false) => ({
|
|
3725
|
+
...includeIdentity ? nestedIdentityShape2("feedback-encrypted-narrative-envelope") : {},
|
|
3726
|
+
schemaVersion: field.string().enum(["1"]),
|
|
3727
|
+
keyId: ingestionKeyIdField(),
|
|
3728
|
+
algorithm: field.string().enum(["RSA-OAEP-256+A256GCM"]),
|
|
3729
|
+
wrappedKey: transientEncryptedField(342, 683, [342, 512, 683]),
|
|
3730
|
+
iv: transientEncryptedField(16, 16, [16]),
|
|
3731
|
+
ciphertext: transientEncryptedField(2, 49152),
|
|
3732
|
+
authenticationTag: transientEncryptedField(22, 22, [22])
|
|
3733
|
+
});
|
|
3734
|
+
var analysisProjectionShape = (includeIdentity = false) => ({
|
|
3735
|
+
...includeIdentity ? nestedIdentityShape2("feedback-derived-analysis") : {},
|
|
3736
|
+
status: field.string().enum(["analyzed", "structured-only"]),
|
|
3737
|
+
sentiment: field.string().enum(FEEDBACK_SENTIMENT_BUCKETS).optional(),
|
|
3738
|
+
intentIds: field.array(field.string().enum(FEEDBACK_INTENT_IDS)).max(8),
|
|
3739
|
+
themeIds: field.array(field.string().enum(FEEDBACK_THEME_IDS)).max(8),
|
|
3740
|
+
confidence: field.string().enum(["low", "medium", "high"]).optional(),
|
|
3741
|
+
policyVersion: field.string().enum(FEEDBACK_PRIVACY_POLICY_VERSIONS),
|
|
3742
|
+
modelVersion: field.string().enum(FEEDBACK_ANALYSIS_MODEL_VERSIONS),
|
|
3743
|
+
deterministicRedactionCount: redactionCountField(),
|
|
3744
|
+
scannerRedactionCount: redactionCountField()
|
|
3745
|
+
});
|
|
3746
|
+
var analysisReceiptShape = () => ({
|
|
3747
|
+
receiptId: transientCorrelationIdField(),
|
|
3748
|
+
...analysisProjectionShape(),
|
|
3749
|
+
analyzedAt: field.dateTimeISO()
|
|
3750
|
+
});
|
|
3751
|
+
var isAnalysisProjectionConsistent = (value) => {
|
|
3752
|
+
const status = value.status;
|
|
3753
|
+
const intentIds = value.intentIds;
|
|
3754
|
+
const themeIds = value.themeIds;
|
|
3755
|
+
if (!Array.isArray(intentIds) || !Array.isArray(themeIds)) {
|
|
3756
|
+
return false;
|
|
3757
|
+
}
|
|
3758
|
+
if (new Set(intentIds).size !== intentIds.length || new Set(themeIds).size !== themeIds.length) {
|
|
3759
|
+
return false;
|
|
3760
|
+
}
|
|
3761
|
+
if (status === "structured-only") {
|
|
3762
|
+
return value.sentiment === void 0 && value.confidence === void 0 && intentIds.length === 0 && themeIds.length === 0;
|
|
3763
|
+
}
|
|
3764
|
+
return status === "analyzed" && value.sentiment !== void 0 && value.confidence !== void 0;
|
|
3765
|
+
};
|
|
3766
|
+
var optionalDraftFields = () => ({
|
|
3767
|
+
surfaceId: surfaceIdField().optional(),
|
|
3768
|
+
issueType: field.string().enum(FEEDBACK_ISSUE_TYPES).optional(),
|
|
3769
|
+
severity: ratingField().optional(),
|
|
3770
|
+
satisfaction: ratingField().optional(),
|
|
3771
|
+
analysisReceiptId: transientCorrelationIdField().optional()
|
|
3772
|
+
});
|
|
3773
|
+
var surfaceEntryShape = () => ({
|
|
3774
|
+
id: surfaceIdField(),
|
|
3775
|
+
labelKey: safeIdField(160),
|
|
3776
|
+
kind: field.string().enum(["public", "authenticated", "admin", "game"]),
|
|
3777
|
+
gameDiagnosticsEligible: field.boolean()
|
|
3778
|
+
});
|
|
3779
|
+
var areSurfacesConsistent = (surfaces) => {
|
|
3780
|
+
if (!Array.isArray(surfaces)) {
|
|
3781
|
+
return false;
|
|
3782
|
+
}
|
|
3783
|
+
const surfaceIds = surfaces.map(
|
|
3784
|
+
(surface) => surface.id
|
|
3785
|
+
);
|
|
3786
|
+
return new Set(surfaceIds).size === surfaceIds.length && surfaces.every(
|
|
3787
|
+
(surface) => {
|
|
3788
|
+
const definition = FEEDBACK_SURFACE_DEFINITIONS.find(
|
|
3789
|
+
({ id }) => id === surface.id
|
|
3790
|
+
);
|
|
3791
|
+
return definition !== void 0 && surface.labelKey === definition.labelKey && surface.kind === definition.kind && surface.gameDiagnosticsEligible === definition.gameDiagnosticsEligible;
|
|
3792
|
+
}
|
|
3793
|
+
);
|
|
3794
|
+
};
|
|
3795
|
+
var isDraftShapeConsistent = (value) => {
|
|
3796
|
+
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;
|
|
3797
|
+
if (!hasDirtyField) {
|
|
3798
|
+
return false;
|
|
3799
|
+
}
|
|
3800
|
+
if (value.kind === "bug") {
|
|
3801
|
+
return value.satisfaction === void 0;
|
|
3802
|
+
}
|
|
3803
|
+
if (value.kind === "review") {
|
|
3804
|
+
return value.surfaceId === void 0 && value.issueType === void 0 && value.severity === void 0;
|
|
3805
|
+
}
|
|
3806
|
+
return false;
|
|
3807
|
+
};
|
|
3808
|
+
var isContextConsistent = (value) => {
|
|
3809
|
+
if (!areSurfacesConsistent(value.surfaces)) {
|
|
3810
|
+
return false;
|
|
3811
|
+
}
|
|
3812
|
+
if (value.selectedSurfaceId !== void 0 && !value.surfaces.some(
|
|
3813
|
+
(surface) => surface.id === value.selectedSurfaceId
|
|
3814
|
+
)) {
|
|
3815
|
+
return false;
|
|
3816
|
+
}
|
|
3817
|
+
const hasProjectedSurface = value.surfaces.length > 0;
|
|
3818
|
+
const hasDiagnosticsSurface = value.surfaces.some(
|
|
3819
|
+
(surface) => surface.gameDiagnosticsEligible === true
|
|
3820
|
+
);
|
|
3821
|
+
if (value.eligibility.bugReport && !hasProjectedSurface || value.eligibility.gameDiagnostics && (!value.eligibility.bugReport || !hasDiagnosticsSurface)) {
|
|
3822
|
+
return false;
|
|
3823
|
+
}
|
|
3824
|
+
const generatedAt = Date.parse(String(value.generatedAt));
|
|
3825
|
+
if (!Number.isFinite(generatedAt)) {
|
|
3826
|
+
return false;
|
|
3827
|
+
}
|
|
3828
|
+
const boundedFutureAvailabilityIsValid = (candidate, maximumSeconds) => {
|
|
3829
|
+
if (candidate === void 0) {
|
|
3830
|
+
return true;
|
|
3831
|
+
}
|
|
3832
|
+
const availableAt = Date.parse(String(candidate));
|
|
3833
|
+
return Number.isFinite(availableAt) && availableAt > generatedAt && availableAt - generatedAt <= maximumSeconds * 1e3;
|
|
3834
|
+
};
|
|
3835
|
+
const maximumBugCooldownSeconds = FEEDBACK_BUG_COOLDOWN_SECONDS[FEEDBACK_BUG_COOLDOWN_SECONDS.length - 1] ?? 0;
|
|
3836
|
+
if (!boundedFutureAvailabilityIsValid(
|
|
3837
|
+
value.eligibility.bugAvailableAt,
|
|
3838
|
+
maximumBugCooldownSeconds
|
|
3839
|
+
) || !boundedFutureAvailabilityIsValid(
|
|
3840
|
+
value.eligibility.reviewAvailableAt,
|
|
3841
|
+
FEEDBACK_REVIEW_COOLDOWN_SECONDS
|
|
3842
|
+
)) {
|
|
3843
|
+
return false;
|
|
3844
|
+
}
|
|
3845
|
+
const bugAvailableAt = value.eligibility.bugAvailableAt === void 0 ? void 0 : Date.parse(String(value.eligibility.bugAvailableAt));
|
|
3846
|
+
const bugRetryAfterSeconds = value.eligibility.bugRetryAfterSeconds;
|
|
3847
|
+
const bugCooldownIsConsistent = bugAvailableAt === void 0 && bugRetryAfterSeconds === void 0 || bugAvailableAt !== void 0 && typeof bugRetryAfterSeconds === "number" && bugRetryAfterSeconds === Math.ceil((bugAvailableAt - generatedAt) / 1e3);
|
|
3848
|
+
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) {
|
|
3849
|
+
return false;
|
|
3850
|
+
}
|
|
3851
|
+
if (!value.flags.transientAnalysis) {
|
|
3852
|
+
return value.ingestionKey === void 0;
|
|
3853
|
+
}
|
|
3854
|
+
if (value.ingestionKey === void 0) {
|
|
3855
|
+
return false;
|
|
3856
|
+
}
|
|
3857
|
+
const expiresAt = Date.parse(String(value.ingestionKey.expiresAt));
|
|
3858
|
+
return Number.isFinite(expiresAt) && expiresAt > generatedAt && expiresAt - generatedAt <= 10 * 60 * 1e3;
|
|
3859
|
+
};
|
|
3860
|
+
var distributionShape = (idField = () => safeIdField(128)) => ({
|
|
3861
|
+
id: idField(),
|
|
3862
|
+
count: countField()
|
|
3863
|
+
});
|
|
3864
|
+
var hasUniqueIds = (values) => Array.isArray(values) && new Set(
|
|
3865
|
+
values.map(
|
|
3866
|
+
(value) => typeof value === "object" && value !== null ? value.id : void 0
|
|
3867
|
+
)
|
|
3868
|
+
).size === values.length;
|
|
3869
|
+
var distributionTotal = (values) => Array.isArray(values) ? values.reduce(
|
|
3870
|
+
(total, value) => total + (typeof value === "object" && value !== null && typeof value.count === "number" ? value.count : 0),
|
|
3871
|
+
0
|
|
3872
|
+
) : 0;
|
|
3873
|
+
var distributionCountsAtMost = (values, maximum) => Array.isArray(values) && values.every(
|
|
3874
|
+
(value) => typeof value === "object" && value !== null && typeof value.count === "number" && value.count <= maximum
|
|
3875
|
+
);
|
|
3876
|
+
var advisoryShape = () => ({
|
|
3877
|
+
code: field.string().enum(
|
|
3878
|
+
[
|
|
3879
|
+
"severity-five",
|
|
3880
|
+
"critical-regression",
|
|
3881
|
+
"satisfaction-drop",
|
|
3882
|
+
"processor-stale"
|
|
3883
|
+
]
|
|
3884
|
+
),
|
|
3885
|
+
level: field.string().enum(["advisory", "critical"]),
|
|
3886
|
+
triggerCount: positiveCountField2(),
|
|
3887
|
+
recommendationIds: field.array(
|
|
3888
|
+
field.string().enum(
|
|
3889
|
+
[
|
|
3890
|
+
"inspect-release",
|
|
3891
|
+
"verify-renderer-health",
|
|
3892
|
+
"review-top-intents",
|
|
3893
|
+
"verify-processor-health"
|
|
3894
|
+
]
|
|
3895
|
+
)
|
|
3896
|
+
).min(1).max(8)
|
|
3897
|
+
});
|
|
3898
|
+
var orderedWindow = (value, durationMilliseconds) => {
|
|
3899
|
+
const start = Date.parse(String(value.windowStart));
|
|
3900
|
+
const end = Date.parse(String(value.windowEnd));
|
|
3901
|
+
const generatedAt = Date.parse(String(value.generatedAt));
|
|
3902
|
+
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;
|
|
3903
|
+
};
|
|
3904
|
+
var processorWindow = (processor, windowKey) => {
|
|
3905
|
+
let iso;
|
|
3906
|
+
let expectedLength;
|
|
3907
|
+
let duration;
|
|
3908
|
+
if (processor === "bug-hourly") {
|
|
3909
|
+
if (!BUG_HOUR_WINDOW_PATTERN.test(windowKey)) {
|
|
3910
|
+
return void 0;
|
|
3911
|
+
}
|
|
3912
|
+
iso = `${windowKey}:00:00.000Z`;
|
|
3913
|
+
expectedLength = 13;
|
|
3914
|
+
duration = 60 * 60 * 1e3;
|
|
3915
|
+
} else if (processor === "review-daily") {
|
|
3916
|
+
if (!REVIEW_DAY_WINDOW_PATTERN.test(windowKey)) {
|
|
3917
|
+
return void 0;
|
|
3918
|
+
}
|
|
3919
|
+
iso = `${windowKey}T00:00:00.000Z`;
|
|
3920
|
+
expectedLength = 10;
|
|
3921
|
+
duration = 24 * 60 * 60 * 1e3;
|
|
3922
|
+
} else {
|
|
3923
|
+
if (!RECONCILIATION_WINDOW_PATTERN.test(windowKey)) {
|
|
3924
|
+
return void 0;
|
|
3925
|
+
}
|
|
3926
|
+
const minute = Number(windowKey.slice(-2));
|
|
3927
|
+
if (minute % 5 !== 0) {
|
|
3928
|
+
return void 0;
|
|
3929
|
+
}
|
|
3930
|
+
iso = `${windowKey}:00.000Z`;
|
|
3931
|
+
expectedLength = 16;
|
|
3932
|
+
duration = 5 * 60 * 1e3;
|
|
3933
|
+
}
|
|
3934
|
+
const start = Date.parse(iso);
|
|
3935
|
+
if (!Number.isFinite(start) || new Date(start).toISOString().slice(0, expectedLength) !== windowKey) {
|
|
3936
|
+
return void 0;
|
|
3937
|
+
}
|
|
3938
|
+
return { start, duration };
|
|
3939
|
+
};
|
|
3940
|
+
var isProcessorArtifactConsistent = (value, artifact) => {
|
|
3941
|
+
const processor = value.processor;
|
|
3942
|
+
const windowKey = String(value.windowKey);
|
|
3943
|
+
const window = processorWindow(processor, windowKey);
|
|
3944
|
+
if (window === void 0) {
|
|
3945
|
+
return false;
|
|
3946
|
+
}
|
|
3947
|
+
const completedAt = Date.parse(
|
|
3948
|
+
String(
|
|
3949
|
+
artifact === "checkpoint" ? value.completedAt : value.generatedAt
|
|
3950
|
+
)
|
|
3951
|
+
);
|
|
3952
|
+
if (!Number.isFinite(completedAt) || completedAt < window.start + window.duration) {
|
|
3953
|
+
return false;
|
|
3954
|
+
}
|
|
3955
|
+
if (artifact === "checkpoint" && value.checkpointId !== `checkpoint:${processor}:${windowKey}`) {
|
|
3956
|
+
return false;
|
|
3957
|
+
}
|
|
3958
|
+
return true;
|
|
3959
|
+
};
|
|
3960
|
+
var roundRate = (value) => Math.round((value + Number.EPSILON) * 1e6) / 1e6;
|
|
3961
|
+
var rateMatches = (actual, expected) => typeof actual === "number" && actual === roundRate(expected);
|
|
3962
|
+
var distributionCountFor = (values, id) => {
|
|
3963
|
+
if (!Array.isArray(values)) {
|
|
3964
|
+
return 0;
|
|
3965
|
+
}
|
|
3966
|
+
const entry = values.find(
|
|
3967
|
+
(value) => typeof value === "object" && value !== null && value.id === id
|
|
3968
|
+
);
|
|
3969
|
+
return typeof entry === "object" && entry !== null && typeof entry.count === "number" ? entry.count : 0;
|
|
3970
|
+
};
|
|
3971
|
+
var advisoryCodesAreConsistent = (advisories, expected) => {
|
|
3972
|
+
if (!Array.isArray(advisories)) {
|
|
3973
|
+
return false;
|
|
3974
|
+
}
|
|
3975
|
+
const expectedCodes = Object.entries(expected).filter(
|
|
3976
|
+
(entry) => entry[1] !== void 0
|
|
3977
|
+
).map(([code]) => code);
|
|
3978
|
+
const codes = advisories.map(
|
|
3979
|
+
(advisory) => advisory?.code
|
|
3980
|
+
);
|
|
3981
|
+
return new Set(codes).size === codes.length && codes.every(
|
|
3982
|
+
(code) => typeof code === "string" && expectedCodes.includes(code)
|
|
3983
|
+
) && codes.length === expectedCodes.length && expectedCodes.every((code) => codes.includes(code)) && advisories.every((advisory) => {
|
|
3984
|
+
const value = advisory;
|
|
3985
|
+
const code = typeof value.code === "string" ? value.code : void 0;
|
|
3986
|
+
const expectation = code === void 0 ? void 0 : expected[code];
|
|
3987
|
+
if (expectation === void 0 || value.level !== expectation.level || value.triggerCount !== expectation.triggerCount || !Array.isArray(value.recommendationIds) || value.recommendationIds.length !== expectation.recommendationIds.length) {
|
|
3988
|
+
return false;
|
|
3989
|
+
}
|
|
3990
|
+
return value.recommendationIds.every(
|
|
3991
|
+
(recommendation, index) => recommendation === expectation.recommendationIds[index]
|
|
3992
|
+
);
|
|
3993
|
+
});
|
|
3994
|
+
};
|
|
3995
|
+
var FeedbackRichTextAstSchema = createSchema(
|
|
3996
|
+
{
|
|
3997
|
+
schemaVersion: field.string().enum(["1"]),
|
|
3998
|
+
children: field.array(field.object(richTextBlockShape())).min(1).max(128)
|
|
3999
|
+
},
|
|
4000
|
+
"doc",
|
|
4001
|
+
{
|
|
4002
|
+
...strictOptions,
|
|
4003
|
+
schemaValidator: (value) => {
|
|
4004
|
+
const blocks = Array.isArray(value.children) ? value.children : [];
|
|
4005
|
+
const blocksAreConsistent = blocks.every(
|
|
4006
|
+
(block) => {
|
|
4007
|
+
const listShapeIsValid = block.type === "paragraph" && block.listType === void 0 || block.type === "listItem" && block.listType === "bullet";
|
|
4008
|
+
const marksAreUnique = Array.isArray(block.children) && block.children.every(
|
|
4009
|
+
(child) => child.marks === void 0 || Array.isArray(child.marks) && new Set(child.marks).size === child.marks.length
|
|
4010
|
+
);
|
|
4011
|
+
return listShapeIsValid && marksAreUnique;
|
|
4012
|
+
}
|
|
4013
|
+
);
|
|
4014
|
+
const characterCount = blocks.reduce(
|
|
4015
|
+
(blockTotal, block) => blockTotal + (Array.isArray(block.children) ? block.children.reduce(
|
|
4016
|
+
(nodeTotal, node) => nodeTotal + (typeof node.text === "string" ? [...node.text].length : 0),
|
|
4017
|
+
0
|
|
4018
|
+
) : 0),
|
|
4019
|
+
0
|
|
4020
|
+
);
|
|
4021
|
+
const extractedCharacterCount = characterCount + Math.max(0, blocks.length - 1);
|
|
4022
|
+
const nodeCount = blocks.reduce(
|
|
4023
|
+
(total, block) => total + (Array.isArray(block.children) ? block.children.length : 0),
|
|
4024
|
+
0
|
|
4025
|
+
);
|
|
4026
|
+
return value.type === "doc" && value.schemaVersion === "1" && blocksAreConsistent && nodeCount <= 256 && extractedCharacterCount > 0 && extractedCharacterCount <= MAX_NARRATIVE_CHARACTERS;
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
);
|
|
4030
|
+
var FeedbackEncryptedNarrativeEnvelopeSchema = createSchema(
|
|
4031
|
+
envelopeShape(),
|
|
4032
|
+
"feedback-encrypted-narrative-envelope",
|
|
4033
|
+
strictOptions
|
|
4034
|
+
);
|
|
4035
|
+
var FeedbackTransientAnalysisRequestSchema = createSchema(
|
|
4036
|
+
{
|
|
4037
|
+
schemaVersion: field.string().enum(["1"]),
|
|
4038
|
+
requestId: transientCorrelationIdField(),
|
|
4039
|
+
purpose: field.string().enum(["bug", "review"]),
|
|
4040
|
+
surfaceId: surfaceIdField().optional(),
|
|
4041
|
+
deterministicRedactionCount: redactionCountField(),
|
|
4042
|
+
envelope: field.object(envelopeShape(true))
|
|
4043
|
+
},
|
|
4044
|
+
"feedback-transient-analysis-request",
|
|
4045
|
+
{
|
|
4046
|
+
...strictOptions,
|
|
4047
|
+
schemaValidator: (value, context) => {
|
|
4048
|
+
const surfaceWasProvided = context?.wasProvided("surfaceId") ?? Object.prototype.hasOwnProperty.call(value, "surfaceId");
|
|
4049
|
+
return value.purpose === "bug" && value.surfaceId !== void 0 || value.purpose === "review" && !surfaceWasProvided;
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
);
|
|
4053
|
+
var validateFeedbackTransientAnalysisRequest = (input) => {
|
|
4054
|
+
const result = FeedbackTransientAnalysisRequestSchema.validate(input);
|
|
4055
|
+
if (!result.valid || result.value === void 0) {
|
|
4056
|
+
return {
|
|
4057
|
+
valid: false,
|
|
4058
|
+
errors: result.errors ?? ["Transient analysis validation failed."],
|
|
4059
|
+
...result.issues === void 0 ? {} : { issues: result.issues }
|
|
4060
|
+
};
|
|
4061
|
+
}
|
|
4062
|
+
return {
|
|
4063
|
+
valid: true,
|
|
4064
|
+
value: result.value
|
|
4065
|
+
};
|
|
4066
|
+
};
|
|
4067
|
+
var FeedbackAnalysisReceiptSchema = createSchema(
|
|
4068
|
+
analysisReceiptShape(),
|
|
4069
|
+
"feedback-analysis-receipt",
|
|
4070
|
+
{
|
|
4071
|
+
...strictOptions,
|
|
4072
|
+
schemaValidator: (value) => value.status === "analyzed" && isAnalysisProjectionConsistent(value)
|
|
4073
|
+
}
|
|
4074
|
+
);
|
|
4075
|
+
var FeedbackDerivedAnalysisSchema = createSchema(
|
|
4076
|
+
analysisProjectionShape(),
|
|
4077
|
+
"feedback-derived-analysis",
|
|
4078
|
+
{
|
|
4079
|
+
...strictOptions,
|
|
4080
|
+
schemaValidator: isAnalysisProjectionConsistent
|
|
4081
|
+
}
|
|
4082
|
+
);
|
|
4083
|
+
var FeedbackSurfaceCatalogSchema = createSchema(
|
|
4084
|
+
{
|
|
4085
|
+
generatedAt: field.dateTimeISO(),
|
|
4086
|
+
catalogVersion: safeIdField(64),
|
|
4087
|
+
surfaces: field.array(field.object(surfaceEntryShape())).max(256)
|
|
4088
|
+
},
|
|
4089
|
+
"feedback-surface-catalog",
|
|
4090
|
+
{
|
|
4091
|
+
...strictOptions,
|
|
4092
|
+
schemaValidator: (value) => areSurfacesConsistent(value.surfaces)
|
|
4093
|
+
}
|
|
4094
|
+
);
|
|
4095
|
+
var FeedbackContextSchema = createSchema(
|
|
4096
|
+
{
|
|
4097
|
+
generatedAt: field.dateTimeISO(),
|
|
4098
|
+
catalogVersion: safeIdField(64),
|
|
4099
|
+
surfaces: field.array(field.object(surfaceEntryShape())).max(256),
|
|
4100
|
+
selectedSurfaceId: surfaceIdField().optional(),
|
|
4101
|
+
flags: field.object({
|
|
4102
|
+
bugReport: field.boolean(),
|
|
4103
|
+
review: field.boolean(),
|
|
4104
|
+
transientAnalysis: field.boolean(),
|
|
4105
|
+
reporting: field.boolean(),
|
|
4106
|
+
admin: field.boolean(),
|
|
4107
|
+
mcp: field.boolean(),
|
|
4108
|
+
publicHonesty: field.boolean(),
|
|
4109
|
+
gameDiagnostics: field.boolean(),
|
|
4110
|
+
anonymous: field.boolean()
|
|
4111
|
+
}),
|
|
4112
|
+
eligibility: field.object({
|
|
4113
|
+
bugReport: field.boolean(),
|
|
4114
|
+
review: field.boolean(),
|
|
4115
|
+
gameDiagnostics: field.boolean(),
|
|
4116
|
+
bugAvailableAt: field.dateTimeISO().optional(),
|
|
4117
|
+
reviewAvailableAt: field.dateTimeISO().optional(),
|
|
4118
|
+
bugRetryAfterSeconds: positiveCountField2().optional()
|
|
4119
|
+
}),
|
|
4120
|
+
ingestionKey: field.object({
|
|
4121
|
+
keyId: ingestionKeyIdField(),
|
|
4122
|
+
algorithm: field.string().enum(["RSA-OAEP-256"]),
|
|
4123
|
+
publicJwk: field.object({
|
|
4124
|
+
kty: field.string().enum(["RSA"]),
|
|
4125
|
+
alg: field.string().enum(["RSA-OAEP-256"]),
|
|
4126
|
+
use: field.string().enum(["enc"]),
|
|
4127
|
+
key_ops: field.array(field.string().enum(["wrapKey"])).min(1).max(1),
|
|
4128
|
+
n: field.string().min(342).max(683).pattern(BASE64URL_PATTERN2).validator(isCanonicalRsaModulus),
|
|
4129
|
+
e: field.string().min(2).max(6).pattern(BASE64URL_PATTERN2).validator(isCanonicalRsaExponent),
|
|
4130
|
+
ext: field.boolean().validator((value) => value === true)
|
|
4131
|
+
}),
|
|
4132
|
+
expiresAt: field.dateTimeISO()
|
|
4133
|
+
}).optional()
|
|
4134
|
+
},
|
|
4135
|
+
"feedback-context",
|
|
4136
|
+
{
|
|
4137
|
+
...strictOptions,
|
|
4138
|
+
schemaValidator: isContextConsistent
|
|
4139
|
+
}
|
|
4140
|
+
);
|
|
4141
|
+
var FeedbackDraftUpsertRequestSchema = createSchema(
|
|
4142
|
+
{
|
|
4143
|
+
draftId: draftIdField(),
|
|
4144
|
+
kind: field.string().enum(["bug", "review"]),
|
|
4145
|
+
revision: revisionField(),
|
|
4146
|
+
...optionalDraftFields()
|
|
4147
|
+
},
|
|
4148
|
+
"feedback-draft-upsert-request",
|
|
4149
|
+
{
|
|
4150
|
+
...strictOptions,
|
|
4151
|
+
schemaValidator: isDraftShapeConsistent
|
|
4152
|
+
}
|
|
4153
|
+
);
|
|
4154
|
+
var FeedbackDraftPacketSchema = createSchema(
|
|
4155
|
+
{
|
|
4156
|
+
draftId: draftIdField(),
|
|
4157
|
+
kind: field.string().enum(["bug", "review"]),
|
|
4158
|
+
revision: revisionField(),
|
|
4159
|
+
surfaceId: surfaceIdField().optional(),
|
|
4160
|
+
issueType: field.string().enum(FEEDBACK_ISSUE_TYPES).optional(),
|
|
4161
|
+
severity: ratingField().optional(),
|
|
4162
|
+
satisfaction: ratingField().optional(),
|
|
4163
|
+
analysis: field.object(analysisProjectionShape(true)).optional(),
|
|
4164
|
+
serverUpdatedAt: field.dateTimeISO(),
|
|
4165
|
+
expiresAt: field.dateTimeISO()
|
|
4166
|
+
},
|
|
4167
|
+
"feedback-draft-packet",
|
|
4168
|
+
{
|
|
4169
|
+
...strictOptions,
|
|
4170
|
+
schemaValidator: (value) => {
|
|
4171
|
+
if (!isDraftShapeConsistent(value)) {
|
|
4172
|
+
return false;
|
|
4173
|
+
}
|
|
4174
|
+
if (value.analysis !== void 0 && !isAnalysisProjectionConsistent(value.analysis)) {
|
|
4175
|
+
return false;
|
|
4176
|
+
}
|
|
4177
|
+
const updatedAt = Date.parse(String(value.serverUpdatedAt));
|
|
4178
|
+
const expiresAt = Date.parse(String(value.expiresAt));
|
|
4179
|
+
return Number.isFinite(updatedAt) && Number.isFinite(expiresAt) && expiresAt > updatedAt && expiresAt - updatedAt <= 24 * 60 * 60 * 1e3;
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
);
|
|
4183
|
+
var FeedbackDraftReceiptSchema = createSchema(
|
|
4184
|
+
{
|
|
4185
|
+
draftId: draftIdField(),
|
|
4186
|
+
revision: revisionField(),
|
|
4187
|
+
savedAt: field.dateTimeISO(),
|
|
4188
|
+
expiresAt: field.dateTimeISO()
|
|
4189
|
+
},
|
|
4190
|
+
"feedback-draft-receipt",
|
|
4191
|
+
{
|
|
4192
|
+
...strictOptions,
|
|
4193
|
+
schemaValidator: (value) => {
|
|
4194
|
+
const savedAt = Date.parse(String(value.savedAt));
|
|
4195
|
+
const expiresAt = Date.parse(String(value.expiresAt));
|
|
4196
|
+
return Number.isFinite(savedAt) && Number.isFinite(expiresAt) && expiresAt > savedAt && expiresAt - savedAt <= 24 * 60 * 60 * 1e3;
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
);
|
|
4200
|
+
var FeedbackBugSubmissionRequestSchema = createSchema(
|
|
4201
|
+
{
|
|
4202
|
+
draftId: draftIdField().optional(),
|
|
4203
|
+
surfaceId: surfaceIdField(),
|
|
4204
|
+
issueType: field.string().enum(FEEDBACK_ISSUE_TYPES),
|
|
4205
|
+
severity: ratingField(),
|
|
4206
|
+
analysisReceiptId: transientCorrelationIdField().optional(),
|
|
4207
|
+
gameDiagnostics: field.object(feedbackGameDiagnosticsShape(true)).optional()
|
|
4208
|
+
},
|
|
4209
|
+
"feedback-bug-submission-request",
|
|
4210
|
+
{
|
|
4211
|
+
...strictOptions,
|
|
4212
|
+
schemaValidator: (value) => value.issueType !== "privacy-security" && (value.gameDiagnostics === void 0 || isFeedbackGameDiagnosticsConsistent(value.gameDiagnostics) && value.gameDiagnostics.surfaceId === value.surfaceId)
|
|
4213
|
+
}
|
|
4214
|
+
);
|
|
4215
|
+
var FeedbackReviewSubmissionRequestSchema = createSchema(
|
|
4216
|
+
{
|
|
4217
|
+
draftId: draftIdField().optional(),
|
|
4218
|
+
satisfaction: ratingField(),
|
|
4219
|
+
analysisReceiptId: transientCorrelationIdField().optional()
|
|
4220
|
+
},
|
|
4221
|
+
"feedback-review-submission-request",
|
|
4222
|
+
strictOptions
|
|
4223
|
+
);
|
|
4224
|
+
var FeedbackAcceptanceReceiptSchema = createSchema(
|
|
4225
|
+
{
|
|
4226
|
+
packetId: uuidField(),
|
|
4227
|
+
kind: field.string().enum(["bug", "review"]),
|
|
4228
|
+
acceptedAt: field.dateTimeISO(),
|
|
4229
|
+
nextEligibleAt: field.dateTimeISO()
|
|
4230
|
+
},
|
|
4231
|
+
"feedback-acceptance-receipt",
|
|
4232
|
+
{
|
|
4233
|
+
...strictOptions,
|
|
4234
|
+
schemaValidator: (value) => {
|
|
4235
|
+
const acceptedAt = Date.parse(String(value.acceptedAt));
|
|
4236
|
+
const nextEligibleAt = Date.parse(String(value.nextEligibleAt));
|
|
4237
|
+
const cooldownMilliseconds = nextEligibleAt - acceptedAt;
|
|
4238
|
+
if (!Number.isFinite(acceptedAt) || !Number.isFinite(nextEligibleAt) || cooldownMilliseconds <= 0 || cooldownMilliseconds % 1e3 !== 0) {
|
|
4239
|
+
return false;
|
|
4240
|
+
}
|
|
4241
|
+
const cooldownSeconds = cooldownMilliseconds / 1e3;
|
|
4242
|
+
return value.kind === "review" ? cooldownSeconds === FEEDBACK_REVIEW_COOLDOWN_SECONDS : FEEDBACK_BUG_COOLDOWN_SECONDS.includes(
|
|
4243
|
+
cooldownSeconds
|
|
4244
|
+
);
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
);
|
|
4248
|
+
var FeedbackStructuredOnlyAnalysisResultSchema = createSchema(
|
|
4249
|
+
{
|
|
4250
|
+
status: field.string().enum(["structured-only-required"]),
|
|
4251
|
+
reasonCode: field.string().enum(FEEDBACK_STRUCTURED_ONLY_REASON_CODES)
|
|
4252
|
+
},
|
|
4253
|
+
"feedback-structured-only-analysis-result",
|
|
4254
|
+
strictOptions
|
|
4255
|
+
);
|
|
4256
|
+
var FeedbackBugPacketSchema = createSchema(
|
|
4257
|
+
{
|
|
4258
|
+
packetId: uuidField(),
|
|
4259
|
+
acceptedAt: field.dateTimeISO(),
|
|
4260
|
+
surfaceId: surfaceIdField(),
|
|
4261
|
+
issueType: field.string().enum(FEEDBACK_PERSISTABLE_ISSUE_TYPES),
|
|
4262
|
+
severity: ratingField(),
|
|
4263
|
+
releaseId: safeIdField(128),
|
|
4264
|
+
buildId: safeIdField(128),
|
|
4265
|
+
analysis: field.object(analysisProjectionShape(true)).optional(),
|
|
4266
|
+
gameDiagnostics: field.object(feedbackGameDiagnosticsShape(true)).optional()
|
|
4267
|
+
},
|
|
4268
|
+
"feedback-bug-packet",
|
|
4269
|
+
{
|
|
4270
|
+
...strictOptions,
|
|
4271
|
+
schemaValidator: (value) => (value.analysis === void 0 || isAnalysisProjectionConsistent(value.analysis)) && (value.gameDiagnostics === void 0 || isFeedbackGameDiagnosticsConsistent(value.gameDiagnostics) && value.gameDiagnostics.surfaceId === value.surfaceId)
|
|
4272
|
+
}
|
|
4273
|
+
);
|
|
4274
|
+
var FeedbackReviewPacketSchema = createSchema(
|
|
4275
|
+
{
|
|
4276
|
+
packetId: uuidField(),
|
|
4277
|
+
acceptedAt: field.dateTimeISO(),
|
|
4278
|
+
satisfaction: ratingField(),
|
|
4279
|
+
analysis: field.object(analysisProjectionShape(true)).optional()
|
|
4280
|
+
},
|
|
4281
|
+
"feedback-review-packet",
|
|
4282
|
+
{
|
|
4283
|
+
...strictOptions,
|
|
4284
|
+
schemaValidator: (value) => value.analysis === void 0 || isAnalysisProjectionConsistent(value.analysis)
|
|
4285
|
+
}
|
|
4286
|
+
);
|
|
4287
|
+
var FeedbackGameReconstructionManifestSchema = createSchema(
|
|
4288
|
+
{
|
|
4289
|
+
reconstructionId: uuidField(),
|
|
4290
|
+
bugPacketId: uuidField(),
|
|
4291
|
+
createdAt: field.dateTimeISO(),
|
|
4292
|
+
expiresAt: field.dateTimeISO(),
|
|
4293
|
+
curatedAssetSetId: safeIdField(128),
|
|
4294
|
+
noticeKey: safeIdField(160),
|
|
4295
|
+
diagnostics: field.object(feedbackGameDiagnosticsShape(true))
|
|
4296
|
+
},
|
|
4297
|
+
"feedback-game-reconstruction-manifest",
|
|
4298
|
+
{
|
|
4299
|
+
...strictOptions,
|
|
4300
|
+
schemaValidator: (value) => {
|
|
4301
|
+
const createdAt = Date.parse(String(value.createdAt));
|
|
4302
|
+
const expiresAt = Date.parse(String(value.expiresAt));
|
|
4303
|
+
return isFeedbackGameDiagnosticsConsistent(value.diagnostics) && Number.isFinite(createdAt) && Number.isFinite(expiresAt) && expiresAt > createdAt && expiresAt - createdAt <= 30 * 24 * 60 * 60 * 1e3;
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
);
|
|
4307
|
+
var FeedbackHourlyBugReportSchema = createSchema(
|
|
4308
|
+
{
|
|
4309
|
+
reportId: uuidField(),
|
|
4310
|
+
windowStart: field.dateTimeISO(),
|
|
4311
|
+
windowEnd: field.dateTimeISO(),
|
|
4312
|
+
revision: revisionField(),
|
|
4313
|
+
generatedAt: field.dateTimeISO(),
|
|
4314
|
+
acceptedCount: countField(),
|
|
4315
|
+
rejectedCount: countField(),
|
|
4316
|
+
diagnosticsAttachedCount: countField(),
|
|
4317
|
+
deterministicRedactionCount: countField(),
|
|
4318
|
+
scannerRedactionCount: countField(),
|
|
4319
|
+
processorLagSeconds: countField(),
|
|
4320
|
+
rates: field.object({
|
|
4321
|
+
rejectionRate: field.number().min(0).max(1),
|
|
4322
|
+
deterministicRedactionsPerAccepted: field.number().min(0).max(4e3),
|
|
4323
|
+
scannerRedactionsPerAccepted: field.number().min(0).max(4e3)
|
|
4324
|
+
}),
|
|
4325
|
+
traffic: field.object({
|
|
4326
|
+
denominator: countField(),
|
|
4327
|
+
acceptedPerTenThousand: field.number().min(0).max(MAX_COUNT).optional()
|
|
4328
|
+
}),
|
|
4329
|
+
targetDistribution: field.array(field.object(distributionShape(surfaceIdField))).max(256),
|
|
4330
|
+
issueTypeDistribution: field.array(
|
|
4331
|
+
field.object(
|
|
4332
|
+
distributionShape(
|
|
4333
|
+
() => field.string().enum(FEEDBACK_PERSISTABLE_ISSUE_TYPES)
|
|
4334
|
+
)
|
|
4335
|
+
)
|
|
4336
|
+
).max(FEEDBACK_ISSUE_TYPES.length),
|
|
4337
|
+
severityDistribution: field.array(
|
|
4338
|
+
field.object(
|
|
4339
|
+
distributionShape(
|
|
4340
|
+
() => field.string().enum(["1", "2", "3", "4", "5"])
|
|
4341
|
+
)
|
|
4342
|
+
)
|
|
4343
|
+
).max(5),
|
|
4344
|
+
intentDistribution: field.array(
|
|
4345
|
+
field.object(
|
|
4346
|
+
distributionShape(
|
|
4347
|
+
() => field.string().enum(FEEDBACK_INTENT_IDS)
|
|
4348
|
+
)
|
|
4349
|
+
)
|
|
4350
|
+
).max(FEEDBACK_INTENT_IDS.length),
|
|
4351
|
+
buildDistribution: field.array(field.object(distributionShape())).max(128),
|
|
4352
|
+
rendererDistribution: field.array(
|
|
4353
|
+
field.object(
|
|
4354
|
+
distributionShape(
|
|
4355
|
+
() => field.string().enum(FEEDBACK_RENDERER_BUCKETS)
|
|
4356
|
+
)
|
|
4357
|
+
)
|
|
4358
|
+
).max(FEEDBACK_RENDERER_BUCKETS.length),
|
|
4359
|
+
backendDistribution: field.array(
|
|
4360
|
+
field.object(
|
|
4361
|
+
distributionShape(
|
|
4362
|
+
() => field.string().enum(FEEDBACK_BACKEND_BUCKETS)
|
|
4363
|
+
)
|
|
4364
|
+
)
|
|
4365
|
+
).max(FEEDBACK_BACKEND_BUCKETS.length),
|
|
4366
|
+
viewportDistribution: field.array(
|
|
4367
|
+
field.object(
|
|
4368
|
+
distributionShape(
|
|
4369
|
+
() => field.string().enum(FEEDBACK_VIEWPORT_BUCKETS)
|
|
4370
|
+
)
|
|
4371
|
+
)
|
|
4372
|
+
).max(FEEDBACK_VIEWPORT_BUCKETS.length),
|
|
4373
|
+
frameRateDistribution: field.array(
|
|
4374
|
+
field.object(
|
|
4375
|
+
distributionShape(
|
|
4376
|
+
() => field.string().enum(FEEDBACK_FRAME_RATE_BUCKETS)
|
|
4377
|
+
)
|
|
4378
|
+
)
|
|
4379
|
+
).max(FEEDBACK_FRAME_RATE_BUCKETS.length),
|
|
4380
|
+
frameTimeDistribution: field.array(
|
|
4381
|
+
field.object(
|
|
4382
|
+
distributionShape(
|
|
4383
|
+
() => field.string().enum(FEEDBACK_FRAME_TIME_BUCKETS)
|
|
4384
|
+
)
|
|
4385
|
+
)
|
|
4386
|
+
).max(FEEDBACK_FRAME_TIME_BUCKETS.length),
|
|
4387
|
+
diagnosticFeatureDistribution: field.array(
|
|
4388
|
+
field.object(
|
|
4389
|
+
distributionShape(
|
|
4390
|
+
() => field.string().enum(FEEDBACK_GAME_FEATURE_IDS)
|
|
4391
|
+
)
|
|
4392
|
+
)
|
|
4393
|
+
).max(FEEDBACK_GAME_FEATURE_IDS.length),
|
|
4394
|
+
diagnosticCounterDistribution: field.array(
|
|
4395
|
+
field.object(
|
|
4396
|
+
distributionShape(
|
|
4397
|
+
() => field.string().enum(FEEDBACK_GAME_COUNTER_CODES)
|
|
4398
|
+
)
|
|
4399
|
+
)
|
|
4400
|
+
).max(FEEDBACK_GAME_COUNTER_CODES.length),
|
|
4401
|
+
diagnosticErrorDistribution: field.array(
|
|
4402
|
+
field.object(
|
|
4403
|
+
distributionShape(
|
|
4404
|
+
() => field.string().enum(FEEDBACK_GAME_ERROR_CODES)
|
|
4405
|
+
)
|
|
4406
|
+
)
|
|
4407
|
+
).max(FEEDBACK_GAME_ERROR_CODES.length),
|
|
4408
|
+
abuseBlockBands: field.array(
|
|
4409
|
+
field.object(
|
|
4410
|
+
distributionShape(
|
|
4411
|
+
() => field.string().enum(
|
|
4412
|
+
[
|
|
4413
|
+
"five-minutes",
|
|
4414
|
+
"fifteen-minutes",
|
|
4415
|
+
"one-hour",
|
|
4416
|
+
"six-hours",
|
|
4417
|
+
"twenty-four-hours",
|
|
4418
|
+
"fail-closed",
|
|
4419
|
+
"edge-blocked"
|
|
4420
|
+
]
|
|
4421
|
+
)
|
|
4422
|
+
)
|
|
4423
|
+
)
|
|
4424
|
+
).max(16),
|
|
4425
|
+
comparison: field.object({
|
|
4426
|
+
previousHourRatio: field.number().min(0).max(1e3),
|
|
4427
|
+
sevenDaySameHourRatio: field.number().min(0).max(1e3)
|
|
4428
|
+
}),
|
|
4429
|
+
advisories: field.array(field.object(advisoryShape())).max(32)
|
|
4430
|
+
},
|
|
4431
|
+
"feedback-hourly-bug-report",
|
|
4432
|
+
{
|
|
4433
|
+
...strictOptions,
|
|
4434
|
+
schemaValidator: (value) => {
|
|
4435
|
+
const distributions = [
|
|
4436
|
+
value.targetDistribution,
|
|
4437
|
+
value.issueTypeDistribution,
|
|
4438
|
+
value.severityDistribution,
|
|
4439
|
+
value.intentDistribution,
|
|
4440
|
+
value.buildDistribution,
|
|
4441
|
+
value.rendererDistribution,
|
|
4442
|
+
value.backendDistribution,
|
|
4443
|
+
value.viewportDistribution,
|
|
4444
|
+
value.frameRateDistribution,
|
|
4445
|
+
value.frameTimeDistribution,
|
|
4446
|
+
value.diagnosticFeatureDistribution,
|
|
4447
|
+
value.diagnosticCounterDistribution,
|
|
4448
|
+
value.diagnosticErrorDistribution,
|
|
4449
|
+
value.abuseBlockBands
|
|
4450
|
+
];
|
|
4451
|
+
const submittedCount = value.acceptedCount + value.rejectedCount;
|
|
4452
|
+
const expectedRejectionRate = submittedCount === 0 ? 0 : value.rejectedCount / submittedCount;
|
|
4453
|
+
const expectedDeterministicRate = value.acceptedCount === 0 ? 0 : value.deterministicRedactionCount / value.acceptedCount;
|
|
4454
|
+
const expectedScannerRate = value.acceptedCount === 0 ? 0 : value.scannerRedactionCount / value.acceptedCount;
|
|
4455
|
+
const trafficIsConsistent = value.traffic.denominator === 0 && value.traffic.acceptedPerTenThousand === void 0 || value.traffic.denominator > 0 && rateMatches(
|
|
4456
|
+
value.traffic.acceptedPerTenThousand,
|
|
4457
|
+
value.acceptedCount * 1e4 / value.traffic.denominator
|
|
4458
|
+
);
|
|
4459
|
+
const severityFiveCount = distributionCountFor(
|
|
4460
|
+
value.severityDistribution,
|
|
4461
|
+
"5"
|
|
4462
|
+
);
|
|
4463
|
+
const criticalRegression = severityFiveCount >= 3 || value.acceptedCount >= 5 && value.comparison.sevenDaySameHourRatio >= 3;
|
|
4464
|
+
const lagSeconds = Math.floor(
|
|
4465
|
+
(Date.parse(String(value.generatedAt)) - Date.parse(String(value.windowEnd))) / 1e3
|
|
4466
|
+
);
|
|
4467
|
+
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(
|
|
4468
|
+
value.rates.deterministicRedactionsPerAccepted,
|
|
4469
|
+
expectedDeterministicRate
|
|
4470
|
+
) && rateMatches(
|
|
4471
|
+
value.rates.scannerRedactionsPerAccepted,
|
|
4472
|
+
expectedScannerRate
|
|
4473
|
+
) && trafficIsConsistent && distributions.every(hasUniqueIds) && advisoryCodesAreConsistent(value.advisories, {
|
|
4474
|
+
"severity-five": severityFiveCount > 0 ? {
|
|
4475
|
+
level: "advisory",
|
|
4476
|
+
triggerCount: severityFiveCount,
|
|
4477
|
+
recommendationIds: ["inspect-release"]
|
|
4478
|
+
} : void 0,
|
|
4479
|
+
"critical-regression": criticalRegression ? {
|
|
4480
|
+
level: "critical",
|
|
4481
|
+
triggerCount: severityFiveCount >= 3 ? severityFiveCount : value.acceptedCount,
|
|
4482
|
+
recommendationIds: [
|
|
4483
|
+
"inspect-release",
|
|
4484
|
+
"verify-renderer-health"
|
|
4485
|
+
]
|
|
4486
|
+
} : void 0,
|
|
4487
|
+
"processor-stale": lagSeconds > 10 * 60 ? {
|
|
4488
|
+
level: "critical",
|
|
4489
|
+
triggerCount: lagSeconds,
|
|
4490
|
+
recommendationIds: ["verify-processor-health"]
|
|
4491
|
+
} : void 0
|
|
4492
|
+
}) && distributionTotal(value.targetDistribution) === value.acceptedCount && distributionTotal(value.issueTypeDistribution) === value.acceptedCount && distributionTotal(value.severityDistribution) === value.acceptedCount && distributionCountsAtMost(
|
|
4493
|
+
value.intentDistribution,
|
|
4494
|
+
value.acceptedCount
|
|
4495
|
+
) && 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(
|
|
4496
|
+
value.diagnosticFeatureDistribution,
|
|
4497
|
+
value.diagnosticsAttachedCount
|
|
4498
|
+
) && distributionTotal(value.diagnosticCounterDistribution) <= value.diagnosticsAttachedCount * FEEDBACK_GAME_COUNTER_CODES.length * 1e4 && distributionCountsAtMost(
|
|
4499
|
+
value.diagnosticCounterDistribution,
|
|
4500
|
+
value.diagnosticsAttachedCount * 1e4
|
|
4501
|
+
) && distributionTotal(value.diagnosticErrorDistribution) <= value.diagnosticsAttachedCount * FEEDBACK_GAME_ERROR_CODES.length && distributionCountsAtMost(
|
|
4502
|
+
value.diagnosticErrorDistribution,
|
|
4503
|
+
value.diagnosticsAttachedCount
|
|
4504
|
+
);
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
);
|
|
4508
|
+
var rollingSatisfactionShape = () => ({
|
|
4509
|
+
period: field.string().enum(["7-days", "30-days", "90-days"]),
|
|
4510
|
+
reviewCount: countField(),
|
|
4511
|
+
meanStars: field.number().min(1).max(5).optional()
|
|
4512
|
+
});
|
|
4513
|
+
var satisfactionStatistics = (distribution, total) => {
|
|
4514
|
+
if (!Array.isArray(distribution) || total <= 0) {
|
|
4515
|
+
return void 0;
|
|
4516
|
+
}
|
|
4517
|
+
const counts = [1, 2, 3, 4, 5].map(
|
|
4518
|
+
(star) => distributionCountFor(distribution, String(star))
|
|
4519
|
+
);
|
|
4520
|
+
const weightedTotal = counts.reduce(
|
|
4521
|
+
(sum, count, index) => sum + count * (index + 1),
|
|
4522
|
+
0
|
|
4523
|
+
);
|
|
4524
|
+
const valueAtPosition = (position) => {
|
|
4525
|
+
let cumulative = 0;
|
|
4526
|
+
for (let index = 0; index < counts.length; index += 1) {
|
|
4527
|
+
cumulative += counts[index] ?? 0;
|
|
4528
|
+
if (cumulative >= position) {
|
|
4529
|
+
return index + 1;
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
return 5;
|
|
4533
|
+
};
|
|
4534
|
+
const lower = valueAtPosition(Math.ceil(total / 2));
|
|
4535
|
+
const upper = valueAtPosition(Math.floor(total / 2) + 1);
|
|
4536
|
+
return {
|
|
4537
|
+
mean: Math.round(weightedTotal / total * 10) / 10,
|
|
4538
|
+
median: (lower + upper) / 2
|
|
4539
|
+
};
|
|
4540
|
+
};
|
|
4541
|
+
var FeedbackDailySatisfactionReportSchema = createSchema(
|
|
4542
|
+
{
|
|
4543
|
+
reportId: uuidField(),
|
|
4544
|
+
windowStart: field.dateTimeISO(),
|
|
4545
|
+
windowEnd: field.dateTimeISO(),
|
|
4546
|
+
revision: revisionField(),
|
|
4547
|
+
generatedAt: field.dateTimeISO(),
|
|
4548
|
+
processorLagSeconds: countField(),
|
|
4549
|
+
acceptedReviewCount: countField(),
|
|
4550
|
+
meanStars: field.number().min(1).max(5).optional(),
|
|
4551
|
+
medianStars: field.number().min(1).max(5).optional(),
|
|
4552
|
+
starDistribution: field.array(
|
|
4553
|
+
field.object(
|
|
4554
|
+
distributionShape(
|
|
4555
|
+
() => field.string().enum(["1", "2", "3", "4", "5"])
|
|
4556
|
+
)
|
|
4557
|
+
)
|
|
4558
|
+
).max(5),
|
|
4559
|
+
sentimentDistribution: field.array(
|
|
4560
|
+
field.object(
|
|
4561
|
+
distributionShape(
|
|
4562
|
+
() => field.string().enum(FEEDBACK_SENTIMENT_BUCKETS)
|
|
4563
|
+
)
|
|
4564
|
+
)
|
|
4565
|
+
).max(FEEDBACK_SENTIMENT_BUCKETS.length),
|
|
4566
|
+
intentDistribution: field.array(
|
|
4567
|
+
field.object(
|
|
4568
|
+
distributionShape(
|
|
4569
|
+
() => field.string().enum(FEEDBACK_INTENT_IDS)
|
|
4570
|
+
)
|
|
4571
|
+
)
|
|
4572
|
+
).max(FEEDBACK_INTENT_IDS.length),
|
|
4573
|
+
rollingWindows: field.array(field.object(rollingSatisfactionShape())).max(3),
|
|
4574
|
+
previousPeriodDeltaStars: field.number().min(-4).max(4).optional(),
|
|
4575
|
+
advisories: field.array(field.object(advisoryShape())).max(32)
|
|
4576
|
+
},
|
|
4577
|
+
"feedback-daily-satisfaction-report",
|
|
4578
|
+
{
|
|
4579
|
+
...strictOptions,
|
|
4580
|
+
schemaValidator: (value) => {
|
|
4581
|
+
if (!orderedWindow(value, 24 * 60 * 60 * 1e3)) {
|
|
4582
|
+
return false;
|
|
4583
|
+
}
|
|
4584
|
+
if (value.acceptedReviewCount === 0 && (value.meanStars !== void 0 || value.medianStars !== void 0)) {
|
|
4585
|
+
return false;
|
|
4586
|
+
}
|
|
4587
|
+
if (value.acceptedReviewCount > 0 && (value.meanStars === void 0 || value.medianStars === void 0)) {
|
|
4588
|
+
return false;
|
|
4589
|
+
}
|
|
4590
|
+
const distributions = [
|
|
4591
|
+
value.starDistribution,
|
|
4592
|
+
value.sentimentDistribution,
|
|
4593
|
+
value.intentDistribution
|
|
4594
|
+
];
|
|
4595
|
+
const periods = value.rollingWindows.map(
|
|
4596
|
+
(window) => window.period
|
|
4597
|
+
);
|
|
4598
|
+
const sevenDayWindow = value.rollingWindows.find(
|
|
4599
|
+
(window) => window.period === "7-days"
|
|
4600
|
+
);
|
|
4601
|
+
const thirtyDayWindow = value.rollingWindows.find(
|
|
4602
|
+
(window) => window.period === "30-days"
|
|
4603
|
+
);
|
|
4604
|
+
const ninetyDayWindow = value.rollingWindows.find(
|
|
4605
|
+
(window) => window.period === "90-days"
|
|
4606
|
+
);
|
|
4607
|
+
if (sevenDayWindow === void 0 || thirtyDayWindow === void 0 || ninetyDayWindow === void 0) {
|
|
4608
|
+
return false;
|
|
4609
|
+
}
|
|
4610
|
+
const statistics = satisfactionStatistics(
|
|
4611
|
+
value.starDistribution,
|
|
4612
|
+
value.acceptedReviewCount
|
|
4613
|
+
);
|
|
4614
|
+
const lagSeconds = Math.floor(
|
|
4615
|
+
(Date.parse(String(value.generatedAt)) - Date.parse(String(value.windowEnd))) / 1e3
|
|
4616
|
+
);
|
|
4617
|
+
const satisfactionDrop = sevenDayWindow.reviewCount >= 20 && typeof value.previousPeriodDeltaStars === "number" && value.previousPeriodDeltaStars <= -0.3;
|
|
4618
|
+
return value.revision >= 1 && value.processorLagSeconds === lagSeconds && distributions.every(hasUniqueIds) && distributionTotal(value.starDistribution) === value.acceptedReviewCount && distributionTotal(value.sentimentDistribution) <= value.acceptedReviewCount && distributionCountsAtMost(
|
|
4619
|
+
value.sentimentDistribution,
|
|
4620
|
+
value.acceptedReviewCount
|
|
4621
|
+
) && distributionTotal(value.intentDistribution) <= value.acceptedReviewCount * 8 && distributionCountsAtMost(
|
|
4622
|
+
value.intentDistribution,
|
|
4623
|
+
value.acceptedReviewCount
|
|
4624
|
+
) && 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, {
|
|
4625
|
+
"satisfaction-drop": satisfactionDrop ? {
|
|
4626
|
+
level: "advisory",
|
|
4627
|
+
triggerCount: sevenDayWindow.reviewCount,
|
|
4628
|
+
recommendationIds: ["review-top-intents"]
|
|
4629
|
+
} : void 0,
|
|
4630
|
+
"processor-stale": lagSeconds > 2.5 * 60 * 60 ? {
|
|
4631
|
+
level: "critical",
|
|
4632
|
+
triggerCount: lagSeconds,
|
|
4633
|
+
recommendationIds: ["verify-processor-health"]
|
|
4634
|
+
} : void 0
|
|
4635
|
+
}) && value.rollingWindows.every(
|
|
4636
|
+
(window) => window.reviewCount === 0 && window.meanStars === void 0 || window.reviewCount > 0 && window.meanStars !== void 0
|
|
4637
|
+
) && value.acceptedReviewCount <= sevenDayWindow.reviewCount && sevenDayWindow.reviewCount <= thirtyDayWindow.reviewCount && thirtyDayWindow.reviewCount <= ninetyDayWindow.reviewCount;
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
);
|
|
4641
|
+
var publicWeeklyPointShape = () => ({
|
|
4642
|
+
weekStart: field.dateTimeISO(),
|
|
4643
|
+
state: field.string().enum(["published", "suppressed"]),
|
|
4644
|
+
acceptedReviewCount: positiveCountField2().optional(),
|
|
4645
|
+
averageStars: field.number().min(1).max(5).optional()
|
|
4646
|
+
});
|
|
4647
|
+
var publicComparisonShape = () => ({
|
|
4648
|
+
previousAcceptedReviewCount: positiveCountField2(),
|
|
4649
|
+
previousAverageStars: field.number().min(1).max(5),
|
|
4650
|
+
deltaStars: field.number().min(-4).max(4)
|
|
4651
|
+
});
|
|
4652
|
+
var FeedbackPublicSummarySchema = createSchema(
|
|
4653
|
+
{
|
|
4654
|
+
snapshotId: uuidField(),
|
|
4655
|
+
generatedAt: field.dateTimeISO(),
|
|
4656
|
+
asOf: field.dateTimeISO(),
|
|
4657
|
+
freshness: field.string().enum(["fresh", "stale"]),
|
|
4658
|
+
reportAgeSeconds: countField(),
|
|
4659
|
+
rollingWindowDays: field.number().enum([90]),
|
|
4660
|
+
state: field.string().enum(["published", "suppressed"]),
|
|
4661
|
+
acceptedReviewCount: positiveCountField2().optional(),
|
|
4662
|
+
averageStars: field.number().min(1).max(5).optional(),
|
|
4663
|
+
trend: field.string().enum(["up", "flat", "down"]).optional(),
|
|
4664
|
+
comparison: field.object(publicComparisonShape()).optional(),
|
|
4665
|
+
weeklyPoints: field.array(field.object(publicWeeklyPointShape())).min(13).max(13)
|
|
4666
|
+
},
|
|
4667
|
+
"feedback-public-summary",
|
|
4668
|
+
{
|
|
4669
|
+
...strictOptions,
|
|
4670
|
+
schemaValidator: (value) => {
|
|
4671
|
+
const summaryIsSuppressed = value.state === "suppressed" && value.acceptedReviewCount === void 0 && value.averageStars === void 0 && value.trend === void 0 && value.comparison === void 0;
|
|
4672
|
+
const comparisonDelta = value.comparison === void 0 ? void 0 : roundRate(
|
|
4673
|
+
value.averageStars - value.comparison.previousAverageStars
|
|
4674
|
+
);
|
|
4675
|
+
const expectedTrend = comparisonDelta === void 0 ? void 0 : comparisonDelta >= 0.1 ? "up" : comparisonDelta <= -0.1 ? "down" : "flat";
|
|
4676
|
+
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;
|
|
4677
|
+
const summaryIsPublished = value.state === "published" && typeof value.acceptedReviewCount === "number" && value.acceptedReviewCount >= 10 && typeof value.averageStars === "number" && comparisonIsConsistent;
|
|
4678
|
+
if (!summaryIsSuppressed && !summaryIsPublished) {
|
|
4679
|
+
return false;
|
|
4680
|
+
}
|
|
4681
|
+
const weekStarts = value.weeklyPoints.map(
|
|
4682
|
+
(point) => point.weekStart
|
|
4683
|
+
);
|
|
4684
|
+
const asOf = Date.parse(String(value.asOf));
|
|
4685
|
+
const generatedAt = Date.parse(String(value.generatedAt));
|
|
4686
|
+
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)) {
|
|
4687
|
+
return false;
|
|
4688
|
+
}
|
|
4689
|
+
const daysSinceMonday = (new Date(asOf).getUTCDay() + 6) % 7;
|
|
4690
|
+
const lastWeekStart = asOf - daysSinceMonday * 24 * 60 * 60 * 1e3;
|
|
4691
|
+
const weeksAreCanonical = weekStarts.every(
|
|
4692
|
+
(weekStart, index) => Date.parse(weekStart) === lastWeekStart - (12 - index) * 7 * 24 * 60 * 60 * 1e3
|
|
4693
|
+
);
|
|
4694
|
+
return new Set(weekStarts).size === weekStarts.length && weeksAreCanonical && (value.state !== "suppressed" || value.weeklyPoints.every(
|
|
4695
|
+
(point) => point.state === "suppressed"
|
|
4696
|
+
)) && value.weeklyPoints.every(
|
|
4697
|
+
(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)
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
);
|
|
4702
|
+
var FeedbackProcessorCheckpointSchema = createSchema(
|
|
4703
|
+
{
|
|
4704
|
+
checkpointId: safeIdField(160),
|
|
4705
|
+
processor: field.string().enum(["bug-hourly", "review-daily", "commit-reconciliation"]),
|
|
4706
|
+
windowKey: safeIdField(160),
|
|
4707
|
+
revision: revisionField(),
|
|
4708
|
+
completedAt: field.dateTimeISO(),
|
|
4709
|
+
reportId: uuidField().optional()
|
|
4710
|
+
},
|
|
4711
|
+
"feedback-processor-checkpoint",
|
|
4712
|
+
{
|
|
4713
|
+
...strictOptions,
|
|
4714
|
+
schemaValidator: (value) => isProcessorArtifactConsistent(value, "checkpoint") && value.revision >= 1 && (value.processor === "commit-reconciliation" && value.reportId === void 0 || value.processor !== "commit-reconciliation" && value.reportId !== void 0)
|
|
4715
|
+
}
|
|
4716
|
+
);
|
|
4717
|
+
var FeedbackMaterializationManifestSchema = createSchema(
|
|
4718
|
+
{
|
|
4719
|
+
manifestId: uuidField(),
|
|
4720
|
+
processor: field.string().enum(["bug-hourly", "review-daily", "commit-reconciliation"]),
|
|
4721
|
+
windowKey: safeIdField(160),
|
|
4722
|
+
revision: revisionField(),
|
|
4723
|
+
generatedAt: field.dateTimeISO(),
|
|
4724
|
+
sourcePacketCount: countField(),
|
|
4725
|
+
lateArrivalCount: countField(),
|
|
4726
|
+
outputReportId: uuidField().optional(),
|
|
4727
|
+
status: field.string().enum(["published", "corrected", "no-op"])
|
|
4728
|
+
},
|
|
4729
|
+
"feedback-materialization-manifest",
|
|
4730
|
+
{
|
|
4731
|
+
...strictOptions,
|
|
4732
|
+
schemaValidator: (value) => {
|
|
4733
|
+
if (!isProcessorArtifactConsistent(value, "manifest") || value.lateArrivalCount > value.sourcePacketCount) {
|
|
4734
|
+
return false;
|
|
4735
|
+
}
|
|
4736
|
+
if (value.processor === "commit-reconciliation") {
|
|
4737
|
+
return value.status === "no-op" && value.revision >= 1 && value.outputReportId === void 0 && value.lateArrivalCount === 0;
|
|
4738
|
+
}
|
|
4739
|
+
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;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
);
|
|
2562
4743
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2563
4744
|
0 && (module.exports = {
|
|
4745
|
+
FEEDBACK_ANALYSIS_MODEL_VERSIONS,
|
|
4746
|
+
FEEDBACK_BACKEND_BUCKETS,
|
|
4747
|
+
FEEDBACK_BUG_COOLDOWN_SECONDS,
|
|
4748
|
+
FEEDBACK_CONTRACT_VERSION,
|
|
4749
|
+
FEEDBACK_FRAME_RATE_BUCKETS,
|
|
4750
|
+
FEEDBACK_FRAME_TIME_BUCKETS,
|
|
4751
|
+
FEEDBACK_GAME_COUNTER_CODES,
|
|
4752
|
+
FEEDBACK_GAME_COUNTER_MAX_COUNT,
|
|
4753
|
+
FEEDBACK_GAME_COUNTER_MAX_ITEMS,
|
|
4754
|
+
FEEDBACK_GAME_ERROR_CODES,
|
|
4755
|
+
FEEDBACK_GAME_FEATURE_IDS,
|
|
4756
|
+
FEEDBACK_GAME_PROVENANCE_CONTRACTS,
|
|
4757
|
+
FEEDBACK_GAME_PROVENANCE_CONTRACT_IDS,
|
|
4758
|
+
FEEDBACK_GAME_SURFACE_IDS,
|
|
4759
|
+
FEEDBACK_INTENT_IDS,
|
|
4760
|
+
FEEDBACK_ISSUE_TYPES,
|
|
4761
|
+
FEEDBACK_PERSISTABLE_ISSUE_TYPES,
|
|
4762
|
+
FEEDBACK_PRIVACY_POLICY_VERSIONS,
|
|
4763
|
+
FEEDBACK_RENDERER_BUCKETS,
|
|
4764
|
+
FEEDBACK_REVIEW_COOLDOWN_SECONDS,
|
|
4765
|
+
FEEDBACK_SATISFACTION_LEVELS,
|
|
4766
|
+
FEEDBACK_SENTIMENT_BUCKETS,
|
|
4767
|
+
FEEDBACK_SEVERITY_LEVELS,
|
|
4768
|
+
FEEDBACK_STRUCTURED_ONLY_REASON_CODES,
|
|
4769
|
+
FEEDBACK_SURFACE_DEFINITIONS,
|
|
4770
|
+
FEEDBACK_SURFACE_IDS,
|
|
4771
|
+
FEEDBACK_THEME_IDS,
|
|
4772
|
+
FEEDBACK_UNICODE_NORMALIZATION_PROFILE,
|
|
4773
|
+
FEEDBACK_VIEWPORT_BUCKETS,
|
|
4774
|
+
FeedbackAcceptanceReceiptSchema,
|
|
4775
|
+
FeedbackAnalysisReceiptSchema,
|
|
4776
|
+
FeedbackBugPacketSchema,
|
|
4777
|
+
FeedbackBugSubmissionRequestSchema,
|
|
4778
|
+
FeedbackContextSchema,
|
|
4779
|
+
FeedbackDailySatisfactionReportSchema,
|
|
4780
|
+
FeedbackDerivedAnalysisSchema,
|
|
4781
|
+
FeedbackDraftPacketSchema,
|
|
4782
|
+
FeedbackDraftReceiptSchema,
|
|
4783
|
+
FeedbackDraftUpsertRequestSchema,
|
|
4784
|
+
FeedbackEncryptedNarrativeEnvelopeSchema,
|
|
4785
|
+
FeedbackGameDiagnosticsSchema,
|
|
4786
|
+
FeedbackGameReconstructionManifestSchema,
|
|
4787
|
+
FeedbackHourlyBugReportSchema,
|
|
4788
|
+
FeedbackMaterializationManifestSchema,
|
|
4789
|
+
FeedbackProcessorCheckpointSchema,
|
|
4790
|
+
FeedbackPublicSummarySchema,
|
|
4791
|
+
FeedbackReviewPacketSchema,
|
|
4792
|
+
FeedbackReviewSubmissionRequestSchema,
|
|
4793
|
+
FeedbackRichTextAstSchema,
|
|
4794
|
+
FeedbackStructuredOnlyAnalysisResultSchema,
|
|
4795
|
+
FeedbackSurfaceCatalogSchema,
|
|
4796
|
+
FeedbackTransientAnalysisRequestSchema,
|
|
2564
4797
|
FieldBuilder,
|
|
2565
4798
|
IsoLanguageCode,
|
|
4799
|
+
PLAYER_SYSTEM_ACHIEVEMENT_TIERS,
|
|
4800
|
+
PLAYER_SYSTEM_ACHIEVEMENT_TRACK_STATUSES,
|
|
4801
|
+
PLAYER_SYSTEM_EVENT_KINDS,
|
|
4802
|
+
PlayerSystemCuratedSnapshotSchema,
|
|
4803
|
+
PlayerSystemNormalizedEventBatchSchema,
|
|
4804
|
+
PlayerSystemObservedEventBatchSchema,
|
|
4805
|
+
SCHEMA_IDENTITY_POLICIES,
|
|
4806
|
+
SCHEMA_UNKNOWN_FIELDS_POLICIES,
|
|
2566
4807
|
createComponentSchema,
|
|
2567
4808
|
createSchema,
|
|
2568
4809
|
field,
|
|
@@ -2585,7 +4826,10 @@ function getAllComponentSchemas() {
|
|
|
2585
4826
|
validateCountryCode,
|
|
2586
4827
|
validateCurrencyCode,
|
|
2587
4828
|
validateDateTimeISO,
|
|
4829
|
+
validateDisplayName,
|
|
2588
4830
|
validateEmail,
|
|
4831
|
+
validateFeedbackGameDiagnostics,
|
|
4832
|
+
validateFeedbackTransientAnalysisRequest,
|
|
2589
4833
|
validateLanguage,
|
|
2590
4834
|
validateName,
|
|
2591
4835
|
validatePercentage,
|