@q9labsai/chalk-client 4.1.17 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +60 -42
  2. package/dist/access/grant.d.ts +0 -10
  3. package/dist/access/grant.d.ts.map +1 -1
  4. package/dist/access/manager.d.ts.map +1 -1
  5. package/dist/{chunk-ZRMGKMX4.js → chunk-2HJIJVE2.js} +552 -3651
  6. package/dist/{chunk-CZUJNAUI.js → chunk-AVXJ4HQU.js} +142 -49
  7. package/dist/{chunk-A7BVZEFM.js → chunk-CMVO7USN.js} +2 -8
  8. package/dist/{chunk-AUQTQOLR.js → chunk-IPAN4LUZ.js} +627 -2434
  9. package/dist/{chunk-2DZUHV6I.js → chunk-O2NOZKJP.js} +129 -721
  10. package/dist/client.d.ts +2559 -5303
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/collaboration/types.d.ts +21 -1
  13. package/dist/collaboration/types.d.ts.map +1 -1
  14. package/dist/collaboration/wire.d.ts +8 -1
  15. package/dist/collaboration/wire.d.ts.map +1 -1
  16. package/dist/connection/dependencies.d.ts +7 -13
  17. package/dist/connection/dependencies.d.ts.map +1 -1
  18. package/dist/connection/index.d.ts +1 -6
  19. package/dist/connection/index.d.ts.map +1 -1
  20. package/dist/connection/lifecycle.d.ts.map +1 -1
  21. package/dist/connection/media-devices.d.ts +11 -0
  22. package/dist/connection/media-devices.d.ts.map +1 -1
  23. package/dist/connection/production.d.ts +1 -1
  24. package/dist/connection/production.d.ts.map +1 -1
  25. package/dist/connection/types.d.ts +4 -4
  26. package/dist/connection/types.d.ts.map +1 -1
  27. package/dist/effect.d.ts +0 -4
  28. package/dist/effect.d.ts.map +1 -1
  29. package/dist/effect.js +53 -776
  30. package/dist/generated/http-api.d.ts +2603 -5293
  31. package/dist/generated/http-api.d.ts.map +1 -1
  32. package/dist/generated/schemas.d.ts +1893 -6870
  33. package/dist/generated/schemas.d.ts.map +1 -1
  34. package/dist/generated/sync.d.ts +191 -28
  35. package/dist/generated/sync.d.ts.map +1 -1
  36. package/dist/generated/whiteboard-v1.d.ts +0 -7
  37. package/dist/generated/whiteboard-v1.d.ts.map +1 -1
  38. package/dist/index.d.ts +0 -4
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +10 -119
  41. package/dist/media/client.d.ts +6 -5
  42. package/dist/media/client.d.ts.map +1 -1
  43. package/dist/media/index.d.ts +1 -2
  44. package/dist/media/index.d.ts.map +1 -1
  45. package/dist/media/index.js +1 -7
  46. package/dist/media/plane.d.ts +12 -1
  47. package/dist/media/plane.d.ts.map +1 -1
  48. package/dist/media/target.d.ts +4 -2
  49. package/dist/media/target.d.ts.map +1 -1
  50. package/dist/media/tracks.d.ts +2 -2
  51. package/dist/media/tracks.d.ts.map +1 -1
  52. package/dist/media/transport.d.ts.map +1 -1
  53. package/dist/media/types.d.ts +4 -11
  54. package/dist/media/types.d.ts.map +1 -1
  55. package/dist/public-invites/client.d.ts.map +1 -1
  56. package/dist/public-invites/index.d.ts +2 -2
  57. package/dist/public-invites/index.d.ts.map +1 -1
  58. package/dist/public-invites/index.js +2 -5
  59. package/dist/public-invites/types.d.ts +2 -6
  60. package/dist/public-invites/types.d.ts.map +1 -1
  61. package/dist/server/client.d.ts.map +1 -1
  62. package/dist/server/index.cjs +12 -112
  63. package/dist/server/index.d.cts +1 -1
  64. package/dist/server/index.d.ts +1 -1
  65. package/dist/server/index.d.ts.map +1 -1
  66. package/dist/server/index.js +9 -109
  67. package/dist/server/types.d.cts +0 -7
  68. package/dist/server/types.d.ts +0 -7
  69. package/dist/server/types.d.ts.map +1 -1
  70. package/dist/space-client/chat-controller-helpers.d.ts.map +1 -1
  71. package/dist/space-client/chat-controller.d.ts +2 -3
  72. package/dist/space-client/chat-controller.d.ts.map +1 -1
  73. package/dist/space-client/controller-effects.d.ts +0 -3
  74. package/dist/space-client/controller-effects.d.ts.map +1 -1
  75. package/dist/space-client/core.d.ts +3 -18
  76. package/dist/space-client/core.d.ts.map +1 -1
  77. package/dist/space-client/core.test.helpers.d.ts.map +1 -1
  78. package/dist/space-client/effect.d.ts +0 -2
  79. package/dist/space-client/effect.d.ts.map +1 -1
  80. package/dist/space-client/index.d.ts +1 -2
  81. package/dist/space-client/index.d.ts.map +1 -1
  82. package/dist/space-client/media-controller.d.ts +4 -4
  83. package/dist/space-client/media-controller.d.ts.map +1 -1
  84. package/dist/space-client/offline-actions-storage.d.ts +18 -3
  85. package/dist/space-client/offline-actions-storage.d.ts.map +1 -1
  86. package/dist/space-client/offline-actions.d.ts +1 -0
  87. package/dist/space-client/offline-actions.d.ts.map +1 -1
  88. package/dist/space-client/participants-controller.d.ts +2 -3
  89. package/dist/space-client/participants-controller.d.ts.map +1 -1
  90. package/dist/space-client/reactions-controller.d.ts +2 -3
  91. package/dist/space-client/reactions-controller.d.ts.map +1 -1
  92. package/dist/space-client/recording-controller.d.ts +2 -3
  93. package/dist/space-client/recording-controller.d.ts.map +1 -1
  94. package/dist/space-client/space-client.d.ts.map +1 -1
  95. package/dist/space-client/store.d.ts.map +1 -1
  96. package/dist/space-client/terminal.test.helpers.d.ts +33 -12
  97. package/dist/space-client/terminal.test.helpers.d.ts.map +1 -1
  98. package/dist/space-client/types.d.ts +15 -8
  99. package/dist/space-client/types.d.ts.map +1 -1
  100. package/dist/sync/index.d.ts +1 -1
  101. package/dist/sync/index.d.ts.map +1 -1
  102. package/dist/sync/index.js +1 -3
  103. package/dist/sync/v1-client.d.ts +4 -3
  104. package/dist/sync/v1-client.d.ts.map +1 -1
  105. package/dist/sync/v1-collaboration-state.d.ts +10 -3
  106. package/dist/sync/v1-collaboration-state.d.ts.map +1 -1
  107. package/dist/sync/v1-types.d.ts +10 -9
  108. package/dist/sync/v1-types.d.ts.map +1 -1
  109. package/dist/whiteboard/file-download-transport.d.ts +8 -0
  110. package/dist/whiteboard/file-download-transport.d.ts.map +1 -0
  111. package/dist/whiteboard/index.d.ts +1 -1
  112. package/dist/whiteboard/index.d.ts.map +1 -1
  113. package/dist/whiteboard/types.d.ts +12 -15
  114. package/dist/whiteboard/types.d.ts.map +1 -1
  115. package/dist/whiteboard/v1-client.d.ts.map +1 -1
  116. package/package.json +4 -16
  117. package/dist/access/episode-diagnostic-credential.d.ts +0 -11
  118. package/dist/access/episode-diagnostic-credential.d.ts.map +0 -1
  119. package/dist/chunk-EWTITNJ3.js +0 -1163
  120. package/dist/chunk-HW5TTZL2.js +0 -99
  121. package/dist/chunk-PBSYXPNJ.js +0 -23
  122. package/dist/chunk-RCDXIDCN.js +0 -33
  123. package/dist/connection/snapshot.d.ts +0 -23
  124. package/dist/connection/snapshot.d.ts.map +0 -1
  125. package/dist/feedback/collector.d.ts +0 -14
  126. package/dist/feedback/collector.d.ts.map +0 -1
  127. package/dist/feedback/context.d.ts +0 -38
  128. package/dist/feedback/context.d.ts.map +0 -1
  129. package/dist/feedback/controller.d.ts +0 -4
  130. package/dist/feedback/controller.d.ts.map +0 -1
  131. package/dist/feedback/index.d.ts +0 -7
  132. package/dist/feedback/index.d.ts.map +0 -1
  133. package/dist/feedback/transport.d.ts +0 -29
  134. package/dist/feedback/transport.d.ts.map +0 -1
  135. package/dist/feedback/types.d.ts +0 -202
  136. package/dist/feedback/types.d.ts.map +0 -1
  137. package/dist/feedback/validation.d.ts +0 -30
  138. package/dist/feedback/validation.d.ts.map +0 -1
  139. package/dist/media/rtk.d.ts +0 -101
  140. package/dist/media/rtk.d.ts.map +0 -1
  141. package/dist/space-client/episode-diagnostic-registry.d.ts +0 -13
  142. package/dist/space-client/episode-diagnostic-registry.d.ts.map +0 -1
  143. package/dist/space-client/episode-diagnostic-render-registry.d.ts +0 -4
  144. package/dist/space-client/episode-diagnostic-render-registry.d.ts.map +0 -1
  145. package/dist/space-client/episode-diagnostic-runtime.d.ts +0 -105
  146. package/dist/space-client/episode-diagnostic-runtime.d.ts.map +0 -1
  147. package/dist/telemetry/attributes.d.ts +0 -7
  148. package/dist/telemetry/attributes.d.ts.map +0 -1
  149. package/dist/telemetry/client.d.ts +0 -44
  150. package/dist/telemetry/client.d.ts.map +0 -1
  151. package/dist/telemetry/delivery.d.ts +0 -107
  152. package/dist/telemetry/delivery.d.ts.map +0 -1
  153. package/dist/telemetry/exporter.d.ts +0 -38
  154. package/dist/telemetry/exporter.d.ts.map +0 -1
  155. package/dist/telemetry/index.d.ts +0 -7
  156. package/dist/telemetry/index.d.ts.map +0 -1
  157. package/dist/telemetry/index.js +0 -50
  158. package/dist/telemetry/journey.d.ts +0 -52
  159. package/dist/telemetry/journey.d.ts.map +0 -1
  160. package/dist/telemetry/random.d.ts +0 -18
  161. package/dist/telemetry/random.d.ts.map +0 -1
  162. package/dist/telemetry/reconnect.d.ts +0 -7
  163. package/dist/telemetry/reconnect.d.ts.map +0 -1
  164. package/dist/telemetry/rtc.d.ts +0 -41
  165. package/dist/telemetry/rtc.d.ts.map +0 -1
  166. package/dist/telemetry/storage.d.ts +0 -22
  167. package/dist/telemetry/storage.d.ts.map +0 -1
  168. package/dist/telemetry/sync.d.ts +0 -10
  169. package/dist/telemetry/sync.d.ts.map +0 -1
  170. package/dist/telemetry/test-support.d.ts +0 -51
  171. package/dist/telemetry/test-support.d.ts.map +0 -1
  172. package/dist/telemetry/trace.d.ts +0 -18
  173. package/dist/telemetry/trace.d.ts.map +0 -1
  174. package/dist/telemetry/types.d.ts +0 -109
  175. package/dist/telemetry/types.d.ts.map +0 -1
  176. package/dist/whiteboard/file-http-transport.d.ts +0 -9
  177. package/dist/whiteboard/file-http-transport.d.ts.map +0 -1
