@rasputin-ai/browser 0.6.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 (52) hide show
  1. package/README.md +111 -0
  2. package/dist/browser-frames.d.ts +11 -0
  3. package/dist/browser-frames.d.ts.map +1 -0
  4. package/dist/browser-ingest-event.d.ts +10 -0
  5. package/dist/browser-ingest-event.d.ts.map +1 -0
  6. package/dist/browser-recorder.d.ts +36 -0
  7. package/dist/browser-recorder.d.ts.map +1 -0
  8. package/dist/browser-runtime-types.d.ts +207 -0
  9. package/dist/browser-runtime-types.d.ts.map +1 -0
  10. package/dist/browser-runtime.d.ts +14 -0
  11. package/dist/browser-runtime.d.ts.map +1 -0
  12. package/dist/browser-transport.d.ts +36 -0
  13. package/dist/browser-transport.d.ts.map +1 -0
  14. package/dist/captured-value.d.ts +48 -0
  15. package/dist/captured-value.d.ts.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +3706 -0
  19. package/dist/ingest-types.d.ts +75 -0
  20. package/dist/ingest-types.d.ts.map +1 -0
  21. package/dist/install-browser-handlers.d.ts +14 -0
  22. package/dist/install-browser-handlers.d.ts.map +1 -0
  23. package/dist/install-browser-runtime.d.ts +11 -0
  24. package/dist/install-browser-runtime.d.ts.map +1 -0
  25. package/dist/map-runtime-state.d.ts +4 -0
  26. package/dist/map-runtime-state.d.ts.map +1 -0
  27. package/dist/native-json-producer.d.ts +40 -0
  28. package/dist/native-json-producer.d.ts.map +1 -0
  29. package/dist/public-types.d.ts +116 -0
  30. package/dist/public-types.d.ts.map +1 -0
  31. package/dist/rasputin-init.d.ts +7 -0
  32. package/dist/rasputin-init.d.ts.map +1 -0
  33. package/dist/sdk-meta.d.ts +4 -0
  34. package/dist/sdk-meta.d.ts.map +1 -0
  35. package/dist/stack/fold-synthetic-frames.d.ts +9 -0
  36. package/dist/stack/fold-synthetic-frames.d.ts.map +1 -0
  37. package/dist/stack/parse-stack.d.ts +3 -0
  38. package/dist/stack/parse-stack.d.ts.map +1 -0
  39. package/dist/stack/raw-frame.d.ts +7 -0
  40. package/dist/stack/raw-frame.d.ts.map +1 -0
  41. package/dist/vite/artifacts.d.ts +102 -0
  42. package/dist/vite/artifacts.d.ts.map +1 -0
  43. package/dist/vite/index.d.ts +3 -0
  44. package/dist/vite/index.d.ts.map +1 -0
  45. package/dist/vite/index.js +4666 -0
  46. package/dist/vite/plugin.d.ts +48 -0
  47. package/dist/vite/plugin.d.ts.map +1 -0
  48. package/dist/vite/resolve-build-identity.d.ts +8 -0
  49. package/dist/vite/resolve-build-identity.d.ts.map +1 -0
  50. package/dist/vite/upload-browser-artifacts.d.ts +10 -0
  51. package/dist/vite/upload-browser-artifacts.d.ts.map +1 -0
  52. package/package.json +49 -0
