@debugbundle/sdk-react-native 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/DebugBundleReactNative.podspec +2 -2
  3. package/MIGRATION-3.1.md +116 -0
  4. package/README.md +37 -1
  5. package/android/build.gradle +1 -1
  6. package/android/src/main/java/com/debugbundle/reactnative/DebugBundleReactNativeModule.java +34 -3
  7. package/app.plugin.js +1 -1
  8. package/dist/NativeDebugBundleReactNative.d.ts +1 -0
  9. package/dist/NativeDebugBundleReactNative.d.ts.map +1 -1
  10. package/dist/before-send.d.ts.map +1 -1
  11. package/dist/before-send.js +9 -131
  12. package/dist/canonical-schema.d.ts +15 -0
  13. package/dist/canonical-schema.d.ts.map +1 -0
  14. package/dist/canonical-schema.js +139 -0
  15. package/dist/client-values.d.ts +17 -0
  16. package/dist/client-values.d.ts.map +1 -0
  17. package/dist/client-values.js +185 -0
  18. package/dist/client.d.ts +7 -0
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +102 -205
  21. package/dist/diagnostic-budget.d.ts +16 -0
  22. package/dist/diagnostic-budget.d.ts.map +1 -0
  23. package/dist/diagnostic-budget.js +120 -0
  24. package/dist/event-schema-data.d.ts +2 -0
  25. package/dist/event-schema-data.d.ts.map +1 -0
  26. package/dist/event-schema-data.js +49 -0
  27. package/dist/evidence-capabilities.d.ts +5 -0
  28. package/dist/evidence-capabilities.d.ts.map +1 -0
  29. package/dist/evidence-capabilities.js +21 -0
  30. package/dist/evidence-controller.d.ts +24 -0
  31. package/dist/evidence-controller.d.ts.map +1 -0
  32. package/dist/evidence-controller.js +113 -0
  33. package/dist/evidence-types.d.ts +33 -0
  34. package/dist/evidence-types.d.ts.map +1 -0
  35. package/dist/evidence-types.js +1 -0
  36. package/dist/evidence-wire.d.ts +7 -0
  37. package/dist/evidence-wire.d.ts.map +1 -0
  38. package/dist/evidence-wire.js +132 -0
  39. package/dist/exception-evidence.d.ts +25 -0
  40. package/dist/exception-evidence.d.ts.map +1 -0
  41. package/dist/exception-evidence.js +175 -0
  42. package/dist/index.d.ts +6 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +4 -0
  45. package/dist/owned-json.d.ts +11 -0
  46. package/dist/owned-json.d.ts.map +1 -0
  47. package/dist/owned-json.js +85 -0
  48. package/dist/privacy-keys.d.ts +2 -0
  49. package/dist/privacy-keys.d.ts.map +1 -1
  50. package/dist/privacy-keys.js +10 -2
  51. package/dist/privacy-telemetry.js +7 -7
  52. package/dist/privacy-text.d.ts +2 -2
  53. package/dist/privacy-text.d.ts.map +1 -1
  54. package/dist/privacy-text.js +35 -33
  55. package/dist/redaction.d.ts.map +1 -1
  56. package/dist/redaction.js +9 -7
  57. package/dist/safe-error-fields.d.ts +4 -0
  58. package/dist/safe-error-fields.d.ts.map +1 -0
  59. package/dist/safe-error-fields.js +54 -0
  60. package/dist/types.d.ts +21 -6
  61. package/dist/types.d.ts.map +1 -1
  62. package/ios/DebugBundleReactNative.swift +58 -4
  63. package/ios/DebugBundleReactNativeBridge.mm +4 -0
  64. package/package.json +3 -2
  65. package/src/NativeDebugBundleReactNative.ts +1 -0
  66. package/src/before-send.ts +10 -174
  67. package/src/canonical-schema.ts +108 -0
  68. package/src/client-values.ts +196 -0
  69. package/src/client.ts +92 -213
  70. package/src/diagnostic-budget.ts +90 -0
  71. package/src/event-schema-data.ts +49 -0
  72. package/src/evidence-capabilities.ts +19 -0
  73. package/src/evidence-controller.ts +89 -0
  74. package/src/evidence-types.ts +14 -0
  75. package/src/evidence-wire.ts +100 -0
  76. package/src/exception-evidence.ts +112 -0
  77. package/src/index.ts +6 -0
  78. package/src/owned-json.ts +60 -0
  79. package/src/privacy-keys.ts +12 -2
  80. package/src/privacy-telemetry.ts +9 -9
  81. package/src/privacy-text.ts +32 -31
  82. package/src/redaction.ts +8 -7
  83. package/src/safe-error-fields.ts +46 -0
  84. package/src/types.ts +23 -5