@@ -1,1163 +0,0 @@
1
- import {
2
- TelemetryEventSourceService,
3
- createTraceContext,
4
- createUuid,
5
- journeyHeaders,
6
- makeTelemetryEventSourceLayer,
7
- traceContextFromJourney
8
- } from "./chunk-HW5TTZL2.js";
9
-
10
- // src/telemetry/client.ts
11
- import { Effect as Effect2, Layer as Layer2, ManagedRuntime as ManagedRuntime2 } from "effect";
12
-
13
- // src/telemetry/attributes.ts
14
- var MAX_ATTRIBUTE_COUNT = 24;
15
- var MAX_ATTRIBUTE_KEY_LENGTH = 64;
16
- var MAX_ATTRIBUTE_VALUE_LENGTH = 256;
17
- var CORRELATION_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set(["childjourneyid", "rootjourneyid"]);
18
- var TELEMETRY_BUILT_IN_ATTRIBUTE_KEYS = [
19
- "journey_kind",
20
- "category",
21
- "code",
22
- "metric_value",
23
- "child_journey_id",
24
- "relationship",
25
- "method",
26
- "route",
27
- "duration_ms",
28
- "status_code",
29
- "direction",
30
- "frame_type",
31
- "connection_state",
32
- "ice_connection_state",
33
- "signaling_state",
34
- "dtls_state",
35
- "selected_candidate_pair_state",
36
- "bytes_received",
37
- "bytes_sent",
38
- "frames_dropped",
39
- "inbound_streams",
40
- "outbound_streams",
41
- "packets_lost",
42
- "packets_received",
43
- "packets_sent",
44
- "transport_entries",
45
- "jitter_ms",
46
- "round_trip_time_ms"
47
- ];
48
- var SENSITIVE_IDENTIFIER_ROOTS = [
49
- "space",
50
- "episode",
51
- "participant",
52
- ["r", "oom"].join(""),
53
- ["sess", "ion"].join(""),
54
- ["meet", "ing"].join(""),
55
- ["con", "ference"].join(""),
56
- "peer",
57
- ["att", "endee"].join(""),
58
- "user",
59
- "agent",
60
- "tenant",
61
- "member",
62
- "external",
63
- "connection",
64
- "provider",
65
- "publication",
66
- "stream",
67
- "track",
68
- "journey",
69
- "rootjourney"
70
- ];
71
- var SENSITIVE_ATTRIBUTE_KEY_PATTERNS = [
72
- /(?:access|refresh|id|auth|bearer|csrf|session)?token/u,
73
- /(?:authorization|cookie|credential|password|secret|privatekey|apikey)/u,
74
- /(?:body|payload|request|response|sdp|candidate|track|media)(?:content|data)?$/u,
75
- /(?:displayname|email|phone|username|ipaddress)/u,
76
- new RegExp(`(?:${SENSITIVE_IDENTIFIER_ROOTS.join("|")})[a-z0-9]*(?:id|ids|identifier|identifiers|slug|key)$`, "u")
77
- ];
78
- function normalizeTelemetryAttributes(attributes, options = {}) {
79
- if (!isAttributeRecord(attributes)) return void 0;
80
- const entries = Object.entries(attributes).filter(validAttributeEntry).filter(([key]) => !isSensitiveAttributeKey(key));
81
- const reservedEntries = reservedAttributeEntries(entries, options.reservedKeys ?? []);
82
- const reservedKeySet = new Set((options.reservedKeys ?? []).map(normalizeAttributeKey));
83
- const ordinaryEntries = entries.filter(([key]) => !reservedKeySet.has(normalizeAttributeKey(key)));
84
- const selectedEntries = [...reservedEntries, ...ordinaryEntries].slice(0, MAX_ATTRIBUTE_COUNT);
85
- const normalizedEntries = selectedEntries.map(([key, value]) => [key, typeof value === "string" ? value.slice(0, MAX_ATTRIBUTE_VALUE_LENGTH) : value]);
86
- return normalizedEntries.length === 0 ? void 0 : Object.fromEntries(normalizedEntries);
87
- }
88
- function validAttributeEntry(entry) {
89
- const [key, value] = entry;
90
- return key.length > 0 && key.length <= MAX_ATTRIBUTE_KEY_LENGTH && isTelemetryAttributeValue(value);
91
- }
92
- function reservedAttributeEntries(entries, reservedKeys) {
93
- return reservedKeys.flatMap((reservedKey) => {
94
- const entry = entries.find(([key]) => key === reservedKey);
95
- return entry ? [entry] : [];
96
- });
97
- }
98
- function isSensitiveAttributeKey(key) {
99
- const normalizedKey = normalizeAttributeKey(key);
100
- return normalizedKey.length === 0 || !CORRELATION_ATTRIBUTE_KEYS.has(normalizedKey) && SENSITIVE_ATTRIBUTE_KEY_PATTERNS.some((pattern) => pattern.test(normalizedKey));
101
- }
102
- function normalizeAttributeKey(key) {
103
- return key.toLowerCase().replaceAll(/[^a-z0-9]/gu, "");
104
- }
105
- function isTelemetryAttributeValue(value) {
106
- return typeof value === "boolean" || typeof value === "number" && Number.isFinite(value) || typeof value === "string";
107
- }
108
- function isAttributeRecord(value) {
109
- return typeof value === "object" && value !== null && !Array.isArray(value);
110
- }
111
-
112
- // src/telemetry/delivery.ts
113
- import { Context, Data, Deferred, Effect, Layer, ManagedRuntime, Queue, Schedule, Semaphore, Stream, SubscriptionRef } from "effect";
114
-
115
- // src/telemetry/exporter.ts
116
- var MAX_KEEPALIVE_BODY_BYTES = 48 * 1024;
117
- var MAX_JOURNEY_INTAKE_EVENTS_PER_BATCH = 100;
118
- var MAX_JOURNEY_INTAKE_BODY_BYTES = 1024 * 1024;
119
- var MAX_JOURNEY_INTAKE_BATCH_BODY_BYTES = MAX_JOURNEY_INTAKE_BODY_BYTES - 64 * 1024;
120
- var TelemetryExportError = class extends Error {
121
- retriable;
122
- status;
123
- constructor(status) {
124
- super(`Journey telemetry export failed with HTTP ${status}`);
125
- this.name = "TelemetryExportError";
126
- this.status = status;
127
- this.retriable = status === 408 || status === 429 || status >= 500;
128
- }
129
- };
130
- function isRetriableTelemetryExportError(error) {
131
- if (error instanceof TelemetryExportError || error instanceof TelemetryPayloadTooLargeError) return error.retriable;
132
- return true;
133
- }
134
- function createJourneyIntakeExporter(options) {
135
- const fetchImplementation = options.fetch || globalThis.fetch;
136
- const endpoint = new URL(options.path || "/v1/telemetry/journey-events", options.baseUrl).toString();
137
- return (events, exportOptions) => exportJourneyIntake(events, exportOptions, endpoint, fetchImplementation, options);
138
- }
139
- async function exportJourneyIntake(events, exportOptions, endpoint, fetchImplementation, options) {
140
- const batches = journeyContextBatches(events).flatMap((batch) => eventBatches(batch));
141
- if (batches.length === 0) return exportJourneyIntakeBatch(events, exportOptions, endpoint, fetchImplementation, options);
142
- const responses = await Promise.all(batches.map((batch) => exportJourneyIntakeBatch(batch, exportOptions, endpoint, fetchImplementation, options)));
143
- return responses.reduce((total, response) => ({ accepted_count: total.accepted_count + response.accepted_count, duplicate_count: total.duplicate_count + response.duplicate_count }), { accepted_count: 0, duplicate_count: 0 });
144
- }
145
- function eventBatches(events) {
146
- const batches = [];
147
- let batch = [];
148
- for (const event of events) {
149
- if (batch.length === 0) {
150
- batch = [event];
151
- continue;
152
- }
153
- if (batch.length === MAX_JOURNEY_INTAKE_EVENTS_PER_BATCH || encodedByteLength(journeyIntakeBody([...batch, event])) > MAX_JOURNEY_INTAKE_BATCH_BODY_BYTES) {
154
- batches.push(batch);
155
- batch = [event];
156
- continue;
157
- }
158
- batch.push(event);
159
- }
160
- if (batch.length > 0) batches.push(batch);
161
- return batches;
162
- }
163
- async function exportJourneyIntakeBatch(events, exportOptions, endpoint, fetchImplementation, options) {
164
- const body = journeyIntakeBody(events);
165
- assertKeepaliveBodySize(body, exportOptions);
166
- const headers = await (typeof options.headers === "function" ? options.headers() : options.headers);
167
- const response = await fetchImplementation(endpoint, journeyIntakeRequest(events, body, headers, options.credentials, exportOptions));
168
- if (!response.ok) throw new TelemetryExportError(response.status);
169
- return intakeResponse(await response.json().catch(() => ({})), events.length);
170
- }
171
- function assertKeepaliveBodySize(body, options) {
172
- if (options?.keepalive && encodedByteLength(body) > MAX_KEEPALIVE_BODY_BYTES) throw new TelemetryPayloadTooLargeError();
173
- }
174
- function journeyIntakeRequest(events, body, headers, credentials, options) {
175
- return {
176
- method: "POST",
177
- credentials,
178
- headers: { Accept: "application/json", "Content-Type": "application/json", ...headers, ...journeyIntakeHeaders(events) },
179
- body,
180
- keepalive: options?.keepalive
181
- };
182
- }
183
- var TelemetryPayloadTooLargeError = class extends Error {
184
- retriable = false;
185
- constructor() {
186
- super(`Journey telemetry keepalive payload exceeds ${MAX_KEEPALIVE_BODY_BYTES} bytes`);
187
- this.name = "TelemetryPayloadTooLargeError";
188
- }
189
- };
190
- function toJourneyIntakeEvent(event) {
191
- const { version: _version, ...intakeEvent } = event;
192
- return intakeEvent;
193
- }
194
- function journeyIntakeHeaders(events) {
195
- const firstEvent = events[0];
196
- if (!firstEvent) return {};
197
- const headers = {
198
- "x-chalk-journey-id": firstEvent.journey_id
199
- };
200
- if (firstEvent.traceparent) headers.traceparent = firstEvent.traceparent;
201
- if (firstEvent.tracestate) headers.tracestate = firstEvent.tracestate;
202
- return headers;
203
- }
204
- function journeyContextBatches(events) {
205
- const batches = [];
206
- for (const event of events) {
207
- const batch = batches.find(([firstEvent]) => firstEvent !== void 0 && sharesJourneyContext(firstEvent, event));
208
- if (batch) batch.push(event);
209
- else batches.push([event]);
210
- }
211
- return batches;
212
- }
213
- function sharesJourneyContext(left, right) {
214
- return left.journey_id === right.journey_id && left.traceparent === right.traceparent && left.tracestate === right.tracestate;
215
- }
216
- function intakeResponse(body, eventCount) {
217
- const candidate = isRecord(body) ? body : {};
218
- return {
219
- accepted_count: typeof candidate.accepted_count === "number" ? candidate.accepted_count : eventCount,
220
- duplicate_count: typeof candidate.duplicate_count === "number" ? candidate.duplicate_count : 0
221
- };
222
- }
223
- function isRecord(value) {
224
- return typeof value === "object" && value !== null;
225
- }
226
- function journeyIntakeBody(events) {
227
- return JSON.stringify({
228
- events: events.map(({ traceparent: _traceparent, tracestate: _tracestate, ...event }) => event)
229
- });
230
- }
231
- function encodedByteLength(value) {
232
- return new Blob([value]).size;
233
- }
234
-
235
- // src/telemetry/delivery.ts
236
- var NORMAL_FLUSH_DELAY_MS = 50;
237
- var TelemetryExportFailure = class extends Data.TaggedError("TelemetryExportFailure") {
238
- };
239
- var TelemetryStorageFailure = class extends Data.TaggedError("TelemetryStorageFailure") {
240
- };
241
- var TelemetryExporterService = class extends Context.Service()("@chalk/telemetry/TelemetryExporter") {
242
- };
243
- var TelemetryStorageService = class extends Context.Service()("@chalk/telemetry/TelemetryStorage") {
244
- };
245
- var makeTelemetryExporterLayer = (exporter) => Layer.succeed(TelemetryExporterService, telemetryExporterCapability(exporter));
246
- var makeFakeTelemetryExporterLayer = (exporter) => makeTelemetryExporterLayer(exporter);
247
- var makeTelemetryStorageLayer = (storage) => Layer.succeed(TelemetryStorageService, telemetryStorageCapability(storage));
248
- var makeFakeTelemetryStorageLayer = (storage) => makeTelemetryStorageLayer(storage);
249
- var TelemetryDeliveryService = class extends Context.Service()("@chalk/telemetry/TelemetryDelivery") {
250
- };
251
- var makeTelemetryDeliveryLayer = (options) => Layer.effect(TelemetryDeliveryService, makeTelemetryDeliveryService(options));
252
- function makeTelemetryDeliveryService(options, exporter = telemetryExporterCapability(options.exporter), storage = telemetryStorageCapability(options.storage)) {
253
- return Effect.gen(function* () {
254
- const scope = yield* Effect.scope;
255
- const delivery = createTelemetryDeliveryService(options, exporter, storage, scope);
256
- yield* delivery.startScoped();
257
- if (!options.enabled || !storage.configured) yield* delivery.restore();
258
- else yield* delivery.restore().pipe(Effect.forkScoped({ startImmediately: true }));
259
- yield* Effect.addFinalizer(() => delivery.dispose());
260
- return delivery;
261
- });
262
- }
263
- var DeliveryService = class {
264
- constructor(options, exporter, storage, healthRef, pendingRef, queue, scope) {
265
- this.options = options;
266
- this.exporter = exporter;
267
- this.storage = storage;
268
- this.healthRef = healthRef;
269
- this.pendingRef = pendingRef;
270
- this.scope = scope;
271
- this.#batchSize = Math.min(options.batchSize, MAX_JOURNEY_INTAKE_EVENTS_PER_BATCH);
272
- this.#queue = queue;
273
- }
274
- options;
275
- exporter;
276
- storage;
277
- healthRef;
278
- pendingRef;
279
- scope;
280
- #activeKeepaliveBatchEventIds = /* @__PURE__ */ new Set();
281
- #activeNormalBatchEventIds = /* @__PURE__ */ new Set();
282
- #batchSize;
283
- #healthListeners = /* @__PURE__ */ new Set();
284
- #knownEventIds = /* @__PURE__ */ new Set();
285
- #normalGate = Semaphore.makeUnsafe(1);
286
- #persistenceGate = Semaphore.makeUnsafe(1);
287
- #queue;
288
- #ready = Deferred.makeUnsafe();
289
- #recentEventIds = [];
290
- #timeline = [];
291
- #disposed = false;
292
- #loaded = false;
293
- #restoreStarted = false;
294
- #retryScheduled = false;
295
- startScoped() {
296
- return Stream.fromQueue(this.#queue).pipe(
297
- Stream.groupedWithin(this.#batchSize, NORMAL_FLUSH_DELAY_MS),
298
- Stream.runForEach(() => this.flush()),
299
- Effect.catch(() => Effect.void),
300
- Effect.forkScoped,
301
- Effect.asVoid
302
- );
303
- }
304
- restore() {
305
- return Effect.suspend(() => {
306
- if (this.#restoreStarted) return Deferred.await(this.#ready);
307
- this.#restoreStarted = true;
308
- if (!this.options.enabled || !this.storage.configured) {
309
- return Effect.sync(() => {
310
- this.#loaded = true;
311
- }).pipe(Effect.andThen(Deferred.succeed(this.#ready, void 0)), Effect.asVoid);
312
- }
313
- return this.storage.load.pipe(
314
- Effect.catch((error) => this.storageFailed(error).pipe(Effect.as([]))),
315
- Effect.tap(
316
- (persisted) => Effect.sync(() => {
317
- this.mergeRestored(persisted);
318
- this.#loaded = true;
319
- Queue.offerAllUnsafe(this.#queue, this.getPendingEventsUnsafe());
320
- })
321
- ),
322
- Effect.andThen(this.persist()),
323
- Effect.ensuring(Deferred.succeed(this.#ready, void 0)),
324
- Effect.asVoid
325
- );
326
- });
327
- }
328
- awaitReady() {
329
- return Deferred.await(this.#ready);
330
- }
331
- dispose() {
332
- return Effect.suspend(() => {
333
- if (this.#disposed) return Effect.void;
334
- this.#disposed = true;
335
- this.#retryScheduled = false;
336
- return Queue.shutdown(this.#queue);
337
- });
338
- }
339
- persist() {
340
- return Effect.suspend(() => {
341
- if (!this.#loaded || !this.options.enabled || !this.storage.configured) return Effect.void;
342
- return this.#persistenceGate.withPermit(Effect.suspend(() => this.storage.save(this.getPendingEventsUnsafe())).pipe(Effect.catch((error) => this.storageFailed(error))));
343
- });
344
- }
345
- enqueue(event) {
346
- return Effect.sync(() => this.enqueueUnsafe(event)).pipe(Effect.andThen(this.persist()));
347
- }
348
- enqueueUnsafe(event) {
349
- if (this.#disposed || !this.options.enabled || this.#knownEventIds.has(event.event_id)) return false;
350
- this.trackEventID(event.event_id);
351
- const pending = [...this.getPendingEventsUnsafe(), event];
352
- this.setPendingUnsafe(pending);
353
- this.#timeline.push({ eventId: event.event_id, name: event.name, occurredAt: event.occurred_at, phase: event.phase, state: event.state });
354
- if (this.#timeline.length > this.options.maxTimelineEntries) this.#timeline.splice(0, this.#timeline.length - this.options.maxTimelineEntries);
355
- this.handleQueueOverflowUnsafe();
356
- this.setHealthUnsafe({ queueDepth: this.getPendingEventsUnsafe().length });
357
- Queue.offerUnsafe(this.#queue, event);
358
- return this.#loaded && this.nextBatch().length === this.#batchSize;
359
- }
360
- flush(options) {
361
- if (options?.keepalive) return this.flushKeepalive();
362
- return this.#normalGate.withPermit(this.flushNormal());
363
- }
364
- getHealthUnsafe() {
365
- return { ...SubscriptionRef.getUnsafe(this.healthRef) };
366
- }
367
- getPendingEventsUnsafe() {
368
- return [...SubscriptionRef.getUnsafe(this.pendingRef)];
369
- }
370
- getTimelineUnsafe() {
371
- return [...this.#timeline];
372
- }
373
- subscribe(listener) {
374
- this.#healthListeners.add(listener);
375
- listener(this.getHealthUnsafe());
376
- return () => this.#healthListeners.delete(listener);
377
- }
378
- flushNormal() {
379
- return Effect.suspend(() => {
380
- if (!this.canFlushNormal()) return Effect.void;
381
- const batch = this.nextBatch();
382
- if (batch.length === 0) return Effect.void;
383
- this.activateNormalBatch(batch);
384
- return this.deliverNormalBatch(batch).pipe(
385
- Effect.flatMap((result) => {
386
- if (result === "retry") return Effect.void;
387
- const removed = this.removeExportedNormalBatch(batch);
388
- this.recordBatchResultUnsafe(result, removed);
389
- return this.persist().pipe(Effect.andThen(this.flushNormal()));
390
- })
391
- );
392
- });
393
- }
394
- deliverNormalBatch(batch) {
395
- return this.exporter.export(batch.map(toJourneyIntakeEvent)).pipe(
396
- Effect.as("delivered"),
397
- Effect.catch((error) => {
398
- this.exportFailedUnsafe(error);
399
- if (!error.retriable) return Effect.succeed("discarded");
400
- return Effect.sync(() => this.deactivateNormalBatch(batch)).pipe(Effect.andThen(this.persist()), Effect.andThen(this.scheduleRetry()), Effect.as("retry"));
401
- })
402
- );
403
- }
404
- flushKeepalive() {
405
- return Effect.suspend(() => {
406
- if (!this.canFlushKeepalive()) return Effect.void;
407
- const batch = this.keepaliveBatch();
408
- if (batch.length === 0) return Effect.void;
409
- this.activateKeepaliveBatch(batch);
410
- return this.exporter.export(batch.map(toJourneyIntakeEvent), { keepalive: true }).pipe(
411
- Effect.tap(
412
- () => Effect.sync(() => {
413
- const removed = this.removePendingEvents(batch);
414
- if (removed.length > 0) this.recordExportSuccessUnsafe(removed.length);
415
- }).pipe(Effect.andThen(this.persist()))
416
- ),
417
- Effect.catch((error) => Effect.sync(() => this.exportFailedUnsafe(error))),
418
- Effect.ensuring(Effect.sync(() => this.deactivateKeepaliveBatch(batch))),
419
- Effect.asVoid
420
- );
421
- });
422
- }
423
- canFlushNormal() {
424
- return !this.#disposed && this.#loaded && this.options.enabled && this.exporter.configured && !this.#retryScheduled && this.getPendingEventsUnsafe().length > 0;
425
- }
426
- canFlushKeepalive() {
427
- return !this.#disposed && this.#loaded && this.options.enabled && this.exporter.configured && this.getPendingEventsUnsafe().length > 0;
428
- }
429
- nextBatch() {
430
- const available = this.getPendingEventsUnsafe().filter((event) => !this.isBatchActive(event));
431
- return journeyContextBatches(available)[0]?.slice(0, this.#batchSize) ?? [];
432
- }
433
- keepaliveBatch() {
434
- const available = this.getPendingEventsUnsafe().filter((event) => !this.#activeKeepaliveBatchEventIds.has(event.event_id));
435
- const [newestEvent] = available.slice(-this.#batchSize).reverse();
436
- if (!newestEvent) return [];
437
- return boundedKeepaliveBatch(
438
- available.slice(-this.#batchSize).reverse().filter((event) => sharesJourneyContext(event, newestEvent))
439
- );
440
- }
441
- activateNormalBatch(batch) {
442
- for (const event of batch) this.#activeNormalBatchEventIds.add(event.event_id);
443
- }
444
- activateKeepaliveBatch(batch) {
445
- for (const event of batch) this.#activeKeepaliveBatchEventIds.add(event.event_id);
446
- }
447
- removeExportedNormalBatch(batch) {
448
- const removed = this.removePendingEvents(batch);
449
- this.deactivateNormalBatch(batch);
450
- return removed;
451
- }
452
- deactivateNormalBatch(batch) {
453
- for (const event of batch) this.#activeNormalBatchEventIds.delete(event.event_id);
454
- }
455
- deactivateKeepaliveBatch(batch) {
456
- for (const event of batch) this.#activeKeepaliveBatchEventIds.delete(event.event_id);
457
- }
458
- isBatchActive(event) {
459
- return this.#activeNormalBatchEventIds.has(event.event_id) || this.#activeKeepaliveBatchEventIds.has(event.event_id);
460
- }
461
- removePendingEvents(events) {
462
- const eventIds = new Set(events.map((event) => event.event_id));
463
- const pending = this.getPendingEventsUnsafe();
464
- const removed = pending.filter((event) => eventIds.has(event.event_id));
465
- this.setPendingUnsafe(pending.filter((event) => !eventIds.has(event.event_id)));
466
- return removed;
467
- }
468
- trackEventID(eventId) {
469
- this.#knownEventIds.add(eventId);
470
- this.#recentEventIds.push(eventId);
471
- if (this.#recentEventIds.length <= this.options.maxQueueSize * 4) return;
472
- const expired = this.#recentEventIds.splice(0, this.#recentEventIds.length - this.options.maxQueueSize * 4);
473
- for (const expiredEventId of expired) this.#knownEventIds.delete(expiredEventId);
474
- }
475
- handleQueueOverflowUnsafe() {
476
- const pending = this.getPendingEventsUnsafe();
477
- const overflow = Math.max(0, pending.length - this.options.maxQueueSize);
478
- const dropped = pending.filter((event) => !this.isBatchActive(event)).slice(0, overflow);
479
- if (dropped.length === 0) return;
480
- const droppedEventIds = new Set(dropped.map((event) => event.event_id));
481
- this.setPendingUnsafe(pending.filter((event) => !droppedEventIds.has(event.event_id)));
482
- this.setHealthUnsafe({ droppedEvents: this.getHealthUnsafe().droppedEvents + dropped.length, queueDepth: this.getPendingEventsUnsafe().length });
483
- queueMicrotask(() => this.notifyDrops(dropped));
484
- }
485
- mergeRestored(persisted) {
486
- const restored = persisted.map((event) => sanitizeRestoredEvent(event)).filter((event) => !this.#knownEventIds.has(event.event_id));
487
- for (const event of restored) this.trackEventID(event.event_id);
488
- this.setPendingUnsafe([...restored, ...this.getPendingEventsUnsafe()]);
489
- this.handleQueueOverflowUnsafe();
490
- this.setHealthUnsafe({ queueDepth: this.getPendingEventsUnsafe().length });
491
- }
492
- recordBatchResultUnsafe(result, batch) {
493
- if (batch.length === 0) return;
494
- if (result === "delivered") {
495
- this.recordExportSuccessUnsafe(batch.length);
496
- return;
497
- }
498
- this.setHealthUnsafe({ droppedEvents: this.getHealthUnsafe().droppedEvents + batch.length, queueDepth: this.getPendingEventsUnsafe().length });
499
- queueMicrotask(() => this.notifyDrops(batch));
500
- }
501
- recordExportSuccessUnsafe(batchSize) {
502
- this.setHealthUnsafe({
503
- exportedEvents: this.getHealthUnsafe().exportedEvents + batchSize,
504
- lastError: void 0,
505
- queueDepth: this.getPendingEventsUnsafe().length,
506
- status: "healthy"
507
- });
508
- }
509
- exportFailedUnsafe(error) {
510
- this.setHealthUnsafe({
511
- failedBatches: this.getHealthUnsafe().failedBatches + 1,
512
- lastError: errorMessage(error.cause),
513
- queueDepth: this.getPendingEventsUnsafe().length,
514
- status: "degraded"
515
- });
516
- }
517
- storageFailed(error) {
518
- return Effect.sync(() => this.setHealthUnsafe({ lastError: errorMessage(error.cause), status: "degraded" }));
519
- }
520
- scheduleRetry() {
521
- return Effect.suspend(() => {
522
- if (this.#disposed || this.#retryScheduled) return Effect.void;
523
- this.#retryScheduled = true;
524
- return Effect.forkIn(
525
- Effect.void.pipe(
526
- Effect.repeat(Schedule.spaced(this.options.retryDelayMs).pipe(Schedule.upTo({ times: 1 }))),
527
- Effect.andThen(
528
- Effect.sync(() => {
529
- this.#retryScheduled = false;
530
- })
531
- ),
532
- Effect.andThen(this.flush()),
533
- Effect.catch(() => Effect.void)
534
- ),
535
- this.scope
536
- ).pipe(Effect.asVoid);
537
- });
538
- }
539
- setPendingUnsafe(events) {
540
- Effect.runSync(SubscriptionRef.set(this.pendingRef, [...events]));
541
- }
542
- setHealthUnsafe(update) {
543
- const health = { ...this.getHealthUnsafe(), ...update };
544
- Effect.runSync(SubscriptionRef.set(this.healthRef, health));
545
- for (const listener of this.#healthListeners) {
546
- try {
547
- listener({ ...health });
548
- } catch {
549
- }
550
- }
551
- }
552
- notifyDrops(dropped) {
553
- try {
554
- this.options.onDrop?.(dropped);
555
- } catch {
556
- }
557
- }
558
- };
559
- function sanitizeRestoredEvent(event) {
560
- const attributes = normalizeTelemetryAttributes(event.attributes, { reservedKeys: TELEMETRY_BUILT_IN_ATTRIBUTE_KEYS });
561
- return attributes ? { ...event, attributes } : { ...event, attributes: void 0 };
562
- }
563
- function createTelemetryDeliveryService(options, exporter, storage, scope) {
564
- return new DeliveryService(
565
- options,
566
- exporter,
567
- storage,
568
- Effect.runSync(
569
- SubscriptionRef.make({
570
- droppedEvents: 0,
571
- exportedEvents: 0,
572
- failedBatches: 0,
573
- queueDepth: 0,
574
- status: "idle"
575
- })
576
- ),
577
- Effect.runSync(SubscriptionRef.make([])),
578
- Effect.runSync(Queue.make()),
579
- scope
580
- );
581
- }
582
- function telemetryExporterCapability(exporter) {
583
- return {
584
- configured: exporter !== void 0,
585
- export: (events, options) => {
586
- if (!exporter) return Effect.void;
587
- return Effect.tryPromise({
588
- try: () => exporter(events, options),
589
- catch: (cause) => new TelemetryExportFailure({ cause, retriable: isRetriableTelemetryExportError(cause) })
590
- }).pipe(Effect.asVoid);
591
- }
592
- };
593
- }
594
- function telemetryStorageCapability(storage) {
595
- return {
596
- configured: storage !== void 0,
597
- load: storage ? Effect.tryPromise({ try: () => storage.load(), catch: (cause) => new TelemetryStorageFailure({ cause }) }) : Effect.succeed([]),
598
- save: (events) => storage ? Effect.tryPromise({ try: () => storage.save(events), catch: (cause) => new TelemetryStorageFailure({ cause }) }) : Effect.void
599
- };
600
- }
601
- function boundedKeepaliveBatch(events) {
602
- const batch = [];
603
- for (const event of events) {
604
- if (!keepaliveEventFits(event, batch)) break;
605
- batch.unshift(event);
606
- }
607
- return batch;
608
- }
609
- function keepaliveEventFits(event, batch) {
610
- return encodedByteLength(journeyIntakeBody([event, ...batch].map(toJourneyIntakeEvent))) <= MAX_KEEPALIVE_BODY_BYTES;
611
- }
612
- function errorMessage(error) {
613
- return error instanceof Error ? error.message : "Telemetry exporter failed";
614
- }
615
-
616
- // src/telemetry/rtc.ts
617
- function summarizeRtcStats(stats) {
618
- const accumulator = {
619
- bytesReceived: 0,
620
- bytesSent: 0,
621
- framesDropped: 0,
622
- inboundStreams: 0,
623
- jitterSamples: [],
624
- outboundStreams: 0,
625
- packetsLost: 0,
626
- packetsReceived: 0,
627
- packetsSent: 0,
628
- roundTripTimeSamples: [],
629
- transportEntries: 0,
630
- dtlsStates: [],
631
- selectedCandidatePairStates: []
632
- };
633
- for (const stat of stats) {
634
- statHandlers[stat.type ?? ""]?.(accumulator, stat);
635
- }
636
- return finalizeAccumulator(accumulator);
637
- }
638
- function rtcSummaryAttributes(connection, stats) {
639
- const summary = summarizeRtcStats(stats);
640
- return {
641
- bytes_received: summary.bytesReceived,
642
- bytes_sent: summary.bytesSent,
643
- frames_dropped: summary.framesDropped,
644
- inbound_streams: summary.inboundStreams,
645
- outbound_streams: summary.outboundStreams,
646
- packets_lost: summary.packetsLost,
647
- packets_received: summary.packetsReceived,
648
- packets_sent: summary.packetsSent,
649
- transport_entries: summary.transportEntries,
650
- ...summary.dtlsState !== void 0 ? { dtls_state: summary.dtlsState } : {},
651
- ...summary.selectedCandidatePairState !== void 0 ? { selected_candidate_pair_state: summary.selectedCandidatePairState } : {},
652
- ...connection.connectionState !== void 0 ? { connection_state: connection.connectionState } : {},
653
- ...connection.iceConnectionState !== void 0 ? { ice_connection_state: connection.iceConnectionState } : {},
654
- ...summary.jitterMs !== void 0 ? { jitter_ms: summary.jitterMs } : {},
655
- ...summary.roundTripTimeMs !== void 0 ? { round_trip_time_ms: summary.roundTripTimeMs } : {},
656
- ...connection.signalingState !== void 0 ? { signaling_state: connection.signalingState } : {}
657
- };
658
- }
659
- var statHandlers = {
660
- "inbound-rtp": accumulateInbound,
661
- "outbound-rtp": accumulateOutbound,
662
- "candidate-pair": accumulateTransport,
663
- transport: accumulateTransport
664
- };
665
- function accumulateInbound(accumulator, stat) {
666
- accumulator.inboundStreams += 1;
667
- accumulator.bytesReceived += numberOrZero(stat.bytesReceived);
668
- accumulator.framesDropped += numberOrZero(stat.framesDropped);
669
- accumulator.packetsLost += numberOrZero(stat.packetsLost);
670
- accumulator.packetsReceived += numberOrZero(stat.packetsReceived);
671
- appendFinite(accumulator.jitterSamples, stat.jitter);
672
- }
673
- function accumulateOutbound(accumulator, stat) {
674
- accumulator.outboundStreams += 1;
675
- accumulator.bytesSent += numberOrZero(stat.bytesSent);
676
- accumulator.packetsSent += numberOrZero(stat.packetsSent);
677
- }
678
- function accumulateTransport(accumulator, stat) {
679
- accumulator.transportEntries += 1;
680
- appendFinite(accumulator.roundTripTimeSamples, stat.roundTripTime);
681
- appendRtcState(accumulator.dtlsStates, stat.dtlsState, ["new", "connecting", "connected", "closed", "failed"]);
682
- if (stat.type === "candidate-pair" && (stat.selected === true || stat.nominated === true)) {
683
- appendRtcState(accumulator.selectedCandidatePairStates, stat.state, ["frozen", "waiting", "in-progress", "failed", "succeeded"]);
684
- }
685
- }
686
- function appendRtcState(states, value, allowed) {
687
- if (value !== void 0 && allowed.includes(value) && !states.includes(value)) states.push(value);
688
- }
689
- function appendFinite(samples, value) {
690
- if (isFiniteNumber(value)) samples.push(value);
691
- }
692
- function finalizeAccumulator(accumulator) {
693
- const jitterMs = averageMilliseconds(accumulator.jitterSamples);
694
- const roundTripTimeMs = averageMilliseconds(accumulator.roundTripTimeSamples);
695
- const dtlsState = lastValue(accumulator.dtlsStates);
696
- const selectedCandidatePairState = lastValue(accumulator.selectedCandidatePairStates);
697
- return {
698
- bytesReceived: accumulator.bytesReceived,
699
- bytesSent: accumulator.bytesSent,
700
- framesDropped: accumulator.framesDropped,
701
- inboundStreams: accumulator.inboundStreams,
702
- outboundStreams: accumulator.outboundStreams,
703
- packetsLost: accumulator.packetsLost,
704
- packetsReceived: accumulator.packetsReceived,
705
- packetsSent: accumulator.packetsSent,
706
- transportEntries: accumulator.transportEntries,
707
- ...dtlsState !== void 0 ? { dtlsState } : {},
708
- ...selectedCandidatePairState !== void 0 ? { selectedCandidatePairState } : {},
709
- ...jitterMs !== void 0 ? { jitterMs } : {},
710
- ...roundTripTimeMs !== void 0 ? { roundTripTimeMs } : {}
711
- };
712
- }
713
- function lastValue(values) {
714
- return values.at(-1);
715
- }
716
- function averageMilliseconds(samples) {
717
- if (samples.length === 0) return void 0;
718
- return Math.round(samples.reduce((total, sample) => total + sample, 0) / samples.length * 1e3);
719
- }
720
- function isFiniteNumber(value) {
721
- return typeof value === "number" && Number.isFinite(value);
722
- }
723
- function numberOrZero(value) {
724
- return isFiniteNumber(value) ? value : 0;
725
- }
726
-
727
- // src/telemetry/journey.ts
728
- var TelemetryJourney = class {
729
- context;
730
- #telemetry;
731
- #lastEventId;
732
- #sequence = 0;
733
- #terminalEvent;
734
- constructor(telemetry, context) {
735
- this.#telemetry = telemetry;
736
- this.context = context;
737
- }
738
- get headers() {
739
- return journeyHeaders(this.context);
740
- }
741
- phase(phase, attributes) {
742
- if (this.#terminalEvent) return void 0;
743
- return this.record({ name: "journey.phase", phase, state: "in_progress", origin_kind: "client", attributes });
744
- }
745
- terminal(state, attributes) {
746
- if (this.#terminalEvent) return this.#terminalEvent;
747
- this.#terminalEvent = this.record({ name: "journey.terminal", phase: "terminal", state, origin_kind: "client", attributes });
748
- return this.#terminalEvent;
749
- }
750
- recordHttpRequest(observation) {
751
- if (this.#terminalEvent) return void 0;
752
- return this.record({
753
- name: "http.request",
754
- phase: "signaling",
755
- state: observation.state || "observed",
756
- origin_kind: "http",
757
- attributes: httpObservationAttributes(observation)
758
- });
759
- }
760
- recordSyncFrame(observation) {
761
- if (this.#terminalEvent) return void 0;
762
- return this.record({
763
- name: "sync.frame",
764
- phase: "signaling",
765
- state: observation.state ?? "observed",
766
- origin_kind: "sync",
767
- upstream_visibility: "propagated",
768
- attributes: { direction: observation.direction, frame_type: observation.frameType }
769
- });
770
- }
771
- recordRtcSummary(connection, stats, state = "observed") {
772
- if (this.#terminalEvent) return void 0;
773
- return this.record({ name: "rtc.summary", phase: "media", state, origin_kind: "rtc", attributes: rtcSummaryAttributes(connection, stats) });
774
- }
775
- recordDiagnostic(observation) {
776
- if (this.#terminalEvent) return void 0;
777
- return this.record(
778
- {
779
- name: "diagnostic.timeline",
780
- phase: observation.phase ?? "recovery",
781
- state: observation.state ?? "observed",
782
- origin_kind: "diagnostic",
783
- attributes: {
784
- ...observation.attributes,
785
- category: observation.category,
786
- code: observation.code,
787
- ...observation.metricValue !== void 0 ? { metric_value: observation.metricValue } : {}
788
- }
789
- },
790
- { reservedKeys: ["category", "code", ...observation.metricValue === void 0 ? [] : ["metric_value"]] }
791
- );
792
- }
793
- startChild(options) {
794
- return this.#telemetry.startJourney({ ...options, parent: this });
795
- }
796
- record(draft, options) {
797
- const event = this.#telemetry.emit(
798
- this.context,
799
- ++this.#sequence,
800
- {
801
- ...draft,
802
- ...draft.parent_event_id || !this.#lastEventId ? {} : { parent_event_id: this.#lastEventId }
803
- },
804
- options
805
- );
806
- this.#lastEventId = event.event_id;
807
- return event;
808
- }
809
- linkChild(child) {
810
- return this.record({
811
- name: "journey.linked",
812
- phase: "root",
813
- state: "observed",
814
- origin_kind: "client",
815
- parent_event_id: this.#lastEventId,
816
- attributes: { child_journey_id: child.journeyId, relationship: "fanout" }
817
- }).event_id;
818
- }
819
- start(kind, attributes, parentEventId) {
820
- const event = this.#telemetry.emit(this.context, ++this.#sequence, {
821
- name: "journey.started",
822
- phase: "root",
823
- state: "started",
824
- origin_kind: "client",
825
- ...parentEventId ? { parent_event_id: parentEventId } : {},
826
- attributes: { ...attributes, journey_kind: kind }
827
- });
828
- this.#lastEventId = event.event_id;
829
- }
830
- };
831
- function httpObservationAttributes(observation) {
832
- return {
833
- method: observation.method.toUpperCase(),
834
- route: observation.route,
835
- ...observation.durationMs !== void 0 ? { duration_ms: Math.round(observation.durationMs) } : {},
836
- ...observation.statusCode !== void 0 ? { status_code: observation.statusCode } : {}
837
- };
838
- }
839
-
840
- // src/telemetry/types.ts
841
- var TELEMETRY_EVENT_VERSION = 1;
842
-
843
- // src/telemetry/client.ts
844
- var DEFAULT_BATCH_SIZE = 25;
845
- var DEFAULT_QUEUE_SIZE = 500;
846
- var DEFAULT_TIMELINE_SIZE = 100;
847
- var DEFAULT_RETRY_DELAY_MS = 2e3;
848
- var journeyIdentifierPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
849
- var TelemetryClient = class {
850
- enabled;
851
- #delivery;
852
- #eventSource;
853
- #ready;
854
- #runtime;
855
- #disposePromise;
856
- constructor(options = {}) {
857
- const { enabled = false } = options;
858
- this.enabled = enabled;
859
- const deliveryOptions = {
860
- batchSize: Math.min(positiveInteger(options.maxBatchSize, DEFAULT_BATCH_SIZE), MAX_JOURNEY_INTAKE_EVENTS_PER_BATCH),
861
- enabled,
862
- exporter: resolveExporter(options),
863
- maxQueueSize: positiveInteger(options.maxQueueSize, DEFAULT_QUEUE_SIZE),
864
- maxTimelineEntries: positiveInteger(options.maxTimelineEntries, DEFAULT_TIMELINE_SIZE),
865
- onDrop: options.onDrop,
866
- retryDelayMs: positiveInteger(options.retryDelayMs, DEFAULT_RETRY_DELAY_MS),
867
- storage: options.storage
868
- };
869
- this.#runtime = ManagedRuntime2.make(Layer2.mergeAll(makeTelemetryDeliveryLayer(deliveryOptions), makeTelemetryEventSourceLayer(options.now)));
870
- this.#delivery = this.#runtime.runSync(Effect2.service(TelemetryDeliveryService));
871
- this.#eventSource = this.#runtime.runSync(Effect2.service(TelemetryEventSourceService));
872
- this.#ready = this.#runtime.runPromiseExit(this.#delivery.awaitReady()).then(() => void 0);
873
- }
874
- startJourney(options) {
875
- const journey = new TelemetryJourney(this, createJourneyContext(options, this.#eventSource));
876
- journey.start(options.kind, options.attributes, linkParentJourney(options.parent, journey.context));
877
- return journey;
878
- }
879
- getExporterHealth() {
880
- return this.#delivery.getHealthUnsafe();
881
- }
882
- getTimeline() {
883
- return this.#delivery.getTimelineUnsafe();
884
- }
885
- getPendingEvents() {
886
- return this.#delivery.getPendingEventsUnsafe();
887
- }
888
- subscribeExporterHealth(listener) {
889
- return this.#delivery.subscribe(listener);
890
- }
891
- async flush(options) {
892
- if (this.#disposePromise) return this.#disposePromise;
893
- await this.#ready;
894
- if (this.#disposePromise) return this.#disposePromise;
895
- await this.#runtime.runPromise(this.#delivery.flush(options));
896
- }
897
- dispose() {
898
- if (this.#disposePromise) return;
899
- Effect2.runSync(this.#delivery.dispose());
900
- this.#disposePromise = this.#runtime.dispose();
901
- }
902
- /** Internal for TelemetryJourney. Event construction remains synchronous and never waits for storage or network I/O. */
903
- emit(context, sequence, draft, options) {
904
- const trace = traceContextFromJourney(context);
905
- const { attributes: draftAttributes, ...eventDraft } = draft;
906
- const attributes = normalizeTelemetryAttributes(draftAttributes, options);
907
- const event = {
908
- ...eventDraft,
909
- version: TELEMETRY_EVENT_VERSION,
910
- event_id: this.#eventSource.createUuid(),
911
- journey_id: context.journeyId,
912
- sequence,
913
- occurred_at: this.#eventSource.now().toISOString(),
914
- first_observed_layer: eventDraft.first_observed_layer || eventDraft.origin_kind,
915
- upstream_visibility: eventDraft.upstream_visibility || "local",
916
- trace_id: trace.traceId,
917
- span_id: trace.spanId,
918
- traceparent: context.traceparent,
919
- ...context.tracestate ? { tracestate: context.tracestate } : {},
920
- ...attributes ? { attributes } : {}
921
- };
922
- const flushNow = this.#delivery.enqueueUnsafe(event);
923
- void this.#runtime.runPromiseExit(flushNow ? this.#delivery.flush() : this.#delivery.persist());
924
- return event;
925
- }
926
- };
927
- function createTelemetryClient(options = {}) {
928
- return new TelemetryClient(options);
929
- }
930
- function resolveExporter(options) {
931
- if (options.exporter) return options.exporter;
932
- if (!options.baseUrl) return void 0;
933
- return createJourneyIntakeExporter({ baseUrl: options.baseUrl, credentials: options.credentials, fetch: options.fetch, path: options.exporterPath });
934
- }
935
- function createJourneyContext(options, eventSource) {
936
- const parentContext = options.parent?.context;
937
- const trace = createTraceContext(parentContext?.traceparent ?? options.traceparent, parentContext?.tracestate ?? options.tracestate);
938
- const journeyId = resolveJourneyId(options.journeyId, eventSource);
939
- return {
940
- journeyId,
941
- rootJourneyId: parentContext?.rootJourneyId ?? journeyId,
942
- traceparent: trace.traceparent,
943
- ...trace.tracestate ? { tracestate: trace.tracestate } : {}
944
- };
945
- }
946
- function linkParentJourney(parent, child) {
947
- if (!parent) return void 0;
948
- return parent.linkChild(child);
949
- }
950
- function resolveJourneyId(value, eventSource) {
951
- if (!value || !validJourneyIdentifier(value)) return eventSource.createUuid();
952
- return value.toLowerCase();
953
- }
954
- function validJourneyIdentifier(value) {
955
- return journeyIdentifierPattern.test(value) && value.replaceAll("0", "").replaceAll("-", "") !== "";
956
- }
957
- function positiveInteger(value, fallback) {
958
- return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : fallback;
959
- }
960
-
961
- // src/telemetry/storage.ts
962
- function createMemoryTelemetryStorage(initialEvents = []) {
963
- let events = [...initialEvents];
964
- return {
965
- async load() {
966
- return [...events];
967
- },
968
- async save(nextEvents) {
969
- events = [...nextEvents];
970
- }
971
- };
972
- }
973
- function createKeyValueTelemetryStorage(storage, key = "chalk.telemetry.v1") {
974
- return {
975
- async load() {
976
- const value = await storage.getItem(key);
977
- if (!value) {
978
- return [];
979
- }
980
- return telemetryEvents(JSON.parse(value));
981
- },
982
- async save(events) {
983
- await storage.setItem(key, JSON.stringify(events));
984
- }
985
- };
986
- }
987
- function createBrowserTelemetryStorage(key = "chalk.telemetry.v1") {
988
- const storage = getBrowserLocalStorage();
989
- if (!storage) return void 0;
990
- return createKeyValueTelemetryStorage(
991
- {
992
- async getItem(storageKey) {
993
- return storage.getItem(storageKey);
994
- },
995
- async setItem(storageKey, value) {
996
- storage.setItem(storageKey, value);
997
- }
998
- },
999
- key
1000
- );
1001
- }
1002
- function createBrowserRuntimeTelemetryStorage(keyPrefix = "chalk.telemetry.v1") {
1003
- const storage = getBrowserLocalStorage();
1004
- if (!storage) return void 0;
1005
- const key = `${keyPrefix}.${createUuid()}`;
1006
- const state = createRuntimeTelemetryStorageState();
1007
- return {
1008
- async load() {
1009
- return loadRuntimeQueue(storage, keyPrefix, key, state);
1010
- },
1011
- async save(events) {
1012
- saveRuntimeQueue(storage, keyPrefix, key, state, events);
1013
- }
1014
- };
1015
- }
1016
- function getBrowserLocalStorage() {
1017
- try {
1018
- return globalThis.localStorage;
1019
- } catch {
1020
- return void 0;
1021
- }
1022
- }
1023
- function createRuntimeTelemetryStorageState() {
1024
- return {
1025
- adoptedQueues: /* @__PURE__ */ new Map(),
1026
- knownEventIds: /* @__PURE__ */ new Set(),
1027
- retainedEvents: /* @__PURE__ */ new Map(),
1028
- loaded: false
1029
- };
1030
- }
1031
- function loadRuntimeQueue(storage, keyPrefix, ownKey, state) {
1032
- const recovered = readAndAdoptQueues(
1033
- storage,
1034
- storageKeys(storage).filter((storageKey) => storageKey !== ownKey && isRuntimeTelemetryKey(storageKey, keyPrefix)),
1035
- state.adoptedQueues
1036
- );
1037
- markKnownEvents(state, recovered);
1038
- state.loaded = true;
1039
- return recovered;
1040
- }
1041
- function saveRuntimeQueue(storage, keyPrefix, ownKey, state, events) {
1042
- if (!state.loaded) loadRuntimeQueue(storage, keyPrefix, ownKey, state);
1043
- const currentEvents = trackCurrentEvents(state, events);
1044
- retainUnseenAdoptedEvents(state, currentEvents, recoverAdoptedQueues(storage, state.adoptedQueues));
1045
- persistRuntimeQueue(storage, ownKey, state, currentEvents);
1046
- }
1047
- function trackCurrentEvents(state, events) {
1048
- const currentEvents = dedupeEvents(events);
1049
- markKnownEvents(state, currentEvents);
1050
- for (const event of currentEvents) state.retainedEvents.delete(event.event_id);
1051
- return currentEvents;
1052
- }
1053
- function markKnownEvents(state, events) {
1054
- for (const event of events) state.knownEventIds.add(event.event_id);
1055
- }
1056
- function retainUnseenAdoptedEvents(state, currentEvents, recovered) {
1057
- const currentEventIds = new Set(currentEvents.map((event) => event.event_id));
1058
- for (const event of recovered) {
1059
- if (!currentEventIds.has(event.event_id) && !state.knownEventIds.has(event.event_id)) {
1060
- state.retainedEvents.set(event.event_id, event);
1061
- }
1062
- }
1063
- }
1064
- function persistRuntimeQueue(storage, ownKey, state, currentEvents) {
1065
- const persistedEvents = dedupeEvents([...currentEvents, ...state.retainedEvents.values()]);
1066
- storage.setItem(ownKey, JSON.stringify(persistedEvents));
1067
- removeAdoptedQueues(storage, state.adoptedQueues);
1068
- }
1069
- function readAndAdoptQueues(storage, keys, adoptedQueues) {
1070
- const recovered = [];
1071
- for (const key of keys) {
1072
- const value = storage.getItem(key);
1073
- if (value === null) continue;
1074
- const events = parseEvents(value);
1075
- if (!events) {
1076
- storage.removeItem(key);
1077
- continue;
1078
- }
1079
- adoptedQueues.set(key, value);
1080
- recovered.push(...events);
1081
- }
1082
- return dedupeEvents(recovered);
1083
- }
1084
- function recoverAdoptedQueues(storage, adoptedQueues) {
1085
- const recovered = [];
1086
- for (const key of adoptedQueues.keys()) {
1087
- const value = storage.getItem(key);
1088
- if (value === null) {
1089
- adoptedQueues.delete(key);
1090
- continue;
1091
- }
1092
- const events = parseEvents(value);
1093
- if (!events) {
1094
- storage.removeItem(key);
1095
- adoptedQueues.delete(key);
1096
- continue;
1097
- }
1098
- adoptedQueues.set(key, value);
1099
- recovered.push(...events);
1100
- }
1101
- return dedupeEvents(recovered);
1102
- }
1103
- function removeAdoptedQueues(storage, adoptedQueues) {
1104
- for (const [key, value] of adoptedQueues) {
1105
- if (storage.getItem(key) !== value) continue;
1106
- storage.removeItem(key);
1107
- adoptedQueues.delete(key);
1108
- }
1109
- }
1110
- function storageKeys(storage) {
1111
- return Array.from({ length: storage.length }, (_, index) => storage.key(index)).filter((key) => key !== null);
1112
- }
1113
- function isRuntimeTelemetryKey(key, keyPrefix) {
1114
- return key === keyPrefix || key.startsWith(`${keyPrefix}.`);
1115
- }
1116
- function parseEvents(value) {
1117
- try {
1118
- return telemetryEvents(JSON.parse(value));
1119
- } catch {
1120
- return void 0;
1121
- }
1122
- }
1123
- function telemetryEvents(value) {
1124
- return Array.isArray(value) ? value.filter(isTelemetryEvent) : [];
1125
- }
1126
- function isTelemetryEvent(value) {
1127
- if (!isRecord2(value)) return false;
1128
- return typeof value.event_id === "string" && typeof value.first_observed_layer === "string" && typeof value.journey_id === "string" && typeof value.name === "string" && typeof value.occurred_at === "string" && typeof value.origin_kind === "string" && typeof value.phase === "string" && typeof value.sequence === "number" && typeof value.state === "string" && typeof value.upstream_visibility === "string" && value.version === 1;
1129
- }
1130
- function isRecord2(value) {
1131
- return typeof value === "object" && value !== null;
1132
- }
1133
- function dedupeEvents(events) {
1134
- const unique = /* @__PURE__ */ new Map();
1135
- for (const event of events) unique.set(event.event_id, event);
1136
- return [...unique.values()];
1137
- }
1138
-
1139
- export {
1140
- normalizeTelemetryAttributes,
1141
- createJourneyIntakeExporter,
1142
- toJourneyIntakeEvent,
1143
- TelemetryExportFailure,
1144
- TelemetryStorageFailure,
1145
- TelemetryExporterService,
1146
- TelemetryStorageService,
1147
- makeTelemetryExporterLayer,
1148
- makeFakeTelemetryExporterLayer,
1149
- makeTelemetryStorageLayer,
1150
- makeFakeTelemetryStorageLayer,
1151
- TelemetryDeliveryService,
1152
- makeTelemetryDeliveryLayer,
1153
- summarizeRtcStats,
1154
- rtcSummaryAttributes,
1155
- TelemetryJourney,
1156
- TELEMETRY_EVENT_VERSION,
1157
- TelemetryClient,
1158
- createTelemetryClient,
1159
- createMemoryTelemetryStorage,
1160
- createKeyValueTelemetryStorage,
1161
- createBrowserTelemetryStorage,
1162
- createBrowserRuntimeTelemetryStorage
1163
- };