package/dist/index.js ADDED
@@ -0,0 +1,3706 @@
1
+ // ../core/src/create-client/parse-dsn.ts
2
+ var DSN_PUBLIC_KEY_PREFIX = "rdn_";
3
+ var parseDsn = (value) => {
4
+ const trimmed = typeof value === "string" ? value.trim() : "";
5
+ if (!trimmed) return null;
6
+ try {
7
+ const url = new URL(trimmed);
8
+ const publicKey = decodeURIComponent(url.username);
9
+ if (!publicKey.startsWith(DSN_PUBLIC_KEY_PREFIX)) return null;
10
+ if (!url.host) return null;
11
+ const projectId = url.pathname.replace(/^\/+|\/+$/g, "").split("/")[0] ?? "";
12
+ if (!projectId) return null;
13
+ return {
14
+ publicKey,
15
+ apiUrl: `${url.protocol}//${url.host}`,
16
+ projectId
17
+ };
18
+ } catch {
19
+ return null;
20
+ }
21
+ };
22
+ var isLocalDsnPublicKey = (publicKey) => publicKey === "rdn_local" || publicKey.endsWith("_local");
23
+
24
+ // ../core/src/derivation/sensitive-property-name.ts
25
+ var DEFAULT_SENSITIVE_PROPERTY_NAMES = [
26
+ "password",
27
+ "passwd",
28
+ "pwd",
29
+ "passphrase",
30
+ "token",
31
+ "accesstoken",
32
+ "refreshtoken",
33
+ "sessiontoken",
34
+ "authorization",
35
+ "auth",
36
+ "bearer",
37
+ "cookie",
38
+ "cookies",
39
+ "signedcookies",
40
+ "setcookie",
41
+ "secret",
42
+ "secrets",
43
+ "clientsecret",
44
+ "apikey",
45
+ "xapikey",
46
+ "credential",
47
+ "credentials",
48
+ "privatekey",
49
+ "privkey",
50
+ "pem",
51
+ "jwt",
52
+ "otp",
53
+ "totp",
54
+ "pin",
55
+ "mfacode",
56
+ "sessionid",
57
+ "sid",
58
+ "databaseurl",
59
+ "connectionstring",
60
+ "dsn",
61
+ "mongouri",
62
+ "redisurl",
63
+ "signingkey",
64
+ "encryptionkey",
65
+ "cvv",
66
+ "cvc",
67
+ "cardnumber",
68
+ "ssn",
69
+ "creditcard",
70
+ "securitycode",
71
+ "twofactorcode",
72
+ "masterkey",
73
+ "servicerolekey",
74
+ "stripekey",
75
+ "rawheaders"
76
+ ];
77
+ var DEFAULT_SENSITIVE_PROPERTY_NAME_SET = new Set(DEFAULT_SENSITIVE_PROPERTY_NAMES);
78
+ var SENSITIVE_SEGMENTS = /* @__PURE__ */ new Set([
79
+ "password",
80
+ "passwd",
81
+ "pwd",
82
+ "passphrase",
83
+ "token",
84
+ "secret",
85
+ "cookie",
86
+ "cookies",
87
+ "authorization",
88
+ "auth",
89
+ "bearer",
90
+ "credential",
91
+ "credentials",
92
+ "jwt",
93
+ "otp",
94
+ "totp",
95
+ "pin",
96
+ "sid",
97
+ "dsn",
98
+ "pem",
99
+ "privkey",
100
+ "cvv",
101
+ "cvc",
102
+ "ssn",
103
+ "secrets"
104
+ ]);
105
+ var SENSITIVE_SUFFIXES = [
106
+ "password",
107
+ "passwd",
108
+ "token",
109
+ "secret",
110
+ "apikey",
111
+ "privatekey",
112
+ "credential",
113
+ "credentials",
114
+ "cookie"
115
+ ];
116
+ var compactKey = (key) => {
117
+ let compacted = "";
118
+ for (let index = 0; index < key.length; index++) {
119
+ const code = key.charCodeAt(index);
120
+ if (code === 45 || code === 46 || code === 95) continue;
121
+ compacted += code >= 65 && code <= 90 ? String.fromCharCode(code + 32) : key[index];
122
+ }
123
+ return compacted;
124
+ };
125
+ var stripTrailingDigits = (value) => {
126
+ let end = value.length;
127
+ while (end > 0) {
128
+ const code = value.charCodeAt(end - 1);
129
+ if (code < 48 || code > 57) break;
130
+ end--;
131
+ }
132
+ return end === 0 ? value : value.slice(0, end);
133
+ };
134
+ var segmentsOf = (key) => {
135
+ const segments = [];
136
+ let current = "";
137
+ for (let index = 0; index < key.length; index++) {
138
+ const char = key[index] ?? "";
139
+ const code = key.charCodeAt(index);
140
+ if (code === 45 || code === 46 || code === 95) {
141
+ if (current) segments.push(current.toLowerCase());
142
+ current = "";
143
+ continue;
144
+ }
145
+ if (current && code >= 65 && code <= 90) {
146
+ const prev = current.charCodeAt(current.length - 1);
147
+ const prevIsLower = prev >= 97 && prev <= 122;
148
+ const next = key.charCodeAt(index + 1);
149
+ const nextIsLower = next >= 97 && next <= 122;
150
+ if (prevIsLower || nextIsLower) {
151
+ segments.push(current.toLowerCase());
152
+ current = char;
153
+ continue;
154
+ }
155
+ }
156
+ current += char;
157
+ }
158
+ if (current) segments.push(current.toLowerCase());
159
+ return segments;
160
+ };
161
+ var adjacentPairsSensitive = (left, right) => left === "api" && right === "key" || left === "private" && right === "key" || left === "client" && right === "secret" || left === "set" && right === "cookie" || left === "database" && right === "url" || left === "connection" && right === "string" || left === "mongo" && (right === "uri" || right === "url") || left === "mongodb" && (right === "uri" || right === "url") || left === "redis" && (right === "uri" || right === "url") || left === "postgres" && (right === "uri" || right === "url") || left === "database" && right === "uri" || left === "signing" && right === "key" || left === "master" && right === "key" || left === "stripe" && right === "key" || left === "service" && right === "role" || left === "security" && right === "code" || left === "two" && right === "factor" || left === "encryption" && right === "key" || left === "session" && (right === "id" || right === "key" || right === "token") || left === "card" && right === "number" || left === "mfa" && right === "code";
162
+ var propertyNameIsSensitive = (key) => {
163
+ const compacted = compactKey(key);
164
+ const compactForms = [compacted, stripTrailingDigits(compacted)].filter(
165
+ (value, index, values) => value.length > 0 && values.indexOf(value) === index
166
+ );
167
+ for (const form of compactForms) {
168
+ if (DEFAULT_SENSITIVE_PROPERTY_NAME_SET.has(form)) return true;
169
+ for (const suffix of SENSITIVE_SUFFIXES) {
170
+ if (form.endsWith(suffix)) return true;
171
+ }
172
+ if (form.includes("apikey") || form.includes("xapikey")) return true;
173
+ }
174
+ const segments = segmentsOf(key).map((segment) => stripTrailingDigits(segment) || segment);
175
+ for (const segment of segments) {
176
+ if (SENSITIVE_SEGMENTS.has(segment)) return true;
177
+ }
178
+ for (let index = 0; index < segments.length - 1; index++) {
179
+ const left = segments[index];
180
+ const right = segments[index + 1];
181
+ if (left && right && adjacentPairsSensitive(left, right)) return true;
182
+ }
183
+ return false;
184
+ };
185
+
186
+ // ../core/src/create-ingest-event/truncate-ingest-message.ts
187
+ var MAX_MESSAGE_BYTES = 8 * 1024;
188
+
189
+ // ../core/src/create-ingest-event/create-ingest-event.ts
190
+ var MAX_STACK_FRAMES = 50;
191
+ var toStackFrame = (frame) => {
192
+ const preferred = frame.original ?? frame.generated;
193
+ const filename = (preferred.file || frame.generated.file).trim();
194
+ if (!filename) return null;
195
+ const fn = frame.function ?? frame.original?.name;
196
+ return {
197
+ filename,
198
+ ...frame.source ? { source: frame.source } : {},
199
+ ...fn ? { function: fn } : {},
200
+ ...preferred.line !== void 0 ? { lineno: preferred.line } : {},
201
+ ...preferred.column !== void 0 ? { colno: preferred.column } : {},
202
+ in_app: frame.in_app
203
+ };
204
+ };
205
+ var framesToStackFrames = (frames) => frames.slice(0, MAX_STACK_FRAMES).map(toStackFrame).filter((frame) => frame !== null);
206
+
207
+ // ../core/src/suppression/identity-needs-message-template.ts
208
+ var identityNeedsMessageTemplate = (type, code) => {
209
+ if (code != null && code !== "") return false;
210
+ return type === "Error";
211
+ };
212
+
213
+ // ../core/src/suppression/parameterize-message.ts
214
+ var MAX_INPUT_LENGTH = 8192;
215
+ var IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
216
+ var isIpAddress = (value) => {
217
+ if (IPV4.test(value)) return true;
218
+ try {
219
+ return new URL(`http://[${value}]`).hostname.length > 2;
220
+ } catch {
221
+ return false;
222
+ }
223
+ };
224
+ var RULES = [
225
+ {
226
+ token: "email",
227
+ pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}\b/giu
228
+ },
229
+ {
230
+ token: "url",
231
+ pattern: /\b(?:https?|wss?):\/\/[^\s"'<>]+/giu
232
+ },
233
+ {
234
+ token: "traceparent",
235
+ pattern: /\b00-[0-9a-f]{32}-[0-9a-f]{16}-0[01]\b/giu
236
+ },
237
+ {
238
+ token: "aws_trace_id",
239
+ pattern: /\b1-[0-9a-f]{8}-[0-9a-f]{24}\b/giu
240
+ },
241
+ {
242
+ token: "uuid",
243
+ pattern: /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/giu
244
+ },
245
+ {
246
+ token: "timestamp",
247
+ pattern: /\b\d{4}-\d{2}-\d{2}[T ][0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?\b/gu
248
+ },
249
+ {
250
+ token: "mac",
251
+ pattern: /\b(?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}\b/giu
252
+ },
253
+ {
254
+ token: "ip",
255
+ pattern: /\b(?:\d{1,3}\.){3}\d{1,3}\b|(?<![\w:])(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}(?![\w:])/giu,
256
+ validate: isIpAddress
257
+ },
258
+ {
259
+ token: "hash",
260
+ pattern: /\b[0-9a-f]{16,128}\b/giu
261
+ },
262
+ {
263
+ token: "hex",
264
+ pattern: /\b0x[0-9a-f]+\b/giu
265
+ },
266
+ {
267
+ token: "duration",
268
+ pattern: /\b\d+(?:\.\d+)?\s?(?:ns|µs|us|ms|secs?|seconds?|mins?|minutes?|hrs?|hours?|days?)\b/giu
269
+ },
270
+ {
271
+ token: "id",
272
+ pattern: /\b(?=[a-z0-9]*[a-z])(?=[a-z0-9]*\d)[a-z0-9]{8,}\b/giu
273
+ }
274
+ ];
275
+ var normalizeForIdentity = (normalized) => {
276
+ try {
277
+ return JSON.stringify(JSON.parse(normalized));
278
+ } catch {
279
+ }
280
+ const lines = normalized.split("\n").filter((line) => line.trim());
281
+ let result = lines.slice(0, 2).join("\n");
282
+ if (result !== normalized) {
283
+ result += "...";
284
+ }
285
+ return result;
286
+ };
287
+ var parameterizeMessage = (rawMessage) => {
288
+ if (rawMessage.length > MAX_INPUT_LENGTH) return rawMessage;
289
+ const normalized = rawMessage.normalize("NFKC").replace(/\r\n?/g, "\n").trim();
290
+ let result = normalizeForIdentity(normalized);
291
+ result = result.replace(/\b([A-Za-z_][\w.-]*)=(["'])(?:\\.|(?!\2).)*\2/gu, "$1=<string>");
292
+ result = result.replace(/\b([A-Za-z_][\w.-]*)=(?:true|false)\b/giu, "$1=<bool>");
293
+ for (const rule of RULES) {
294
+ result = result.replace(rule.pattern, (match) => {
295
+ if (rule.validate && !rule.validate(match)) {
296
+ return match;
297
+ }
298
+ return `<${rule.token}>`;
299
+ });
300
+ }
301
+ result = result.replace(/(?<![\w.])-?\d+\.\d+(?:e[+-]?\d+)?(?![\w.])/giu, "<float>").replace(/(?<![\w.])-?\d+(?![\w.])/gu, "<int>");
302
+ return result;
303
+ };
304
+
305
+ // ../core/src/suppression/compute-burst-key.ts
306
+ var FRAME_COUNT = 4;
307
+ var preferredFile = (frame) => (frame.original ?? frame.generated).file;
308
+ var normalizeFunction = (raw) => {
309
+ if (!raw) return "";
310
+ const name = raw.trim();
311
+ if (name === "<anonymous>" || name === "anonymous") return "";
312
+ return name.replace(/^(async|new|get|set|bound)\s+/, "").replace(/^Object\./, "").replace(/\s+\[as .+\]$/, "");
313
+ };
314
+ var collapseConsecutive = (frames) => {
315
+ const out = [];
316
+ let previousKey = null;
317
+ for (const frame of frames) {
318
+ const file = preferredFile(frame);
319
+ const fn = normalizeFunction(frame.function ?? frame.original?.name);
320
+ const key = `${file}#${fn}`;
321
+ if (key !== previousKey) {
322
+ out.push(frame);
323
+ previousKey = key;
324
+ }
325
+ }
326
+ return out;
327
+ };
328
+ var frameBurstParts = (frames) => {
329
+ const collapsed = collapseConsecutive(frames);
330
+ const inApp = collapsed.filter((frame) => frame.in_app);
331
+ const selected = (inApp.length > 0 ? inApp : collapsed).slice(0, FRAME_COUNT);
332
+ return selected.map((frame) => {
333
+ const file = preferredFile(frame);
334
+ const fn = normalizeFunction(frame.function ?? frame.original?.name);
335
+ return `${file}:${fn}`;
336
+ });
337
+ };
338
+ var computeBurstKey = (errorType, message, code, frames) => {
339
+ const parts = frameBurstParts(frames);
340
+ const codePart = code ?? "";
341
+ let key = `${errorType}\0${codePart}\0${parts.join("\0")}`;
342
+ if (parts.length === 0 || identityNeedsMessageTemplate(errorType, code)) {
343
+ key += `\0${parameterizeMessage(message)}`;
344
+ }
345
+ return key;
346
+ };
347
+
348
+ // ../core/src/suppression/create-suppression.ts
349
+ var DEFAULTS = {
350
+ windowMs: 6e4,
351
+ capPerWindow: 5,
352
+ maxBurstBuckets: 1e3,
353
+ maxPendingSummaries: 1e3
354
+ };
355
+ var sameDim = (a, b) => (a ?? null) === (b ?? null);
356
+ var captureIdentity = (input) => ({
357
+ type: input.errorType,
358
+ message: input.message,
359
+ stack_frames: framesToStackFrames(input.frames),
360
+ origin: input.origin,
361
+ ...input.code !== void 0 ? { code: input.code } : {},
362
+ resolution: "none"
363
+ });
364
+ var createSuppression = (options = {}) => {
365
+ const windowMs = options.windowMs ?? DEFAULTS.windowMs;
366
+ const capPerWindow = options.capPerWindow ?? DEFAULTS.capPerWindow;
367
+ const maxBurstBuckets = options.maxBurstBuckets ?? DEFAULTS.maxBurstBuckets;
368
+ const maxPendingSummaries = options.maxPendingSummaries ?? DEFAULTS.maxPendingSummaries;
369
+ const burstBuckets = /* @__PURE__ */ new Map();
370
+ const pending = [];
371
+ const pushPending = (draft) => {
372
+ pending.push(draft);
373
+ if (pending.length > maxPendingSummaries)
374
+ pending.splice(0, pending.length - maxPendingSummaries);
375
+ };
376
+ const touch = (burstKey, bucket) => {
377
+ burstBuckets.delete(burstKey);
378
+ burstBuckets.set(burstKey, bucket);
379
+ };
380
+ const evictIfNeeded = () => {
381
+ while (burstBuckets.size > maxBurstBuckets) {
382
+ const oldest = burstBuckets.keys().next().value;
383
+ if (oldest === void 0) break;
384
+ const bucket = burstBuckets.get(oldest);
385
+ if (bucket && bucket.suppressed > 0) {
386
+ pushPending(toDraft(bucket, Date.now()));
387
+ }
388
+ burstBuckets.delete(oldest);
389
+ }
390
+ };
391
+ const toDraft = (bucket, windowEndMs) => ({
392
+ count: bucket.suppressed,
393
+ windowStart: bucket.windowStart,
394
+ windowEndMs,
395
+ lastRelease: bucket.lastRelease,
396
+ lastEnvironment: bucket.lastEnvironment,
397
+ identity: bucket.identity,
398
+ captureRawFrames: bucket.captureRawFrames
399
+ });
400
+ const toSummary = (draft, identity) => ({
401
+ count: draft.count,
402
+ window_start: new Date(draft.windowStart).toISOString(),
403
+ window_end: new Date(draft.windowEndMs).toISOString(),
404
+ release: draft.lastRelease,
405
+ environment: draft.lastEnvironment,
406
+ ...identity
407
+ });
408
+ const flushSuppressed = (bucket, windowEndMs) => {
409
+ if (bucket.suppressed <= 0) return;
410
+ pushPending(toDraft(bucket, windowEndMs));
411
+ bucket.suppressed = 0;
412
+ };
413
+ const decide = (input) => {
414
+ const now = input.now ?? Date.now();
415
+ const identity = captureIdentity(input);
416
+ const captureRawFrames = (input.rawFrames ?? []).slice(0, MAX_STACK_FRAMES);
417
+ const burstKey = computeBurstKey(input.errorType, input.message, input.code, input.frames);
418
+ const release = input.release;
419
+ const route = input.route ?? null;
420
+ let bucket = burstBuckets.get(burstKey);
421
+ if (!bucket) {
422
+ bucket = {
423
+ sentInWindow: 1,
424
+ suppressed: 0,
425
+ windowStart: now,
426
+ lastRelease: release,
427
+ lastRoute: route,
428
+ lastEnvironment: input.environment,
429
+ identity,
430
+ captureRawFrames
431
+ };
432
+ burstBuckets.set(burstKey, bucket);
433
+ evictIfNeeded();
434
+ return { action: "send", priority: "first" };
435
+ }
436
+ touch(burstKey, bucket);
437
+ const dimensionChanged = !sameDim(release, bucket.lastRelease) || !sameDim(route, bucket.lastRoute) || bucket.lastEnvironment !== input.environment;
438
+ const windowExpired = now - bucket.windowStart >= windowMs;
439
+ if (dimensionChanged || windowExpired) {
440
+ flushSuppressed(bucket, now);
441
+ bucket.sentInWindow = 1;
442
+ bucket.windowStart = now;
443
+ bucket.lastRelease = release;
444
+ bucket.lastRoute = route;
445
+ bucket.lastEnvironment = input.environment;
446
+ bucket.identity = identity;
447
+ bucket.captureRawFrames = captureRawFrames;
448
+ return { action: "send", priority: "first" };
449
+ }
450
+ if (bucket.sentInWindow < capPerWindow) {
451
+ bucket.sentInWindow += 1;
452
+ bucket.lastRelease = release;
453
+ bucket.lastRoute = route;
454
+ bucket.lastEnvironment = input.environment;
455
+ return { action: "send", priority: "repeat" };
456
+ }
457
+ bucket.suppressed += 1;
458
+ return { action: "suppress" };
459
+ };
460
+ const drainSummaries = (now = Date.now()) => {
461
+ const drafts = pending.splice(0, pending.length);
462
+ for (const bucket of burstBuckets.values()) {
463
+ if (bucket.suppressed > 0) {
464
+ drafts.push(toDraft(bucket, now));
465
+ bucket.suppressed = 0;
466
+ }
467
+ }
468
+ return drafts.map((draft) => ({
469
+ summary: toSummary(draft, draft.identity),
470
+ rawFrames: draft.captureRawFrames
471
+ }));
472
+ };
473
+ const takeSummaries = async (now = Date.now(), prepareSummary) => {
474
+ const drafts = drainSummaries(now);
475
+ if (!prepareSummary) return drafts.map((draft) => draft.summary);
476
+ const out = [];
477
+ for (const draft of drafts) {
478
+ if (draft.rawFrames.length === 0) {
479
+ out.push(draft.summary);
480
+ continue;
481
+ }
482
+ try {
483
+ const resolved = await prepareSummary(draft.rawFrames);
484
+ out.push({ ...draft.summary, ...resolved });
485
+ } catch {
486
+ out.push(draft.summary);
487
+ }
488
+ }
489
+ return out;
490
+ };
491
+ return {
492
+ decide,
493
+ drainSummaries,
494
+ takeSummaries,
495
+ /** Test helper — number of active burst buckets. */
496
+ size: () => burstBuckets.size
497
+ };
498
+ };
499
+
500
+ // src/stack/fold-synthetic-frames.ts
501
+ var RASPUTIN_SHELL_PREFIX = "__rasputinShell__";
502
+ var RASPUTIN_HANDLE_RUN_PREFIX = "__rasputinHandleRun__";
503
+ var frameFile = (frame) => (frame.file ?? frame.generated?.file ?? "").replaceAll("\\", "/");
504
+ var functionName = (frame) => frame.function ?? "";
505
+ var coreFunctionName = (name) => name.trim().replace(/^(async|new|get|set|bound)\s+/, "").replace(/^Object\./, "");
506
+ var isRasputinShellFrame = (name) => typeof name === "string" && coreFunctionName(name).startsWith(RASPUTIN_SHELL_PREFIX);
507
+ var isRasputinHandleRunFrame = (name) => typeof name === "string" && coreFunctionName(name).startsWith(RASPUTIN_HANDLE_RUN_PREFIX);
508
+ var isEngineAsyncStoreFile = (file) => /^(node:|bun:)/i.test(file) && /async[_-]?hooks|async_local_storage|async_context_frame/i.test(file);
509
+ var isInternalAsyncStoreFrame = (frame) => {
510
+ const name = functionName(frame);
511
+ const file = frameFile(frame);
512
+ if (name === "AsyncLocalStorage.run") return true;
513
+ if ((name === "run" || name === "Object.run") && isEngineAsyncStoreFile(file)) return true;
514
+ return false;
515
+ };
516
+ var isSyntheticRasputinFrame = (frame) => {
517
+ const name = functionName(frame);
518
+ if (isRasputinShellFrame(name) || isRasputinHandleRunFrame(name)) return true;
519
+ return isInternalAsyncStoreFrame(frame);
520
+ };
521
+ var foldSyntheticFrames = (frames) => {
522
+ const filtered = frames.filter((frame) => !isSyntheticRasputinFrame(frame));
523
+ if (filtered.length > 0) return filtered;
524
+ const withoutInternals = frames.filter((frame) => !isInternalAsyncStoreFrame(frame));
525
+ return withoutInternals.length > 0 ? withoutInternals : [...frames];
526
+ };
527
+
528
+ // src/stack/parse-stack.ts
529
+ var V8 = /^\s*at (?<fn>.*?) ?\((?<loc>(?:file|https?|blob|native|eval|webpack|rsc|node|bun|data|<anonymous>|\/|[a-z]:\\|\\\\).*?)(?::(?<line>\d+))?(?::(?<col>\d+))?\)?\s*$/i;
530
+ var V8_EVAL = /\((?<loc>\S+)(?::(?<line>\d+))(?::(?<col>\d+))\)/;
531
+ var NODE = /^\s*at (?:(?<fn>(?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(?<loc>.+?):(?<line>\d+)(?::(?<col>\d+))?\)?\s*$/i;
532
+ var JSC = /^\s*(?:(?<fn>[^@]*)(?:\(.*?\))?@)?(?<loc>\S.*?):(?<line>\d+)(?::(?<col>\d+))?\s*$/i;
533
+ var convertToNumber = (value) => {
534
+ if (value === void 0 || value === "") return void 0;
535
+ const n = Number(value);
536
+ return Number.isFinite(n) ? n : void 0;
537
+ };
538
+ var cleanFunctionName = (fn) => {
539
+ if (!fn) return void 0;
540
+ const trimmed = fn.trim();
541
+ if (!trimmed || trimmed === "<anonymous>" || trimmed === "<unknown>") return void 0;
542
+ return trimmed.startsWith("async ") ? trimmed.slice(6) || void 0 : trimmed;
543
+ };
544
+ var stripQueryAndHash = (file) => {
545
+ const query = file.indexOf("?");
546
+ const hash = file.indexOf("#");
547
+ let end = file.length;
548
+ if (query >= 0) end = query;
549
+ if (hash >= 0 && hash < end) end = hash;
550
+ return file.slice(0, end);
551
+ };
552
+ var cleanFilePath = (loc) => {
553
+ if (loc.startsWith("native")) return "native";
554
+ if (loc.startsWith("file:///")) {
555
+ const rest = loc.slice("file://".length);
556
+ const path = /^\/[A-Za-z]:/.test(rest) ? rest.slice(1) : rest;
557
+ return stripQueryAndHash(path);
558
+ }
559
+ if (loc.startsWith("file://")) return stripQueryAndHash(loc.slice("file://".length));
560
+ if (/^https?:\/\//i.test(loc)) {
561
+ try {
562
+ return decodeURIComponent(new URL(loc).pathname);
563
+ } catch {
564
+ return stripQueryAndHash(loc);
565
+ }
566
+ }
567
+ return stripQueryAndHash(loc);
568
+ };
569
+ var createFrame = (fn, loc, line, col) => ({
570
+ file: cleanFilePath(loc),
571
+ function: cleanFunctionName(fn),
572
+ line: convertToNumber(line),
573
+ column: convertToNumber(col)
574
+ });
575
+ var parseV8 = (line) => {
576
+ const g = V8.exec(line)?.groups;
577
+ if (!g?.loc) return null;
578
+ if (g.loc.startsWith("eval")) {
579
+ const inner = V8_EVAL.exec(g.loc)?.groups;
580
+ if (inner?.loc) return createFrame(g.fn, inner.loc, inner.line, inner.col);
581
+ }
582
+ return createFrame(g.fn, g.loc, g.line, g.col);
583
+ };
584
+ var parseNode = (line) => {
585
+ const g = NODE.exec(line)?.groups;
586
+ return g?.loc ? createFrame(g.fn, g.loc, g.line, g.col) : null;
587
+ };
588
+ var parseJsc = (line) => {
589
+ if (!line.includes("@")) return null;
590
+ const g = JSC.exec(line)?.groups;
591
+ return g?.loc ? createFrame(g.fn, g.loc, g.line, g.col) : null;
592
+ };
593
+ var parseStack = (stack) => {
594
+ const frames = [];
595
+ for (const line of stack.split(/\r?\n/)) {
596
+ const parsed = parseV8(line) ?? parseNode(line) ?? parseJsc(line);
597
+ if (parsed) frames.push(parsed);
598
+ }
599
+ return frames;
600
+ };
601
+
602
+ // src/browser-frames.ts
603
+ var stackOf = (error) => {
604
+ if (error instanceof Error && typeof error.stack === "string") return error.stack;
605
+ return "";
606
+ };
607
+ var isAppFrame = (file) => {
608
+ if (!file) return false;
609
+ if (file === "native" || file === "<anonymous>") return false;
610
+ return !/node_modules|chrome-extension:|moz-extension:|safari-extension:|webkit-masked-url:|\/@rasputin-ai\/|rasputin-javascript-sdk\//i.test(
611
+ file
612
+ );
613
+ };
614
+ var framesFromError = (error) => {
615
+ const raw = foldSyntheticFrames(parseStack(stackOf(error)));
616
+ return raw.map((frame) => ({
617
+ generated: { file: frame.file, line: frame.line, column: frame.column },
618
+ ...frame.function ? { function: frame.function } : {},
619
+ in_app: isAppFrame(frame.file)
620
+ }));
621
+ };
622
+
623
+ // src/browser-ingest-event.ts
624
+ var MAX_MESSAGE_BYTES2 = 8 * 1024;
625
+ var MAX_STACK_FRAMES2 = 50;
626
+ var utf8 = new TextEncoder();
627
+ var truncateMessage = (value) => {
628
+ if (utf8.encode(value).byteLength <= MAX_MESSAGE_BYTES2) return value;
629
+ let end = Math.min(value.length, MAX_MESSAGE_BYTES2);
630
+ while (end > 0 && utf8.encode(value.slice(0, end)).byteLength > MAX_MESSAGE_BYTES2) end -= 1;
631
+ return value.slice(0, end);
632
+ };
633
+ var errorFields = (error) => {
634
+ if (error instanceof Error) {
635
+ const code = "code" in error && (typeof error.code === "string" || typeof error.code === "number") ? String(error.code) : void 0;
636
+ return {
637
+ type: error.name || "Error",
638
+ message: error.message || String(error),
639
+ ...code === void 0 ? {} : { code }
640
+ };
641
+ }
642
+ if (typeof error === "string") return { type: "string", message: error };
643
+ return { type: "Error", message: String(error) };
644
+ };
645
+ var createBrowserIngestEvent = (input) => {
646
+ const extracted = errorFields(input.error);
647
+ const event = {
648
+ type: extracted.type,
649
+ message: truncateMessage(extracted.message),
650
+ stack_frames: input.frames.slice(0, MAX_STACK_FRAMES2).flatMap((frame) => {
651
+ const filename = frame.generated.file.trim();
652
+ if (!filename) return [];
653
+ return [
654
+ {
655
+ filename,
656
+ ...frame.function ? { function: frame.function } : {},
657
+ ...frame.generated.line !== void 0 ? { lineno: frame.generated.line } : {},
658
+ ...frame.generated.column !== void 0 ? { colno: frame.generated.column } : {},
659
+ in_app: frame.in_app
660
+ }
661
+ ];
662
+ }),
663
+ occurred_at: (/* @__PURE__ */ new Date()).toISOString(),
664
+ environment: input.environment,
665
+ origin: "client",
666
+ resolution: "none",
667
+ release: input.release
668
+ };
669
+ if (extracted.code !== void 0) event.code = extracted.code;
670
+ if (input.runtimeState) event.runtime_state = input.runtimeState;
671
+ return event;
672
+ };
673
+
674
+ // src/captured-value.ts
675
+ var MAX_BROWSER_STRING_LENGTH = 500;
676
+ var MAX_ORDINARY_CAPTURE_DEPTH = 3;
677
+ var MAX_ORDINARY_KEYS = 30;
678
+ var MAX_ORDINARY_ARRAY_ELEMENTS = 20;
679
+ var MAX_ORDINARY_NODES = 80;
680
+ var MAX_ORDINARY_BYTES = 8 * 1024;
681
+ var MAX_OWN_KEYS_ENUMERATION = 256;
682
+ var REDACTED_SENTINEL = Symbol.for("rasputin.redacted.v1");
683
+ var ownKeys = Reflect.ownKeys;
684
+ var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
685
+ var isArray = Array.isArray;
686
+ var verifiedOrdinary = /* @__PURE__ */ new WeakSet();
687
+ var wideOrdinary = /* @__PURE__ */ new WeakSet();
688
+ var redactKey = (key) => key.length > 0 && propertyNameIsSensitive(key);
689
+ var safeArrayFlag = (value) => {
690
+ try {
691
+ return isArray(value);
692
+ } catch {
693
+ return void 0;
694
+ }
695
+ };
696
+ var tagVerifiedOrdinary = (object) => {
697
+ if (object === null || typeof object !== "object") return object;
698
+ try {
699
+ verifiedOrdinary.add(object);
700
+ } catch {
701
+ }
702
+ return object;
703
+ };
704
+ var isVerifiedOrdinary = (object) => {
705
+ if (object === null || typeof object !== "object") return false;
706
+ try {
707
+ return verifiedOrdinary.has(object);
708
+ } catch {
709
+ return false;
710
+ }
711
+ };
712
+ var rememberKnownLiteral = (object, _keys, _values) => tagVerifiedOrdinary(object);
713
+ var passthroughKnownLiteral = (object) => object;
714
+ var tagDescriptorChild = (object, key, depth, nodes) => {
715
+ let descriptor;
716
+ try {
717
+ descriptor = getOwnPropertyDescriptor(object, key);
718
+ } catch {
719
+ return;
720
+ }
721
+ if (!descriptor || descriptor.get !== void 0 || descriptor.set !== void 0) return;
722
+ if ("value" in descriptor) tagJsonTree(descriptor.value, depth + 1, nodes);
723
+ };
724
+ var tagJsonTree = (value, depth, nodes) => {
725
+ if (value === null || typeof value !== "object") return;
726
+ if (depth > MAX_ORDINARY_CAPTURE_DEPTH || nodes.count >= MAX_ORDINARY_NODES) return;
727
+ if (isVerifiedOrdinary(value)) return;
728
+ tagVerifiedOrdinary(value);
729
+ nodes.count += 1;
730
+ if (safeArrayFlag(value) === true) {
731
+ let length = 0;
732
+ try {
733
+ const lengthDescriptor = getOwnPropertyDescriptor(value, "length");
734
+ if (lengthDescriptor && typeof lengthDescriptor.value === "number") {
735
+ length = lengthDescriptor.value;
736
+ }
737
+ } catch {
738
+ return;
739
+ }
740
+ const limit2 = Math.min(Math.max(0, length), MAX_ORDINARY_ARRAY_ELEMENTS);
741
+ for (let index = 0; index < limit2; index += 1) {
742
+ tagDescriptorChild(value, String(index), depth, nodes);
743
+ }
744
+ return;
745
+ }
746
+ let keys;
747
+ try {
748
+ keys = ownKeys(value);
749
+ } catch {
750
+ return;
751
+ }
752
+ if (keys.length > MAX_OWN_KEYS_ENUMERATION) {
753
+ wideOrdinary.add(value);
754
+ return;
755
+ }
756
+ const limit = Math.min(keys.length, MAX_ORDINARY_KEYS);
757
+ for (let index = 0; index < limit; index += 1) {
758
+ const key = keys[index];
759
+ if (key === void 0 || typeof key === "symbol") continue;
760
+ tagDescriptorChild(value, key, depth, nodes);
761
+ }
762
+ };
763
+ var tagJsonParseResult = (value) => {
764
+ tagJsonTree(value, 0, { count: 0 });
765
+ return value;
766
+ };
767
+ var addBytes = (budget, amount) => {
768
+ budget.bytes += amount;
769
+ };
770
+ var budgetOverflowReason = (budget) => budget.bytes >= MAX_ORDINARY_BYTES ? "max_bytes" : "max_nodes";
771
+ var captureValueAt = (value, depth, path, seen, budget) => {
772
+ try {
773
+ if (value === REDACTED_SENTINEL) return { kind: "redacted" };
774
+ if (value === null) return { kind: "null" };
775
+ if (typeof value === "undefined") return { kind: "undefined" };
776
+ if (typeof value === "string") {
777
+ if (value.length > MAX_BROWSER_STRING_LENGTH) {
778
+ addBytes(budget, 24 + MAX_BROWSER_STRING_LENGTH);
779
+ return {
780
+ kind: "primitive",
781
+ value: value.slice(0, MAX_BROWSER_STRING_LENGTH),
782
+ originalLength: value.length,
783
+ truncated: true,
784
+ reason: "max_string_length"
785
+ };
786
+ }
787
+ addBytes(budget, 24 + value.length);
788
+ return { kind: "primitive", value };
789
+ }
790
+ if (typeof value === "number" || typeof value === "boolean") {
791
+ addBytes(budget, 32);
792
+ return { kind: "primitive", value };
793
+ }
794
+ if (typeof value === "bigint") {
795
+ const text = value.toString();
796
+ addBytes(budget, 24 + text.length);
797
+ return { kind: "bigint", value: text };
798
+ }
799
+ if (typeof value === "symbol") return { kind: "opaque", jsType: "symbol" };
800
+ if (typeof value === "function") return { kind: "opaque", jsType: "function" };
801
+ if (typeof value !== "object") return { kind: "opaque", jsType: typeof value };
802
+ if (path.has(value)) return { kind: "circular" };
803
+ if (seen.has(value)) return { kind: "shared" };
804
+ if (verifiedOrdinary.has(value)) {
805
+ if (wideOrdinary.has(value)) {
806
+ return { kind: "struct", entries: [], truncated: true, reason: "wide_object" };
807
+ }
808
+ return walkOrdinary(value, depth, path, seen, budget);
809
+ }
810
+ const array = safeArrayFlag(value);
811
+ addBytes(budget, 32);
812
+ return array === void 0 ? { kind: "opaque", jsType: "object" } : { kind: "opaque", jsType: "object", array };
813
+ } catch {
814
+ return { kind: "opaque", jsType: "object" };
815
+ }
816
+ };
817
+ var walkOrdinary = (value, depth, path, seen, budget) => {
818
+ if (budget.nodes >= MAX_ORDINARY_NODES) {
819
+ return { kind: "struct", entries: [], truncated: true, reason: "max_nodes" };
820
+ }
821
+ if (budget.bytes >= MAX_ORDINARY_BYTES) {
822
+ return { kind: "struct", entries: [], truncated: true, reason: "max_bytes" };
823
+ }
824
+ if (depth >= MAX_ORDINARY_CAPTURE_DEPTH) {
825
+ return { kind: "struct", entries: [], truncated: true, reason: "max_depth" };
826
+ }
827
+ path.add(value);
828
+ seen.add(value);
829
+ budget.nodes += 1;
830
+ addBytes(budget, 24);
831
+ const array = safeArrayFlag(value) === true;
832
+ if (array) {
833
+ const captured = walkOrdinaryArray(value, depth, path, seen, budget);
834
+ path.delete(value);
835
+ return captured;
836
+ }
837
+ let keys;
838
+ try {
839
+ keys = ownKeys(value);
840
+ } catch {
841
+ path.delete(value);
842
+ return { kind: "opaque", jsType: "object" };
843
+ }
844
+ if (keys.length > MAX_OWN_KEYS_ENUMERATION) {
845
+ path.delete(value);
846
+ return { kind: "struct", entries: [], truncated: true, reason: "wide_object" };
847
+ }
848
+ const overKeyCap = keys.length > MAX_ORDINARY_KEYS;
849
+ const limit = Math.min(keys.length, MAX_ORDINARY_KEYS);
850
+ const entries = [];
851
+ let skippedSymbols = false;
852
+ for (let index = 0; index < limit; index += 1) {
853
+ if (budget.nodes >= MAX_ORDINARY_NODES || budget.bytes >= MAX_ORDINARY_BYTES) {
854
+ path.delete(value);
855
+ return { kind: "struct", entries, truncated: true, reason: budgetOverflowReason(budget) };
856
+ }
857
+ const key = keys[index];
858
+ if (key === void 0) continue;
859
+ if (typeof key === "symbol") {
860
+ skippedSymbols = true;
861
+ continue;
862
+ }
863
+ const text = String(key);
864
+ addBytes(budget, 8 + text.length);
865
+ if (redactKey(text)) {
866
+ entries.push({ key: text, value: { kind: "redacted" } });
867
+ continue;
868
+ }
869
+ entries.push({
870
+ key: text,
871
+ value: captureDescriptorValue(value, key, depth, path, seen, budget)
872
+ });
873
+ }
874
+ if (!skippedSymbols) {
875
+ for (let index = limit; index < keys.length; index += 1) {
876
+ if (typeof keys[index] === "symbol") {
877
+ skippedSymbols = true;
878
+ break;
879
+ }
880
+ }
881
+ }
882
+ path.delete(value);
883
+ const truncated = overKeyCap || skippedSymbols;
884
+ const reason = overKeyCap ? "max_keys" : "omitted_symbols";
885
+ return {
886
+ kind: "struct",
887
+ entries,
888
+ ...truncated ? { truncated: true, reason } : {}
889
+ };
890
+ };
891
+ var walkOrdinaryArray = (value, depth, path, seen, budget) => {
892
+ let length = 0;
893
+ try {
894
+ const lengthDescriptor = getOwnPropertyDescriptor(value, "length");
895
+ if (lengthDescriptor && typeof lengthDescriptor.value === "number") {
896
+ length = lengthDescriptor.value;
897
+ }
898
+ } catch {
899
+ return { kind: "opaque", jsType: "object", array: true };
900
+ }
901
+ const truncatedByLength = length > MAX_ORDINARY_ARRAY_ELEMENTS;
902
+ const limit = Math.min(Math.max(0, length), MAX_ORDINARY_ARRAY_ELEMENTS);
903
+ const entries = [];
904
+ for (let index = 0; index < limit; index += 1) {
905
+ if (budget.nodes >= MAX_ORDINARY_NODES || budget.bytes >= MAX_ORDINARY_BYTES) {
906
+ return {
907
+ kind: "struct",
908
+ array: true,
909
+ entries,
910
+ truncated: true,
911
+ reason: budgetOverflowReason(budget)
912
+ };
913
+ }
914
+ const key = String(index);
915
+ addBytes(budget, 8 + key.length);
916
+ entries.push({
917
+ key,
918
+ value: captureDescriptorValue(value, key, depth, path, seen, budget, "hole")
919
+ });
920
+ }
921
+ let omittedExtraKeys = false;
922
+ if (!truncatedByLength && length <= MAX_ORDINARY_ARRAY_ELEMENTS) {
923
+ try {
924
+ const keys = ownKeys(value);
925
+ for (const key of keys) {
926
+ if (key === "length") continue;
927
+ if (typeof key === "symbol") {
928
+ omittedExtraKeys = true;
929
+ break;
930
+ }
931
+ if (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= length) {
932
+ omittedExtraKeys = true;
933
+ break;
934
+ }
935
+ }
936
+ } catch {
937
+ omittedExtraKeys = true;
938
+ }
939
+ }
940
+ const truncated = truncatedByLength || omittedExtraKeys;
941
+ const reason = truncatedByLength ? "max_keys" : "omitted_extra_keys";
942
+ return {
943
+ kind: "struct",
944
+ array: true,
945
+ entries,
946
+ ...truncated ? { truncated: true, reason } : {}
947
+ };
948
+ };
949
+ var captureDescriptorValue = (object, key, depth, path, seen, budget, absent = "undefined") => {
950
+ let descriptor;
951
+ try {
952
+ descriptor = getOwnPropertyDescriptor(object, key);
953
+ } catch {
954
+ return { kind: "opaque", jsType: "object" };
955
+ }
956
+ if (!descriptor) return { kind: absent };
957
+ if (descriptor.get !== void 0 || descriptor.set !== void 0) return { kind: "accessor" };
958
+ if (!("value" in descriptor)) return { kind: absent };
959
+ return captureValueAt(descriptor.value, depth + 1, path, seen, budget);
960
+ };
961
+ var captureValue = (value) => captureValueAt(value, 0, /* @__PURE__ */ new WeakSet(), /* @__PURE__ */ new WeakSet(), { nodes: 0, bytes: 0 });
962
+ var estimateCapturedBytes = (value) => {
963
+ if (value.kind === "primitive" && typeof value.value === "string") {
964
+ return 24 + value.value.length;
965
+ }
966
+ if (value.kind === "bigint") return 24 + value.value.length;
967
+ if (value.kind === "struct") {
968
+ return 24 + value.entries.reduce(
969
+ (sum, entry) => sum + 8 + entry.key.length + estimateCapturedBytes(entry.value),
970
+ 0
971
+ );
972
+ }
973
+ return 32;
974
+ };
975
+
976
+ // src/native-json-producer.ts
977
+ var nativeResponseJson = (() => {
978
+ const response = Reflect.get(globalThis, "Response");
979
+ if (typeof response !== "function") return void 0;
980
+ const proto = Reflect.get(response, "prototype");
981
+ if (!proto || typeof proto !== "object") return void 0;
982
+ const json = Reflect.get(proto, "json");
983
+ return typeof json === "function" ? json : void 0;
984
+ })();
985
+ var nativeJsonParse = (() => {
986
+ const json = Reflect.get(globalThis, "JSON");
987
+ if (!json || typeof json !== "object") return void 0;
988
+ const parse = Reflect.get(json, "parse");
989
+ return typeof parse === "function" ? parse : void 0;
990
+ })();
991
+ var nativeJsonPromises = /* @__PURE__ */ new WeakSet();
992
+ var notedDependents = /* @__PURE__ */ new WeakMap();
993
+ var adoptStack = [];
994
+ var jsonProducerStats = {
995
+ method: 0,
996
+ await: 0,
997
+ adopt: 0,
998
+ handle: 0
999
+ };
1000
+ var applyCall = (fn, receiver, args) => {
1001
+ if (typeof fn === "function") return Reflect.apply(fn, receiver, args);
1002
+ return Reflect.apply(fn, receiver, args);
1003
+ };
1004
+ var isObject = (value) => value !== null && typeof value === "object";
1005
+ var isFn = (value) => typeof value === "function";
1006
+ var pruneAdoptedFrames = () => {
1007
+ while (adoptStack.length > 0) {
1008
+ const top = adoptStack[adoptStack.length - 1];
1009
+ if (!top?.adopted) break;
1010
+ adoptStack.pop();
1011
+ }
1012
+ };
1013
+ var noteNativeJsonPromise = (value) => {
1014
+ if (!isObject(value)) return;
1015
+ try {
1016
+ if (nativeJsonPromises.has(value)) return;
1017
+ nativeJsonPromises.add(value);
1018
+ } catch {
1019
+ return;
1020
+ }
1021
+ const dependents = notedDependents.get(value);
1022
+ if (!dependents) return;
1023
+ notedDependents.delete(value);
1024
+ for (const dependent of dependents) noteNativeJsonPromise(dependent);
1025
+ };
1026
+ var linkNotedPromise = (from, to) => {
1027
+ if (from === to) return;
1028
+ if (isNotedNativeJsonPromise(from)) {
1029
+ noteNativeJsonPromise(to);
1030
+ return;
1031
+ }
1032
+ const existing = notedDependents.get(from);
1033
+ if (existing) existing.push(to);
1034
+ else notedDependents.set(from, [to]);
1035
+ };
1036
+ var isNotedNativeJsonPromise = (value) => {
1037
+ if (!isObject(value)) return false;
1038
+ try {
1039
+ return nativeJsonPromises.has(value);
1040
+ } catch {
1041
+ return false;
1042
+ }
1043
+ };
1044
+ var hasCallableReviver = (args) => args.some((argument) => typeof argument === "function");
1045
+ var finishNativeCall = (fn, result, args) => {
1046
+ if (fn === nativeResponseJson) {
1047
+ noteNativeJsonPromise(result);
1048
+ return result;
1049
+ }
1050
+ if (fn === nativeJsonParse && !hasCallableReviver(args)) {
1051
+ tagJsonParseResult(result);
1052
+ }
1053
+ return result;
1054
+ };
1055
+ var invokeNativeJsonMethod = (receiver, fn, ...args) => {
1056
+ jsonProducerStats.method += 1;
1057
+ const result = applyCall(fn, receiver, args);
1058
+ return finishNativeCall(fn, result, args);
1059
+ };
1060
+ var tagJsonAwaitResult = (value, promise) => {
1061
+ jsonProducerStats.await += 1;
1062
+ if (isNotedNativeJsonPromise(promise)) tagJsonParseResult(value);
1063
+ return value;
1064
+ };
1065
+ var emptyFrame = () => ({
1066
+ fn: void 0,
1067
+ yielded: false,
1068
+ exited: false,
1069
+ adopted: true,
1070
+ bound: false,
1071
+ left: void 0,
1072
+ asyncPromise: void 0
1073
+ });
1074
+ var discardUnboundFramesFor = (fn) => {
1075
+ for (const frame of adoptStack) {
1076
+ if (frame.fn !== fn || frame.bound || frame.adopted || !frame.exited) continue;
1077
+ frame.adopted = true;
1078
+ }
1079
+ pruneAdoptedFrames();
1080
+ };
1081
+ var createJsonAdoptFrame = (fn) => {
1082
+ if (!fn) return emptyFrame();
1083
+ discardUnboundFramesFor(fn);
1084
+ const frame = {
1085
+ fn,
1086
+ yielded: false,
1087
+ exited: false,
1088
+ adopted: false,
1089
+ bound: false,
1090
+ left: void 0,
1091
+ asyncPromise: void 0
1092
+ };
1093
+ adoptStack.push(frame);
1094
+ return frame;
1095
+ };
1096
+ var bindFrame = (frame, value) => {
1097
+ frame.asyncPromise = value;
1098
+ frame.bound = true;
1099
+ if (frame.left && frame.left !== value) linkNotedPromise(frame.left, value);
1100
+ if (isNotedNativeJsonPromise(frame.left) || isNotedNativeJsonPromise(value)) {
1101
+ noteNativeJsonPromise(value);
1102
+ }
1103
+ frame.adopted = true;
1104
+ pruneAdoptedFrames();
1105
+ };
1106
+ var bindByCallee = (value, callee) => {
1107
+ for (let index = adoptStack.length - 1; index >= 0; index -= 1) {
1108
+ const frame = adoptStack[index];
1109
+ if (!frame || frame.adopted || frame.bound) continue;
1110
+ if (frame.fn !== callee) continue;
1111
+ bindFrame(frame, value);
1112
+ return true;
1113
+ }
1114
+ return false;
1115
+ };
1116
+ var notifyJsonProducerLeave = (frame, value) => {
1117
+ frame.exited = true;
1118
+ if (isObject(value)) frame.left = value;
1119
+ if (isObject(value) && frame.asyncPromise && value !== frame.asyncPromise) {
1120
+ linkNotedPromise(value, frame.asyncPromise);
1121
+ }
1122
+ if (isNotedNativeJsonPromise(value) && frame.asyncPromise) {
1123
+ noteNativeJsonPromise(frame.asyncPromise);
1124
+ }
1125
+ return value;
1126
+ };
1127
+ var notifyJsonProducerSuspend = (frame) => {
1128
+ frame.yielded = true;
1129
+ };
1130
+ var notifyJsonProducerEnd = (frame) => {
1131
+ frame.exited = true;
1132
+ if (frame.bound || frame.left === void 0 || !frame.fn) frame.adopted = true;
1133
+ pruneAdoptedFrames();
1134
+ };
1135
+ var adoptAsyncJsonPromise = (value, callee) => {
1136
+ jsonProducerStats.adopt += 1;
1137
+ if (!isObject(value) || !isFn(callee)) return value;
1138
+ bindByCallee(value, callee);
1139
+ return value;
1140
+ };
1141
+ var resetJsonProducerAdopt = () => {
1142
+ adoptStack.length = 0;
1143
+ jsonProducerStats.method = 0;
1144
+ jsonProducerStats.await = 0;
1145
+ jsonProducerStats.adopt = 0;
1146
+ jsonProducerStats.handle = 0;
1147
+ };
1148
+ var passthroughAdopt = (value) => value;
1149
+ var createProvenanceHandle = (fn) => {
1150
+ jsonProducerStats.handle += 1;
1151
+ const frame = createJsonAdoptFrame(isFn(fn) ? fn : void 0);
1152
+ return {
1153
+ leave: (value) => notifyJsonProducerLeave(frame, value),
1154
+ end: () => {
1155
+ notifyJsonProducerEnd(frame);
1156
+ },
1157
+ suspend: (value) => {
1158
+ notifyJsonProducerSuspend(frame);
1159
+ return value;
1160
+ },
1161
+ resume: (value) => value
1162
+ };
1163
+ };
1164
+ var passthroughProvenanceHandle = (_fn) => ({
1165
+ leave: (value) => value,
1166
+ end: () => {
1167
+ },
1168
+ suspend: (value) => value,
1169
+ resume: (value) => value
1170
+ });
1171
+ var passthroughNativeJsonMethod = (receiver, fn, ...args) => applyCall(fn, receiver, args);
1172
+ var passthroughJsonAwait = (value, _promise) => value;
1173
+
1174
+ // src/browser-recorder.ts
1175
+ var THROW_TIME_NOTE = "throw-time ancestor materialized";
1176
+ var DISCRETE_EVENTS = /* @__PURE__ */ new Set([
1177
+ "click",
1178
+ "submit",
1179
+ "pointerdown",
1180
+ "pointerup",
1181
+ "keydown",
1182
+ "keyup",
1183
+ "input",
1184
+ "change"
1185
+ ]);
1186
+ var VALUES_OFF_AFTER = 64;
1187
+ var HELPERS_SPARSE_AFTER = 160;
1188
+ var STRUCTURAL_NOTE = "structural events truncated";
1189
+ var VALUE_NOTE = "value observations omitted";
1190
+ var REPEAT_NOTE = "repeated calls suppressed";
1191
+ var OPEN_CAP_NOTE = "maximum open executions reached";
1192
+ var HOLD_NOTE = "closed by hold budget";
1193
+ var VALUES_OFF_NOTE = "capture profile: values-off";
1194
+ var HELPERS_SPARSE_NOTE = "capture profile: helpers-sparse";
1195
+ var nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
1196
+ var eventTypeOf = (event) => {
1197
+ if (!("type" in event)) return "";
1198
+ const type = Reflect.get(event, "type");
1199
+ return typeof type === "string" ? type : "";
1200
+ };
1201
+ var isDiscreteEvent = (event) => DISCRETE_EVENTS.has(eventTypeOf(event));
1202
+ var windowEvent = () => {
1203
+ const root = globalThis;
1204
+ const event = root.window?.event;
1205
+ return event && typeof event === "object" ? event : void 0;
1206
+ };
1207
+ var summarize = (execution) => ({
1208
+ executionId: execution.id,
1209
+ kind: execution.kind,
1210
+ ...execution.eventType ? { eventType: execution.eventType } : {},
1211
+ functionNames: [...execution.functions.values()].map((definition) => definition.name),
1212
+ durationMs: execution.lastActivityAt - execution.startedAt,
1213
+ hadError: execution.hasError
1214
+ });
1215
+ var snapshotCaptureMode = (execution, configured) => execution.captureProfile === "normal" ? configured : "structure";
1216
+ var snapshotOf = (execution, captureMode) => ({
1217
+ executionId: execution.id,
1218
+ kind: execution.kind,
1219
+ ...execution.eventType ? { eventType: execution.eventType } : {},
1220
+ startedAt: execution.startedAt,
1221
+ startedAtIso: new Date(execution.startedAtWallMs).toISOString(),
1222
+ durationMs: execution.lastActivityAt - execution.startedAt,
1223
+ provenanceNotes: [...execution.provenanceNotes],
1224
+ functions: [...execution.functions.values()],
1225
+ events: [...execution.events],
1226
+ captureMode: snapshotCaptureMode(execution, captureMode),
1227
+ writes: [...execution.writes],
1228
+ reads: [...execution.reads],
1229
+ historyLinks: [],
1230
+ ...execution.partial ? { partial: true } : {},
1231
+ captureProfile: execution.captureProfile
1232
+ });
1233
+ var emptyCosts = () => ({
1234
+ enterMs: 0,
1235
+ leaveEndMs: 0,
1236
+ journalMs: 0,
1237
+ resolveMs: 0,
1238
+ handleAllocMs: 0,
1239
+ structureMs: 0,
1240
+ suppressMs: 0,
1241
+ observeMs: 0,
1242
+ captureMs: 0,
1243
+ historyMs: 0,
1244
+ statsMs: 0,
1245
+ entersFull: 0,
1246
+ entersStructure: 0,
1247
+ entersSkip: 0,
1248
+ entersSparse: 0,
1249
+ observeCalls: 0,
1250
+ reactiveHitMs: 0,
1251
+ reactiveRepeats: 0
1252
+ });
1253
+ var emptyStats = () => ({
1254
+ functionCallsObserved: 0,
1255
+ functionCallsCaptured: 0,
1256
+ successfulExecutionsDiscarded: 0,
1257
+ errorSnapshots: 0,
1258
+ activeExecutions: 0,
1259
+ peakActiveExecutions: 0,
1260
+ activeEstimatedBytes: 0,
1261
+ peakActiveEstimatedBytes: 0,
1262
+ attributionInferred: 0,
1263
+ attributionInferredAmbiguous: 0,
1264
+ attributionUnlinked: 0,
1265
+ truncatedEvents: 0,
1266
+ truncatedStructural: 0,
1267
+ omittedValueObservations: 0,
1268
+ expressionObservations: 0,
1269
+ repeatedCallsSuppressed: 0,
1270
+ executionsDroppedUnderPressure: 0,
1271
+ reactiveCallbackInvocations: 0,
1272
+ reactiveRecordedEntries: 0,
1273
+ reactiveSparseCounted: 0,
1274
+ reactiveThrows: 0
1275
+ });
1276
+ var addCost = (costs, key, started) => {
1277
+ const current = costs[key];
1278
+ if (typeof current === "number") {
1279
+ costs[key] = current + (nowMs() - started);
1280
+ }
1281
+ };
1282
+ var maxRepeatsFor = (captureClass, profile, fullCap) => {
1283
+ if (captureClass === "skip") return 0;
1284
+ if (captureClass === "structure" || captureClass === "reactive-sparse" || profile === "helpers-sparse") {
1285
+ return 1;
1286
+ }
1287
+ return fullCap;
1288
+ };
1289
+ var createBrowserRecorder = (options = {}) => {
1290
+ const config = {
1291
+ settlingMs: options.settlingMs ?? 80,
1292
+ holdMs: options.holdMs ?? 3e4,
1293
+ captureMode: options.captureMode ?? "values",
1294
+ maxStructuralEvents: options.maxStructuralEvents ?? 240,
1295
+ maxValueEvents: options.maxValueEvents ?? 80,
1296
+ maxCapturedCallsPerFunction: options.maxCapturedCallsPerFunction ?? 3,
1297
+ maxRecentExecutions: options.maxRecentExecutions ?? 20,
1298
+ maxRetainedSnapshots: options.maxRetainedSnapshots ?? 32,
1299
+ maxHistory: options.maxHistory ?? 16,
1300
+ maxOpenExecutions: options.maxOpenExecutions ?? 32,
1301
+ profileCosts: options.profileCosts === true
1302
+ };
1303
+ const costs = emptyCosts();
1304
+ const identities = /* @__PURE__ */ new Map();
1305
+ const identitiesBySite = /* @__PURE__ */ new Map();
1306
+ const registeredReactiveSites = /* @__PURE__ */ new Map();
1307
+ const internedIds = /* @__PURE__ */ new Map();
1308
+ let nextIntern = 1;
1309
+ let current = null;
1310
+ let nextCallId = 1;
1311
+ let nextExecution = 1;
1312
+ const open = /* @__PURE__ */ new Set();
1313
+ const recent = [];
1314
+ const snapshots = [];
1315
+ const history = [];
1316
+ const errors = /* @__PURE__ */ new WeakMap();
1317
+ const eventRoots = /* @__PURE__ */ new WeakMap();
1318
+ const stats = emptyStats();
1319
+ const reactiveHandles = [];
1320
+ const reactiveSampled = [];
1321
+ const reactiveInvocations = [];
1322
+ const reactiveOwned = [];
1323
+ const emptyArgs = [];
1324
+ const passthrough = isolateHandle({
1325
+ leave: (value) => value,
1326
+ fail: () => {
1327
+ },
1328
+ end: () => {
1329
+ },
1330
+ run: (callback) => callback(),
1331
+ suspend: (value) => value,
1332
+ resume: (value) => value
1333
+ });
1334
+ const identityFromIndex = (moduleIndex, planIndex, captureClass, plan) => {
1335
+ const compactId = `${moduleIndex}.${planIndex}`;
1336
+ const cached = identities.get(compactId) ?? (plan ? identities.get(plan[0]) : void 0);
1337
+ if (cached) {
1338
+ if (plan) {
1339
+ if (!cached.packageRelativePath && plan[3]) cached.packageRelativePath = plan[3];
1340
+ if (cached.line == null && plan[4] != null) cached.line = plan[4];
1341
+ if (cached.column == null && plan[5] != null) cached.column = plan[5];
1342
+ if (!cached.name || cached.name === compactId) cached.name = plan[1];
1343
+ if (cached.packageName == null && plan[2]) cached.packageName = plan[2];
1344
+ }
1345
+ identities.set(compactId, cached);
1346
+ if (plan) identities.set(plan[0], cached);
1347
+ return cached;
1348
+ }
1349
+ const resolvedClass = plan?.[9] === 1 ? "reactive-sparse" : plan?.[8] === 1 ? "skip" : plan?.[6] === 1 ? "structure" : captureClass;
1350
+ const identity = plan ? {
1351
+ functionId: compactId,
1352
+ name: plan[1],
1353
+ packageName: plan[2],
1354
+ packageRelativePath: plan[3],
1355
+ line: plan[4],
1356
+ column: plan[5],
1357
+ captureClass: resolvedClass,
1358
+ moduleIndex,
1359
+ planIndex
1360
+ } : {
1361
+ functionId: compactId,
1362
+ name: compactId,
1363
+ packageName: null,
1364
+ packageRelativePath: null,
1365
+ line: null,
1366
+ column: null,
1367
+ captureClass: resolvedClass,
1368
+ moduleIndex,
1369
+ planIndex
1370
+ };
1371
+ identities.set(compactId, identity);
1372
+ identities.set(identity.functionId, identity);
1373
+ return identity;
1374
+ };
1375
+ const registerIdentities = (items) => {
1376
+ for (const item of items) {
1377
+ const identity = {
1378
+ functionId: item.functionId,
1379
+ name: item.name,
1380
+ packageName: null,
1381
+ packageRelativePath: item.packageRelativePath ?? null,
1382
+ line: item.line,
1383
+ column: item.column,
1384
+ captureClass: item.captureClass,
1385
+ moduleIndex: item.moduleIndex,
1386
+ planIndex: item.planIndex
1387
+ };
1388
+ identities.set(item.compactId, identity);
1389
+ identities.set(item.functionId, identity);
1390
+ if (item.siteId !== void 0) {
1391
+ identitiesBySite.set(item.siteId, identity);
1392
+ rememberReactiveSite(item.siteId, identity);
1393
+ }
1394
+ }
1395
+ };
1396
+ const addBytes2 = (execution, delta) => {
1397
+ execution.estimatedBytes += delta;
1398
+ stats.activeEstimatedBytes += delta;
1399
+ if (stats.activeEstimatedBytes > stats.peakActiveEstimatedBytes) {
1400
+ stats.peakActiveEstimatedBytes = stats.activeEstimatedBytes;
1401
+ }
1402
+ };
1403
+ const noteOnce = (execution, note) => {
1404
+ if (execution.provenanceNotes.includes(note)) return;
1405
+ execution.provenanceNotes.push(note);
1406
+ execution.partial = true;
1407
+ };
1408
+ const updateProfile = (execution) => {
1409
+ if (execution.enterCount >= HELPERS_SPARSE_AFTER) {
1410
+ if (execution.captureProfile !== "helpers-sparse") {
1411
+ execution.captureProfile = "helpers-sparse";
1412
+ noteOnce(execution, HELPERS_SPARSE_NOTE);
1413
+ }
1414
+ return;
1415
+ }
1416
+ if (execution.valueCount >= VALUES_OFF_AFTER && execution.captureProfile === "normal") {
1417
+ execution.captureProfile = "values-off";
1418
+ noteOnce(execution, VALUES_OFF_NOTE);
1419
+ }
1420
+ };
1421
+ const internId = (functionId) => {
1422
+ const existing = internedIds.get(functionId);
1423
+ if (existing !== void 0) return existing;
1424
+ const assigned = nextIntern;
1425
+ nextIntern += 1;
1426
+ internedIds.set(functionId, assigned);
1427
+ return assigned;
1428
+ };
1429
+ const repeatKey = (functionId, parentFunctionId) => internId(functionId) << 16 | (parentFunctionId ? internId(parentFunctionId) : 0);
1430
+ const recordingOpen = (execution) => execution.phase !== "closed";
1431
+ const activeCarrier = () => current && !current.ended && recordingOpen(current.execution) ? current : null;
1432
+ const valuesAllowed = (execution) => config.captureMode === "values" && execution.captureProfile === "normal";
1433
+ const frontierAllowsValues = (execution) => {
1434
+ if (config.captureMode !== "values") return false;
1435
+ if (current?.identity.captureClass === "reactive-sparse") {
1436
+ return execution.captureProfile === "normal";
1437
+ }
1438
+ if (execution.captureProfile === "normal") return true;
1439
+ return !!current && current.recordedEnter && current.identity.captureClass === "full";
1440
+ };
1441
+ const appendEvent = (execution, event, extraBytes) => {
1442
+ if (!recordingOpen(execution)) return;
1443
+ execution.lastActivityAt = event.timestampMs;
1444
+ execution.events.push(event);
1445
+ addBytes2(execution, 96 + extraBytes);
1446
+ };
1447
+ const liveCallIds = (execution) => {
1448
+ const ids = /* @__PURE__ */ new Set();
1449
+ let cursor = current;
1450
+ while (cursor) {
1451
+ if (cursor.execution === execution && !cursor.ended) ids.add(cursor.callId);
1452
+ cursor = cursor.previous;
1453
+ }
1454
+ return ids;
1455
+ };
1456
+ const evictCompletedLeaf = (execution) => {
1457
+ const live = liveCallIds(execution);
1458
+ const parentIds = /* @__PURE__ */ new Set();
1459
+ const exitByCall = /* @__PURE__ */ new Map();
1460
+ const throwIds = /* @__PURE__ */ new Set();
1461
+ for (let index = 0; index < execution.events.length; index += 1) {
1462
+ const event = execution.events[index];
1463
+ if (!event) continue;
1464
+ if (event.type === "function_enter" && event.parentCallId !== void 0) {
1465
+ parentIds.add(event.parentCallId);
1466
+ } else if (event.type === "function_calls_suppressed" && event.parentCallId !== void 0) {
1467
+ parentIds.add(event.parentCallId);
1468
+ } else if (event.type === "function_exit") {
1469
+ exitByCall.set(event.callId, index);
1470
+ } else if (event.type === "function_throw") {
1471
+ throwIds.add(event.callId);
1472
+ }
1473
+ }
1474
+ for (let index = 0; index < execution.events.length; index += 1) {
1475
+ const event = execution.events[index];
1476
+ if (event?.type !== "function_enter") continue;
1477
+ if (live.has(event.callId) || parentIds.has(event.callId) || throwIds.has(event.callId)) {
1478
+ continue;
1479
+ }
1480
+ const terminal = exitByCall.get(event.callId);
1481
+ if (terminal === void 0) continue;
1482
+ const later = Math.max(index, terminal);
1483
+ const earlier = Math.min(index, terminal);
1484
+ execution.events.splice(later, 1);
1485
+ execution.events.splice(earlier, 1);
1486
+ execution.structuralCount = Math.max(0, execution.structuralCount - 2);
1487
+ stats.truncatedEvents += 1;
1488
+ stats.truncatedStructural += 1;
1489
+ noteOnce(execution, STRUCTURAL_NOTE);
1490
+ return true;
1491
+ }
1492
+ return false;
1493
+ };
1494
+ const pushStructural = (execution, event, extraBytes) => {
1495
+ const journalStarted = config.profileCosts ? nowMs() : 0;
1496
+ if (!recordingOpen(execution)) {
1497
+ if (config.profileCosts) addCost(costs, "journalMs", journalStarted);
1498
+ return;
1499
+ }
1500
+ if (event.type === "function_throw") {
1501
+ while (execution.structuralCount >= config.maxStructuralEvents) {
1502
+ if (!evictCompletedLeaf(execution)) break;
1503
+ }
1504
+ execution.structuralCount += 1;
1505
+ appendEvent(execution, event, extraBytes);
1506
+ if (config.profileCosts) addCost(costs, "journalMs", journalStarted);
1507
+ return;
1508
+ }
1509
+ if (execution.structuralCount >= config.maxStructuralEvents) {
1510
+ if (!evictCompletedLeaf(execution) || execution.structuralCount >= config.maxStructuralEvents) {
1511
+ stats.truncatedEvents += 1;
1512
+ stats.truncatedStructural += 1;
1513
+ noteOnce(execution, STRUCTURAL_NOTE);
1514
+ if (config.profileCosts) addCost(costs, "journalMs", journalStarted);
1515
+ return;
1516
+ }
1517
+ }
1518
+ execution.structuralCount += 1;
1519
+ appendEvent(execution, event, extraBytes);
1520
+ if (config.profileCosts) addCost(costs, "journalMs", journalStarted);
1521
+ };
1522
+ const hasValueBudget = (execution) => {
1523
+ if (execution.valueCount < config.maxValueEvents) return true;
1524
+ stats.omittedValueObservations += 1;
1525
+ noteOnce(execution, VALUE_NOTE);
1526
+ return false;
1527
+ };
1528
+ const consumeValue = (execution) => {
1529
+ execution.valueCount += 1;
1530
+ };
1531
+ const captureArgs = (args) => {
1532
+ try {
1533
+ const captureStarted = config.profileCosts ? nowMs() : 0;
1534
+ const captured = [];
1535
+ for (let index = 0; index < args.length; index += 1) {
1536
+ captured.push(captureValue(args[index]));
1537
+ }
1538
+ if (config.profileCosts) addCost(costs, "captureMs", captureStarted);
1539
+ return captured;
1540
+ } catch {
1541
+ return void 0;
1542
+ }
1543
+ };
1544
+ const linkHistory = (execution) => {
1545
+ const historyStarted = config.profileCosts ? nowMs() : 0;
1546
+ const links = [];
1547
+ const seen = /* @__PURE__ */ new Set();
1548
+ for (const read of execution.reads) {
1549
+ let last;
1550
+ for (const record of history) {
1551
+ if (record.executionId === execution.id) continue;
1552
+ if (record.writes.some((write) => write.key === read.key)) last = record;
1553
+ }
1554
+ if (!last) continue;
1555
+ seen.add(last.executionId);
1556
+ links.push({
1557
+ executionId: last.executionId,
1558
+ relationship: "last-observed-write",
1559
+ key: read.key
1560
+ });
1561
+ for (const record of history) {
1562
+ if (record.executionId === last.executionId || record.executionId === execution.id) {
1563
+ continue;
1564
+ }
1565
+ if (!record.writes.some((write) => write.key === read.key)) continue;
1566
+ if (seen.has(`${record.executionId}:${read.key}`)) continue;
1567
+ seen.add(`${record.executionId}:${read.key}`);
1568
+ links.push({
1569
+ executionId: record.executionId,
1570
+ relationship: "observed-write",
1571
+ key: read.key
1572
+ });
1573
+ }
1574
+ }
1575
+ if (links.length === 0 && history.length > 0) {
1576
+ const prior = history[history.length - 1];
1577
+ if (prior && prior.executionId !== execution.id) {
1578
+ links.push({ executionId: prior.executionId, relationship: "inferred" });
1579
+ }
1580
+ }
1581
+ if (config.profileCosts) addCost(costs, "historyMs", historyStarted);
1582
+ return links;
1583
+ };
1584
+ const closeExecution = (execution) => {
1585
+ if (execution.phase === "closed") return;
1586
+ execution.phase = "closed";
1587
+ if (execution.settlingTimer !== void 0) clearTimeout(execution.settlingTimer);
1588
+ if (execution.holdTimer !== void 0) clearTimeout(execution.holdTimer);
1589
+ execution.settlingTimer = void 0;
1590
+ execution.holdTimer = void 0;
1591
+ open.delete(execution);
1592
+ stats.activeEstimatedBytes = Math.max(0, stats.activeEstimatedBytes - execution.estimatedBytes);
1593
+ stats.activeExecutions = open.size;
1594
+ execution.lastActivityAt = nowMs();
1595
+ const summary = summarize(execution);
1596
+ recent.push(summary);
1597
+ if (recent.length > config.maxRecentExecutions) recent.shift();
1598
+ if (execution.hasError) {
1599
+ const snapshot = snapshotOf(execution, config.captureMode);
1600
+ snapshot.historyLinks = linkHistory(execution);
1601
+ snapshots.push(snapshot);
1602
+ if (snapshots.length > config.maxRetainedSnapshots) snapshots.shift();
1603
+ stats.errorSnapshots += 1;
1604
+ } else {
1605
+ const forcedClose = execution.provenanceNotes.includes(HOLD_NOTE) || execution.provenanceNotes.includes(OPEN_CAP_NOTE);
1606
+ if (!forcedClose) {
1607
+ history.push({
1608
+ executionId: execution.id,
1609
+ kind: execution.kind,
1610
+ ...execution.eventType ? { eventType: execution.eventType } : {},
1611
+ startedAt: execution.startedAt,
1612
+ durationMs: execution.lastActivityAt - execution.startedAt,
1613
+ functionNames: [...execution.functions.values()].map((definition) => definition.name),
1614
+ writes: [...execution.writes]
1615
+ });
1616
+ if (history.length > config.maxHistory) history.shift();
1617
+ stats.successfulExecutionsDiscarded += 1;
1618
+ }
1619
+ execution.events.length = 0;
1620
+ execution.functions.clear();
1621
+ execution.suppressionByContext.clear();
1622
+ execution.estimatedBytes = 0;
1623
+ }
1624
+ };
1625
+ const scheduleClose = (execution) => {
1626
+ if (execution.phase !== "open" || execution.inFlight > 0) return;
1627
+ execution.phase = "settling";
1628
+ execution.settlingTimer = setTimeout(() => {
1629
+ execution.settlingTimer = void 0;
1630
+ if (execution.inFlight > 0) {
1631
+ execution.phase = "open";
1632
+ return;
1633
+ }
1634
+ closeExecution(execution);
1635
+ }, config.settlingMs);
1636
+ };
1637
+ const reopenIfSettling = (execution) => {
1638
+ if (execution.phase === "closed") return;
1639
+ if (execution.settlingTimer !== void 0) clearTimeout(execution.settlingTimer);
1640
+ execution.settlingTimer = void 0;
1641
+ if (execution.phase === "settling") execution.phase = "open";
1642
+ };
1643
+ const openEvictionScore = (execution) => {
1644
+ const errorBias = execution.hasError ? 1e3 : 0;
1645
+ const kindBias = execution.kind === "interaction" ? 100 : 0;
1646
+ const flight = execution.inFlight > 0 ? 20 + execution.inFlight : 0;
1647
+ const settling = execution.phase === "settling" ? 0 : 10;
1648
+ return errorBias + kindBias + flight + settling;
1649
+ };
1650
+ const closeLowestValueOpen = () => {
1651
+ const carrier = activeCarrier()?.execution;
1652
+ let victim;
1653
+ let best = Number.POSITIVE_INFINITY;
1654
+ for (const execution of open) {
1655
+ if (execution === carrier || execution.phase === "closed") continue;
1656
+ const score = openEvictionScore(execution);
1657
+ if (score < best || score === best && victim !== void 0 && execution.startedAt < victim.startedAt) {
1658
+ best = score;
1659
+ victim = execution;
1660
+ }
1661
+ }
1662
+ if (!victim) return false;
1663
+ noteOnce(victim, OPEN_CAP_NOTE);
1664
+ stats.executionsDroppedUnderPressure += 1;
1665
+ closeExecution(victim);
1666
+ return true;
1667
+ };
1668
+ const ensureOpenCapacity = () => {
1669
+ while (open.size >= config.maxOpenExecutions) {
1670
+ if (!closeLowestValueOpen()) break;
1671
+ }
1672
+ };
1673
+ const createExecution = (kind, eventType) => {
1674
+ ensureOpenCapacity();
1675
+ const startedAt = nowMs();
1676
+ const execution = {
1677
+ id: `be_${nextExecution}`,
1678
+ kind,
1679
+ ...eventType ? { eventType } : {},
1680
+ phase: "open",
1681
+ startedAt,
1682
+ startedAtWallMs: Date.now(),
1683
+ lastActivityAt: startedAt,
1684
+ inFlight: 0,
1685
+ hasError: false,
1686
+ partial: false,
1687
+ events: [],
1688
+ functions: /* @__PURE__ */ new Map(),
1689
+ successCounts: /* @__PURE__ */ new Map(),
1690
+ suppressionByContext: /* @__PURE__ */ new Map(),
1691
+ structuralCount: 0,
1692
+ valueCount: 0,
1693
+ enterCount: 0,
1694
+ captureProfile: "normal",
1695
+ estimatedBytes: 128,
1696
+ provenanceNotes: [],
1697
+ writes: [],
1698
+ reads: [],
1699
+ capturedErrors: /* @__PURE__ */ new WeakMap(),
1700
+ settlingTimer: void 0,
1701
+ holdTimer: void 0
1702
+ };
1703
+ nextExecution += 1;
1704
+ open.add(execution);
1705
+ stats.activeEstimatedBytes += execution.estimatedBytes;
1706
+ stats.activeExecutions = open.size;
1707
+ if (stats.activeEstimatedBytes > stats.peakActiveEstimatedBytes) {
1708
+ stats.peakActiveEstimatedBytes = stats.activeEstimatedBytes;
1709
+ }
1710
+ if (open.size > stats.peakActiveExecutions) stats.peakActiveExecutions = open.size;
1711
+ execution.holdTimer = setTimeout(() => {
1712
+ execution.partial = true;
1713
+ execution.provenanceNotes.push(HOLD_NOTE);
1714
+ closeExecution(execution);
1715
+ }, config.holdMs);
1716
+ return execution;
1717
+ };
1718
+ const resolveExecution = () => {
1719
+ const carrier = activeCarrier();
1720
+ if (carrier) {
1721
+ return {
1722
+ execution: carrier.execution,
1723
+ parentCallId: carrier.callId,
1724
+ provenance: "observed"
1725
+ };
1726
+ }
1727
+ const event = windowEvent();
1728
+ if (event) {
1729
+ const existing = eventRoots.get(event);
1730
+ if (existing && existing.phase !== "closed") {
1731
+ reopenIfSettling(existing);
1732
+ return { execution: existing, provenance: "observed" };
1733
+ }
1734
+ if (isDiscreteEvent(event)) {
1735
+ const execution2 = createExecution("interaction", eventTypeOf(event));
1736
+ eventRoots.set(event, execution2);
1737
+ return { execution: execution2, provenance: "observed" };
1738
+ }
1739
+ }
1740
+ const live = [...open].filter((execution2) => execution2.phase !== "closed");
1741
+ const joinable = live.filter(
1742
+ (execution2) => execution2.phase === "settling" || execution2.inFlight === 0
1743
+ );
1744
+ if (joinable.length === 1) {
1745
+ const execution2 = joinable[0];
1746
+ reopenIfSettling(execution2);
1747
+ execution2.provenanceNotes.push("joined during settling window");
1748
+ stats.attributionInferred += 1;
1749
+ return { execution: execution2, provenance: "inferred" };
1750
+ }
1751
+ const settling = live.filter((execution2) => execution2.phase === "settling");
1752
+ if (settling.length > 1) {
1753
+ const execution2 = settling.reduce(
1754
+ (latest, candidate) => candidate.lastActivityAt >= latest.lastActivityAt ? candidate : latest
1755
+ );
1756
+ reopenIfSettling(execution2);
1757
+ execution2.provenanceNotes.push("joined most recent of multiple settling executions");
1758
+ stats.attributionInferred += 1;
1759
+ stats.attributionInferredAmbiguous += 1;
1760
+ return { execution: execution2, provenance: "inferred-ambiguous" };
1761
+ }
1762
+ const execution = createExecution("detached");
1763
+ execution.provenanceNotes.push("no observed carrier or discrete event");
1764
+ stats.attributionUnlinked += 1;
1765
+ return { execution, provenance: "unlinked" };
1766
+ };
1767
+ const safeFact = (key, value) => {
1768
+ if (typeof value === "object" && value !== null) return void 0;
1769
+ return {
1770
+ key,
1771
+ value: captureValue(value),
1772
+ timestampMs: nowMs()
1773
+ };
1774
+ };
1775
+ const activeExecution = () => {
1776
+ const carrier = activeCarrier();
1777
+ if (carrier) return carrier.execution;
1778
+ const settling = [...open].filter((execution) => execution.phase !== "closed");
1779
+ return settling.length === 1 ? settling[0] : void 0;
1780
+ };
1781
+ const noteStateWrite = (key, value) => {
1782
+ try {
1783
+ const execution = activeExecution();
1784
+ const fact = safeFact(key, value);
1785
+ if (!execution || !fact || !recordingOpen(execution)) return;
1786
+ const carrier = activeCarrier();
1787
+ if (carrier) {
1788
+ const identity = execution.functions.get(carrier.functionId);
1789
+ if (identity) fact.functionName = identity.name;
1790
+ }
1791
+ execution.writes.push(fact);
1792
+ } catch {
1793
+ }
1794
+ };
1795
+ const noteStateRead = (key, value) => {
1796
+ try {
1797
+ const execution = activeExecution();
1798
+ const fact = safeFact(key, value);
1799
+ if (!execution || !fact || !recordingOpen(execution)) return;
1800
+ const carrier = activeCarrier();
1801
+ if (carrier) {
1802
+ const identity = execution.functions.get(carrier.functionId);
1803
+ if (identity) fact.functionName = identity.name;
1804
+ }
1805
+ execution.reads.push(fact);
1806
+ } catch {
1807
+ }
1808
+ };
1809
+ const rememberError = (error, execution) => {
1810
+ execution.hasError = true;
1811
+ if (typeof error === "object" && error !== null && !errors.has(error)) {
1812
+ errors.set(error, execution);
1813
+ }
1814
+ };
1815
+ const inheritedDataString = (error, key) => {
1816
+ let current2 = error;
1817
+ while (current2) {
1818
+ const descriptor = Object.getOwnPropertyDescriptor(current2, key);
1819
+ if (descriptor) {
1820
+ if (descriptor.get || typeof descriptor.value !== "string") return void 0;
1821
+ return descriptor.value;
1822
+ }
1823
+ current2 = Object.getPrototypeOf(current2);
1824
+ }
1825
+ return void 0;
1826
+ };
1827
+ const thrownFields = (error) => {
1828
+ if (typeof error !== "object" || error === null) return {};
1829
+ const errorName = inheritedDataString(error, "name") ?? (error instanceof Error && typeof error.constructor.name === "string" ? error.constructor.name : void 0);
1830
+ const errorMessage = inheritedDataString(error, "message");
1831
+ return {
1832
+ ...errorName === void 0 || errorName.length === 0 ? {} : { errorName },
1833
+ ...errorMessage === void 0 ? {} : { errorMessage }
1834
+ };
1835
+ };
1836
+ const suppressionContextKey = (functionId, parentFunctionId) => parentFunctionId ? `${functionId}\0${parentFunctionId}` : functionId;
1837
+ const noteSuppressed = (execution, identity, parentCallId, parentFunctionId) => {
1838
+ stats.repeatedCallsSuppressed += 1;
1839
+ noteOnce(execution, REPEAT_NOTE);
1840
+ if (!recordingOpen(execution)) return;
1841
+ const key = suppressionContextKey(identity.functionId, parentFunctionId);
1842
+ const existing = execution.suppressionByContext.get(key);
1843
+ if (existing) {
1844
+ existing.suppressedCallCount += 1;
1845
+ return;
1846
+ }
1847
+ const event = {
1848
+ type: "function_calls_suppressed",
1849
+ functionId: identity.functionId,
1850
+ ...parentCallId === void 0 ? {} : { parentCallId },
1851
+ timestampMs: nowMs(),
1852
+ suppressedCallCount: 1
1853
+ };
1854
+ execution.suppressionByContext.set(key, event);
1855
+ appendEvent(execution, event, 16);
1856
+ };
1857
+ const recordThrow = (execution, identity, error, callId, priorSuccessfulRuns) => {
1858
+ if (!recordingOpen(execution)) return;
1859
+ execution.functions.set(identity.functionId, identity);
1860
+ rememberError(error, execution);
1861
+ const key = (typeof error === "object" || typeof error === "function") && error !== null ? error : void 0;
1862
+ const referencedCallId = key ? execution.capturedErrors.get(key) : void 0;
1863
+ if (referencedCallId !== void 0) {
1864
+ pushStructural(
1865
+ execution,
1866
+ {
1867
+ type: "function_throw",
1868
+ callId,
1869
+ timestampMs: nowMs(),
1870
+ error: { __rasputin_type: "ErrorReference", callId: referencedCallId },
1871
+ ...priorSuccessfulRuns === void 0 ? {} : { priorSuccessfulRuns }
1872
+ },
1873
+ 32
1874
+ );
1875
+ return;
1876
+ }
1877
+ if (key) execution.capturedErrors.set(key, callId);
1878
+ pushStructural(
1879
+ execution,
1880
+ {
1881
+ type: "function_throw",
1882
+ callId,
1883
+ timestampMs: nowMs(),
1884
+ ...thrownFields(error),
1885
+ ...priorSuccessfulRuns === void 0 ? {} : { priorSuccessfulRuns }
1886
+ },
1887
+ 32
1888
+ );
1889
+ };
1890
+ const countSuccessfulRepeat = (frame) => {
1891
+ if (frame.threw || frame.successCounted || !frame.recordedEnter) return;
1892
+ frame.successCounted = true;
1893
+ const parentId = frame.previous?.functionId;
1894
+ const key = repeatKey(frame.functionId, parentId);
1895
+ frame.execution.successCounts.set(key, (frame.execution.successCounts.get(key) ?? 0) + 1);
1896
+ };
1897
+ const isCheapPrimitive = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
1898
+ const leaveRecorded = (frame, value) => {
1899
+ const leaveStarted = config.profileCosts ? nowMs() : 0;
1900
+ if (frame.recordedEnter && !frame.exited) {
1901
+ frame.exited = true;
1902
+ const structurePrimitive = frame.omitValues && frame.identity.captureClass === "structure" && isCheapPrimitive(value);
1903
+ const capture = (!frame.omitValues || structurePrimitive) && valuesAllowed(frame.execution) && hasValueBudget(frame.execution) ? captureValue(value) : void 0;
1904
+ if (capture) consumeValue(frame.execution);
1905
+ pushStructural(
1906
+ frame.execution,
1907
+ {
1908
+ type: "function_exit",
1909
+ callId: frame.callId,
1910
+ timestampMs: nowMs(),
1911
+ ...capture ? { returnValue: capture } : {}
1912
+ },
1913
+ capture ? estimateCapturedBytes(capture) : 0
1914
+ );
1915
+ }
1916
+ countSuccessfulRepeat(frame);
1917
+ if (config.profileCosts) addCost(costs, "leaveEndMs", leaveStarted);
1918
+ return value;
1919
+ };
1920
+ const failRecorded = (frame, error, liveArgs) => {
1921
+ if (frame.threw) return;
1922
+ frame.threw = true;
1923
+ materializeThrowChain(frame, liveArgs);
1924
+ recordThrow(frame.execution, frame.identity, error, frame.callId);
1925
+ };
1926
+ const endFrame = (frame) => {
1927
+ const endStarted = config.profileCosts ? nowMs() : 0;
1928
+ if (frame.ended) {
1929
+ if (config.profileCosts) addCost(costs, "leaveEndMs", endStarted);
1930
+ return;
1931
+ }
1932
+ frame.ended = true;
1933
+ frame.suspended = false;
1934
+ if (frame.recordedEnter && !frame.threw && !frame.exited) {
1935
+ frame.exited = true;
1936
+ pushStructural(
1937
+ frame.execution,
1938
+ {
1939
+ type: "function_exit",
1940
+ callId: frame.callId,
1941
+ timestampMs: nowMs()
1942
+ },
1943
+ 0
1944
+ );
1945
+ }
1946
+ countSuccessfulRepeat(frame);
1947
+ if (current === frame) current = frame.previous;
1948
+ if (recordingOpen(frame.execution)) {
1949
+ frame.execution.inFlight = Math.max(0, frame.execution.inFlight - 1);
1950
+ if (frame.execution.inFlight === 0) scheduleClose(frame.execution);
1951
+ }
1952
+ if (config.profileCosts) addCost(costs, "leaveEndMs", endStarted);
1953
+ };
1954
+ const suspendFrame = (frame) => {
1955
+ if (frame.ended || frame.suspended) return;
1956
+ frame.suspended = true;
1957
+ if (current === frame) current = frame.previous;
1958
+ };
1959
+ const resumeFrame = (frame, restored) => {
1960
+ if (frame.ended) return restored[0];
1961
+ frame.suspended = false;
1962
+ if (!recordingOpen(frame.execution)) return restored[0];
1963
+ current = frame;
1964
+ reopenIfSettling(frame.execution);
1965
+ if (restored.length > 0 && frame.recordedEnter) {
1966
+ const captured = !frame.omitValues && valuesAllowed(frame.execution) && hasValueBudget(frame.execution) ? captureValue(restored[0]) : void 0;
1967
+ if (captured) consumeValue(frame.execution);
1968
+ pushStructural(
1969
+ frame.execution,
1970
+ {
1971
+ type: "awaited",
1972
+ callId: frame.callId,
1973
+ timestampMs: nowMs(),
1974
+ ...captured ? { value: captured } : { omitted: true }
1975
+ },
1976
+ captured ? estimateCapturedBytes(captured) : 0
1977
+ );
1978
+ }
1979
+ return restored[0];
1980
+ };
1981
+ const attachThrowArgs = (event, liveArgs) => {
1982
+ if (event.args !== void 0) return;
1983
+ const captured = captureArgs(liveArgs);
1984
+ if (!captured) return;
1985
+ event.args = captured;
1986
+ delete event.argsOmitted;
1987
+ event.materialized = event.materialized ?? "throw-time";
1988
+ };
1989
+ const materializeThrowChain = (frame, liveArgs) => {
1990
+ if (!recordingOpen(frame.execution)) return;
1991
+ const chain = [];
1992
+ let cursor = frame;
1993
+ while (cursor && cursor.execution === frame.execution) {
1994
+ chain.push(cursor);
1995
+ cursor = cursor.previous;
1996
+ }
1997
+ chain.reverse();
1998
+ let missing = false;
1999
+ for (const ancestor of chain) {
2000
+ const present = frame.execution.events.find(
2001
+ (event) => event.type === "function_enter" && event.callId === ancestor.callId
2002
+ );
2003
+ if (present && present.type === "function_enter") {
2004
+ if (ancestor === frame && liveArgs !== void 0) attachThrowArgs(present, liveArgs);
2005
+ continue;
2006
+ }
2007
+ missing = true;
2008
+ const parentCallId = ancestor.previous && ancestor.previous.execution === frame.execution ? ancestor.previous.callId : ancestor.parentCallId;
2009
+ const captured = ancestor === frame && liveArgs !== void 0 ? captureArgs(liveArgs) : void 0;
2010
+ appendEvent(
2011
+ frame.execution,
2012
+ {
2013
+ type: "function_enter",
2014
+ callId: ancestor.callId,
2015
+ functionId: ancestor.functionId,
2016
+ ...parentCallId === void 0 ? {} : { parentCallId },
2017
+ timestampMs: nowMs(),
2018
+ provenance: ancestor.recordedEnter ? "observed" : "inferred",
2019
+ materialized: "throw-time",
2020
+ ...captured ? { args: captured } : { argsOmitted: true }
2021
+ },
2022
+ 16
2023
+ );
2024
+ frame.execution.functions.set(ancestor.functionId, ancestor.identity);
2025
+ frame.execution.structuralCount += 1;
2026
+ }
2027
+ if (missing) noteOnce(frame.execution, THROW_TIME_NOTE);
2028
+ };
2029
+ const isAdoptValue = (value) => value !== null && typeof value === "object";
2030
+ const ensureAdopt = (frame) => {
2031
+ if (frame.adopt) return frame.adopt;
2032
+ if (frame.identity.captureClass === "reactive-sparse") return void 0;
2033
+ frame.adopt = createJsonAdoptFrame(frame.callee);
2034
+ return frame.adopt;
2035
+ };
2036
+ const makeHandle = (frame) => isolateHandle({
2037
+ leave: (value) => {
2038
+ const result = frame.recordedEnter ? leaveRecorded(frame, value) : value;
2039
+ if (isAdoptValue(result) || frame.adopt) {
2040
+ const adopt = ensureAdopt(frame);
2041
+ if (adopt) notifyJsonProducerLeave(adopt, result);
2042
+ }
2043
+ return result;
2044
+ },
2045
+ fail: (error, liveArgs) => {
2046
+ failRecorded(frame, error, liveArgs);
2047
+ },
2048
+ end: () => {
2049
+ if (frame.adopt) notifyJsonProducerEnd(frame.adopt);
2050
+ endFrame(frame);
2051
+ },
2052
+ run: (callback) => callback(),
2053
+ suspend: (value) => {
2054
+ const adopt = ensureAdopt(frame);
2055
+ if (adopt) notifyJsonProducerSuspend(adopt);
2056
+ suspendFrame(frame);
2057
+ return value;
2058
+ },
2059
+ resume: (...restored) => resumeFrame(frame, restored)
2060
+ });
2061
+ const pushFrame = (execution, identity, parentCallId, omitValues, recordedEnter, counted, callee) => {
2062
+ const frame = {
2063
+ callId: nextCallId,
2064
+ functionId: identity.functionId,
2065
+ identity,
2066
+ execution,
2067
+ previous: current,
2068
+ parentCallId,
2069
+ omitValues,
2070
+ recordedEnter,
2071
+ counted,
2072
+ suspended: false,
2073
+ ended: false,
2074
+ threw: false,
2075
+ exited: false,
2076
+ successCounted: false,
2077
+ ...callee ? { callee } : {}
2078
+ };
2079
+ nextCallId += 1;
2080
+ current = frame;
2081
+ if (recordingOpen(execution)) execution.inFlight += 1;
2082
+ execution.functions.set(identity.functionId, identity);
2083
+ return frame;
2084
+ };
2085
+ const enterSparse = (execution, identity, parentCallId, parentFunctionId, callee) => {
2086
+ const suppressStarted = config.profileCosts ? nowMs() : 0;
2087
+ const frame = pushFrame(execution, identity, parentCallId, true, false, true, callee);
2088
+ noteSuppressed(execution, identity, parentCallId, parentFunctionId);
2089
+ if (config.profileCosts) {
2090
+ if (identity.captureClass === "skip") costs.entersSkip += 1;
2091
+ else costs.entersSparse += 1;
2092
+ addCost(costs, "suppressMs", suppressStarted);
2093
+ }
2094
+ return makeHandle(frame);
2095
+ };
2096
+ const enterRecorded = (execution, identity, parentCallId, provenance, args, omitValues, callee) => {
2097
+ const omit = omitValues || !valuesAllowed(execution);
2098
+ const frame = pushFrame(execution, identity, parentCallId, omit, true, false, callee);
2099
+ const captured = omit || !hasValueBudget(execution) ? void 0 : captureArgs(args);
2100
+ if (captured) consumeValue(execution);
2101
+ let extra = 0;
2102
+ if (captured) {
2103
+ for (const value of captured) extra += estimateCapturedBytes(value);
2104
+ }
2105
+ pushStructural(
2106
+ execution,
2107
+ {
2108
+ type: "function_enter",
2109
+ callId: frame.callId,
2110
+ functionId: identity.functionId,
2111
+ ...parentCallId === void 0 ? {} : { parentCallId },
2112
+ timestampMs: nowMs(),
2113
+ provenance,
2114
+ ...captured ? { args: captured } : { argsOmitted: true }
2115
+ },
2116
+ extra
2117
+ );
2118
+ stats.functionCallsCaptured += 1;
2119
+ const handleStarted = config.profileCosts ? nowMs() : 0;
2120
+ const handle = makeHandle(frame);
2121
+ if (config.profileCosts) {
2122
+ if (identity.captureClass === "full") costs.entersFull += 1;
2123
+ else costs.entersStructure += 1;
2124
+ addCost(costs, "handleAllocMs", handleStarted);
2125
+ }
2126
+ return handle;
2127
+ };
2128
+ const enterWithIdentity = (identity, args, omitValues, callee) => {
2129
+ const enterStarted = config.profileCosts ? nowMs() : 0;
2130
+ const liveArgs = args ?? emptyArgs;
2131
+ stats.functionCallsObserved += 1;
2132
+ const carrier = activeCarrier();
2133
+ if (carrier) {
2134
+ const execution2 = carrier.execution;
2135
+ const parentCallId2 = carrier.callId;
2136
+ const parentFn2 = carrier.functionId;
2137
+ execution2.enterCount += 1;
2138
+ updateProfile(execution2);
2139
+ const seen2 = execution2.successCounts.get(repeatKey(identity.functionId, parentFn2)) ?? 0;
2140
+ const cap2 = maxRepeatsFor(
2141
+ identity.captureClass,
2142
+ execution2.captureProfile,
2143
+ config.maxCapturedCallsPerFunction
2144
+ );
2145
+ const handle2 = seen2 >= cap2 ? enterSparse(execution2, identity, parentCallId2, parentFn2, callee) : enterRecorded(
2146
+ execution2,
2147
+ identity,
2148
+ parentCallId2,
2149
+ "observed",
2150
+ liveArgs,
2151
+ omitValues === true || identity.captureClass !== "full",
2152
+ callee
2153
+ );
2154
+ if (config.profileCosts) addCost(costs, "enterMs", enterStarted);
2155
+ return handle2;
2156
+ }
2157
+ const resolveStarted = config.profileCosts ? nowMs() : 0;
2158
+ const { execution, parentCallId, provenance } = resolveExecution();
2159
+ if (config.profileCosts) addCost(costs, "resolveMs", resolveStarted);
2160
+ if (!recordingOpen(execution)) return passthrough;
2161
+ const parentFn = current?.functionId;
2162
+ execution.enterCount += 1;
2163
+ updateProfile(execution);
2164
+ const seen = execution.successCounts.get(repeatKey(identity.functionId, parentFn)) ?? 0;
2165
+ const cap = maxRepeatsFor(
2166
+ identity.captureClass,
2167
+ execution.captureProfile,
2168
+ config.maxCapturedCallsPerFunction
2169
+ );
2170
+ const handle = seen >= cap ? enterSparse(execution, identity, parentCallId, parentFn, callee) : enterRecorded(
2171
+ execution,
2172
+ identity,
2173
+ parentCallId,
2174
+ provenance,
2175
+ liveArgs,
2176
+ omitValues === true || identity.captureClass !== "full",
2177
+ callee
2178
+ );
2179
+ if (config.profileCosts) addCost(costs, "enterMs", enterStarted);
2180
+ return handle;
2181
+ };
2182
+ const enterInner = (moduleIndex, planIndex, args, omitValues, plan, callee) => {
2183
+ const captureClass = omitValues === true || args === void 0 ? "structure" : "full";
2184
+ return enterWithIdentity(
2185
+ identityFromIndex(moduleIndex, planIndex, captureClass, plan),
2186
+ args,
2187
+ omitValues,
2188
+ callee
2189
+ );
2190
+ };
2191
+ const enter = (moduleIndex, planIndex, args, omitValues, plan, callee) => {
2192
+ try {
2193
+ return enterInner(moduleIndex, planIndex, args, omitValues, plan, callee);
2194
+ } catch {
2195
+ return passthrough;
2196
+ }
2197
+ };
2198
+ const observe = (siteId, value) => {
2199
+ try {
2200
+ const observeStarted = config.profileCosts ? nowMs() : 0;
2201
+ if (config.profileCosts) costs.observeCalls += 1;
2202
+ if (current?.counted) {
2203
+ if (config.profileCosts) addCost(costs, "observeMs", observeStarted);
2204
+ return value;
2205
+ }
2206
+ const execution = activeCarrier()?.execution ?? activeExecution();
2207
+ if (!execution || !recordingOpen(execution) || !frontierAllowsValues(execution) || !hasValueBudget(execution)) {
2208
+ if (config.profileCosts) addCost(costs, "observeMs", observeStarted);
2209
+ return value;
2210
+ }
2211
+ const captureStarted = config.profileCosts ? nowMs() : 0;
2212
+ const captured = captureValue(value);
2213
+ if (config.profileCosts) addCost(costs, "captureMs", captureStarted);
2214
+ consumeValue(execution);
2215
+ appendEvent(
2216
+ execution,
2217
+ {
2218
+ type: "expression_observed",
2219
+ siteId,
2220
+ ...current ? { callId: current.callId } : {},
2221
+ timestampMs: nowMs(),
2222
+ value: captured
2223
+ },
2224
+ estimateCapturedBytes(captured)
2225
+ );
2226
+ stats.expressionObservations += 1;
2227
+ if (config.profileCosts) addCost(costs, "observeMs", observeStarted);
2228
+ return value;
2229
+ } catch {
2230
+ return value;
2231
+ }
2232
+ };
2233
+ const enterReactiveIdentity = (identity) => {
2234
+ try {
2235
+ return enterWithIdentity(identity, emptyArgs, true);
2236
+ } catch {
2237
+ return passthrough;
2238
+ }
2239
+ };
2240
+ const rememberReactiveSite = (id, identity) => {
2241
+ registeredReactiveSites.set(id, identity);
2242
+ identities.set(identity.functionId, identity);
2243
+ identitiesBySite.set(id, identity);
2244
+ if (id >= reactiveSampled.length) {
2245
+ reactiveSampled.length = id + 1;
2246
+ reactiveInvocations.length = id + 1;
2247
+ reactiveHandles.length = id + 1;
2248
+ }
2249
+ };
2250
+ const reactiveRegister = (entries) => {
2251
+ if (!Array.isArray(entries)) return;
2252
+ for (const entry of entries) {
2253
+ if (typeof entry === "number") {
2254
+ const identity = identitiesBySite.get(entry) ?? {
2255
+ functionId: `r${entry}`,
2256
+ name: `r${entry}`,
2257
+ packageName: null,
2258
+ packageRelativePath: null,
2259
+ line: null,
2260
+ column: null,
2261
+ captureClass: "reactive-sparse"
2262
+ };
2263
+ rememberReactiveSite(entry, identity);
2264
+ continue;
2265
+ }
2266
+ if (!Array.isArray(entry) || typeof entry[0] !== "number") continue;
2267
+ const id = entry[0];
2268
+ const functionId = typeof entry[1] === "string" ? entry[1] : `r${id}`;
2269
+ const name = typeof entry[2] === "string" ? entry[2] : functionId;
2270
+ rememberReactiveSite(id, {
2271
+ functionId,
2272
+ name,
2273
+ packageName: null,
2274
+ packageRelativePath: null,
2275
+ line: typeof entry[3] === "number" ? entry[3] : null,
2276
+ column: typeof entry[4] === "number" ? entry[4] : null,
2277
+ captureClass: "reactive-sparse"
2278
+ });
2279
+ }
2280
+ };
2281
+ const reactiveHit = (id) => {
2282
+ try {
2283
+ const hitStarted = config.profileCosts ? nowMs() : 0;
2284
+ stats.reactiveCallbackInvocations += 1;
2285
+ if (id >= reactiveInvocations.length) {
2286
+ reactiveSampled.length = id + 1;
2287
+ reactiveInvocations.length = id + 1;
2288
+ reactiveHandles.length = id + 1;
2289
+ }
2290
+ reactiveInvocations[id] = (reactiveInvocations[id] ?? 0) + 1;
2291
+ const identity = registeredReactiveSites.get(id);
2292
+ if (reactiveSampled[id]) {
2293
+ let owned = false;
2294
+ try {
2295
+ if (identity) {
2296
+ const carrier = activeCarrier();
2297
+ const resolved = carrier ? {
2298
+ execution: carrier.execution,
2299
+ parentCallId: carrier.callId
2300
+ } : resolveExecution();
2301
+ if (recordingOpen(resolved.execution)) {
2302
+ pushFrame(resolved.execution, identity, resolved.parentCallId, true, false, true);
2303
+ owned = true;
2304
+ }
2305
+ }
2306
+ } finally {
2307
+ reactiveOwned.push(owned);
2308
+ }
2309
+ if (config.profileCosts) addCost(costs, "reactiveHitMs", hitStarted);
2310
+ return true;
2311
+ }
2312
+ reactiveOwned.push(false);
2313
+ if (!identity) {
2314
+ reactiveSampled[id] = true;
2315
+ if (config.profileCosts) addCost(costs, "reactiveHitMs", hitStarted);
2316
+ return true;
2317
+ }
2318
+ reactiveHandles[id] = enterReactiveIdentity(identity);
2319
+ stats.reactiveRecordedEntries += 1;
2320
+ if (config.profileCosts) addCost(costs, "reactiveHitMs", hitStarted);
2321
+ return false;
2322
+ } catch {
2323
+ return true;
2324
+ }
2325
+ };
2326
+ const reactiveLeave = (id, value) => {
2327
+ try {
2328
+ const handle = reactiveHandles[id];
2329
+ if (!handle) return value;
2330
+ reactiveHandles[id] = void 0;
2331
+ reactiveSampled[id] = true;
2332
+ const result = handle.leave(value);
2333
+ handle.end();
2334
+ return result;
2335
+ } catch {
2336
+ return value;
2337
+ }
2338
+ };
2339
+ const reactiveThrow = (id, error) => {
2340
+ try {
2341
+ stats.reactiveThrows += 1;
2342
+ const handle = reactiveHandles[id];
2343
+ if (handle) {
2344
+ reactiveHandles[id] = void 0;
2345
+ handle.fail(error);
2346
+ handle.end();
2347
+ return;
2348
+ }
2349
+ const identity = registeredReactiveSites.get(id);
2350
+ if (current?.counted && identity && current.functionId === identity.functionId) {
2351
+ if (!current.threw) {
2352
+ current.threw = true;
2353
+ materializeThrowChain(current);
2354
+ recordThrow(
2355
+ current.execution,
2356
+ identity,
2357
+ error,
2358
+ current.callId,
2359
+ Math.max(0, (reactiveInvocations[id] ?? 1) - 1)
2360
+ );
2361
+ }
2362
+ return;
2363
+ }
2364
+ if (!identity) return;
2365
+ const resolved = resolveExecution();
2366
+ if (!recordingOpen(resolved.execution)) return;
2367
+ let enteredCallId;
2368
+ for (let index = resolved.execution.events.length - 1; index >= 0; index -= 1) {
2369
+ const event = resolved.execution.events[index];
2370
+ if (event?.type === "function_enter" && event.functionId === identity.functionId) {
2371
+ enteredCallId = event.callId;
2372
+ break;
2373
+ }
2374
+ }
2375
+ if (enteredCallId === void 0) return;
2376
+ recordThrow(
2377
+ resolved.execution,
2378
+ identity,
2379
+ error,
2380
+ enteredCallId,
2381
+ Math.max(0, (reactiveInvocations[id] ?? 1) - 1)
2382
+ );
2383
+ if (resolved.execution.inFlight === 0) scheduleClose(resolved.execution);
2384
+ } catch {
2385
+ }
2386
+ };
2387
+ const reactiveDone = () => {
2388
+ try {
2389
+ const owned = reactiveOwned.pop() === true;
2390
+ if (!owned || !current?.counted || current.ended) return;
2391
+ const thrown = current.threw;
2392
+ endFrame(current);
2393
+ if (thrown) return;
2394
+ stats.reactiveSparseCounted += 1;
2395
+ if (config.profileCosts) costs.reactiveRepeats += 1;
2396
+ } catch {
2397
+ }
2398
+ };
2399
+ return {
2400
+ enter,
2401
+ registerIdentities,
2402
+ observe,
2403
+ reactiveRegister,
2404
+ reactiveHit,
2405
+ reactiveLeave,
2406
+ reactiveThrow,
2407
+ reactiveDone,
2408
+ noteStateWrite,
2409
+ noteStateRead,
2410
+ getHistory: () => [...history],
2411
+ getErrorState(error) {
2412
+ if (typeof error !== "object" || error === null) return void 0;
2413
+ const execution = errors.get(error);
2414
+ if (!execution) return void 0;
2415
+ const snapshot = snapshotOf(execution, config.captureMode);
2416
+ snapshot.historyLinks = linkHistory(execution);
2417
+ return snapshot;
2418
+ },
2419
+ getState() {
2420
+ return {
2421
+ open: [...open].map(summarize),
2422
+ recent: [...recent],
2423
+ snapshots: [...snapshots],
2424
+ history: [...history],
2425
+ stats: { ...stats, ...config.profileCosts ? { costs: { ...costs } } : {} }
2426
+ };
2427
+ },
2428
+ reset() {
2429
+ for (const execution of [...open]) closeExecution(execution);
2430
+ open.clear();
2431
+ recent.length = 0;
2432
+ snapshots.length = 0;
2433
+ history.length = 0;
2434
+ current = null;
2435
+ nextCallId = 1;
2436
+ nextExecution = 1;
2437
+ identities.clear();
2438
+ identitiesBySite.clear();
2439
+ registeredReactiveSites.clear();
2440
+ internedIds.clear();
2441
+ nextIntern = 1;
2442
+ reactiveHandles.length = 0;
2443
+ reactiveSampled.length = 0;
2444
+ reactiveInvocations.length = 0;
2445
+ reactiveOwned.length = 0;
2446
+ Object.assign(stats, emptyStats());
2447
+ Object.assign(costs, emptyCosts());
2448
+ resetJsonProducerAdopt();
2449
+ }
2450
+ };
2451
+ };
2452
+ var isolateHandle = (handle) => ({
2453
+ leave: (value) => {
2454
+ try {
2455
+ return handle.leave(value);
2456
+ } catch {
2457
+ return value;
2458
+ }
2459
+ },
2460
+ fail: (error, liveArgs) => {
2461
+ try {
2462
+ handle.fail(error, liveArgs);
2463
+ } catch {
2464
+ }
2465
+ },
2466
+ end: () => {
2467
+ try {
2468
+ handle.end();
2469
+ } catch {
2470
+ }
2471
+ },
2472
+ run: (callback) => {
2473
+ let outcome;
2474
+ const wrapped = () => {
2475
+ try {
2476
+ const value = callback();
2477
+ outcome = { kind: "value", value };
2478
+ return value;
2479
+ } catch (error) {
2480
+ outcome = { kind: "throw", error };
2481
+ throw error;
2482
+ }
2483
+ };
2484
+ try {
2485
+ handle.run(wrapped);
2486
+ } catch {
2487
+ }
2488
+ if (outcome?.kind === "throw") throw outcome.error;
2489
+ if (outcome?.kind === "value") return outcome.value;
2490
+ return callback();
2491
+ },
2492
+ suspend: (value) => {
2493
+ try {
2494
+ return handle.suspend(value);
2495
+ } catch {
2496
+ return value;
2497
+ }
2498
+ },
2499
+ // Rest args, not `arguments`: isolateHandle is an arrow and ESM has no arguments object.
2500
+ resume: (...restored) => {
2501
+ try {
2502
+ return restored.length === 0 ? handle.resume() : handle.resume(restored[0]);
2503
+ } catch {
2504
+ return restored[0];
2505
+ }
2506
+ }
2507
+ });
2508
+
2509
+ // ../recorder/src/run-exactly-once.ts
2510
+ var runExactlyOnce = function __rasputinHandleRun__(invoke, callback) {
2511
+ let outcome;
2512
+ const wrapped = function __rasputinHandleRun__2() {
2513
+ try {
2514
+ const value = callback();
2515
+ outcome = { kind: "value", value };
2516
+ return value;
2517
+ } catch (caught) {
2518
+ outcome = { kind: "throw", error: caught };
2519
+ throw caught;
2520
+ }
2521
+ };
2522
+ try {
2523
+ invoke(wrapped);
2524
+ } catch {
2525
+ }
2526
+ if (outcome?.kind === "throw") throw outcome.error;
2527
+ if (outcome?.kind === "value") return outcome.value;
2528
+ return callback();
2529
+ };
2530
+
2531
+ // ../recorder/src/call-handle.ts
2532
+ var passthroughHandle = () => ({
2533
+ leave: (value) => value,
2534
+ fail: () => {
2535
+ },
2536
+ end: () => {
2537
+ },
2538
+ run: function __rasputinHandleRun__2(callback) {
2539
+ return runExactlyOnce((invoke) => invoke(), callback);
2540
+ },
2541
+ suspend: (value) => value,
2542
+ resume: (value) => value
2543
+ });
2544
+
2545
+ // ../recorder/src/runtime-symbol.ts
2546
+ var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v3";
2547
+
2548
+ // ../recorder/src/registry.ts
2549
+ var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
2550
+ var passthroughRun = (_planIndex, _args, callback) => callback();
2551
+ var passthroughEnter = () => passthroughHandle();
2552
+ var attachRunner = (cell) => {
2553
+ const run = ((planIndex, args, callback, omitValues) => cell.run(planIndex, args, callback, omitValues));
2554
+ run.enter = (planIndex, args, omitValues, callee) => cell.enter(planIndex, args, omitValues, callee);
2555
+ return run;
2556
+ };
2557
+ var isCompatibleRegistry = (value) => typeof Reflect.get(value, "register") === "function" && Array.isArray(Reflect.get(value, "runtimes")) && Array.isArray(Reflect.get(value, "modules"));
2558
+ var registerModule = (registry, plans, moduleIndex) => {
2559
+ const cell = { run: passthroughRun, enter: passthroughEnter };
2560
+ const module = {
2561
+ plans,
2562
+ cell,
2563
+ ...moduleIndex === void 0 ? {} : { moduleIndex }
2564
+ };
2565
+ registry.modules.push(module);
2566
+ registry.runtimes.at(-1)?.bind(module);
2567
+ return attachRunner(cell);
2568
+ };
2569
+ var createRegistry = () => {
2570
+ const registry = {
2571
+ runtimes: [],
2572
+ modules: [],
2573
+ register(plans, moduleIndex) {
2574
+ return registerModule(registry, plans, moduleIndex);
2575
+ }
2576
+ };
2577
+ return registry;
2578
+ };
2579
+ var getAutomaticRuntimeRegistry = () => {
2580
+ const existing = Reflect.get(globalThis, runtimeSymbol);
2581
+ if (existing && typeof existing === "object" && isCompatibleRegistry(existing)) {
2582
+ return existing;
2583
+ }
2584
+ if (existing != null) {
2585
+ return createRegistry();
2586
+ }
2587
+ const registry = createRegistry();
2588
+ Reflect.set(globalThis, runtimeSymbol, registry);
2589
+ return registry;
2590
+ };
2591
+
2592
+ // src/install-browser-runtime.ts
2593
+ var installBrowserExecutionRuntime = (enter) => {
2594
+ const registry = getAutomaticRuntimeRegistry();
2595
+ const runtime = {
2596
+ bind(module) {
2597
+ module.cell.enter = (planIndex, args, omitValues, callee) => {
2598
+ return enter(
2599
+ module.moduleIndex ?? 0,
2600
+ planIndex,
2601
+ args,
2602
+ omitValues,
2603
+ module.plans[planIndex],
2604
+ callee
2605
+ );
2606
+ };
2607
+ module.cell.run = (planIndex, args, callback, omitValues) => {
2608
+ const handle = module.cell.enter(planIndex, args, omitValues);
2609
+ try {
2610
+ const result = handle.run(callback);
2611
+ handle.leave(result);
2612
+ return result;
2613
+ } catch (error) {
2614
+ handle.fail(error, args);
2615
+ throw error;
2616
+ } finally {
2617
+ handle.end();
2618
+ }
2619
+ };
2620
+ }
2621
+ };
2622
+ registry.runtimes.push(runtime);
2623
+ for (const module of registry.modules) runtime.bind(module);
2624
+ return () => {
2625
+ const index = registry.runtimes.lastIndexOf(runtime);
2626
+ if (index >= 0) registry.runtimes.splice(index, 1);
2627
+ const current = registry.runtimes.at(-1);
2628
+ for (const module of registry.modules) {
2629
+ if (current) current.bind(module);
2630
+ else {
2631
+ module.cell.enter = () => passthroughHandle();
2632
+ module.cell.run = (_planIndex, _args, callback) => callback();
2633
+ }
2634
+ }
2635
+ };
2636
+ };
2637
+
2638
+ // src/browser-runtime.ts
2639
+ var installed;
2640
+ var pendingIdentities = [];
2641
+ var OBSERVE_SYMBOL = Symbol.for("rasputin.observe.v1");
2642
+ var OBSERVE_NAME = "__rasputinO";
2643
+ var STRUCT_NAME = "__rasputinK";
2644
+ var JSON_METHOD_NAME = "__rasputinM";
2645
+ var JSON_AWAIT_NAME = "__rasputinA";
2646
+ var JSON_ADOPT_NAME = "__rasputinP";
2647
+ var PROVENANCE_HANDLE_NAME = "__rasputinN";
2648
+ var REACTIVE_HIT = "__rasputinR";
2649
+ var REACTIVE_LEAVE = "__rasputinL";
2650
+ var REACTIVE_THROW = "__rasputinT";
2651
+ var REACTIVE_DONE = "__rasputinD";
2652
+ var REACTIVE_REG = "__rasputinReg";
2653
+ var identityObserve = (_siteId, value) => value;
2654
+ var passthroughHit = () => 1;
2655
+ var passthroughLeave = (_id, value) => value;
2656
+ var passthroughThrow = () => {
2657
+ };
2658
+ var passthroughDone = () => {
2659
+ };
2660
+ var passthroughReg = () => {
2661
+ };
2662
+ var installObserve = (observe) => {
2663
+ const guarded = (siteId, value) => {
2664
+ if (typeof siteId !== "string" && typeof siteId !== "number") return value;
2665
+ try {
2666
+ return observe(siteId, value);
2667
+ } catch {
2668
+ return value;
2669
+ }
2670
+ };
2671
+ Reflect.set(globalThis, OBSERVE_SYMBOL, guarded);
2672
+ Reflect.set(globalThis, OBSERVE_NAME, guarded);
2673
+ };
2674
+ var installLiteralRemember = (remember) => {
2675
+ const guarded = (object, keys, values) => {
2676
+ try {
2677
+ return remember(object, keys, values);
2678
+ } catch {
2679
+ return object;
2680
+ }
2681
+ };
2682
+ Reflect.set(globalThis, STRUCT_NAME, guarded);
2683
+ };
2684
+ var installReactive = (recorder) => {
2685
+ Reflect.set(globalThis, REACTIVE_HIT, (id) => {
2686
+ if (typeof id !== "number") return 1;
2687
+ try {
2688
+ return recorder.reactiveHit(id) ? 1 : 0;
2689
+ } catch {
2690
+ return 1;
2691
+ }
2692
+ });
2693
+ Reflect.set(globalThis, REACTIVE_LEAVE, (id, value) => {
2694
+ if (typeof id !== "number") return value;
2695
+ try {
2696
+ return recorder.reactiveLeave(id, value);
2697
+ } catch {
2698
+ return value;
2699
+ }
2700
+ });
2701
+ Reflect.set(globalThis, REACTIVE_THROW, (id, error) => {
2702
+ if (typeof id !== "number") return;
2703
+ try {
2704
+ recorder.reactiveThrow(id, error);
2705
+ } catch {
2706
+ }
2707
+ });
2708
+ Reflect.set(globalThis, REACTIVE_DONE, () => {
2709
+ try {
2710
+ recorder.reactiveDone();
2711
+ } catch {
2712
+ }
2713
+ });
2714
+ Reflect.set(globalThis, REACTIVE_REG, (entries) => {
2715
+ try {
2716
+ recorder.reactiveRegister(entries);
2717
+ } catch {
2718
+ }
2719
+ });
2720
+ };
2721
+ var uninstallReactive = () => {
2722
+ Reflect.set(globalThis, REACTIVE_HIT, passthroughHit);
2723
+ Reflect.set(globalThis, REACTIVE_LEAVE, passthroughLeave);
2724
+ Reflect.set(globalThis, REACTIVE_THROW, passthroughThrow);
2725
+ Reflect.set(globalThis, REACTIVE_DONE, passthroughDone);
2726
+ Reflect.set(globalThis, REACTIVE_REG, passthroughReg);
2727
+ };
2728
+ var RasputinBrowserInit = (options = {}) => {
2729
+ installed?.uninstall();
2730
+ const recorder = createBrowserRecorder(options);
2731
+ if (pendingIdentities.length > 0) {
2732
+ recorder.registerIdentities(pendingIdentities);
2733
+ pendingIdentities.length = 0;
2734
+ }
2735
+ const fromBuild = Reflect.get(globalThis, "__RASPUTIN_BUILD_IDENTITIES__");
2736
+ if (Array.isArray(fromBuild)) recorder.registerIdentities(fromBuild);
2737
+ if (Array.isArray(options.identities)) recorder.registerIdentities(options.identities);
2738
+ const uninstall = installBrowserExecutionRuntime(recorder.enter);
2739
+ installObserve(recorder.observe);
2740
+ installLiteralRemember(rememberKnownLiteral);
2741
+ installReactive(recorder);
2742
+ Reflect.set(globalThis, JSON_METHOD_NAME, invokeNativeJsonMethod);
2743
+ Reflect.set(globalThis, JSON_AWAIT_NAME, tagJsonAwaitResult);
2744
+ Reflect.set(globalThis, JSON_ADOPT_NAME, adoptAsyncJsonPromise);
2745
+ Reflect.set(globalThis, PROVENANCE_HANDLE_NAME, createProvenanceHandle);
2746
+ installed = {
2747
+ getState: recorder.getState,
2748
+ getErrorState: recorder.getErrorState,
2749
+ getHistory: recorder.getHistory,
2750
+ noteStateWrite: recorder.noteStateWrite,
2751
+ noteStateRead: recorder.noteStateRead,
2752
+ registerIdentities: recorder.registerIdentities,
2753
+ reset: recorder.reset,
2754
+ uninstall: () => {
2755
+ uninstall();
2756
+ Reflect.set(globalThis, OBSERVE_SYMBOL, identityObserve);
2757
+ Reflect.set(globalThis, OBSERVE_NAME, identityObserve);
2758
+ Reflect.set(globalThis, STRUCT_NAME, passthroughKnownLiteral);
2759
+ Reflect.set(globalThis, JSON_METHOD_NAME, passthroughNativeJsonMethod);
2760
+ Reflect.set(globalThis, JSON_AWAIT_NAME, passthroughJsonAwait);
2761
+ Reflect.set(globalThis, JSON_ADOPT_NAME, passthroughAdopt);
2762
+ Reflect.set(globalThis, PROVENANCE_HANDLE_NAME, passthroughProvenanceHandle);
2763
+ uninstallReactive();
2764
+ recorder.reset();
2765
+ }
2766
+ };
2767
+ const publicApi = {
2768
+ getState: recorder.getState,
2769
+ getErrorState: recorder.getErrorState,
2770
+ getHistory: recorder.getHistory,
2771
+ noteStateWrite: recorder.noteStateWrite,
2772
+ noteStateRead: recorder.noteStateRead
2773
+ };
2774
+ Reflect.set(globalThis, "__rasputinRuntime", publicApi);
2775
+ const browserWindow = Reflect.get(globalThis, "window");
2776
+ if (browserWindow && typeof browserWindow === "object") {
2777
+ Reflect.set(browserWindow, "__rasputinRuntime", publicApi);
2778
+ }
2779
+ return {
2780
+ getState: recorder.getState,
2781
+ getErrorState: recorder.getErrorState,
2782
+ getHistory: recorder.getHistory
2783
+ };
2784
+ };
2785
+ var getBrowserRuntimeState = () => installed?.getState() ?? {
2786
+ open: [],
2787
+ recent: [],
2788
+ snapshots: [],
2789
+ history: [],
2790
+ stats: {
2791
+ functionCallsObserved: 0,
2792
+ functionCallsCaptured: 0,
2793
+ successfulExecutionsDiscarded: 0,
2794
+ errorSnapshots: 0,
2795
+ activeExecutions: 0,
2796
+ peakActiveExecutions: 0,
2797
+ activeEstimatedBytes: 0,
2798
+ peakActiveEstimatedBytes: 0,
2799
+ attributionInferred: 0,
2800
+ attributionInferredAmbiguous: 0,
2801
+ attributionUnlinked: 0,
2802
+ truncatedEvents: 0,
2803
+ truncatedStructural: 0,
2804
+ omittedValueObservations: 0,
2805
+ expressionObservations: 0,
2806
+ repeatedCallsSuppressed: 0,
2807
+ executionsDroppedUnderPressure: 0,
2808
+ reactiveCallbackInvocations: 0,
2809
+ reactiveRecordedEntries: 0,
2810
+ reactiveSparseCounted: 0,
2811
+ reactiveThrows: 0
2812
+ }
2813
+ };
2814
+ var getBrowserErrorState = (error) => installed?.getErrorState(error);
2815
+ var registerBrowserIdentities = (items) => {
2816
+ if (installed) installed.registerIdentities(items);
2817
+ else pendingIdentities.push(...items);
2818
+ };
2819
+ Reflect.set(globalThis, "__rasputinRegisterIdentities", registerBrowserIdentities);
2820
+ var uninstallBrowserRuntime = () => {
2821
+ installed?.uninstall();
2822
+ installed = void 0;
2823
+ Reflect.deleteProperty(globalThis, "__rasputinRuntime");
2824
+ const browserWindow = Reflect.get(globalThis, "window");
2825
+ if (browserWindow && typeof browserWindow === "object") {
2826
+ Reflect.deleteProperty(browserWindow, "__rasputinRuntime");
2827
+ }
2828
+ };
2829
+
2830
+ // src/browser-transport.ts
2831
+ var KEEPALIVE_MAX_BYTES = 60 * 1024;
2832
+ var DEFAULTS2 = {
2833
+ maxQueueSize: 40,
2834
+ maxQueueBytes: 256 * 1024,
2835
+ batchSize: 8,
2836
+ flushIntervalMs: 8e3,
2837
+ maxRetries: 3
2838
+ };
2839
+ var utf82 = new TextEncoder();
2840
+ var encodedBytes = (value) => {
2841
+ try {
2842
+ return utf82.encode(JSON.stringify(value)).byteLength;
2843
+ } catch {
2844
+ return 256;
2845
+ }
2846
+ };
2847
+ var isOnline = () => {
2848
+ const nav = Reflect.get(globalThis, "navigator");
2849
+ if (!nav || typeof nav !== "object" || !("onLine" in nav)) return true;
2850
+ return Reflect.get(nav, "onLine") !== false;
2851
+ };
2852
+ var defaultFetch = async (input, init) => {
2853
+ const response = await fetch(input, init);
2854
+ return { status: response.status, ok: response.ok };
2855
+ };
2856
+ var backoffMs = (attempt) => Math.min(8e3, 400 * 2 ** attempt);
2857
+ var sleep = (ms, signal) => new Promise((resolve) => {
2858
+ if (ms <= 0 || signal?.aborted) {
2859
+ resolve();
2860
+ return;
2861
+ }
2862
+ const timer = setTimeout(resolve, ms);
2863
+ signal?.addEventListener(
2864
+ "abort",
2865
+ () => {
2866
+ clearTimeout(timer);
2867
+ resolve();
2868
+ },
2869
+ { once: true }
2870
+ );
2871
+ });
2872
+ var createBrowserTransport = (options) => {
2873
+ const enabled = options.enabled;
2874
+ const maxQueueSize = options.maxQueueSize ?? DEFAULTS2.maxQueueSize;
2875
+ const maxQueueBytes = options.maxQueueBytes ?? DEFAULTS2.maxQueueBytes;
2876
+ const batchSize = options.batchSize ?? DEFAULTS2.batchSize;
2877
+ const flushIntervalMs = options.flushIntervalMs ?? DEFAULTS2.flushIntervalMs;
2878
+ const maxRetries = options.maxRetries ?? DEFAULTS2.maxRetries;
2879
+ const fetchFn = options.fetch ?? defaultFetch;
2880
+ const queue = [];
2881
+ let queuedBytes = 0;
2882
+ let timer;
2883
+ let flushing;
2884
+ let closed = false;
2885
+ let enqueued = 0;
2886
+ let droppedIncoming = 0;
2887
+ let droppedQueued = 0;
2888
+ let droppedKeepalive = 0;
2889
+ let deliverySeq = 0;
2890
+ let flushesStarted = 0;
2891
+ let sendAttempts = 0;
2892
+ let sendRetries = 0;
2893
+ let batchesSucceeded = 0;
2894
+ let batchesDropped = 0;
2895
+ let batchesFailed = 0;
2896
+ let networkErrors = 0;
2897
+ let rateLimitedResponses = 0;
2898
+ let serverErrorResponses = 0;
2899
+ let lastFailureKind = null;
2900
+ let lastFailureStatusCode = null;
2901
+ let uninstallLifecycle = () => {
2902
+ };
2903
+ const dropOldest = () => {
2904
+ const removed = queue.shift();
2905
+ if (!removed) return;
2906
+ queuedBytes = Math.max(0, queuedBytes - removed.bytes);
2907
+ droppedQueued += 1;
2908
+ };
2909
+ const push = (item) => {
2910
+ if (!enabled || closed) return;
2911
+ if (item.bytes > maxQueueBytes) {
2912
+ droppedIncoming += 1;
2913
+ return;
2914
+ }
2915
+ while (queue.length >= maxQueueSize || queuedBytes + item.bytes > maxQueueBytes) {
2916
+ if (queue.length === 0) {
2917
+ droppedIncoming += 1;
2918
+ return;
2919
+ }
2920
+ dropOldest();
2921
+ }
2922
+ queue.push(item);
2923
+ queuedBytes += item.bytes;
2924
+ enqueued += 1;
2925
+ };
2926
+ const enqueue = (event) => {
2927
+ push({ kind: "event", event, bytes: encodedBytes(event) });
2928
+ };
2929
+ const enqueueSummary = (summary) => {
2930
+ push({ kind: "summary", summary, bytes: encodedBytes(summary) });
2931
+ };
2932
+ const authHeaders = {
2933
+ authorization: `Bearer ${options.publicKey}`,
2934
+ "content-type": "application/json"
2935
+ };
2936
+ const nextDeliveryKey = () => `b_${(++deliverySeq).toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
2937
+ const takeDelivery = (mode) => {
2938
+ const first = queue[0];
2939
+ if (!first) return [];
2940
+ if (first.deliveryKey) {
2941
+ const key2 = first.deliveryKey;
2942
+ const taken2 = [];
2943
+ while (queue[0]?.deliveryKey === key2) {
2944
+ const item = queue.shift();
2945
+ if (item) taken2.push(item);
2946
+ }
2947
+ return taken2;
2948
+ }
2949
+ const taken = queue.splice(0, mode === "unload" ? queue.length : batchSize);
2950
+ const key = nextDeliveryKey();
2951
+ for (const item of taken) item.deliveryKey = key;
2952
+ return taken;
2953
+ };
2954
+ const encodeBatch = (events, summaries, idempotencyKey) => JSON.stringify({
2955
+ events,
2956
+ ...summaries.length > 0 ? { summaries } : {},
2957
+ sdk_version: options.sdkVersion,
2958
+ sdk_name: options.sdkName,
2959
+ idempotency_key: idempotencyKey
2960
+ });
2961
+ const shrinkUnloadBody = (events, summaries, idempotencyKey) => {
2962
+ const total = events.length + summaries.length;
2963
+ const candidates = [
2964
+ { events, summaries },
2965
+ { events, summaries: [] },
2966
+ { events: events.slice(0, 1), summaries: [] }
2967
+ ];
2968
+ for (const candidate of candidates) {
2969
+ const body = encodeBatch(candidate.events, candidate.summaries, idempotencyKey);
2970
+ if (utf82.encode(body).byteLength <= KEEPALIVE_MAX_BYTES) {
2971
+ return {
2972
+ body,
2973
+ events: candidate.events,
2974
+ summaries: candidate.summaries,
2975
+ omitted: total - candidate.events.length - candidate.summaries.length
2976
+ };
2977
+ }
2978
+ }
2979
+ return { tooLarge: true, omitted: total };
2980
+ };
2981
+ const sendBatch = async (events, summaries, mode, idempotencyKey) => {
2982
+ if (mode === "unload") {
2983
+ const shrunk = shrinkUnloadBody(events, summaries, idempotencyKey);
2984
+ if ("tooLarge" in shrunk) {
2985
+ droppedKeepalive += shrunk.omitted;
2986
+ droppedQueued += shrunk.omitted;
2987
+ batchesDropped += 1;
2988
+ lastFailureKind = "too_large";
2989
+ return "dropped";
2990
+ }
2991
+ if (shrunk.omitted > 0) {
2992
+ droppedKeepalive += shrunk.omitted;
2993
+ droppedQueued += shrunk.omitted;
2994
+ }
2995
+ try {
2996
+ sendAttempts += 1;
2997
+ const response = await fetchFn(options.ingestUrl, {
2998
+ method: "POST",
2999
+ headers: authHeaders,
3000
+ body: shrunk.body,
3001
+ keepalive: true
3002
+ });
3003
+ if (response.ok) {
3004
+ batchesSucceeded += 1;
3005
+ return "ok";
3006
+ }
3007
+ droppedKeepalive += shrunk.events.length + shrunk.summaries.length;
3008
+ droppedQueued += shrunk.events.length + shrunk.summaries.length;
3009
+ batchesDropped += 1;
3010
+ lastFailureKind = "non_retryable_http";
3011
+ lastFailureStatusCode = response.status;
3012
+ return "dropped";
3013
+ } catch {
3014
+ droppedKeepalive += shrunk.events.length + shrunk.summaries.length;
3015
+ droppedQueued += shrunk.events.length + shrunk.summaries.length;
3016
+ batchesDropped += 1;
3017
+ lastFailureKind = "network_error";
3018
+ return "dropped";
3019
+ }
3020
+ }
3021
+ const body = encodeBatch(events, summaries, idempotencyKey);
3022
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
3023
+ if (attempt > 0) {
3024
+ if (!isOnline()) return "failed";
3025
+ sendRetries += 1;
3026
+ await sleep(backoffMs(attempt - 1));
3027
+ }
3028
+ try {
3029
+ sendAttempts += 1;
3030
+ const response = await fetchFn(options.ingestUrl, {
3031
+ method: "POST",
3032
+ headers: authHeaders,
3033
+ body
3034
+ });
3035
+ if (response.ok) {
3036
+ batchesSucceeded += 1;
3037
+ return "ok";
3038
+ }
3039
+ lastFailureStatusCode = response.status;
3040
+ if (response.status === 429) {
3041
+ rateLimitedResponses += 1;
3042
+ lastFailureKind = "rate_limited";
3043
+ continue;
3044
+ }
3045
+ if (response.status >= 500) {
3046
+ serverErrorResponses += 1;
3047
+ lastFailureKind = "server_error";
3048
+ continue;
3049
+ }
3050
+ batchesDropped += 1;
3051
+ lastFailureKind = "non_retryable_http";
3052
+ return "dropped";
3053
+ } catch {
3054
+ networkErrors += 1;
3055
+ lastFailureKind = "network_error";
3056
+ }
3057
+ }
3058
+ batchesFailed += 1;
3059
+ return "failed";
3060
+ };
3061
+ const flush = async (mode = "normal") => {
3062
+ options.beforeFlush?.();
3063
+ if (!enabled || queue.length === 0) return;
3064
+ if (flushing) {
3065
+ if (mode === "unload") return;
3066
+ await flushing;
3067
+ return;
3068
+ }
3069
+ flushing = (async () => {
3070
+ flushesStarted += 1;
3071
+ while (queue.length > 0) {
3072
+ if (mode === "normal" && !isOnline()) return;
3073
+ const take = takeDelivery(mode);
3074
+ queuedBytes = queue.reduce((sum, item) => sum + item.bytes, 0);
3075
+ const events = take.flatMap((item) => item.kind === "event" ? [item.event] : []);
3076
+ const summaries = take.flatMap((item) => item.kind === "summary" ? [item.summary] : []);
3077
+ const idempotencyKey = take[0]?.deliveryKey ?? nextDeliveryKey();
3078
+ const outcome = await sendBatch(events, summaries, mode, idempotencyKey);
3079
+ if (outcome === "failed" && mode === "normal") {
3080
+ queue.unshift(...take);
3081
+ queuedBytes = queue.reduce((sum, item) => sum + item.bytes, 0);
3082
+ return;
3083
+ }
3084
+ if (mode === "unload") {
3085
+ if (outcome !== "ok" && queue.length > 0) {
3086
+ droppedKeepalive += queue.length;
3087
+ droppedQueued += queue.length;
3088
+ queue.length = 0;
3089
+ queuedBytes = 0;
3090
+ }
3091
+ if (outcome !== "ok") return;
3092
+ }
3093
+ }
3094
+ })();
3095
+ try {
3096
+ await flushing;
3097
+ } finally {
3098
+ flushing = void 0;
3099
+ }
3100
+ };
3101
+ const startTimer = () => {
3102
+ if (timer || !enabled) return;
3103
+ timer = setInterval(() => {
3104
+ void flush("normal");
3105
+ }, flushIntervalMs);
3106
+ };
3107
+ const installLifecycle = () => {
3108
+ const target = Reflect.get(globalThis, "window");
3109
+ if (!target || typeof target !== "object" || !("addEventListener" in target)) return;
3110
+ const onHide = () => {
3111
+ void flush("unload");
3112
+ };
3113
+ const onOnline = () => {
3114
+ void flush("normal");
3115
+ };
3116
+ try {
3117
+ target.addEventListener("pagehide", onHide);
3118
+ target.addEventListener("online", onOnline);
3119
+ } catch {
3120
+ return;
3121
+ }
3122
+ uninstallLifecycle = () => {
3123
+ try {
3124
+ target.removeEventListener("pagehide", onHide);
3125
+ target.removeEventListener("online", onOnline);
3126
+ } catch {
3127
+ }
3128
+ };
3129
+ };
3130
+ if (enabled) {
3131
+ startTimer();
3132
+ installLifecycle();
3133
+ }
3134
+ return {
3135
+ enqueue,
3136
+ enqueueSummary,
3137
+ flush: async (_timeoutMs) => {
3138
+ try {
3139
+ await flush("normal");
3140
+ } catch {
3141
+ }
3142
+ },
3143
+ close: async () => {
3144
+ closed = true;
3145
+ if (timer) clearInterval(timer);
3146
+ timer = void 0;
3147
+ uninstallLifecycle();
3148
+ try {
3149
+ await flush("unload");
3150
+ } catch {
3151
+ }
3152
+ queue.length = 0;
3153
+ queuedBytes = 0;
3154
+ },
3155
+ getStats: () => ({
3156
+ queued: queue.length,
3157
+ enqueued,
3158
+ droppedIncoming,
3159
+ droppedQueued,
3160
+ droppedKeepalive,
3161
+ droppedTotal: droppedIncoming + droppedQueued,
3162
+ flushesStarted,
3163
+ sendAttempts,
3164
+ sendRetries,
3165
+ batchesSucceeded,
3166
+ batchesDropped,
3167
+ batchesFailed,
3168
+ networkErrors,
3169
+ rateLimitedResponses,
3170
+ serverErrorResponses,
3171
+ lastFailureKind,
3172
+ lastFailureStatusCode
3173
+ })
3174
+ };
3175
+ };
3176
+
3177
+ // src/install-browser-handlers.ts
3178
+ var RASPUTIN_WINDOW_LISTENER = Symbol.for("rasputin.browser.listener.v1");
3179
+ var registered = [];
3180
+ var uninstallShared;
3181
+ var seenObjects = /* @__PURE__ */ new WeakSet();
3182
+ var seenPrimitives = /* @__PURE__ */ new Map();
3183
+ var PRIMITIVE_DEDUP_MS = 2e3;
3184
+ var tagListener = (listener) => {
3185
+ Object.defineProperty(listener, RASPUTIN_WINDOW_LISTENER, { value: true });
3186
+ return listener;
3187
+ };
3188
+ var primitiveKey = (value) => {
3189
+ if (typeof value === "string") return `s:${value}`;
3190
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
3191
+ return `${typeof value}:${String(value)}`;
3192
+ }
3193
+ if (value === null) return "null";
3194
+ if (value === void 0) return "undefined";
3195
+ return `other:${String(value)}`;
3196
+ };
3197
+ var shouldCaptureBrowserError = (error) => {
3198
+ if (error && typeof error === "object") {
3199
+ if (seenObjects.has(error)) return false;
3200
+ seenObjects.add(error);
3201
+ return true;
3202
+ }
3203
+ const key = primitiveKey(error);
3204
+ const now = Date.now();
3205
+ const previous = seenPrimitives.get(key);
3206
+ if (previous !== void 0 && now - previous < PRIMITIVE_DEDUP_MS) return false;
3207
+ seenPrimitives.set(key, now);
3208
+ if (seenPrimitives.size > 64) {
3209
+ for (const [item, at] of seenPrimitives) {
3210
+ if (now - at >= PRIMITIVE_DEDUP_MS) seenPrimitives.delete(item);
3211
+ }
3212
+ }
3213
+ return true;
3214
+ };
3215
+ var captureOnAll = (error) => {
3216
+ for (const entry of registered) {
3217
+ try {
3218
+ entry.client.captureException(error);
3219
+ } catch {
3220
+ }
3221
+ }
3222
+ };
3223
+ var installSharedListeners = () => {
3224
+ if (uninstallShared) return;
3225
+ const target = Reflect.get(globalThis, "window");
3226
+ if (!target || typeof target !== "object" || !("addEventListener" in target)) return;
3227
+ const onError = tagListener((event) => {
3228
+ if (!event || typeof event !== "object") return;
3229
+ const error = "error" in event ? Reflect.get(event, "error") : void 0;
3230
+ if (error !== void 0 && error !== null) {
3231
+ captureOnAll(error);
3232
+ return;
3233
+ }
3234
+ const message = "message" in event ? Reflect.get(event, "message") : void 0;
3235
+ captureOnAll(typeof message === "string" ? message : "Script error.");
3236
+ });
3237
+ const onRejection = tagListener((event) => {
3238
+ if (!event || typeof event !== "object") return;
3239
+ const reason = "reason" in event ? Reflect.get(event, "reason") : void 0;
3240
+ captureOnAll(reason);
3241
+ });
3242
+ try {
3243
+ target.addEventListener("error", onError);
3244
+ target.addEventListener("unhandledrejection", onRejection);
3245
+ } catch {
3246
+ return;
3247
+ }
3248
+ uninstallShared = () => {
3249
+ try {
3250
+ target.removeEventListener("error", onError);
3251
+ target.removeEventListener("unhandledrejection", onRejection);
3252
+ } catch {
3253
+ }
3254
+ uninstallShared = void 0;
3255
+ };
3256
+ };
3257
+ var installBrowserHandlers = (client) => {
3258
+ const entry = { client };
3259
+ registered.push(entry);
3260
+ installSharedListeners();
3261
+ return () => {
3262
+ const index = registered.indexOf(entry);
3263
+ if (index >= 0) registered.splice(index, 1);
3264
+ if (registered.length === 0) uninstallShared?.();
3265
+ };
3266
+ };
3267
+
3268
+ // src/map-runtime-state.ts
3269
+ var compactName = (moduleIndex, planIndex) => {
3270
+ if (moduleIndex === void 0 || planIndex === void 0) return void 0;
3271
+ return `${moduleIndex}.${planIndex}`;
3272
+ };
3273
+ var mapFunction = (fn) => {
3274
+ const compact = compactName(fn.moduleIndex, fn.planIndex);
3275
+ const definition = {
3276
+ functionId: compact ?? fn.functionId,
3277
+ name: fn.name || compact || fn.functionId
3278
+ };
3279
+ if (fn.packageRelativePath) {
3280
+ definition.source = {
3281
+ packageRelativePath: fn.packageRelativePath,
3282
+ ...fn.packageName ? { packageName: fn.packageName } : {},
3283
+ ...fn.line ? { line: fn.line } : {},
3284
+ ...fn.column ? { column: fn.column } : {}
3285
+ };
3286
+ }
3287
+ if (fn.moduleIndex !== void 0) definition.moduleIndex = fn.moduleIndex;
3288
+ if (fn.planIndex !== void 0) definition.planIndex = fn.planIndex;
3289
+ return definition;
3290
+ };
3291
+ var measuredDurationMs = (startedAt, endedAt) => startedAt !== void 0 && endedAt > startedAt ? endedAt - startedAt : void 0;
3292
+ var mapEvents = (events) => {
3293
+ const enteredAt = /* @__PURE__ */ new Map();
3294
+ const mapped = [];
3295
+ for (const event of events) {
3296
+ if (event.type === "function_enter") {
3297
+ enteredAt.set(event.callId, event.timestampMs);
3298
+ mapped.push({
3299
+ type: "function_enter",
3300
+ callId: event.callId,
3301
+ functionId: event.functionId,
3302
+ timestampMs: event.timestampMs,
3303
+ ...event.args !== void 0 ? { args: event.args } : {},
3304
+ ...event.argsOmitted ? { argsOmitted: true } : {},
3305
+ ...event.parentCallId === void 0 ? {} : { parentCallId: event.parentCallId },
3306
+ ...event.materialized === "throw-time" ? { capturedAt: "throw" } : {},
3307
+ provenance: event.provenance
3308
+ });
3309
+ continue;
3310
+ }
3311
+ if (event.type === "function_exit") {
3312
+ const durationMs = measuredDurationMs(enteredAt.get(event.callId), event.timestampMs);
3313
+ mapped.push({
3314
+ type: "function_exit",
3315
+ callId: event.callId,
3316
+ timestampMs: event.timestampMs,
3317
+ ...durationMs === void 0 ? {} : { durationMs },
3318
+ ...event.returnValue === void 0 ? {} : { returnValue: event.returnValue }
3319
+ });
3320
+ continue;
3321
+ }
3322
+ if (event.type === "function_throw") {
3323
+ const durationMs = measuredDurationMs(enteredAt.get(event.callId), event.timestampMs);
3324
+ mapped.push({
3325
+ type: "function_throw",
3326
+ callId: event.callId,
3327
+ timestampMs: event.timestampMs,
3328
+ ...durationMs === void 0 ? {} : { durationMs },
3329
+ ...mapThrowError(event)
3330
+ });
3331
+ continue;
3332
+ }
3333
+ if (event.type === "function_calls_suppressed") {
3334
+ mapped.push({
3335
+ type: "function_calls_suppressed",
3336
+ functionId: event.functionId,
3337
+ timestampMs: event.timestampMs,
3338
+ suppressedCallCount: event.suppressedCallCount,
3339
+ ...event.parentCallId === void 0 ? {} : { parentCallId: event.parentCallId }
3340
+ });
3341
+ continue;
3342
+ }
3343
+ if (event.type === "awaited") {
3344
+ mapped.push({
3345
+ type: "awaited",
3346
+ callId: event.callId,
3347
+ timestampMs: event.timestampMs,
3348
+ ...event.value === void 0 ? {} : { value: event.value },
3349
+ ...event.omitted ? { omitted: true } : {}
3350
+ });
3351
+ continue;
3352
+ }
3353
+ mapped.push({
3354
+ type: "expression_observed",
3355
+ siteId: event.siteId,
3356
+ timestampMs: event.timestampMs,
3357
+ value: event.value,
3358
+ ...event.callId === void 0 ? {} : { callId: event.callId }
3359
+ });
3360
+ }
3361
+ return mapped;
3362
+ };
3363
+ var mapThrowError = (event) => {
3364
+ if (event.error) return { error: event.error };
3365
+ const error = {};
3366
+ if (event.errorName !== void 0) error.name = event.errorName;
3367
+ if (event.errorMessage !== void 0) error.message = event.errorMessage;
3368
+ if (error.name === void 0 && error.message === void 0) return {};
3369
+ return { error };
3370
+ };
3371
+ var mapBrowserRuntimeState = (snapshot, buildId) => {
3372
+ const valuesOff = snapshot.captureProfile === "values-off";
3373
+ const helpersSparse = snapshot.captureProfile === "helpers-sparse";
3374
+ const notes = snapshot.provenanceNotes;
3375
+ const valuesOmitted = notes.includes("value observations omitted");
3376
+ const structuralTruncated = notes.includes("structural events truncated");
3377
+ const holdClosed = notes.includes("closed by hold budget");
3378
+ const openCapped = notes.includes("maximum open executions reached");
3379
+ const boundariesPartial = structuralTruncated || holdClosed || openCapped || helpersSparse;
3380
+ const valuesPartial = valuesOmitted || valuesOff;
3381
+ const state = {
3382
+ version: 4,
3383
+ captureProfile: {
3384
+ source: "automatic",
3385
+ functionBoundaries: boundariesPartial ? "partial" : "full",
3386
+ functionValues: snapshot.captureMode === "values" && !valuesOff ? valuesOmitted ? "selective" : "full" : "none",
3387
+ timings: "partial"
3388
+ },
3389
+ executionId: snapshot.executionId,
3390
+ execution: {
3391
+ kind: snapshot.kind,
3392
+ ...snapshot.eventType ? { name: snapshot.eventType, eventType: snapshot.eventType } : {}
3393
+ },
3394
+ functions: snapshot.functions.map(mapFunction),
3395
+ events: mapEvents(snapshot.events),
3396
+ browser: {
3397
+ degradation: snapshot.captureProfile ?? "normal"
3398
+ }
3399
+ };
3400
+ if (snapshot.startedAtIso) state.startedAt = snapshot.startedAtIso;
3401
+ if (snapshot.durationMs > 0) state.durationMs = snapshot.durationMs;
3402
+ if (buildId) {
3403
+ state.buildId = buildId;
3404
+ state.browser = {
3405
+ ...state.browser,
3406
+ buildId
3407
+ };
3408
+ }
3409
+ if (valuesOff || helpersSparse || valuesPartial || boundariesPartial) {
3410
+ state.capture = {
3411
+ completeness: "partial",
3412
+ reason: valuesOff ? "values_off" : helpersSparse ? "helpers_sparse" : holdClosed ? "long_lived_execution_budget" : openCapped ? "open_execution_budget" : valuesOmitted ? "value_observations_omitted" : "structural_events_truncated"
3413
+ };
3414
+ }
3415
+ if (snapshot.historyLinks.length > 0) {
3416
+ state.historyLinks = snapshot.historyLinks.map((link) => ({
3417
+ executionId: link.executionId,
3418
+ relationship: link.relationship,
3419
+ ...link.key === void 0 ? {} : { key: link.key }
3420
+ }));
3421
+ }
3422
+ if (snapshot.provenanceNotes.length > 0) state.notes = snapshot.provenanceNotes;
3423
+ return state;
3424
+ };
3425
+
3426
+ // src/sdk-meta.ts
3427
+ var SDK_NAME = "@rasputin-ai/browser";
3428
+ var SDK_VERSION = "0.6.0";
3429
+
3430
+ // src/rasputin-init.ts
3431
+ var DEFAULT_API_URL = "https://api.rasputinai.dev";
3432
+ var SLOT = Symbol.for("rasputin.browser.client.v1");
3433
+ var emptyTransport = () => ({
3434
+ queued: 0,
3435
+ enqueued: 0,
3436
+ droppedIncoming: 0,
3437
+ droppedQueued: 0,
3438
+ droppedKeepalive: 0,
3439
+ droppedTotal: 0,
3440
+ flushesStarted: 0,
3441
+ sendAttempts: 0,
3442
+ sendRetries: 0,
3443
+ batchesSucceeded: 0,
3444
+ batchesDropped: 0,
3445
+ batchesFailed: 0,
3446
+ networkErrors: 0,
3447
+ rateLimitedResponses: 0,
3448
+ serverErrorResponses: 0,
3449
+ lastFailureKind: null,
3450
+ lastFailureStatusCode: null
3451
+ });
3452
+ var emptyRecorder = (active) => ({
3453
+ active,
3454
+ functionCallsObserved: 0,
3455
+ functionCallsCaptured: 0,
3456
+ successfulExecutionsDiscarded: 0,
3457
+ errorSnapshots: 0,
3458
+ capturedExecutions: 0,
3459
+ degradedExecutions: 0
3460
+ });
3461
+ var emptyInstrumentation = () => ({
3462
+ functions: 0,
3463
+ full: 0,
3464
+ structure: 0,
3465
+ skip: 0,
3466
+ reactiveSparse: 0,
3467
+ verificationFailed: 0
3468
+ });
3469
+ var readBuildStats = () => {
3470
+ const value = Reflect.get(globalThis, "__RASPUTIN_BUILD_STATS__");
3471
+ if (!value || typeof value !== "object") return emptyInstrumentation();
3472
+ const numberAt = (key) => {
3473
+ const item = Reflect.get(value, key);
3474
+ return typeof item === "number" && Number.isFinite(item) ? item : 0;
3475
+ };
3476
+ return {
3477
+ functions: numberAt("functions"),
3478
+ full: numberAt("full"),
3479
+ structure: numberAt("structure"),
3480
+ skip: numberAt("skip"),
3481
+ reactiveSparse: numberAt("reactiveSparse"),
3482
+ verificationFailed: numberAt("verificationFailed")
3483
+ };
3484
+ };
3485
+ var readGlobalString = (key) => {
3486
+ const value = Reflect.get(globalThis, key);
3487
+ return typeof value === "string" ? value.trim() : "";
3488
+ };
3489
+ var resolveDsn = (options) => {
3490
+ const explicit = typeof options.dsn === "string" ? options.dsn.trim() : "";
3491
+ if (explicit) return explicit;
3492
+ return readGlobalString("__RASPUTIN_DSN__");
3493
+ };
3494
+ var resolveBuildId = (options) => {
3495
+ const explicit = typeof options.buildId === "string" ? options.buildId.trim() : "";
3496
+ if (explicit) return explicit;
3497
+ return readGlobalString("__RASPUTIN_BUILD_ID__");
3498
+ };
3499
+ var resolveRelease = (options) => {
3500
+ const explicit = typeof options.release === "string" ? options.release.trim() : "";
3501
+ if (explicit) return explicit;
3502
+ return readGlobalString("__RASPUTIN_RELEASE__");
3503
+ };
3504
+ var resolveEnvironment = (options) => {
3505
+ const explicit = typeof options.environment === "string" ? options.environment.trim() : "";
3506
+ if (explicit) return explicit;
3507
+ return readGlobalString("__RASPUTIN_ENVIRONMENT__");
3508
+ };
3509
+ var activationReason = (options, dsn, release, environment) => {
3510
+ if (options.enabled === false) return "disabled";
3511
+ if (!dsn) return "missing_dsn";
3512
+ if (!parseDsn(dsn)) return "invalid_dsn";
3513
+ if (!release) return "missing_release";
3514
+ if (!environment) return "missing_environment";
3515
+ const windowRef = Reflect.get(globalThis, "window");
3516
+ const fetchRef = Reflect.get(globalThis, "fetch");
3517
+ if (typeof fetchRef !== "function" && !(windowRef && typeof windowRef === "object")) {
3518
+ if (!options.fetch) return "unsupported_environment";
3519
+ }
3520
+ return "ok";
3521
+ };
3522
+ var inertClient = (configuration) => ({
3523
+ captureException: () => {
3524
+ },
3525
+ flush: async () => {
3526
+ },
3527
+ close: async () => {
3528
+ },
3529
+ getStats: () => ({
3530
+ enabled: false,
3531
+ release: configuration.release,
3532
+ buildId: configuration.buildId,
3533
+ transport: emptyTransport(),
3534
+ recorder: emptyRecorder(false),
3535
+ instrumentation: emptyInstrumentation()
3536
+ }),
3537
+ verifyConfiguration: () => configuration
3538
+ });
3539
+ var recorderStats = () => {
3540
+ const state = getBrowserRuntimeState();
3541
+ return {
3542
+ active: true,
3543
+ functionCallsObserved: state.stats.functionCallsObserved,
3544
+ functionCallsCaptured: state.stats.functionCallsCaptured,
3545
+ successfulExecutionsDiscarded: state.stats.successfulExecutionsDiscarded,
3546
+ errorSnapshots: state.stats.errorSnapshots,
3547
+ capturedExecutions: state.stats.errorSnapshots,
3548
+ degradedExecutions: state.snapshots.filter(
3549
+ (snapshot) => snapshot.captureProfile === "values-off" || snapshot.captureProfile === "helpers-sparse"
3550
+ ).length
3551
+ };
3552
+ };
3553
+ var getSlot = () => {
3554
+ const value = Reflect.get(globalThis, SLOT);
3555
+ if (!value || typeof value !== "object") return void 0;
3556
+ if (!("refs" in value) || !("client" in value)) return void 0;
3557
+ return value;
3558
+ };
3559
+ var setSlot = (slot) => {
3560
+ if (slot) Reflect.set(globalThis, SLOT, slot);
3561
+ else Reflect.deleteProperty(globalThis, SLOT);
3562
+ };
3563
+ var RasputinInit = (options) => {
3564
+ try {
3565
+ const existing = getSlot();
3566
+ if (existing) {
3567
+ existing.refs += 1;
3568
+ return existing.client;
3569
+ }
3570
+ const internal = options ?? {};
3571
+ const dsn = resolveDsn(internal);
3572
+ const parsed = parseDsn(dsn);
3573
+ const buildId = resolveBuildId(internal);
3574
+ const release = resolveRelease(internal);
3575
+ const environment = resolveEnvironment(internal);
3576
+ const reason = activationReason(internal, dsn, release, environment);
3577
+ const configuration = {
3578
+ enabled: reason === "ok",
3579
+ reason,
3580
+ recorder: reason === "ok" ? "active" : "inert",
3581
+ handlersInstalled: false,
3582
+ ingestEnabled: false,
3583
+ release: release || null,
3584
+ buildId: buildId || null,
3585
+ buildIdentity: buildId ? "injected" : "missing",
3586
+ environment: environment || null
3587
+ };
3588
+ if (reason !== "ok" || !parsed) {
3589
+ return inertClient(configuration);
3590
+ }
3591
+ const ingestEnabled = internal.ingest !== false && !isLocalDsnPublicKey(parsed.publicKey);
3592
+ const apiUrl = (internal.apiUrl ?? parsed.apiUrl ?? DEFAULT_API_URL).replace(/\/+$/, "");
3593
+ RasputinBrowserInit({
3594
+ settlingMs: 80,
3595
+ holdMs: 3e4,
3596
+ maxStructuralEvents: 240,
3597
+ maxValueEvents: 80,
3598
+ maxCapturedCallsPerFunction: 3,
3599
+ maxOpenExecutions: 32,
3600
+ ...internal.identities ? { identities: internal.identities } : {}
3601
+ });
3602
+ const suppression = createSuppression({ capPerWindow: 1 });
3603
+ let enqueuePendingSummaries = () => {
3604
+ };
3605
+ const transport = createBrowserTransport({
3606
+ ingestUrl: `${apiUrl}/ingest`,
3607
+ publicKey: parsed.publicKey,
3608
+ sdkName: SDK_NAME,
3609
+ sdkVersion: SDK_VERSION,
3610
+ enabled: ingestEnabled,
3611
+ fetch: internal.fetch,
3612
+ beforeFlush: () => enqueuePendingSummaries()
3613
+ });
3614
+ enqueuePendingSummaries = () => {
3615
+ for (const { summary } of suppression.drainSummaries()) {
3616
+ transport.enqueueSummary(summary);
3617
+ }
3618
+ };
3619
+ const captureException = (error) => {
3620
+ try {
3621
+ if (!shouldCaptureBrowserError(error)) return;
3622
+ const frames = framesFromError(error);
3623
+ const event = createBrowserIngestEvent({
3624
+ error,
3625
+ frames,
3626
+ environment,
3627
+ release
3628
+ });
3629
+ const decision = suppression.decide({
3630
+ errorType: event.type,
3631
+ message: event.message,
3632
+ frames,
3633
+ origin: "client",
3634
+ code: event.code,
3635
+ environment,
3636
+ release
3637
+ });
3638
+ if (decision.action !== "send") return;
3639
+ const snapshot = getBrowserErrorState(error);
3640
+ if (snapshot) {
3641
+ event.runtime_state = mapBrowserRuntimeState(snapshot, buildId || void 0);
3642
+ }
3643
+ if (ingestEnabled) transport.enqueue(event);
3644
+ } catch {
3645
+ }
3646
+ };
3647
+ let uninstallHandlers = () => {
3648
+ };
3649
+ if (internal.installGlobalHandlers !== false) {
3650
+ uninstallHandlers = installBrowserHandlers({ captureException });
3651
+ configuration.handlersInstalled = true;
3652
+ }
3653
+ configuration.ingestEnabled = ingestEnabled;
3654
+ const client = {
3655
+ captureException,
3656
+ flush: async (timeoutMs) => {
3657
+ await transport.flush(timeoutMs);
3658
+ },
3659
+ close: async () => {
3660
+ const slot = getSlot();
3661
+ if (slot && slot.client === client) {
3662
+ slot.refs -= 1;
3663
+ if (slot.refs > 0) return;
3664
+ setSlot(void 0);
3665
+ }
3666
+ uninstallHandlers();
3667
+ uninstallBrowserRuntime();
3668
+ await transport.close();
3669
+ },
3670
+ getStats: () => ({
3671
+ enabled: true,
3672
+ release: release || null,
3673
+ buildId: buildId || null,
3674
+ transport: transport.getStats(),
3675
+ recorder: recorderStats(),
3676
+ instrumentation: readBuildStats()
3677
+ }),
3678
+ verifyConfiguration: () => configuration
3679
+ };
3680
+ setSlot({ refs: 1, client });
3681
+ if (internal.logSuccess !== false) {
3682
+ console.info("[rasputin] browser SDK ready", {
3683
+ environment,
3684
+ release: release || null,
3685
+ buildId: buildId || null,
3686
+ ingest: ingestEnabled
3687
+ });
3688
+ }
3689
+ return client;
3690
+ } catch {
3691
+ return inertClient({
3692
+ enabled: false,
3693
+ reason: "unsupported_environment",
3694
+ recorder: "inert",
3695
+ handlersInstalled: false,
3696
+ ingestEnabled: false,
3697
+ release: null,
3698
+ buildId: null,
3699
+ buildIdentity: "missing",
3700
+ environment: null
3701
+ });
3702
+ }
3703
+ };
3704
+ export {
3705
+ RasputinInit
3706
+ };