@@ -0,0 +1,90 @@
1
+ import type { DebugBundleEventEnvelope } from "./types.js";
2
+ import { evidenceReference } from "./evidence-wire.js";
3
+ import { utf8Bytes } from "./owned-json.js";
4
+
5
+ interface Window { startsAt: number; failureAt: number | null; failureId: string | null; attempts: Set<string>; observations: number;
6
+ resource: boolean; ambiguous: boolean; pending: number }
7
+ interface Pending { key: string; bytes: number; attempt: string | null; kind: string }
8
+ const cost = (value: unknown): number => utf8Bytes(JSON.stringify(value));
9
+ function scopeKey(event: DebugBundleEventEnvelope, reference: Record<string, unknown>): string {
10
+ const scope = reference.scope as Record<string, unknown>;
11
+ return JSON.stringify([event.service.name, event.service.environment, event.correlation?.trace_id ?? null,
12
+ event.correlation?.session_id ?? null, reference.failure_ref, [scope.kind, scope.operation_id ?? scope.page_id]]);
13
+ }
14
+ /** Bridge ownership lasts until its promise settles; native offline ownership then
15
+ * remains with the native SDK. Observation/attempt allowances are never refunded. */
16
+ export class MobileDiagnosticBudget {
17
+ private windows = new Map<string, Window>();
18
+ private pending = new Map<string, Pending>();
19
+ get accountedBytes(): number {
20
+ let bytes = 0;
21
+ for (const [key, window] of this.windows) bytes += cost([key, { ...window, attempts: [...window.attempts] }]);
22
+ for (const [id, pending] of this.pending) bytes += pending.bytes + cost([id, pending.key, pending.attempt, pending.kind]);
23
+ return bytes;
24
+ }
25
+ private prune(now: number): void {
26
+ for (const [key, window] of this.windows) if (!window.pending && now >= window.startsAt + 300_000) this.windows.delete(key);
27
+ }
28
+ register(event: DebugBundleEventEnvelope): void {
29
+ const reference = event.context?.evidence_reference;
30
+ if (!evidenceReference(reference)) return;
31
+ const now = Date.parse(event.occurred_at); if (!Number.isFinite(now)) return;
32
+ this.prune(now);
33
+ const key = scopeKey(event, reference as Record<string, unknown>), previous = this.windows.get(key);
34
+ if (!previous && this.windows.size >= 50) return;
35
+ const next = { ...(previous ?? this.fresh(now)), failureAt: previous?.failureAt ?? now, failureId: event.event_id };
36
+ if (previous?.failureId && previous.failureId !== event.event_id) next.ambiguous = true;
37
+ this.windows.set(key, next);
38
+ if (this.accountedBytes > 65_536) { if (previous) this.windows.set(key, previous); else this.windows.delete(key); }
39
+ }
40
+ reserve(event: DebugBundleEventEnvelope, owner = event.event_id): boolean {
41
+ const now = Date.parse(event.occurred_at);
42
+ if (!Number.isFinite(now) || this.pending.has(owner)) return false;
43
+ this.prune(now);
44
+ const payload = event.payload, key = scopeKey(event, payload), previous = this.windows.get(key);
45
+ if (!previous && this.windows.size >= 50 || previous?.ambiguous) return false;
46
+ const next = { ...(previous ?? this.fresh(now)), attempts: new Set(previous?.attempts), pending: (previous?.pending ?? 0) + 1 };
47
+ if (now < (next.failureAt ?? next.startsAt) || now > (next.failureAt ?? next.startsAt) + 300_000 || next.observations >= 20) return false;
48
+ const attempt = payload.kind === "recovery" ? String(payload.attempt_id) : null;
49
+ if (attempt !== null) { if (!next.attempts.has(attempt) && next.attempts.size >= 5) return false; next.attempts.add(attempt); }
50
+ else {
51
+ if (next.resource || next.failureId !== payload.source_event_id || next.failureAt === null || now > next.failureAt + 30_000) return false;
52
+ next.resource = true;
53
+ }
54
+ next.observations++;
55
+ this.windows.set(key, next);
56
+ this.pending.set(owner, { key, attempt, kind: String(payload.kind), bytes: cost(event) });
57
+ if (this.accountedBytes > 65_536) {
58
+ this.pending.delete(owner);
59
+ if (previous) this.windows.set(key, previous); else this.windows.delete(key);
60
+ return false;
61
+ }
62
+ return true;
63
+ }
64
+ replace(owner: string, event: DebugBundleEventEnvelope): boolean {
65
+ const current = this.pending.get(owner), payload = event.payload, key = scopeKey(event, payload);
66
+ const attempt = payload.kind === "recovery" ? String(payload.attempt_id) : null;
67
+ if (!current || current.key !== key || current.attempt !== attempt || current.kind !== payload.kind) {
68
+ this.release(owner); return this.reserve(event, owner);
69
+ }
70
+ // Hook identity may stay stable while occurrence time or resource identity
71
+ // changes. Revalidate association without spending another observation.
72
+ const window = this.windows.get(key), now = Date.parse(event.occurred_at);
73
+ if (!window || window.ambiguous || !Number.isFinite(now) ||
74
+ now < (window.failureAt ?? window.startsAt) || now > (window.failureAt ?? window.startsAt) + 300_000 ||
75
+ attempt === null && (window.failureAt === null || payload.source_event_id !== window.failureId || now > window.failureAt + 30_000)) {
76
+ this.release(owner); return false;
77
+ }
78
+ const previous = current.bytes;
79
+ current.bytes = cost(event);
80
+ if (this.accountedBytes > 65_536) { current.bytes = previous; this.release(owner); return false; }
81
+ return true;
82
+ }
83
+ release(id: string): void {
84
+ const pending = this.pending.get(id); if (!pending) return;
85
+ this.pending.delete(id);
86
+ const window = this.windows.get(pending.key); if (window) window.pending = Math.max(0, window.pending - 1);
87
+ }
88
+ clear(): void { this.windows.clear(); this.pending.clear(); }
89
+ private fresh(now: number): Window { return { startsAt: now, failureAt: null, failureId: null, attempts: new Set(), observations: 0, resource: false, ambiguous: false, pending: 0 }; }
90
+ }
@@ -0,0 +1,49 @@
1
+ // Generated by scripts/sync-evidence-fixtures.ts. Do not edit by hand.
2
+ // Plain contract data keeps the native JS runtime independent of schema-library versions.
3
+ export const eventSchemaData: unknown = JSON.parse([
4
+ "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"diagnostic_context\"},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{",
5
+ "\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"kind\":{\"type\":\"string\",\"const\":\"recovery\"},\"failure_ref\":{\"type\":\"string\",\"format\":\"uuid\"},\"scope\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"page\"},\"page_id\":{\"type\":\"string\",\"format\":\"uuid\"}},\"required\":[\"kind\",\"page_id\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"operation\"},\"operation_id\":{\"type\":\"string\",\"format\":\"uuid\"}},\"required\":[\"kind\",\"operation_id\"],\"additionalProperties\":false}]},\"attempt_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"state\":{\"type\":\"string\",\"enum\":[\"attempt_started\",\"attempt_completed\",\"attempt_failed\",\"recovery_observed\",\"failure_persisted\"]},\"operation\":{\"type\":\"string\",\"maxLength\":64},\"status_code\":{\"type\":\"integer\",\"minimum\":100,\"maximum\":599},\"duration_ms\":{\"type\":\"number\",\"minimum\":0}},\"required\":[\"vers",
6
+ "ion\",\"kind\",\"failure_ref\",\"scope\",\"attempt_id\",\"state\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"kind\":{\"type\":\"string\",\"const\":\"resource_observation\"},\"failure_ref\":{\"type\":\"string\",\"format\":\"uuid\"},\"scope\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"page\"},\"page_id\":{\"type\":\"string\",\"format\":\"uuid\"}},\"required\":[\"kind\",\"page_id\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"operation\"},\"operation_id\":{\"type\":\"string\",\"format\":\"uuid\"}},\"required\":[\"kind\",\"operation_id\"],\"additionalProperties\":false}]},\"source_event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"observations\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"items\":{\"minItems\":1,\"maxItems\":5,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"timing\"},\"observed_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"source_url\":{\"anyOf\":[{\"type\":\"string\",\"maxLeng",
7
+ "th\":2048},{\"type\":\"null\"}]},\"availability\":{\"type\":\"string\",\"enum\":[\"observed\",\"unsupported\",\"no_match\",\"withheld\",\"ambiguous\",\"unknown\"]},\"response_status\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":100,\"maximum\":599},{\"type\":\"null\"}]},\"duration_ms\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"transfer_size_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]}},\"required\":[\"kind\",\"observed_at\",\"source_url\",\"availability\",\"response_status\",\"duration_ms\",\"transfer_size_bytes\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"csp\"},\"observed_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"source_url\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":2048},{\"type\":\"null\"}]},\"effective_directive\":{\"type\":\"string\",\"pattern\":\"^[a-z-]{1,64}$\"},\"disposition\":{\"type\":\"string\",\"enum\":[\"enforce\",\"report\"]},\"document_status_code\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":100,\"maximum\":599},{\"type\":\"null\"}]}},\"required\":[\"kind\",\"observed_at\",\"source",
8
+ "_url\",\"effective_directive\",\"disposition\",\"document_status_code\"],\"additionalProperties\":false}]}}},\"required\":[\"version\",\"items\"],\"additionalProperties\":false}},\"required\":[\"version\",\"kind\",\"failure_ref\",\"scope\",\"source_event_id\",\"observations\"],\"additionalProperties\":false}]}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"backend_exception\"},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{",
9
+ "\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"message\":{\"type\":\"string\",\"minLength\":1},\"stack\":{\"type\":\"string\",\"minLength\":1},\"handled\":{\"type\":\"boolean\"},\"request\":{\"type\":\"object\",\"properties\":{\"method\":{\"type\":\"string\",\"minLength\":1},\"path\":{\"type\":\"string\",\"minLength\":1},\"query\":{\"type\":\"object\",\"additionalProperties\":{}},\"headers\":{\"type\":\"object\",\"additionalProperties\":{}},\"body\":{\"anyOf\":[{},{\"type\":\"null\"}]}},\"required\":[\"method\",\"path\",\"query\",\"headers\"],\"additionalProperties\":false},\"response\":{\"type\":",
10
+ "\"object\",\"properties\":{\"status_code\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"headers\":{\"type\":\"object\",\"additionalProperties\":{}},\"body\":{}},\"required\":[\"status_code\"],\"additionalProperties\":false},\"runtime\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"string\",\"minLength\":1},\"platform\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"arch\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"pid\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"cwd\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"uptime_sec\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"hostname\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"thread_id\":{\"type\":[\"string\",\"number\",\"null\"]},\"framework_version\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"memory\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"rss\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"heap_total\":{\"anyOf\":[{\"type\":\"number\",\"min",
11
+ "imum\":0},{\"type\":\"null\"}]},\"heap_used\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"external\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"peak\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]}},\"required\":[\"rss\",\"heap_total\",\"heap_used\",\"external\",\"peak\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"framework_extras\":{\"anyOf\":[{\"type\":\"object\",\"additionalProperties\":{}},{\"type\":\"null\"}]}},\"required\":[\"version\"],\"additionalProperties\":false},\"probe_data\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"items\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1},\"data\":{\"type\":\"object\",\"additionalProperties\":{}},\"timestamp\":{\"type\":\"string\",\"format\":\"date-time\"},\"activation_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]}},\"required\":[\"label\",\"data\",\"timestamp\",\"activation_id\"],\"additionalProperties\":false}}},\"required\":[\"version\",\"items\"],\"additionalProperties\":false},\"exception_evidence\":{",
12
+ "\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"runtime\":{\"type\":\"string\",\"enum\":[\"node\",\"browser\",\"python\",\"php\",\"jvm\",\"go\",\"ruby\",\"dotnet\",\"android\",\"swift\",\"react_native\",\"unknown\"]},\"root_id\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":7},\"nodes\":{\"minItems\":1,\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":7},\"name\":{\"type\":\"string\",\"maxLength\":256},\"message\":{\"type\":\"string\",\"maxLength\":2048},\"stack\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":16384},{\"type\":\"null\"}]},\"trace_origin\":{\"type\":\"string\",\"enum\":[\"throw\",\"native_fatal\",\"capture\",\"runtime_reported\",\"unavailable\",\"unknown\"]},\"parsing_status\":{\"type\":\"string\",\"enum\":[\"parsed\",\"partial\",\"unparsed\",\"unavailable\",\"truncated\"]},\"frames\":{\"maxItems\":32,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"function\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":256},{\"type\":\"null\"}]},\"file\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":2048},{\"type\":\"null\"}]},\"line\":{\"anyOf\":[{\"type\":\"inte",
13
+ "ger\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"column\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"classification\":{\"type\":\"string\",\"enum\":[\"application\",\"framework\",\"sdk\",\"unknown\"]}},\"required\":[\"function\",\"file\",\"line\",\"column\",\"classification\"],\"additionalProperties\":false}},\"relationships\":{\"maxItems\":5,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"cause\",\"context\",\"aggregate\",\"suppressed\"]},\"target_id\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":7}},\"required\":[\"kind\",\"target_id\"],\"additionalProperties\":false}}},\"required\":[\"id\",\"name\",\"message\",\"stack\",\"trace_origin\",\"parsing_status\",\"frames\",\"relationships\"],\"additionalProperties\":false}},\"truncated\":{\"type\":\"boolean\"},\"unavailable_reason\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"unknown\",\"unsupported\",\"unsafe\",\"budget_exceeded\"]},{\"type\":\"null\"}]}},\"required\":[\"version\",\"runtime\",\"root_id\",\"nodes\",\"truncated\",\"unavailable_reason\"],\"additionalPropertie",
14
+ "s\":false},\"execution\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"kind\":{\"type\":\"string\",\"enum\":[\"request\",\"command\",\"job\",\"unknown\"]},\"name\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":128},{\"type\":\"null\"}]},\"elapsed_ms\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"limit_ms\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]}},\"required\":[\"version\",\"kind\",\"name\",\"elapsed_ms\",\"limit_ms\"],\"additionalProperties\":false}},\"required\":[\"name\",\"message\",\"stack\",\"handled\",\"request\",\"response\",\"runtime\"],\"additionalProperties\":false}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"request_event\"},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_",
15
+ "name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"method\":{\"type\":\"string\",\"minLength\":1},\"path\":{\"type\":\"string\",\"minLength\":1},\"query\":{\"type\":\"object\",\"additionalProperties\":{}},\"headers\":{\"type\":\"object\",\"additionalProperties\":{}},\"body\":{\"anyOf\":[{},{\"type\"",
16
+ ":\"null\"}]},\"response_status\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"duration_ms\":{\"type\":\"number\",\"minimum\":0},\"route_template\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"response_headers\":{\"type\":\"object\",\"additionalProperties\":{}},\"response_body\":{}},\"required\":[\"method\",\"path\",\"query\",\"headers\",\"response_status\",\"duration_ms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"method\":{\"type\":\"string\",\"minLength\":1},\"path\":{\"type\":\"string\",\"minLength\":1},\"query\":{\"type\":\"object\",\"additionalProperties\":{}},\"headers\":{\"type\":\"object\",\"additionalProperties\":{}},\"body\":{\"anyOf\":[{},{\"type\":\"null\"}]},\"response_status\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"duration_ms\":{\"type\":\"number\",\"minimum\":0},\"route_template\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"response_headers\":{\"type\":\"object\",\"additionalProperties\":{}},\"response_body\":{},\"device\":{\"type\":\"object\",\"properties\":{\"user_agent\":{\"type\":[\"string\",\"null\"]},\"os\":{\"typ",
17
+ "e\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"]},\"version\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"name\",\"version\"],\"additionalProperties\":false},\"device_type\":{\"type\":\"string\",\"enum\":[\"desktop\",\"mobile\",\"tablet\",\"unknown\"]},\"screen\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"viewport\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"device_pixel_ratio\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"null\"}]},\"touch_capable\":{\"type\":[\"boolean\",\"null\"]},\"language\":{\"type\":[\"string\",\"null\"]},\"connection_type\":{\"type\":[\"string\",\"null\"]},\"color_scheme_preference\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"light\",\"dark\",\"no-preference\"]},{\"type\":",
18
+ "\"null\"}]},\"app_version\":{\"type\":[\"string\",\"null\"]},\"build_number\":{\"type\":[\"string\",\"null\"]},\"release_channel\":{\"type\":[\"string\",\"null\"]},\"api_level\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"manufacturer\":{\"type\":[\"string\",\"null\"]},\"model\":{\"type\":[\"string\",\"null\"]},\"timezone\":{\"type\":[\"string\",\"null\"]},\"battery_level\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"battery_charging\":{\"type\":[\"boolean\",\"null\"]},\"free_disk_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"free_memory_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"jailbroken\":{\"type\":[\"boolean\",\"null\"]}},\"required\":[\"user_agent\",\"os\",\"device_type\",\"screen\",\"viewport\",\"device_pixel_ratio\",\"touch_capable\",\"language\",\"connection_type\",\"color_scheme_preference\"],\"additionalProperties\":false}},\"required\":[\"method\",\"path\",\"query\",\"headers\",\"response_status\",\"duration_ms\",\"device\"],\"additionalProperties\":f",
19
+ "alse}]}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"log_event\"},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"",
20
+ "type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"level\":{\"type\":\"string\",\"minLength\":1},\"message\":{\"type\":\"string\",\"minLength\":1},\"attributes\":{\"type\":\"object\",\"additionalProperties\":{}}},\"required\":[\"level\",\"message\",\"attributes\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"level\":{\"type\":\"string\",\"minLength\":1},\"message\":{\"type\":\"string\",\"minLength\":1},\"attributes\":{\"type\":\"object\",\"additionalProperties\":{}},\"device\":{\"type\":\"object\",\"properties\":{\"user_agent\":{\"type\":[\"string\",\"null\"]},\"os\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"]},\"version\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"name\",\"version\"],\"additionalProperties\":false},\"device_type\":{\"type\":\"string\",\"enum\":[\"desktop\",\"mobile\",\"tablet\",\"unknown\"]},\"screen\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"min",
21
+ "imum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"viewport\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"device_pixel_ratio\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"null\"}]},\"touch_capable\":{\"type\":[\"boolean\",\"null\"]},\"language\":{\"type\":[\"string\",\"null\"]},\"connection_type\":{\"type\":[\"string\",\"null\"]},\"color_scheme_preference\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"light\",\"dark\",\"no-preference\"]},{\"type\":\"null\"}]},\"app_version\":{\"type\":[\"string\",\"null\"]},\"build_number\":{\"type\":[\"string\",\"null\"]},\"release_channel\":{\"type\":[\"string\",\"null\"]},\"api_level\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"manufacturer\":{\"type\":[\"string\",\"null\"]},\"model\":{\"type\":[\"string\",\"nu",
22
+ "ll\"]},\"timezone\":{\"type\":[\"string\",\"null\"]},\"battery_level\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"battery_charging\":{\"type\":[\"boolean\",\"null\"]},\"free_disk_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"free_memory_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"jailbroken\":{\"type\":[\"boolean\",\"null\"]}},\"required\":[\"user_agent\",\"os\",\"device_type\",\"screen\",\"viewport\",\"device_pixel_ratio\",\"touch_capable\",\"language\",\"connection_type\",\"color_scheme_preference\"],\"additionalProperties\":false}},\"required\":[\"level\",\"message\",\"attributes\",\"device\"],\"additionalProperties\":false}]}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"frontend_breadcrumb\"},\"pro",
23
+ "ject_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"breadcrumb_type\":{\"type\":\"string\",\"enum\":[\"route_change\",\"click\",\"form_submit\",\"console_",
24
+ "log\",\"network_request\"]},\"route\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"data\":{\"type\":\"object\",\"additionalProperties\":{}}},\"required\":[\"breadcrumb_type\",\"data\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"breadcrumb_type\":{\"type\":\"string\",\"minLength\":1},\"route\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"data\":{\"type\":\"object\",\"additionalProperties\":{}},\"device\":{\"type\":\"object\",\"properties\":{\"user_agent\":{\"type\":[\"string\",\"null\"]},\"os\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"]},\"version\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"name\",\"version\"],\"additionalProperties\":false},\"device_type\":{\"type\":\"string\",\"enum\":[\"desktop\",\"mobile\",\"tablet\",\"unknown\"]},\"screen\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"viewport\":{\"type\":\"object\",\"properties\":{\"width\":",
25
+ "{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"device_pixel_ratio\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"null\"}]},\"touch_capable\":{\"type\":[\"boolean\",\"null\"]},\"language\":{\"type\":[\"string\",\"null\"]},\"connection_type\":{\"type\":[\"string\",\"null\"]},\"color_scheme_preference\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"light\",\"dark\",\"no-preference\"]},{\"type\":\"null\"}]},\"app_version\":{\"type\":[\"string\",\"null\"]},\"build_number\":{\"type\":[\"string\",\"null\"]},\"release_channel\":{\"type\":[\"string\",\"null\"]},\"api_level\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"manufacturer\":{\"type\":[\"string\",\"null\"]},\"model\":{\"type\":[\"string\",\"null\"]},\"timezone\":{\"type\":[\"string\",\"null\"]},\"battery_level\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"battery_charging\":{\"type\":[\"boolean\",\"null\"]},\"free_disk_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"mi",
26
+ "nimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"free_memory_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"jailbroken\":{\"type\":[\"boolean\",\"null\"]}},\"required\":[\"user_agent\",\"os\",\"device_type\",\"screen\",\"viewport\",\"device_pixel_ratio\",\"touch_capable\",\"language\",\"connection_type\",\"color_scheme_preference\"],\"additionalProperties\":false}},\"required\":[\"breadcrumb_type\",\"data\",\"device\"],\"additionalProperties\":false}]}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"frontend_exception\"},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"t",
27
+ "ype\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"message\":{\"type\":\"string\",\"minLength\":1},\"stack\":{\"type\":\"string\",\"minLength\":1},\"route\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"browser\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"version\":{\"type\":\"string\",\"minLength\":1}},\"require",
28
+ "d\":[\"name\",\"version\"],\"additionalProperties\":false},\"breadcrumbs\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"breadcrumb_type\":{\"type\":\"string\",\"enum\":[\"route_change\",\"click\",\"form_submit\",\"console_log\",\"network_request\"]},\"route\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"data\":{\"type\":\"object\",\"additionalProperties\":{}},\"ts\":{\"type\":\"string\",\"format\":\"date-time\"}},\"required\":[\"breadcrumb_type\",\"data\",\"ts\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"breadcrumb_type\":{\"type\":\"string\",\"minLength\":1},\"route\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"data\":{\"type\":\"object\",\"additionalProperties\":{}},\"ts\":{\"type\":\"string\",\"format\":\"date-time\"}},\"required\":[\"breadcrumb_type\",\"data\",\"ts\"],\"additionalProperties\":false}]}},\"device\":{\"anyOf\":[{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"user_agent\":{\"type\":[\"string\",\"null\"]},\"os\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"]},\"version\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"",
29
+ "name\",\"version\"],\"additionalProperties\":false},\"device_type\":{\"type\":\"string\",\"enum\":[\"desktop\",\"mobile\",\"tablet\",\"unknown\"]},\"screen\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"viewport\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"device_pixel_ratio\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"null\"}]},\"touch_capable\":{\"type\":[\"boolean\",\"null\"]},\"language\":{\"type\":[\"string\",\"null\"]},\"connection_type\":{\"type\":[\"string\",\"null\"]},\"color_scheme_preference\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"light\",\"dark\",\"no-preference\"]},{\"type\":\"null\"}]}},\"required\":[\"user_agent\",\"os\",\"device_type\",\"screen\",\"viewport\",\"device_pixel_ratio\",\"touch_capable\"",
30
+ ",\"language\",\"connection_type\",\"color_scheme_preference\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"user_agent\":{\"type\":[\"string\",\"null\"]},\"os\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"]},\"version\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"name\",\"version\"],\"additionalProperties\":false},\"device_type\":{\"type\":\"string\",\"enum\":[\"desktop\",\"mobile\",\"tablet\",\"unknown\"]},\"screen\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"viewport\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"device_pixel_ratio\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"null\"}]},\"touch_capable\":{\"type\":[\"boolean\",\"null\"]},\"language\":{\"type",
31
+ "\":[\"string\",\"null\"]},\"connection_type\":{\"type\":[\"string\",\"null\"]},\"color_scheme_preference\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"light\",\"dark\",\"no-preference\"]},{\"type\":\"null\"}]},\"app_version\":{\"type\":[\"string\",\"null\"]},\"build_number\":{\"type\":[\"string\",\"null\"]},\"release_channel\":{\"type\":[\"string\",\"null\"]},\"api_level\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"manufacturer\":{\"type\":[\"string\",\"null\"]},\"model\":{\"type\":[\"string\",\"null\"]},\"timezone\":{\"type\":[\"string\",\"null\"]},\"battery_level\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"battery_charging\":{\"type\":[\"boolean\",\"null\"]},\"free_disk_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"free_memory_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"jailbroken\":{\"type\":[\"boolean\",\"null\"]}},\"required\":[\"user_agent\",\"os\",\"device_type\",\"screen\",\"viewport\",\"device_pixel_ratio\",\"touch_capable\",\"language\",\"connection_type\",\"c",
32
+ "olor_scheme_preference\"],\"additionalProperties\":false}]},{\"type\":\"null\"}]},\"browser_event\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"window_error\",\"resource_error\"]},\"message\":{\"type\":[\"string\",\"null\"]},\"file_name\":{\"type\":[\"string\",\"null\"]},\"line_number\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"column_number\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"target\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"tag_name\":{\"type\":[\"string\",\"null\"]},\"source_url\":{\"type\":[\"string\",\"null\"]},\"attributes\":{\"type\":\"object\",\"properties\":{\"rel\":{\"type\":\"string\"},\"as\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"},\"media\":{\"type\":\"string\"},\"cross_origin\":{\"type\":\"string\"},\"async\":{\"type\":\"boolean\"},\"defer\":{\"type\":\"boolean\"},\"integrity_present\":{\"type\":\"boolean\"}},\"additionalProperties\":false}},\"required\":[\"tag_name\",\"source_url\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"page\":{\"type\":\"object\",\"properties\":{\"url\":{",
33
+ "\"type\":[\"string\",\"null\"]},\"referrer\":{\"type\":[\"string\",\"null\"]},\"ready_state\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"loading\",\"interactive\",\"complete\"]},{\"type\":\"null\"}]},\"visibility_state\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"visible\",\"hidden\",\"prerender\",\"unloaded\"]},{\"type\":\"null\"}]}},\"required\":[\"url\",\"referrer\",\"ready_state\",\"visibility_state\"],\"additionalProperties\":false},\"opaque\":{\"type\":\"boolean\"}},\"required\":[\"kind\",\"message\",\"file_name\",\"line_number\",\"column_number\",\"target\",\"opaque\"],\"additionalProperties\":false},\"rejection_reason\":{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"enum\":[\"error\",\"string\",\"object\",\"null\",\"undefined\",\"unknown\"]},\"name\":{\"type\":\"string\",\"minLength\":1},\"message\":{\"type\":\"string\",\"minLength\":1},\"preview\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"kind\"],\"additionalProperties\":false},\"dom_context\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"mode\":{\"type\":\"string\",\"const\":\"lightweight\"},\"html_excerpt\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"mode\",\"html_excerpt\"]",
34
+ ",\"additionalProperties\":false},{\"type\":\"null\"}]},\"probe_data\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"items\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1},\"data\":{\"type\":\"object\",\"additionalProperties\":{}},\"timestamp\":{\"type\":\"string\",\"format\":\"date-time\"},\"activation_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]}},\"required\":[\"label\",\"data\",\"timestamp\",\"activation_id\"],\"additionalProperties\":false}}},\"required\":[\"version\",\"items\"],\"additionalProperties\":false},\"exception_evidence\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"runtime\":{\"type\":\"string\",\"enum\":[\"node\",\"browser\",\"python\",\"php\",\"jvm\",\"go\",\"ruby\",\"dotnet\",\"android\",\"swift\",\"react_native\",\"unknown\"]},\"root_id\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":7},\"nodes\":{\"minItems\":1,\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":7},\"name\":{\"type\":\"string\",\"maxLength\":256},",
35
+ "\"message\":{\"type\":\"string\",\"maxLength\":2048},\"stack\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":16384},{\"type\":\"null\"}]},\"trace_origin\":{\"type\":\"string\",\"enum\":[\"throw\",\"native_fatal\",\"capture\",\"runtime_reported\",\"unavailable\",\"unknown\"]},\"parsing_status\":{\"type\":\"string\",\"enum\":[\"parsed\",\"partial\",\"unparsed\",\"unavailable\",\"truncated\"]},\"frames\":{\"maxItems\":32,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"function\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":256},{\"type\":\"null\"}]},\"file\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":2048},{\"type\":\"null\"}]},\"line\":{\"anyOf\":[{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"column\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"classification\":{\"type\":\"string\",\"enum\":[\"application\",\"framework\",\"sdk\",\"unknown\"]}},\"required\":[\"function\",\"file\",\"line\",\"column\",\"classification\"],\"additionalProperties\":false}},\"relationships\":{\"maxItems\":5,\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"kind",
36
+ "\":{\"type\":\"string\",\"enum\":[\"cause\",\"context\",\"aggregate\",\"suppressed\"]},\"target_id\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":7}},\"required\":[\"kind\",\"target_id\"],\"additionalProperties\":false}}},\"required\":[\"id\",\"name\",\"message\",\"stack\",\"trace_origin\",\"parsing_status\",\"frames\",\"relationships\"],\"additionalProperties\":false}},\"truncated\":{\"type\":\"boolean\"},\"unavailable_reason\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"unknown\",\"unsupported\",\"unsafe\",\"budget_exceeded\"]},{\"type\":\"null\"}]}},\"required\":[\"version\",\"runtime\",\"root_id\",\"nodes\",\"truncated\",\"unavailable_reason\"],\"additionalProperties\":false},\"resource_observations\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"number\",\"const\":1},\"items\":{\"minItems\":1,\"maxItems\":5,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"timing\"},\"observed_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"source_url\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":2048},{\"type\":\"null\"}]},\"availability\":{\"type\":\"string\",\"enum\":[\"observed\",\"unsupported\",",
37
+ "\"no_match\",\"withheld\",\"ambiguous\",\"unknown\"]},\"response_status\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":100,\"maximum\":599},{\"type\":\"null\"}]},\"duration_ms\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"transfer_size_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]}},\"required\":[\"kind\",\"observed_at\",\"source_url\",\"availability\",\"response_status\",\"duration_ms\",\"transfer_size_bytes\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"kind\":{\"type\":\"string\",\"const\":\"csp\"},\"observed_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"source_url\":{\"anyOf\":[{\"type\":\"string\",\"maxLength\":2048},{\"type\":\"null\"}]},\"effective_directive\":{\"type\":\"string\",\"pattern\":\"^[a-z-]{1,64}$\"},\"disposition\":{\"type\":\"string\",\"enum\":[\"enforce\",\"report\"]},\"document_status_code\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":100,\"maximum\":599},{\"type\":\"null\"}]}},\"required\":[\"kind\",\"observed_at\",\"source_url\",\"effective_directive\",\"disposition\",\"document_status_code\"],\"additionalProperties\":fals",
38
+ "e}]}}},\"required\":[\"version\",\"items\"],\"additionalProperties\":false}},\"required\":[\"name\",\"message\",\"stack\"],\"additionalProperties\":false}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"deploy_metadata\"},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"",
39
+ "type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"type\":\"object\",\"properties\":{\"commit_sha\":{\"type\":\"string\",\"minLength\":1},\"version\":{\"type\":\"string\",\"minLength\":1},\"branch\":{\"type\":\"string\",\"minLength\":1},\"environment\":{\"type\":\"string\",\"minLength\":1},\"deployed_at\":{\"type\":\"string\",\"format\":\"date-time\"}},\"required\":[\"commit_sha\",\"version\",\"branch\",\"environment\",\"deployed_at\"],\"additionalProperties\":false}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"error_suppressed\"",
40
+ "},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"string\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"fingerprint\":{\"type\":\"string\",\"minLength\":1},\"suppressed_count\":{\"type\":\"integer\",",
41
+ "\"minimum\":0,\"maximum\":9007199254740991},\"window_seconds\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"first_seen\":{\"type\":\"string\",\"format\":\"date-time\"},\"last_seen\":{\"type\":\"string\",\"format\":\"date-time\"},\"reason\":{\"type\":\"string\",\"const\":\"queue_pressure\"},\"level\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"fingerprint\",\"suppressed_count\",\"window_seconds\",\"first_seen\",\"last_seen\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"fingerprint\":{\"type\":\"string\",\"minLength\":1},\"suppressed_count\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"window_seconds\":{\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"first_seen\":{\"type\":\"string\",\"format\":\"date-time\"},\"last_seen\":{\"type\":\"string\",\"format\":\"date-time\"},\"reason\":{\"type\":\"string\",\"const\":\"queue_pressure\"},\"level\":{\"type\":\"string\",\"minLength\":1},\"device\":{\"type\":\"object\",\"properties\":{\"user_agent\":{\"type\":[\"string\",\"null\"]},\"os\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"]},\"ve",
42
+ "rsion\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"name\",\"version\"],\"additionalProperties\":false},\"device_type\":{\"type\":\"string\",\"enum\":[\"desktop\",\"mobile\",\"tablet\",\"unknown\"]},\"screen\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"viewport\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"device_pixel_ratio\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"null\"}]},\"touch_capable\":{\"type\":[\"boolean\",\"null\"]},\"language\":{\"type\":[\"string\",\"null\"]},\"connection_type\":{\"type\":[\"string\",\"null\"]},\"color_scheme_preference\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"light\",\"dark\",\"no-preference\"]},{\"type\":\"null\"}]},\"app_version\":{\"type\":[\"string\",\"null\"]},\"build_numbe",
43
+ "r\":{\"type\":[\"string\",\"null\"]},\"release_channel\":{\"type\":[\"string\",\"null\"]},\"api_level\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"manufacturer\":{\"type\":[\"string\",\"null\"]},\"model\":{\"type\":[\"string\",\"null\"]},\"timezone\":{\"type\":[\"string\",\"null\"]},\"battery_level\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"battery_charging\":{\"type\":[\"boolean\",\"null\"]},\"free_disk_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"free_memory_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"jailbroken\":{\"type\":[\"boolean\",\"null\"]}},\"required\":[\"user_agent\",\"os\",\"device_type\",\"screen\",\"viewport\",\"device_pixel_ratio\",\"touch_capable\",\"language\",\"connection_type\",\"color_scheme_preference\"],\"additionalProperties\":false}},\"required\":[\"fingerprint\",\"suppressed_count\",\"window_seconds\",\"first_seen\",\"last_seen\",\"device\"],\"additionalProperties\":false}]}},\"required\":[\"schema_version\",\"event_id\",\"even",
44
+ "t_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"schema_version\":{\"type\":\"string\",\"minLength\":1},\"event_id\":{\"type\":\"string\",\"format\":\"uuid\"},\"event_type\":{\"type\":\"string\",\"const\":\"probe_event\"},\"project_token\":{\"type\":\"string\",\"minLength\":1},\"project_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"sdk_name\":{\"type\":\"string\",\"minLength\":1},\"sdk_version\":{\"type\":\"string\",\"minLength\":1},\"service\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"minLength\":1},\"runtime\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"framework\":{\"anyOf\":[{\"type\":\"string\",\"minLength\":1},{\"type\":\"null\"}]},\"environment\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"name\",\"environment\"],\"additionalProperties\":false},\"occurred_at\":{\"type\":\"string\",\"format\":\"date-time\"},\"correlation\":{\"type\":\"object\",\"properties\":{\"request_id\":{\"type\":[\"string\",\"null\"]},\"trace_id\":{\"type\":[\"string\",\"null\"]},\"session_id\":{\"type\":[\"stri",
45
+ "ng\",\"null\"]},\"user_id_hash\":{\"type\":[\"string\",\"null\"]}},\"additionalProperties\":false},\"context\":{\"type\":\"object\",\"additionalProperties\":{}},\"payload\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1},\"data\":{\"type\":\"object\",\"additionalProperties\":{}},\"activation_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"probe_label_pattern\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"label\",\"data\",\"activation_id\",\"probe_label_pattern\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\",\"minLength\":1},\"data\":{\"type\":\"object\",\"additionalProperties\":{}},\"activation_id\":{\"anyOf\":[{\"type\":\"string\",\"format\":\"uuid\"},{\"type\":\"null\"}]},\"probe_label_pattern\":{\"type\":\"string\",\"minLength\":1},\"device\":{\"type\":\"object\",\"properties\":{\"user_agent\":{\"type\":[\"string\",\"null\"]},\"os\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"]},\"version\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"name\",\"version\"],\"additionalProperties\":false},\"device_typ",
46
+ "e\":{\"type\":\"string\",\"enum\":[\"desktop\",\"mobile\",\"tablet\",\"unknown\"]},\"screen\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"viewport\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},\"height\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991}},\"required\":[\"width\",\"height\"],\"additionalProperties\":false},\"device_pixel_ratio\":{\"anyOf\":[{\"type\":\"number\",\"exclusiveMinimum\":0},{\"type\":\"null\"}]},\"touch_capable\":{\"type\":[\"boolean\",\"null\"]},\"language\":{\"type\":[\"string\",\"null\"]},\"connection_type\":{\"type\":[\"string\",\"null\"]},\"color_scheme_preference\":{\"anyOf\":[{\"type\":\"string\",\"enum\":[\"light\",\"dark\",\"no-preference\"]},{\"type\":\"null\"}]},\"app_version\":{\"type\":[\"string\",\"null\"]},\"build_number\":{\"type\":[\"string\",\"null\"]},\"release_channel\":{\"type\":[\"string\",\"null\"]},\"api_level\":{\"anyOf\":[{\"type\":\"",
47
+ "integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"manufacturer\":{\"type\":[\"string\",\"null\"]},\"model\":{\"type\":[\"string\",\"null\"]},\"timezone\":{\"type\":[\"string\",\"null\"]},\"battery_level\":{\"anyOf\":[{\"type\":\"number\",\"minimum\":0},{\"type\":\"null\"}]},\"battery_charging\":{\"type\":[\"boolean\",\"null\"]},\"free_disk_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"free_memory_bytes\":{\"anyOf\":[{\"type\":\"integer\",\"minimum\":0,\"maximum\":9007199254740991},{\"type\":\"null\"}]},\"jailbroken\":{\"type\":[\"boolean\",\"null\"]}},\"required\":[\"user_agent\",\"os\",\"device_type\",\"screen\",\"viewport\",\"device_pixel_ratio\",\"touch_capable\",\"language\",\"connection_type\",\"color_scheme_preference\"],\"additionalProperties\":false}},\"required\":[\"label\",\"data\",\"activation_id\",\"probe_label_pattern\",\"device\"],\"additionalProperties\":false}]}},\"required\":[\"schema_version\",\"event_id\",\"event_type\",\"sdk_name\",\"sdk_version\",\"service\",\"occurred_at\",\"payload\"],\"additionalProperties\":false}],\"$id\":\"https://debugbundle.co",
48
+ "m/schemas/event-envelope.json\",\"$comment\":\"Generated by scripts/sync-evidence-fixtures.ts from EventEnvelopeSchema. Do not edit by hand.\"}"
49
+ ].join(""));
@@ -0,0 +1,19 @@
1
+ import { ownJSON, jsonObject } from "./owned-json.js";
2
+ import type { EvidenceCapabilities, EvidenceFeature } from "./evidence-types.js";
3
+
4
+ export const EVIDENCE_FEATURES: EvidenceFeature[] = ["exception_evidence_v1", "resource_observations_v1", "recovery_v1", "evidence_provenance_v1"];
5
+ export function normalizeCapabilities(value: unknown): EvidenceCapabilities {
6
+ try {
7
+ const descriptor = jsonObject(ownJSON(value));
8
+ if (!descriptor || Object.keys(descriptor).length !== 2 || descriptor.version !== 1 || !Array.isArray(descriptor.features) ||
9
+ descriptor.features.length > 4 || new Set(descriptor.features).size !== descriptor.features.length ||
10
+ descriptor.features.some(item => !EVIDENCE_FEATURES.includes(item as EvidenceFeature))) return { version: 1, features: [] };
11
+ return { version: 1, features: EVIDENCE_FEATURES.filter(feature => (descriptor.features as unknown[]).includes(feature)) };
12
+ } catch { return { version: 1, features: [] }; }
13
+ }
14
+
15
+ export function intersectCapabilities(hops: unknown[]): EvidenceCapabilities {
16
+ if (!hops.length || hops.length > 8) return { version: 1, features: [] };
17
+ const descriptors = hops.map(normalizeCapabilities);
18
+ return { version: 1, features: EVIDENCE_FEATURES.filter(feature => descriptors.every(hop => hop.features.includes(feature))) };
19
+ }
@@ -0,0 +1,89 @@
1
+ import type { DebugBundleEventEnvelope, ResolvedDebugBundleConfig, NativeDebugBundleState } from "./types.js";
2
+ import type { EvidenceCapabilities, EvidenceFeature, EvidenceScope, OperationScope, RecoveryObservation } from "./evidence-types.js";
3
+ import { intersectCapabilities, normalizeCapabilities } from "./evidence-capabilities.js";
4
+ import { MobileDiagnosticBudget } from "./diagnostic-budget.js";
5
+ import { evidenceReference, evidenceScope, freshReference } from "./evidence-wire.js";
6
+ import { ownJSON, jsonObject, utf8Bytes } from "./owned-json.js";
7
+ import { generateId } from "./client-values.js";
8
+
9
+ interface Lease { id: string; ended: boolean; parent: Lease | null }
10
+ export class MobileEvidenceController {
11
+ readonly budget = new MobileDiagnosticBudget();
12
+ private current: Lease | null = null;
13
+ private ended = false;
14
+ constructor(private config: ResolvedDebugBundleConfig) {}
15
+ close(): void { this.ended = true; this.current = null; this.budget.clear(); }
16
+ private active(lease: Lease | null): boolean {
17
+ let depth = 0;
18
+ for (let item = lease; item; item = item.parent) if (item.ended || ++depth > 32 || this.ended) return false;
19
+ return lease !== null && !this.ended;
20
+ }
21
+ begin(): OperationScope {
22
+ let depth = 0;
23
+ for (let item = this.current; item; item = item.parent) depth++;
24
+ const lease: Lease = { id: generateId(), ended: this.ended || depth >= 32, parent: this.current };
25
+ if (!lease.ended) this.current = lease;
26
+ const evidenceScope: EvidenceScope = { kind: "operation", operation_id: lease.id };
27
+ return { operationId: lease.id, evidenceScope, close: () => {
28
+ lease.ended = true;
29
+ if (this.current === lease) this.current = this.active(lease.parent) ? lease.parent : null;
30
+ } };
31
+ }
32
+ private scope(): EvidenceScope | null { return this.active(this.current) ? { kind: "operation", operation_id: this.current!.id } : null; }
33
+ capabilities(state: NativeDebugBundleState, canonicalBridge: boolean): EvidenceCapabilities {
34
+ // A JS pin cannot assert that an old native binary preserves fields it never implemented.
35
+ if (!canonicalBridge || this.ended) return { version: 1, features: [] };
36
+ const native = normalizeCapabilities(state.evidenceCapabilities);
37
+ const proof = this.config.evidenceCapabilities ? intersectCapabilities([native, this.config.evidenceCapabilities]) : native;
38
+ return { version: 1, features: proof.features.filter(feature =>
39
+ feature !== "exception_evidence_v1" || this.config.captureExceptionEvidence).filter(feature =>
40
+ feature !== "recovery_v1" || this.config.captureRecoveryObservations) };
41
+ }
42
+ supports(feature: EvidenceFeature, state: NativeDebugBundleState, bridge: boolean): boolean {
43
+ return this.capabilities(state, bridge).features.includes(feature);
44
+ }
45
+ context(source: Record<string, unknown>, state: NativeDebugBundleState, bridge: boolean): Record<string, unknown> {
46
+ if (!this.supports("recovery_v1", state, bridge) && !this.supports("resource_observations_v1", state, bridge)) return source;
47
+ try {
48
+ const failure = Object.getOwnPropertyDescriptor(source, "failureRef"), explicit = Object.getOwnPropertyDescriptor(source, "evidenceScope");
49
+ if (!failure || !("value" in failure) || !freshReference(failure.value)) return source;
50
+ const scope = explicit && "value" in explicit ? ownJSON(explicit.value) : this.scope();
51
+ if (!evidenceScope(scope)) return source;
52
+ const result: Record<string, unknown> = { ...source, evidence_reference: { version: 1, failure_ref: failure.value, scope } };
53
+ delete result.failureRef; delete result.evidenceScope;
54
+ return result;
55
+ } catch { return source; }
56
+ }
57
+ recovery(observation: RecoveryObservation): { payload: Record<string, unknown>; correlation: DebugBundleEventEnvelope["correlation"] } | null {
58
+ try {
59
+ const input = jsonObject(ownJSON(observation)); if (!input) return null;
60
+ const scope = input.evidenceScope ?? this.scope();
61
+ if (!freshReference(input.failureRef) || !freshReference(input.attemptId) || !evidenceScope(scope)) return null;
62
+ const payload: Record<string, unknown> = { version: 1, kind: "recovery", failure_ref: input.failureRef,
63
+ scope, attempt_id: input.attemptId, state: input.state };
64
+ for (const [local, wire] of [["operation", "operation"], ["statusCode", "status_code"], ["durationMs", "duration_ms"]] as const) {
65
+ if (input[local] !== undefined) payload[wire] = input[local];
66
+ }
67
+ return { payload, correlation: input.correlation as DebugBundleEventEnvelope["correlation"] };
68
+ } catch { return null; }
69
+ }
70
+ filter(original: DebugBundleEventEnvelope, state: NativeDebugBundleState, bridge: boolean): DebugBundleEventEnvelope | null {
71
+ const proof = this.capabilities(state, bridge).features;
72
+ if (original.event_type === "diagnostic_context") {
73
+ return proof.includes(original.payload.kind === "recovery" ? "recovery_v1" : "resource_observations_v1") ? original : null;
74
+ }
75
+ const event = { ...original, payload: { ...original.payload }, ...(original.context ? { context: { ...original.context } } : {}) };
76
+ if (!proof.includes("exception_evidence_v1")) { delete event.payload.exception_evidence; delete event.payload.execution; }
77
+ if (!proof.includes("resource_observations_v1")) delete event.payload.resource_observations;
78
+ if (!proof.includes("recovery_v1") && !proof.includes("resource_observations_v1") && evidenceReference(event.context?.evidence_reference)) delete event.context!.evidence_reference;
79
+ return event;
80
+ }
81
+ }
82
+
83
+ export function fitMobileEvent(original: DebugBundleEventEnvelope, maximum = 65_536): DebugBundleEventEnvelope | null {
84
+ if (utf8Bytes(JSON.stringify(original)) <= maximum) return original;
85
+ const event = { ...original, payload: { ...original.payload }, ...(original.context ? { context: { ...original.context } } : {}) };
86
+ for (const field of ["exception_evidence", "execution", "resource_observations"]) delete event.payload[field];
87
+ if (evidenceReference(event.context?.evidence_reference)) delete event.context!.evidence_reference;
88
+ return utf8Bytes(JSON.stringify(event)) <= maximum ? event : null;
89
+ }
@@ -0,0 +1,14 @@
1
+ export type EvidenceFeature = "exception_evidence_v1" | "resource_observations_v1" | "recovery_v1" | "evidence_provenance_v1";
2
+ export interface EvidenceCapabilities { version: 1; features: EvidenceFeature[] }
3
+ export type EvidenceScope = { kind: "operation"; operation_id: string } | { kind: "page"; page_id: string };
4
+ export interface OperationScope { readonly operationId: string; readonly evidenceScope: EvidenceScope; close(): void }
5
+ export interface RecoveryObservation {
6
+ failureRef: string;
7
+ evidenceScope?: EvidenceScope;
8
+ attemptId: string;
9
+ state: "attempt_started" | "attempt_completed" | "attempt_failed" | "recovery_observed" | "failure_persisted";
10
+ operation?: string;
11
+ statusCode?: number;
12
+ durationMs?: number;
13
+ correlation?: { request_id?: string | null; trace_id?: string | null; session_id?: string | null; user_id_hash?: string | null };
14
+ }
@@ -0,0 +1,100 @@
1
+ import { canonicalShape } from "./canonical-schema.js";
2
+ import { jsonObject, utf8Bytes, type OwnedJSON } from "./owned-json.js";
3
+
4
+ export function freshReference(value: unknown): value is string {
5
+ return typeof value === "string" && /^[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/i.test(value);
6
+ }
7
+ export function evidenceScope(value: unknown): boolean {
8
+ const scope = jsonObject(value);
9
+ if (!scope || !["operation", "page"].includes(String(scope.kind))) return false;
10
+ const key = scope.kind === "page" ? "page_id" : "operation_id";
11
+ return Object.keys(scope).length === 2 && freshReference(scope[key]);
12
+ }
13
+ export function evidenceReference(value: unknown): boolean {
14
+ const ref = jsonObject(value);
15
+ return ref !== null && Object.keys(ref).length === 3 && ref.version === 1 && freshReference(ref.failure_ref) && evidenceScope(ref.scope);
16
+ }
17
+ const fits = (value: OwnedJSON | undefined, maximum: number): boolean => value === null ||
18
+ typeof value === "string" && value.length <= maximum && utf8Bytes(value) <= maximum;
19
+ const size = (value: unknown): number => utf8Bytes(JSON.stringify(value));
20
+
21
+ function graph(value: OwnedJSON): boolean {
22
+ const graph = jsonObject(value)!;
23
+ const nodes = graph.nodes as Array<Record<string, OwnedJSON>>;
24
+ const edges = new Map<number, Array<Record<string, OwnedJSON>>>();
25
+ for (const node of nodes) {
26
+ const id = node.id as number;
27
+ if (edges.has(id) || !fits(node.name, 256) || !fits(node.message, 2048) || !fits(node.stack, 16_384)) return false;
28
+ for (const frame of node.frames as Array<Record<string, OwnedJSON>>) if (!fits(frame.function, 256) || !fits(frame.file, 2048)) return false;
29
+ edges.set(id, node.relationships as Array<Record<string, OwnedJSON>>);
30
+ }
31
+ for (const children of edges.values()) {
32
+ const seen = new Set<string>(); let causes = 0;
33
+ for (const child of children) {
34
+ const identity = `${child.kind}:${child.target_id}`;
35
+ if (!edges.has(child.target_id as number) || seen.has(identity)) return false;
36
+ seen.add(identity);
37
+ if (child.kind === "cause" || child.kind === "context") causes++;
38
+ }
39
+ if (causes > 1) return false;
40
+ }
41
+ const active = new Set<number>(), reached = new Set<number>();
42
+ const visit = (id: number, depth: number): boolean => {
43
+ if (depth > 4 || active.has(id) || !edges.has(id)) return false;
44
+ active.add(id); reached.add(id);
45
+ for (const child of edges.get(id)!) if (!visit(child.target_id as number, depth + 1)) return false;
46
+ active.delete(id); return true;
47
+ };
48
+ return visit(graph.root_id as number, 0) && reached.size === nodes.length && size(value) <= 32_768;
49
+ }
50
+
51
+ function resourceURL(value: string): boolean {
52
+ if (!value || utf8Bytes(value) > 2048 || /[\u0000-\u0020\u007f\\?#]/.test(value) || value.startsWith("//")) return false;
53
+ if (value.startsWith("/")) return true;
54
+ if (!/^https?:\/\//i.test(value)) return false;
55
+ try { const url = new URL(value); return !!url.hostname && !url.username && !url.password; } catch { return false; }
56
+ }
57
+ function resources(value: OwnedJSON): boolean {
58
+ const entries = jsonObject(value)!.items as Array<Record<string, OwnedJSON>>;
59
+ for (const entry of entries) {
60
+ if (entry.kind === "csp" && (typeof entry.effective_directive !== "string" ||
61
+ !/^[a-z-]{1,64}$/.test(entry.effective_directive))) return false;
62
+ if (entry.source_url !== null && !resourceURL(entry.source_url as string)) return false;
63
+ }
64
+ return size(value) <= 32_768;
65
+ }
66
+
67
+ /** Semantic refinements accompany the generated schema, as in every native SDK. */
68
+ export function canonicalEvent(value: OwnedJSON): boolean {
69
+ if (!canonicalShape(value)) return false;
70
+ const event = jsonObject(value)!, payload = jsonObject(event.payload)!;
71
+ const type = event.event_type;
72
+ if (type === "frontend_exception") {
73
+ const mobile = ["@debugbundle/sdk-swift", "@debugbundle/sdk-android", "@debugbundle/sdk-react-native"].includes(event.sdk_name as string);
74
+ if (!jsonObject(payload[mobile ? "device" : "browser"])) return false;
75
+ }
76
+ if (type === "diagnostic_context") {
77
+ if (!freshReference(payload.failure_ref) || !evidenceScope(payload.scope)) return false;
78
+ if (payload.kind === "recovery") {
79
+ if (!freshReference(payload.attempt_id) || payload.operation !== undefined && !fits(payload.operation, 64)) return false;
80
+ } else if (!resources(payload.observations!)) return false;
81
+ if (jsonObject(payload.scope)?.kind === "page") {
82
+ const session = jsonObject(event.correlation)?.session_id;
83
+ if (typeof session !== "string" || !session.trim()) return false;
84
+ }
85
+ return true;
86
+ }
87
+ const additions: Record<string, OwnedJSON> = {};
88
+ for (const field of ["exception_evidence", "execution", "resource_observations"]) {
89
+ const item = payload[field];
90
+ if (item === undefined) continue;
91
+ if (!(type === "backend_exception" && field !== "resource_observations" || type === "frontend_exception" && field !== "execution")) return false;
92
+ if (field === "exception_evidence" && !graph(item) || field === "execution" && !fits(jsonObject(item)?.name, 128) ||
93
+ field === "resource_observations" && !resources(item)) return false;
94
+ additions[field] = item;
95
+ }
96
+ if (!Object.keys(additions).length) return true;
97
+ const ref = jsonObject(event.context)?.evidence_reference;
98
+ if (evidenceReference(ref)) additions.evidence_reference = ref!;
99
+ return size(additions) <= 32_768;
100
+ }