@lvce-editor/renderer-process 30.57.1 → 30.58.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.
@@ -1,219 +1,1164 @@
1
- const version = 1;
2
- const maxEventBytes = 750_000;
3
- const maxSessionBytes = 64 * 1024 * 1024;
4
- const bytes = (value) => new TextEncoder().encode(JSON.stringify(value)).byteLength;
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // ../session-replay-worker/src/api/transfer.ts
8
+ var getTransferrables = (value) => {
9
+ const seen = /* @__PURE__ */ new Set();
10
+ const result = /* @__PURE__ */ new Set();
11
+ const visit = (item) => {
12
+ if (!item || typeof item !== "object" || seen.has(item)) return;
13
+ seen.add(item);
14
+ if (Object.prototype.toString.call(item) === "[object ArrayBuffer]") result.add(item);
15
+ else if (ArrayBuffer.isView(item)) visit(item.buffer);
16
+ else if (["MessagePort", "OffscreenCanvas", "ImageBitmap", "AudioData", "VideoFrame", "ReadableStream", "WritableStream", "TransformStream"].some(
17
+ (name) => typeof globalThis[name] === "function" && item instanceof globalThis[name]
18
+ ))
19
+ result.add(item);
20
+ else if (Object.prototype.toString.call(item) === "[object Map]") {
21
+ for (const [key, entry] of item) {
22
+ visit(key);
23
+ visit(entry);
24
+ }
25
+ } else if (Object.prototype.toString.call(item) === "[object Set]") {
26
+ for (const entry of item) visit(entry);
27
+ } else for (const entry of Object.values(item)) visit(entry);
28
+ };
29
+ visit(value);
30
+ return [...result];
31
+ };
32
+
33
+ // ../../node_modules/@lvce-editor/constants/dist/parts/ElementTagMap/ElementTagMap.js
34
+ var ElementTagMap_exports = {};
35
+ __export(ElementTagMap_exports, {
36
+ getElementTag: () => getElementTag
37
+ });
38
+
39
+ // ../../node_modules/@lvce-editor/constants/dist/parts/ElementTags/ElementTags.js
40
+ var Audio = "audio";
41
+ var Button = "button";
42
+ var Col = "col";
43
+ var ColGroup = "colgroup";
44
+ var Del = "del";
45
+ var Div = "div";
46
+ var H1 = "h1";
47
+ var H2 = "h2";
48
+ var H3 = "h3";
49
+ var H4 = "h4";
50
+ var H5 = "h5";
51
+ var H6 = "h6";
52
+ var I = "i";
53
+ var Img = "img";
54
+ var Input = "input";
55
+ var Ins = "ins";
56
+ var Kbd = "kbd";
57
+ var Span = "span";
58
+ var Table = "table";
59
+ var TBody = "tbody";
60
+ var Td = "td";
61
+ var Th = "th";
62
+ var THead = "thead";
63
+ var Tr = "tr";
64
+ var Article = "article";
65
+ var Aside = "aside";
66
+ var Footer = "footer";
67
+ var Header = "header";
68
+ var Nav = "nav";
69
+ var Section = "section";
70
+ var Search = "search";
71
+ var Dd = "dd";
72
+ var Dl = "dl";
73
+ var Figcaption = "figcaption";
74
+ var Figure = "figure";
75
+ var Hr = "hr";
76
+ var Li = "li";
77
+ var Ol = "ol";
78
+ var P = "p";
79
+ var Pre = "pre";
80
+ var A = "a";
81
+ var Br = "br";
82
+ var Cite = "cite";
83
+ var Data = "data";
84
+ var Time = "time";
85
+ var Tfoot = "tfoot";
86
+ var Ul = "ul";
87
+ var Video = "video";
88
+ var TextArea = "textarea";
89
+ var Select = "select";
90
+ var Option = "option";
91
+ var Code = "code";
92
+ var Label = "label";
93
+ var Dt = "dt";
94
+ var Iframe = "iframe";
95
+ var Main = "main";
96
+ var Em = "em";
97
+ var Strong = "strong";
98
+ var Style = "style";
99
+ var Html = "html";
100
+ var Head = "head";
101
+ var Title = "title";
102
+ var Meta = "meta";
103
+ var Canvas = "canvas";
104
+ var Circle = "circle";
105
+ var Form = "form";
106
+ var Defs = "defs";
107
+ var Ellipse = "ellipse";
108
+ var G = "g";
109
+ var Line = "line";
110
+ var Path = "path";
111
+ var Polygon = "polygon";
112
+ var Polyline = "polyline";
113
+ var Quote = "quote";
114
+ var BlockQuote = "blockquote";
115
+ var Rect = "rect";
116
+ var Svg = "svg";
117
+ var Use = "use";
118
+
119
+ // ../../node_modules/@lvce-editor/constants/dist/parts/VirtualDomElements/VirtualDomElements.js
120
+ var VirtualDomElements_exports = {};
121
+ __export(VirtualDomElements_exports, {
122
+ A: () => A2,
123
+ Abbr: () => Abbr,
124
+ Article: () => Article2,
125
+ Aside: () => Aside2,
126
+ Audio: () => Audio2,
127
+ BlockQuote: () => BlockQuote2,
128
+ Br: () => Br2,
129
+ Button: () => Button2,
130
+ Canvas: () => Canvas2,
131
+ Circle: () => Circle2,
132
+ Cite: () => Cite2,
133
+ Code: () => Code2,
134
+ Col: () => Col2,
135
+ ColGroup: () => ColGroup2,
136
+ Data: () => Data2,
137
+ Dd: () => Dd2,
138
+ Defs: () => Defs2,
139
+ Del: () => Del2,
140
+ Div: () => Div2,
141
+ Dl: () => Dl2,
142
+ Dt: () => Dt2,
143
+ Ellipse: () => Ellipse2,
144
+ Em: () => Em2,
145
+ Figcaption: () => Figcaption2,
146
+ Figure: () => Figure2,
147
+ Footer: () => Footer2,
148
+ Form: () => Form2,
149
+ G: () => G2,
150
+ H1: () => H12,
151
+ H2: () => H22,
152
+ H3: () => H32,
153
+ H4: () => H42,
154
+ H5: () => H52,
155
+ H6: () => H62,
156
+ Head: () => Head2,
157
+ Header: () => Header2,
158
+ Hr: () => Hr2,
159
+ Html: () => Html2,
160
+ I: () => I2,
161
+ Iframe: () => Iframe2,
162
+ Img: () => Img2,
163
+ Input: () => Input2,
164
+ Ins: () => Ins2,
165
+ Kbd: () => Kbd2,
166
+ Label: () => Label2,
167
+ Li: () => Li2,
168
+ Line: () => Line2,
169
+ Main: () => Main2,
170
+ Meta: () => Meta2,
171
+ Nav: () => Nav2,
172
+ Ol: () => Ol2,
173
+ Option: () => Option2,
174
+ P: () => P2,
175
+ Path: () => Path2,
176
+ Polygon: () => Polygon2,
177
+ Polyline: () => Polyline2,
178
+ Pre: () => Pre2,
179
+ Quote: () => Quote2,
180
+ Rect: () => Rect2,
181
+ Reference: () => Reference,
182
+ Root: () => Root,
183
+ Search: () => Search2,
184
+ Section: () => Section2,
185
+ Select: () => Select2,
186
+ Span: () => Span2,
187
+ Strong: () => Strong2,
188
+ Style: () => Style2,
189
+ Svg: () => Svg2,
190
+ TBody: () => TBody2,
191
+ THead: () => THead2,
192
+ Table: () => Table2,
193
+ Td: () => Td2,
194
+ Text: () => Text,
195
+ TextArea: () => TextArea2,
196
+ Tfoot: () => Tfoot2,
197
+ Th: () => Th2,
198
+ Time: () => Time2,
199
+ Title: () => Title2,
200
+ Tr: () => Tr2,
201
+ Ul: () => Ul2,
202
+ Use: () => Use2,
203
+ Video: () => Video2
204
+ });
205
+ var Audio2 = 0;
206
+ var Button2 = 1;
207
+ var Col2 = 2;
208
+ var ColGroup2 = 3;
209
+ var Div2 = 4;
210
+ var H12 = 5;
211
+ var Input2 = 6;
212
+ var Kbd2 = 7;
213
+ var Span2 = 8;
214
+ var Table2 = 9;
215
+ var TBody2 = 10;
216
+ var Td2 = 11;
217
+ var Text = 12;
218
+ var Th2 = 13;
219
+ var THead2 = 14;
220
+ var Tr2 = 15;
221
+ var I2 = 16;
222
+ var Img2 = 17;
223
+ var Root = 0;
224
+ var Ins2 = 20;
225
+ var Del2 = 21;
226
+ var H22 = 22;
227
+ var H32 = 23;
228
+ var H42 = 24;
229
+ var H52 = 25;
230
+ var H62 = 26;
231
+ var Article2 = 27;
232
+ var Aside2 = 28;
233
+ var Footer2 = 29;
234
+ var Header2 = 30;
235
+ var Nav2 = 40;
236
+ var Section2 = 41;
237
+ var Search2 = 42;
238
+ var Dd2 = 43;
239
+ var Dl2 = 44;
240
+ var Figcaption2 = 45;
241
+ var Figure2 = 46;
242
+ var Hr2 = 47;
243
+ var Li2 = 48;
244
+ var Ol2 = 49;
245
+ var P2 = 50;
246
+ var Pre2 = 51;
247
+ var A2 = 53;
248
+ var Abbr = 54;
249
+ var Br2 = 55;
250
+ var Cite2 = 56;
251
+ var Data2 = 57;
252
+ var Time2 = 58;
253
+ var Tfoot2 = 59;
254
+ var Ul2 = 60;
255
+ var Video2 = 61;
256
+ var TextArea2 = 62;
257
+ var Select2 = 63;
258
+ var Option2 = 64;
259
+ var Code2 = 65;
260
+ var Label2 = 66;
261
+ var Dt2 = 67;
262
+ var Iframe2 = 68;
263
+ var Main2 = 69;
264
+ var Strong2 = 70;
265
+ var Em2 = 71;
266
+ var Style2 = 72;
267
+ var Html2 = 73;
268
+ var Head2 = 74;
269
+ var Title2 = 75;
270
+ var Meta2 = 76;
271
+ var Canvas2 = 77;
272
+ var Form2 = 78;
273
+ var BlockQuote2 = 79;
274
+ var Quote2 = 80;
275
+ var Circle2 = 81;
276
+ var Defs2 = 82;
277
+ var Ellipse2 = 83;
278
+ var G2 = 84;
279
+ var Line2 = 85;
280
+ var Path2 = 86;
281
+ var Polygon2 = 87;
282
+ var Polyline2 = 88;
283
+ var Rect2 = 89;
284
+ var Svg2 = 90;
285
+ var Use2 = 91;
286
+ var Reference = 100;
287
+
288
+ // ../../node_modules/@lvce-editor/constants/dist/parts/ElementTagMap/ElementTagMap.js
289
+ var elementTagMap = {
290
+ [A2]: A,
291
+ [Article2]: Article,
292
+ [Aside2]: Aside,
293
+ [Audio2]: Audio,
294
+ [BlockQuote2]: BlockQuote,
295
+ [Br2]: Br,
296
+ [Button2]: Button,
297
+ [Canvas2]: Canvas,
298
+ [Circle2]: Circle,
299
+ [Cite2]: Cite,
300
+ [Code2]: Code,
301
+ [Col2]: Col,
302
+ [ColGroup2]: ColGroup,
303
+ [Data2]: Data,
304
+ [Dd2]: Dd,
305
+ [Defs2]: Defs,
306
+ [Del2]: Del,
307
+ [Div2]: Div,
308
+ [Dl2]: Dl,
309
+ [Dt2]: Dt,
310
+ [Ellipse2]: Ellipse,
311
+ [Em2]: Em,
312
+ [Figcaption2]: Figcaption,
313
+ [Figure2]: Figure,
314
+ [Footer2]: Footer,
315
+ [Form2]: Form,
316
+ [G2]: G,
317
+ [H12]: H1,
318
+ [H22]: H2,
319
+ [H32]: H3,
320
+ [H42]: H4,
321
+ [H52]: H5,
322
+ [H62]: H6,
323
+ [Head2]: Head,
324
+ [Header2]: Header,
325
+ [Hr2]: Hr,
326
+ [Html2]: Html,
327
+ [I2]: I,
328
+ [Iframe2]: Iframe,
329
+ [Img2]: Img,
330
+ [Input2]: Input,
331
+ [Ins2]: Ins,
332
+ [Kbd2]: Kbd,
333
+ [Label2]: Label,
334
+ [Li2]: Li,
335
+ [Line2]: Line,
336
+ [Main2]: Main,
337
+ [Meta2]: Meta,
338
+ [Nav2]: Nav,
339
+ [Ol2]: Ol,
340
+ [Option2]: Option,
341
+ [P2]: P,
342
+ [Path2]: Path,
343
+ [Polygon2]: Polygon,
344
+ [Polyline2]: Polyline,
345
+ [Pre2]: Pre,
346
+ [Quote2]: Quote,
347
+ [Rect2]: Rect,
348
+ [Search2]: Search,
349
+ [Section2]: Section,
350
+ [Select2]: Select,
351
+ [Span2]: Span,
352
+ [Strong2]: Strong,
353
+ [Style2]: Style,
354
+ [Svg2]: Svg,
355
+ [Table2]: Table,
356
+ [TBody2]: TBody,
357
+ [Td2]: Td,
358
+ [TextArea2]: TextArea,
359
+ [Tfoot2]: Tfoot,
360
+ [Th2]: Th,
361
+ [THead2]: THead,
362
+ [Time2]: Time,
363
+ [Title2]: Title,
364
+ [Tr2]: Tr,
365
+ [Ul2]: Ul,
366
+ [Use2]: Use,
367
+ [Video2]: Video
368
+ };
369
+ var getElementTag = (type) => {
370
+ const elementTag = elementTagMap[type];
371
+ if (elementTag) {
372
+ return elementTag;
373
+ }
374
+ throw new Error(`element tag not found ${type}`);
375
+ };
376
+
377
+ // ../session-replay-worker/src/visualDom.ts
378
+ var element = (tag = "div") => ({ attrs: {}, children: [], tag });
379
+ var styleKeys = /* @__PURE__ */ new Set(["width", "height", "top", "left", "marginTop", "paddingLeft", "paddingRight"]);
380
+ var attributes = { className: "class", htmlFor: "for", inputType: "type" };
381
+ var setStyle = (node, key, value) => {
382
+ node.attrs ||= {};
383
+ const declarations = (node.attrs.style || "").split(";").filter((entry) => entry.trim() && entry.split(":", 1)[0].trim() !== key);
384
+ if (value !== void 0 && value !== "") declarations.push(`${key}:${typeof value === "number" || typeof value === "string" ? value : ""}`);
385
+ node.attrs.style = declarations.join(";");
386
+ };
387
+ var property = (node, key, value) => {
388
+ if (!node || /^on/i.test(key) || ["type", "childCount", "uid"].includes(key)) return;
389
+ switch (key) {
390
+ case "checked":
391
+ node.checked = Boolean(value);
392
+ return;
393
+ case "scrollLeft":
394
+ case "scrollTop": {
395
+ node.scroll ||= [0, 0];
396
+ node.scroll[key === "scrollTop" ? 1 : 0] = Number(value) || 0;
397
+ return;
398
+ }
399
+ case "value":
400
+ node.value = typeof value === "string" ? value : "";
401
+ return;
402
+ default:
403
+ break;
404
+ }
405
+ if (styleKeys.has(key)) {
406
+ setStyle(
407
+ node,
408
+ key.replaceAll(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`),
409
+ typeof value === "number" ? `${value}px` : value
410
+ );
411
+ return;
412
+ }
413
+ const attribute = attributes[key] || (key.startsWith("aria") ? `aria-${key.slice(4).toLowerCase()}` : key);
414
+ node.attrs ||= {};
415
+ if ([void 0, null, false].includes(value)) delete node.attrs[attribute];
416
+ else node.attrs[attribute] = typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : "";
417
+ };
418
+ var matches = (node, selector) => {
419
+ if (selector.startsWith(".")) return (node.attrs?.class || "").split(" ").includes(selector.slice(1));
420
+ if (selector.startsWith("#")) return node.attrs?.id === selector.slice(1);
421
+ const name = /^\[name="([^"]+)"\]$/.exec(selector);
422
+ return name ? node.attrs?.name === name[1] : node.tag === selector;
423
+ };
424
+ var find = (node, selector) => {
425
+ if (matches(node, selector)) return node;
426
+ const children = node.children || [];
427
+ for (const child of children) {
428
+ const result = find(child, selector);
429
+ if (result) return result;
430
+ }
431
+ return void 0;
432
+ };
433
+ var createVisualDom = () => {
434
+ const views = /* @__PURE__ */ new Map();
435
+ const parents = /* @__PURE__ */ new WeakMap();
436
+ const owners = /* @__PURE__ */ new WeakMap();
437
+ let nodeCount = 0;
438
+ const detach = (node) => {
439
+ const parent = parents.get(node);
440
+ if (parent) parent.children.splice(parent.children.indexOf(node), 1);
441
+ parents.delete(node);
442
+ };
443
+ const clear = (node) => {
444
+ while (node.children?.length) detach(node.children.at(0));
445
+ };
446
+ const append = (parent, node, index) => {
447
+ let depth = 0;
448
+ for (let ancestor = parent; ancestor; ancestor = parents.get(ancestor)) {
449
+ if (ancestor === node || ++depth > 150) throw new Error("Invalid replay DOM reference or depth");
450
+ }
451
+ const oldParent = parents.get(node);
452
+ const oldIndex = oldParent?.children?.indexOf(node);
453
+ if (oldParent === parent && index !== void 0 && oldIndex !== void 0 && oldIndex < index) index--;
454
+ detach(node);
455
+ parent.children ||= [];
456
+ parent.children.splice(index ?? parent.children.length, 0, node);
457
+ parents.set(node, parent);
458
+ };
459
+ const replace = (node, next) => {
460
+ if (node === next) return next;
461
+ const parent = parents.get(node);
462
+ const index = parent?.children.indexOf(node);
463
+ detach(node);
464
+ if (parent) append(parent, next, index);
465
+ const uid = owners.get(node);
466
+ if (uid !== void 0) {
467
+ views.set(uid, next);
468
+ owners.set(next, uid);
469
+ }
470
+ return next;
471
+ };
472
+ const renderNode = (value) => {
473
+ let node;
474
+ if (value.type === VirtualDomElements_exports.Reference) node = views.get(value.uid) || { text: "Reference node not found" };
475
+ else if (value.type === VirtualDomElements_exports.Text) node = { text: value.text };
476
+ else {
477
+ node = element(ElementTagMap_exports.getElementTag(value.type));
478
+ for (const [key, entry] of Object.entries(value)) property(node, key, entry);
479
+ }
480
+ return node;
481
+ };
482
+ const render = (nodes) => {
483
+ const roots = [];
484
+ const stack = [];
485
+ for (const value of nodes) {
486
+ if (++nodeCount > 2e6) throw new Error("Session replay DOM operation limit reached");
487
+ while (stack.at(-1)?.remaining === 0) stack.pop();
488
+ const node = renderNode(value);
489
+ const parent = stack.at(-1);
490
+ if (parent) {
491
+ append(parent.node, node);
492
+ parent.remaining--;
493
+ } else roots.push(node);
494
+ const childCount = value.childCount || 0;
495
+ if (!Number.isSafeInteger(childCount) || childCount < 0 || stack.length > 150) throw new Error("Invalid virtual DOM child count or depth");
496
+ if (childCount) stack.push({ node, remaining: childCount });
497
+ }
498
+ if (stack.some((entry) => entry.remaining !== 0)) throw new Error("Incomplete replay virtual DOM");
499
+ return roots;
500
+ };
501
+ const patch = (uid, patches) => {
502
+ let current = views.get(uid);
503
+ if (!current) return;
504
+ owners.set(current, uid);
505
+ const mutations = {
506
+ 1: (node, value) => {
507
+ node.text = String(value.value);
508
+ return node;
509
+ },
510
+ 10: (node, value) => parents.get(node)?.children?.[value.index],
511
+ 11: (node, value) => {
512
+ const next = views.get(value.uid);
513
+ return next ? replace(node, next) : node;
514
+ },
515
+ 2: (node, value) => {
516
+ const next = render(value.nodes)[0];
517
+ if (next) return replace(node, next);
518
+ detach(node);
519
+ return node;
520
+ },
521
+ 3: (node, value) => {
522
+ property(node, value.key, value.value);
523
+ return node;
524
+ },
525
+ 4: (node, value) => {
526
+ property(node, value.key, void 0);
527
+ return node;
528
+ },
529
+ 6: (node, value) => {
530
+ const children = render(value.nodes);
531
+ for (const child of children) append(node, child);
532
+ return node;
533
+ },
534
+ 7: (node, value) => node.children?.[value.index],
535
+ 8: (node) => parents.get(node),
536
+ 9: (node, value) => {
537
+ if (node.children?.[value.index]) detach(node.children[value.index]);
538
+ return node;
539
+ }
540
+ };
541
+ for (let index = 0; index < patches.length; index++) {
542
+ const value = patches[index];
543
+ if (value.type === 7 && value.index === current.children?.length && patches[index + 1]?.type === 2) append(current, element());
544
+ if (!mutations[value.type]) continue;
545
+ current = mutations[value.type](current, value);
546
+ if (!current) return;
547
+ }
548
+ };
549
+ return { append, clear, detach, element, find, patch, property, render, replace, views };
550
+ };
551
+
552
+ // ../session-replay-worker/src/visualState.ts
553
+ var createVisualState = (initial) => {
554
+ const frame = structuredClone(initial);
555
+ const dom = createVisualDom();
556
+ const { append, clear, detach, element: element2, find: find2, patch, property: property2, render, replace, views } = dom;
557
+ const sheets = new Map((frame.styles || []).map((text, index) => [`initial-${index}`, text]));
558
+ const calls = /* @__PURE__ */ new Map();
559
+ const batches = /* @__PURE__ */ new Map();
560
+ const setDom = (uid, nodes) => {
561
+ const view = views.get(uid);
562
+ if (!view) return;
563
+ const next = render(nodes)[0];
564
+ if (next) {
565
+ replace(view, next);
566
+ views.set(uid, next);
567
+ }
568
+ };
569
+ const apply = (method, params = []) => {
570
+ if (method && Object.hasOwn(handlers, method)) handlers[method](...params);
571
+ };
572
+ const execute = (commands2) => {
573
+ for (const [method, ...params] of commands2) apply(method, params);
574
+ };
575
+ const commit = (uid, transaction) => {
576
+ const batch = batches.get(transaction);
577
+ if (!batch || batch.uid !== uid) return;
578
+ batch.committed = true;
579
+ for (const [key, pending2] of batches) {
580
+ if (pending2.uid !== uid) continue;
581
+ if (!pending2.committed) return;
582
+ execute(pending2.commands);
583
+ batches.delete(key);
584
+ }
585
+ };
586
+ const appendView = (uid, childId, references) => {
587
+ const view = views.get(uid);
588
+ const child = views.get(childId);
589
+ if (!view || !child) return;
590
+ const nextReferences = references?.slice(references.indexOf(childId) + 1) || [];
591
+ const next = nextReferences.map((id) => views.get(id)).find((node) => node && view.children?.includes(node));
592
+ append(view, child, next ? view.children.indexOf(next) : void 0);
593
+ };
594
+ const setProperty = (uid, selector, key, value) => {
595
+ const view = views.get(uid);
596
+ const node = view && find2(view, selector);
597
+ if (!node) return;
598
+ if (key === "textContent") {
599
+ clear(node);
600
+ append(node, { text: String(value) });
601
+ } else property2(node, key, value);
602
+ };
603
+ const setBounds = (uid, left, top, width, height) => {
604
+ const view = views.get(uid);
605
+ const bounds = Object.entries({ height, left, top, width });
606
+ for (const [key, value] of bounds) property2(view, key, value);
607
+ };
608
+ const setCss = (uid, text) => {
609
+ sheets.set(uid, text);
610
+ };
611
+ const create = (_id, uid = Number(_id)) => {
612
+ views.set(uid, element2());
613
+ };
614
+ const handlers = {
615
+ "Css.addCssStyleSheet": setCss,
616
+ "Css.removeCssStyleSheet": (uid) => {
617
+ sheets.delete(uid);
618
+ },
619
+ "Viewlet.addCss": setCss,
620
+ "Viewlet.append": appendView,
621
+ "Viewlet.appendToBody": (uid) => {
622
+ const view = views.get(uid);
623
+ if (view) append(frame.dom, view);
624
+ },
625
+ "Viewlet.appendToRoot": (uid, root) => {
626
+ const view = views.get(uid);
627
+ const parent = find2(frame.dom, `#${root}`);
628
+ if (view && parent) append(parent, view);
629
+ },
630
+ "Viewlet.commitPending": commit,
631
+ "Viewlet.create": create,
632
+ "Viewlet.createFunctionalRoot": create,
633
+ "Viewlet.createPlaceholder": (uid, parentId, top, left, width, height) => {
634
+ views.set(uid, element2());
635
+ property2(views.get(uid), "className", `Viewlet ${uid}`);
636
+ setBounds(uid, left, top, width, height);
637
+ appendView(parentId, uid);
638
+ },
639
+ "Viewlet.dispose": (uid) => {
640
+ const view = views.get(uid);
641
+ if (view) detach(view);
642
+ views.delete(uid);
643
+ sheets.delete(uid);
644
+ },
645
+ "Viewlet.executeCommands": execute,
646
+ "Viewlet.patchCss": (uid, start, count, replacement) => {
647
+ const text = sheets.get(uid);
648
+ if (typeof text === "string") sheets.set(uid, text.slice(0, start) + replacement + text.slice(start + count));
649
+ },
650
+ "Viewlet.replaceChildren": (uid, children) => {
651
+ const view = views.get(uid);
652
+ if (!view) return;
653
+ clear(view);
654
+ for (const id of children) {
655
+ const child = views.get(id);
656
+ if (child) append(view, child);
657
+ }
658
+ },
659
+ "Viewlet.scrollSelectorBy": (uid, selector, delta) => {
660
+ const view = views.get(uid);
661
+ const node = view && find2(view, selector);
662
+ if (node) property2(node, "scrollLeft", (node.scroll?.[0] || 0) + delta);
663
+ },
664
+ "Viewlet.sendMultiple": execute,
665
+ "Viewlet.setBounds": setBounds,
666
+ "Viewlet.setCheckBoxValue": (uid, name, value) => setProperty(uid, `[name="${name}"]`, "checked", value),
667
+ "Viewlet.setComponentDom": setDom,
668
+ "Viewlet.setCss": setCss,
669
+ "Viewlet.setDom": (uid, nodes) => {
670
+ const view = views.get(uid);
671
+ if (!view) return;
672
+ clear(view);
673
+ const children = render(nodes);
674
+ for (const child of children) append(view, child);
675
+ },
676
+ "Viewlet.setDom2": setDom,
677
+ "Viewlet.setInputValues": (uid, items) => {
678
+ for (const { name, value } of items) setProperty(uid, `[name="${name}"]`, "value", value);
679
+ },
680
+ "Viewlet.setPatches": (uid, patches) => {
681
+ if (patches.length === 1 && patches[0].type === 6) setDom(uid, patches[0].nodes);
682
+ else patch(uid, patches);
683
+ },
684
+ "Viewlet.setProperty": setProperty,
685
+ "Viewlet.setTreePatches": patch,
686
+ "Viewlet.setValueByName": (uid, name, value) => setProperty(uid, `[name="${name}"]`, "value", value),
687
+ "Viewlet.show": (uid) => {
688
+ const view = views.get(uid);
689
+ if (view) append(find2(frame.dom, "#Workbench") || frame.dom, view);
690
+ }
691
+ };
692
+ return {
693
+ accept(data) {
694
+ if (!data?.renderer || !data.message || typeof data.message !== "object") return;
695
+ const { connection, direction, message } = data;
696
+ const key = `${connection}:${message.id}`;
697
+ if (direction === "to-renderer") {
698
+ if (message.method === "Viewlet.queueCommands") calls.set(key, message.params);
699
+ else apply(message.method, message.params);
700
+ } else if (calls.has(key)) {
701
+ const [uid, commands2] = calls.get(key);
702
+ calls.delete(key);
703
+ if (Number.isSafeInteger(message.result)) batches.set(message.result, { commands: commands2, committed: false, uid });
704
+ }
705
+ },
706
+ frame() {
707
+ return { ...frame, styles: [...sheets.values()] };
708
+ }
709
+ };
710
+ };
5
711
 
6
- const validateSession = (session) => {
7
- if (session?.version !== version || !Array.isArray(session.events) || session.events.length > 200_000 || bytes(session) > maxSessionBytes) {
8
- throw new Error('Unsupported or oversized session replay')
712
+ // ../session-replay-worker/src/protocol.ts
713
+ var version = 1;
714
+ var maxEventBytes = 75e4;
715
+ var maxSessionBytes = 64 * 1024 * 1024;
716
+ var bytes = (value) => new TextEncoder().encode(JSON.stringify(value)).byteLength;
717
+ var validateSession = (value) => {
718
+ const session = value;
719
+ if (session?.version !== version || !Array.isArray(session.events) || session.events.length > 2e5 || bytes(session) > maxSessionBytes) {
720
+ throw new Error("Unsupported or oversized session replay");
9
721
  }
10
722
  let time = 0;
11
723
  for (const [sequence, event] of session.events.entries()) {
12
- if (event.sequence !== sequence || !Number.isFinite(event.timestamp) || event.timestamp < time || !['frame', 'message'].includes(event.type)) {
13
- throw new Error('Invalid session replay event sequence')
724
+ if (!event || event.sequence !== sequence || !Number.isFinite(event.timestamp) || event.timestamp < time || !["frame", "message"].includes(event.type)) {
725
+ throw new Error("Invalid session replay event sequence");
14
726
  }
15
- if (bytes(event) > maxEventBytes) throw new Error('Session replay event is too large')
727
+ if (bytes(event) > maxEventBytes) throw new Error("Session replay event is too large");
16
728
  time = event.timestamp;
17
729
  }
18
- return session
730
+ return session;
19
731
  };
20
-
21
- // Binary search makes dragging independent of the number of diagnostic messages.
22
- const loadContent = (session) => {
23
- validateSession(session);
24
- const frames = session.events.filter((event) => event.type === 'frame');
25
- if (!frames.length) throw new Error('This session has no visual frames')
732
+ var loadContent = (value) => {
733
+ const session = validateSession(value);
734
+ const frames = session.events.filter((event) => event.type === "frame");
735
+ if (frames.length === 0) throw new Error("This session has no visual frames");
26
736
  const duration = session.events.at(-1)?.timestamp || 0;
737
+ let visual = createVisualState(frames[0].data);
738
+ let eventIndex = 0;
739
+ let lastPosition = -1;
27
740
  const seek = (timestamp) => {
28
- const position = Math.max(0, Math.min(duration, Number(timestamp) || 0));
741
+ const position = Math.max(0, Math.min(duration, timestamp || 0));
742
+ if (frames[0].data.commandReplay) {
743
+ if (position < lastPosition) {
744
+ visual = createVisualState(frames[0].data);
745
+ eventIndex = 0;
746
+ }
747
+ while (eventIndex < session.events.length && session.events[eventIndex].timestamp <= position) {
748
+ const event = session.events[eventIndex++];
749
+ if (event.type === "message") visual.accept(event.data);
750
+ }
751
+ lastPosition = position;
752
+ return { duration, frame: structuredClone(visual.frame()), position };
753
+ }
29
754
  let low = 0;
30
755
  let high = frames.length;
31
756
  while (low < high) {
32
- const middle = (low + high) >>> 1;
757
+ const middle = low + high >>> 1;
33
758
  if (frames[middle].timestamp <= position) low = middle + 1;
34
759
  else high = middle;
35
760
  }
36
- return { position, duration, frame: frames[Math.max(0, low - 1)].data }
761
+ return { duration, frame: frames[Math.max(0, low - 1)].data, position };
37
762
  };
38
- return { duration, seek }
763
+ return { duration, seek };
39
764
  };
40
765
 
41
- const createRecorder = ({ storage, fetch: request = globalThis.fetch, now = () => performance.now() }) => {
766
+ // ../session-replay-worker/src/proxy.ts
767
+ var wrapPorts = (value, replacePort, seen = /* @__PURE__ */ new Map()) => {
768
+ if (!value || typeof value !== "object") return value;
769
+ if (seen.has(value)) return seen.get(value);
770
+ if (value instanceof MessagePort) {
771
+ const replacement = replacePort(value);
772
+ seen.set(value, replacement);
773
+ return replacement;
774
+ }
775
+ seen.set(value, value);
776
+ if (value instanceof Map) {
777
+ const entries = [...value];
778
+ value.clear();
779
+ for (const [key, entry] of entries) value.set(wrapPorts(key, replacePort, seen), wrapPorts(entry, replacePort, seen));
780
+ } else if (value instanceof Set) {
781
+ const entries = [...value];
782
+ value.clear();
783
+ for (const entry of entries) value.add(wrapPorts(entry, replacePort, seen));
784
+ } else if (Array.isArray(value) || Object.prototype.toString.call(value) === "[object Object]") {
785
+ const object = value;
786
+ for (const key of Object.keys(object)) object[key] = wrapPorts(object[key], replacePort, seen);
787
+ }
788
+ return value;
789
+ };
790
+ var createProxyRegistry = ({
791
+ record,
792
+ report: report2 = () => {
793
+ }
794
+ }) => {
795
+ const connections = /* @__PURE__ */ new Set();
796
+ let nextId = 0;
797
+ const createNestedPort = (connection, renderer, port) => create(port, `${connection}/port`, renderer);
798
+ const create = (port, label = "renderer", renderer = true) => {
799
+ const channel = new MessageChannel();
800
+ const peer = channel.port1;
801
+ const connection = ++nextId;
802
+ const close = () => {
803
+ for (const endpoint of [port, peer]) {
804
+ endpoint.removeEventListener("close", close);
805
+ endpoint.onmessage = null;
806
+ endpoint.onmessageerror = null;
807
+ endpoint.close();
808
+ }
809
+ connections.delete(close);
810
+ };
811
+ connections.add(close);
812
+ const ignoredReplies = { "from-renderer": /* @__PURE__ */ new Set(), "to-renderer": /* @__PURE__ */ new Set() };
813
+ const forward = (source, target, direction) => {
814
+ source.onmessage = ({ data }) => {
815
+ const opposite = direction === "to-renderer" ? "from-renderer" : "to-renderer";
816
+ const ignored = typeof data?.method === "string" && data.method.startsWith("SessionReplay.");
817
+ if (ignored && data.id !== void 0) ignoredReplies[opposite].add(data.id);
818
+ const ignoredReply = !data?.method && ignoredReplies[direction].delete(data?.id);
819
+ if (!ignored && !ignoredReply) {
820
+ try {
821
+ record({ connection, direction, label, message: data, renderer });
822
+ } catch (error) {
823
+ report2(error);
824
+ }
825
+ }
826
+ const directRenderer = renderer && data?.method === "HandleMessagePort.handleMessagePort";
827
+ const message = wrapPorts(data, createNestedPort.bind(null, connection, directRenderer));
828
+ target.postMessage(message, getTransferrables(message));
829
+ if (data?.method === "Exit.exit") close();
830
+ };
831
+ source.onmessageerror = () => report2(new Error(`Cannot decode session replay proxy message (${connection})`));
832
+ source.addEventListener("close", close);
833
+ source.start();
834
+ };
835
+ forward(port, peer, "to-renderer");
836
+ forward(peer, port, "from-renderer");
837
+ return channel.port2;
838
+ };
839
+ return {
840
+ create,
841
+ dispose() {
842
+ for (const close of connections) close();
843
+ }
844
+ };
845
+ };
846
+
847
+ // ../session-replay-worker/src/recorder.ts
848
+ var createRecorder = ({
849
+ fetch: request = globalThis.fetch,
850
+ now = () => performance.now(),
851
+ storage: storage2
852
+ }) => {
42
853
  let metadata;
43
854
  let options;
44
- let events = [];
45
- let pending = [];
855
+ const events = [];
856
+ const pending2 = [];
46
857
  let size = 0;
47
858
  let origin = 0;
48
859
  let remote;
49
860
  let uploadPromise;
50
- let lastError = '';
861
+ let lastError = "";
51
862
  let saved = 0;
52
863
  let uploaded = 0;
53
864
  const start = async (config) => {
54
- if (metadata) throw new Error('A session is already recording')
865
+ if (metadata) throw new Error("A session is already recording");
55
866
  options = config;
56
867
  origin = now();
57
- metadata = { id: crypto.randomUUID(), version, createdAt: new Date().toISOString() };
58
- if (options.local) await storage.save(metadata, []);
59
- return metadata.id
868
+ metadata = { createdAt: (/* @__PURE__ */ new Date()).toISOString(), id: crypto.randomUUID(), version };
869
+ if (options.local) await storage2.save(metadata, []);
870
+ return metadata.id;
60
871
  };
61
- const record = async (type, data) => {
62
- if (!metadata) return
63
- const event = { sequence: events.length, timestamp: Math.max(events.at(-1)?.timestamp || 0, now() - origin), type, data };
872
+ const record = async (...[type, data]) => {
873
+ if (!metadata) return;
874
+ const event = { data, sequence: events.length, timestamp: Math.max(events.at(-1)?.timestamp || 0, now() - origin), type };
64
875
  const eventSize = bytes(event);
65
876
  if (eventSize > maxEventBytes || size + eventSize > maxSessionBytes)
66
- throw new Error('Session replay storage limit reached; download this session and start a new one')
877
+ throw new Error("Session replay storage limit reached; download this session and start a new one");
67
878
  size += eventSize;
68
879
  events.push(event);
69
- if (options.upload) pending.push(event);
880
+ if (options.upload) pending2.push(event);
70
881
  if (options.local) {
71
- await storage.save(metadata, [event]);
882
+ await storage2.save(metadata, [event]);
72
883
  saved++;
73
884
  }
74
885
  };
75
886
  const doUpload = async () => {
76
- if (!options?.upload || !pending.length) return
887
+ if (!options?.upload || pending2.length === 0) return;
77
888
  const endpoint = new URL(options.endpoint);
78
- if (!['https:', 'http:'].includes(endpoint.protocol)) throw new Error('Invalid session replay backend URL')
79
- const headers = { 'Content-Type': 'application/json', ...(options.token ? { Authorization: `Bearer ${options.token}` } : {}) };
889
+ if (!["https:", "http:"].includes(endpoint.protocol)) throw new Error("Invalid session replay backend URL");
890
+ const headers = { "Content-Type": "application/json", ...options.token && { Authorization: `Bearer ${options.token}` } };
80
891
  const send = async (url, body) => {
81
892
  const response = await request(url, {
82
- method: 'POST',
83
- credentials: 'include',
84
- headers,
85
893
  body: JSON.stringify(body),
86
- signal: AbortSignal.timeout(10_000),
894
+ credentials: "include",
895
+ headers,
896
+ method: "POST",
897
+ signal: AbortSignal.timeout(1e4)
87
898
  });
88
- if (!response.ok) throw new Error(`Session replay upload failed (${response.status})`)
89
- return response.json()
899
+ if (!response.ok) throw new Error(`Session replay upload failed (${response.status})`);
900
+ return response.json();
90
901
  };
91
- if (!remote) remote = await send(endpoint, { version, createdAt: metadata.createdAt });
92
- while (pending.length) {
902
+ if (!remote) remote = await send(endpoint, { createdAt: metadata.createdAt, version });
903
+ while (pending2.length > 0) {
93
904
  const batch = [];
94
905
  let batchBytes = 0;
95
- for (const event of pending) {
906
+ for (const event of pending2) {
96
907
  const length = bytes(event);
97
- if (batch.length && (batchBytes + length > 800_000 || batch.length >= 250)) break
908
+ if (batch.length > 0 && (batchBytes + length > 8e5 || batch.length >= 250)) break;
98
909
  batch.push(event);
99
910
  batchBytes += length;
100
911
  }
101
912
  const url = new URL(endpoint);
102
- url.pathname = `${url.pathname.replace(/\/$/, '')}/${encodeURIComponent(remote.id)}/events`;
103
- await send(url, { uploadToken: remote.uploadToken, events: batch });
104
- pending.splice(0, batch.length);
913
+ url.pathname = `${url.pathname.replace(/\/$/, "")}/${encodeURIComponent(remote.id)}/events`;
914
+ await send(url, { events: batch, uploadToken: remote.uploadToken });
915
+ pending2.splice(0, batch.length);
105
916
  uploaded += batch.length;
106
917
  }
107
918
  };
108
919
  const flush = () => {
109
- uploadPromise ||= doUpload()
110
- .then(
111
- () => {
112
- lastError = '';
113
- },
114
- (error) => {
115
- lastError = error.message;
116
- throw error
117
- },
118
- )
119
- .finally(() => {
120
- uploadPromise = undefined;
121
- });
122
- return uploadPromise
920
+ uploadPromise ||= doUpload().then(
921
+ () => {
922
+ lastError = "";
923
+ },
924
+ (error) => {
925
+ lastError = error.message;
926
+ throw error;
927
+ }
928
+ ).finally(() => {
929
+ uploadPromise = void 0;
930
+ });
931
+ return uploadPromise;
123
932
  };
124
933
  return {
125
- start,
126
- record,
127
- flush,
128
934
  export: () => ({ ...metadata, events }),
129
- status: () => ({ id: metadata?.id, events: events.length, saved, uploaded, pending: pending.length, bytes: size, error: lastError }),
130
- }
935
+ flush,
936
+ record,
937
+ start,
938
+ status: () => ({
939
+ bytes: size,
940
+ error: lastError,
941
+ events: events.length,
942
+ id: metadata?.id,
943
+ pending: pending2.length,
944
+ saved,
945
+ uploaded
946
+ })
947
+ };
131
948
  };
132
949
 
133
- const open = () =>
134
- new Promise((resolve, reject) => {
135
- const request = indexedDB.open('lvce-session-replays', 1);
136
- request.onupgradeneeded = () => {
137
- request.result.createObjectStore('sessions', { keyPath: 'id' });
138
- request.result.createObjectStore('events', { keyPath: ['sessionId', 'sequence'] });
139
- };
140
- request.onsuccess = () => resolve(request.result);
141
- request.onerror = () => reject(request.error);
142
- });
950
+ // ../session-replay-worker/src/api/capture.ts
951
+ var serializeMessage = (message) => {
952
+ const ancestors = [];
953
+ let count = 0;
954
+ return JSON.parse(
955
+ JSON.stringify(message, function(key, value) {
956
+ if (++count > 2e5) throw new Error("Session replay message is too complex");
957
+ if (/password|token|secret|authorization|cookie/i.test(key)) return "[redacted]";
958
+ if (typeof value === "bigint") return String(value);
959
+ if (typeof value !== "object" || value === null) return value;
960
+ while (ancestors.length > 0 && ancestors.at(-1) !== this) ancestors.pop();
961
+ if (ancestors.includes(value)) return "[circular]";
962
+ ancestors.push(value);
963
+ if (value instanceof Error) return { message: value.message, name: value.name, stack: value.stack };
964
+ if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return { byteLength: value.byteLength, type: value.constructor.name };
965
+ if (value.constructor?.name === "MessagePort") return { type: "MessagePort" };
966
+ return value;
967
+ })
968
+ );
969
+ };
970
+
971
+ // ../session-replay-worker/src/serializeProxyMessage.ts
972
+ var redacted = "[redacted]";
973
+ var isMasked = (node) => node.inputType === "password" || Object.hasOwn(node, "data-session-replay-mask") || Object.hasOwn(node, "data-session-replay-ignore");
974
+ var structuralKeys = /* @__PURE__ */ new Set(["type", "childCount", "uid"]);
975
+ var redactNode = (node) => {
976
+ for (const key of Object.keys(node)) {
977
+ if (structuralKeys.has(key)) continue;
978
+ if (["text", "value", "textContent"].includes(key)) node[key] = redacted;
979
+ else if (key === "inputType" && node[key] === "password") continue;
980
+ else if (["data-session-replay-mask", "data-session-replay-ignore"].includes(key)) node[key] = "";
981
+ else delete node[key];
982
+ }
983
+ };
984
+ var redactUpdates = (method, params) => {
985
+ if (method === "Viewlet.setValueByName") params[2] = redacted;
986
+ else if (method === "Viewlet.setProperty") params[3] = redacted;
987
+ const updates = params[1];
988
+ if (!Array.isArray(updates)) return;
989
+ if (method === "Viewlet.setInputValues") for (const item of updates) item.value = redacted;
990
+ if (!["Viewlet.setPatches", "Viewlet.setTreePatches"].includes(method)) return;
991
+ for (const patch of updates) {
992
+ if ([1, 3].includes(patch.type)) patch.value = redacted;
993
+ }
994
+ };
995
+ var createMessageSerializer = () => {
996
+ const maskedViews = /* @__PURE__ */ new Set();
997
+ const sanitizeCommand = (method, params, masked) => {
998
+ if (!method.startsWith("Viewlet.") || typeof params[0] !== "number") return;
999
+ const uid = params[0];
1000
+ const patches = params[1];
1001
+ const masksInput = Array.isArray(patches) && patches.some((patch) => patch?.key === "inputType" && patch.value === "password");
1002
+ if (masked || masksInput) maskedViews.add(uid);
1003
+ if (maskedViews.has(uid)) redactUpdates(method, params);
1004
+ };
1005
+ const sanitizeArray = (array) => {
1006
+ let remaining = 0;
1007
+ let masked = false;
1008
+ for (const entry of array) {
1009
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
1010
+ const node = entry;
1011
+ if (remaining > 0 || isMasked(node)) {
1012
+ remaining = Math.max(0, remaining - 1) + (typeof node.childCount === "number" ? node.childCount : 0);
1013
+ redactNode(node);
1014
+ masked = true;
1015
+ }
1016
+ }
1017
+ masked = sanitize(entry) || masked;
1018
+ }
1019
+ if (typeof array[0] === "string" && array[0].startsWith("Viewlet.")) {
1020
+ const params = array.slice(1);
1021
+ sanitizeCommand(array[0], params, masked);
1022
+ array.splice(1, array.length - 1, ...params);
1023
+ }
1024
+ return masked;
1025
+ };
1026
+ const sanitize = (value) => {
1027
+ if (!value || typeof value !== "object") return false;
1028
+ if (Array.isArray(value)) return sanitizeArray(value);
1029
+ const object = value;
1030
+ let masked = isMasked(object);
1031
+ if (masked) redactNode(object);
1032
+ for (const child of Object.values(object)) masked = sanitize(child) || masked;
1033
+ if (typeof object.method === "string" && Array.isArray(object.params)) sanitizeCommand(object.method, object.params, masked);
1034
+ return masked;
1035
+ };
1036
+ return (message) => {
1037
+ const result = serializeMessage(message);
1038
+ sanitize(result);
1039
+ return result;
1040
+ };
1041
+ };
143
1042
 
144
- const createStorage = async () => {
1043
+ // ../session-replay-worker/src/storage.ts
1044
+ var withoutSessionId = ({ sessionId, ...event }) => event;
1045
+ var open = () => new Promise((resolve, reject) => {
1046
+ const request = indexedDB.open("lvce-session-replays", 1);
1047
+ request.onupgradeneeded = () => {
1048
+ request.result.createObjectStore("sessions", { keyPath: "id" });
1049
+ request.result.createObjectStore("events", { keyPath: ["sessionId", "sequence"] });
1050
+ };
1051
+ request.onsuccess = () => resolve(request.result);
1052
+ request.onerror = () => reject(request.error || new Error("Replay storage open failed"));
1053
+ });
1054
+ var createStorage = async () => {
145
1055
  const db = await open();
146
- const transaction = (stores, action) =>
147
- new Promise((resolve, reject) => {
148
- const tx = db.transaction(stores, 'readwrite');
149
- action(tx);
150
- tx.oncomplete = () => resolve();
151
- tx.onerror = tx.onabort = () => reject(tx.error || new Error('Replay storage transaction aborted'));
152
- });
1056
+ const transaction = (stores, action) => new Promise((resolve, reject) => {
1057
+ const tx = db.transaction(stores, "readwrite");
1058
+ action(tx);
1059
+ tx.oncomplete = () => resolve();
1060
+ tx.onerror = tx.onabort = () => reject(tx.error || new Error("Replay storage transaction aborted"));
1061
+ });
153
1062
  return {
154
- save: (session, events) =>
155
- transaction(['sessions', 'events'], (tx) => {
156
- tx.objectStore('sessions').put(session);
157
- for (const event of events) tx.objectStore('events').put({ ...event, sessionId: session.id });
158
- }),
159
- read: (id) =>
160
- new Promise((resolve, reject) => {
161
- const tx = db.transaction(['sessions', 'events']);
162
- const metadata = tx.objectStore('sessions').get(id);
163
- const events = tx.objectStore('events').getAll(IDBKeyRange.bound([id, 0], [id, Number.MAX_SAFE_INTEGER]));
164
- tx.oncomplete = () =>
165
- metadata.result
166
- ? resolve({ ...metadata.result, events: events.result.map(({ sessionId, ...event }) => event) })
167
- : reject(new Error('Session replay not found'));
168
- tx.onerror = () => reject(tx.error);
169
- }),
170
1063
  close: () => db.close(),
171
- }
1064
+ read: (id) => new Promise((resolve, reject) => {
1065
+ const tx = db.transaction(["sessions", "events"]);
1066
+ const metadata = tx.objectStore("sessions").get(id);
1067
+ const events = tx.objectStore("events").getAll(IDBKeyRange.bound([id, 0], [id, Number.MAX_SAFE_INTEGER]));
1068
+ tx.oncomplete = () => metadata.result ? resolve({ ...metadata.result, events: events.result.map(withoutSessionId) }) : reject(new Error("Session replay not found"));
1069
+ tx.onerror = () => reject(tx.error || new Error("Replay storage read failed"));
1070
+ }),
1071
+ save: (session, events) => transaction(["sessions", "events"], (tx) => {
1072
+ tx.objectStore("sessions").put(session);
1073
+ for (const event of events) tx.objectStore("events").put({ ...event, sessionId: session.id });
1074
+ })
1075
+ };
172
1076
  };
173
1077
 
174
- let storage;
175
- let recorder;
176
- let content;
177
- let queue = Promise.resolve();
178
- let timer;
179
- const getStorage = async () => (storage ||= await createStorage());
180
- const commands = {
181
- async start(options) {
182
- recorder = createRecorder({ storage: options.local ? await getStorage() : undefined });
183
- const id = await recorder.start(options);
184
- clearInterval(timer);
185
- timer = setInterval(() => recorder.flush().catch(() => {}), 2000);
186
- return id
1078
+ // ../session-replay-worker/src/worker.ts
1079
+ var storage;
1080
+ var recorder;
1081
+ var content;
1082
+ var queue = Promise.resolve();
1083
+ var timer;
1084
+ var getStorage = async () => storage ||= await createStorage();
1085
+ var recording = false;
1086
+ var captureError = "";
1087
+ var pending = 0;
1088
+ var writes = Promise.resolve();
1089
+ var report = (error) => {
1090
+ captureError = error instanceof Error ? error.message : String(error);
1091
+ recording = false;
1092
+ };
1093
+ var serializeMessage2 = createMessageSerializer();
1094
+ var proxies = createProxyRegistry({
1095
+ record(message) {
1096
+ if (!recording) return;
1097
+ if (pending >= 500) {
1098
+ report(new Error("Recording cannot keep up with worker messages"));
1099
+ return;
1100
+ }
1101
+ const data = serializeMessage2(message);
1102
+ pending++;
1103
+ const write = recorder.record("message", data).catch(report).finally(() => {
1104
+ pending--;
1105
+ });
1106
+ writes = Promise.all([writes, write]).then(() => {
1107
+ });
1108
+ },
1109
+ report
1110
+ });
1111
+ var commands = {
1112
+ export: async () => {
1113
+ await writes;
1114
+ return recorder.export();
187
1115
  },
188
- record: (type, data) => recorder.record(type, data),
189
1116
  flush: () => recorder.flush(),
190
- export: () => recorder.export(),
191
- status: () => recorder.status(),
192
1117
  async load(source) {
193
1118
  let session;
194
- if (source.localId) session = await (await getStorage()).read(source.localId);
195
- else if (source.url) {
196
- const response = await fetch(source.url, { credentials: 'include', headers: { Accept: 'application/json' } });
197
- if (!response.ok) throw new Error(`Cannot load session replay (${response.status})`)
1119
+ if ("localId" in source) {
1120
+ const localStorage = await getStorage();
1121
+ session = await localStorage.read(source.localId);
1122
+ } else if ("url" in source) {
1123
+ const response = await fetch(source.url, { credentials: "include", headers: { Accept: "application/json" } });
1124
+ if (!response.ok) throw new Error(`Cannot load session replay (${response.status})`);
198
1125
  session = await response.json();
199
- } else session = source.session;
1126
+ } else ({ session } = source);
200
1127
  content = loadContent(session);
201
- return content.seek(0)
1128
+ return content.seek(0);
202
1129
  },
1130
+ proxy: (port) => proxies.create(port),
1131
+ record: (...params) => recorder.record(...params),
203
1132
  seek: (timestamp) => content.seek(timestamp),
1133
+ async start(options, initialFrame) {
1134
+ if (recorder) throw new Error("Reload the window to start a new command recording");
1135
+ recorder = createRecorder({ storage: options.local ? await getStorage() : void 0 });
1136
+ const id = await recorder.start(options);
1137
+ if (initialFrame) await recorder.record("frame", { ...initialFrame, commandReplay: true });
1138
+ recording = true;
1139
+ clearInterval(timer);
1140
+ timer = setInterval(() => {
1141
+ void recorder.flush().catch(() => {
1142
+ });
1143
+ }, 2e3);
1144
+ return id;
1145
+ },
1146
+ status: () => ({ ...recorder.status(), error: captureError || recorder.status().error }),
204
1147
  async stop() {
205
1148
  clearInterval(timer);
1149
+ recording = false;
1150
+ await writes;
206
1151
  await recorder?.flush();
207
- storage?.close();
208
- },
1152
+ }
209
1153
  };
210
- self.onmessage = ({ data: { id, method, params = [] } }) => {
1154
+ globalThis.onmessage = ({ data: { id, method, params = [] } }) => {
211
1155
  queue = queue.then(async () => {
212
1156
  try {
213
- if (!Object.hasOwn(commands, method)) throw new Error('Unknown session replay command')
214
- self.postMessage({ id, result: await commands[method](...params) });
1157
+ if (!Object.hasOwn(commands, method)) throw new Error("Unknown session replay command");
1158
+ const result = await commands[method](...params);
1159
+ globalThis.postMessage({ id, result }, { transfer: getTransferrables(result) });
215
1160
  } catch (error) {
216
- self.postMessage({ id, error: error.message });
1161
+ globalThis.postMessage({ error: error instanceof Error ? error.message : String(error), id });
217
1162
  }
218
1163
  });
219
1164
  };