@ifc-lite/load-trace 0.0.0-stage → 0.2.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 (51) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +130 -2
  3. package/dist/call-meter.d.ts +16 -0
  4. package/dist/call-meter.d.ts.map +1 -0
  5. package/dist/call-meter.js +50 -0
  6. package/dist/call-meter.js.map +1 -0
  7. package/dist/chrome-trace.d.ts +23 -0
  8. package/dist/chrome-trace.d.ts.map +1 -0
  9. package/dist/chrome-trace.js +53 -0
  10. package/dist/chrome-trace.js.map +1 -0
  11. package/dist/counters.d.ts +34 -0
  12. package/dist/counters.d.ts.map +1 -0
  13. package/dist/counters.js +69 -0
  14. package/dist/counters.js.map +1 -0
  15. package/dist/frames.d.ts +69 -0
  16. package/dist/frames.d.ts.map +1 -0
  17. package/dist/frames.js +85 -0
  18. package/dist/frames.js.map +1 -0
  19. package/dist/index.d.ts +12 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +13 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/load-trace.d.ts +62 -0
  24. package/dist/load-trace.d.ts.map +1 -0
  25. package/dist/load-trace.js +199 -0
  26. package/dist/load-trace.js.map +1 -0
  27. package/dist/messages.d.ts +43 -0
  28. package/dist/messages.d.ts.map +1 -0
  29. package/dist/messages.js +137 -0
  30. package/dist/messages.js.map +1 -0
  31. package/dist/perf-sink.d.ts +15 -0
  32. package/dist/perf-sink.d.ts.map +1 -0
  33. package/dist/perf-sink.js +37 -0
  34. package/dist/perf-sink.js.map +1 -0
  35. package/dist/tree.d.ts +11 -0
  36. package/dist/tree.d.ts.map +1 -0
  37. package/dist/tree.js +25 -0
  38. package/dist/tree.js.map +1 -0
  39. package/dist/types.d.ts +70 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +5 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/worker-phases.d.ts +44 -0
  44. package/dist/worker-phases.d.ts.map +1 -0
  45. package/dist/worker-phases.js +78 -0
  46. package/dist/worker-phases.js.map +1 -0
  47. package/dist/worker.d.ts +72 -0
  48. package/dist/worker.d.ts.map +1 -0
  49. package/dist/worker.js +120 -0
  50. package/dist/worker.js.map +1 -0
  51. package/package.json +46 -3
@@ -0,0 +1,43 @@
1
+ import { type PerfCounterRegistry } from './counters.js';
2
+ /**
3
+ * Worker message accounting (#6957): the ONE wrapper every loader worker
4
+ * boundary goes through. It patches the worker's `postMessage` (main -> worker,
5
+ * `out`) and listens for its messages (worker -> main, `in`), adding per label:
6
+ *
7
+ * msg.<label>.<dir>.count messages
8
+ * msg.<label>.<dir>.cloneBytes estimated structured-clone payload
9
+ * msg.<label>.out.transferBytes ArrayBuffers in the transfer list (moved, not copied)
10
+ * msg.<label>.in.bufferBytes ArrayBuffers received: the receiver cannot
11
+ * tell a transferred buffer from a cloned one
12
+ * msg.<label>.<dir>.sharedBytes SharedArrayBuffers (shared, never copied)
13
+ *
14
+ * With counters off at wrap time it returns the worker untouched, so the
15
+ * production path keeps the native `postMessage`. Load-trace control messages
16
+ * are not counted (they exist only while tracing).
17
+ */
18
+ export interface MessageEndpoint {
19
+ postMessage(message: unknown, transfer?: unknown): void;
20
+ /** Absent on a bare port double: then only the outbound side is counted. */
21
+ addEventListener?(type: 'message', listener: (event: {
22
+ data: unknown;
23
+ }) => void): void;
24
+ }
25
+ export interface CloneEstimate {
26
+ cloneBytes: number;
27
+ transferBytes: number;
28
+ sharedBytes: number;
29
+ }
30
+ /**
31
+ * Estimate what structured clone serialises for `value`. Typed-array views
32
+ * count their WHOLE backing buffer once (clone copies the buffer, not the
33
+ * view's window); strings count one byte per code unit; numbers 8. Iterative,
34
+ * so a deep payload cannot overflow the stack.
35
+ */
36
+ export declare function estimateCloneBytes(value: unknown, transferred?: (buffer: ArrayBuffer) => boolean): CloneEstimate;
37
+ export declare function accountWorkerMessages<W extends MessageEndpoint>(worker: W, label: string | (() => string), registry?: PerfCounterRegistry): W;
38
+ /** Attach sender-side accounting once tracing is enabled in a worker (#6993).
39
+ * Publish increments before each output: the host may terminate this realm
40
+ * immediately on receipt of a terminal message. Trace-control posts bypass
41
+ * accounting and flushing, so publishing counters cannot recurse. */
42
+ export declare function accountWorkerOutput(endpoint: MessageEndpoint, registry: PerfCounterRegistry, flush: () => void): void;
43
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxD,4EAA4E;IAC5E,gBAAgB,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CACxF;AAED,MAAM,WAAW,aAAa;IAAG,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE;AAYjG;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EAAE,WAAW,GAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAqB,GAC1E,aAAa,CA+Bf;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,eAAe,EAC7D,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,EAAE,QAAQ,GAAE,mBAAkC,GACtF,CAAC,CAkCH;AAED;;;qEAGqE;AACrE,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,IAAI,GAC1E,IAAI,CASN"}
@@ -0,0 +1,137 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { perfCounters } from './counters.js';
5
+ const isTraceControl = (message) => {
6
+ const type = message?.type;
7
+ return typeof type === 'string' && type.startsWith('load-trace:');
8
+ };
9
+ function transferSet(arg) {
10
+ const list = Array.isArray(arg) ? arg : arg?.transfer;
11
+ return new Set(list ?? []);
12
+ }
13
+ /**
14
+ * Estimate what structured clone serialises for `value`. Typed-array views
15
+ * count their WHOLE backing buffer once (clone copies the buffer, not the
16
+ * view's window); strings count one byte per code unit; numbers 8. Iterative,
17
+ * so a deep payload cannot overflow the stack.
18
+ */
19
+ export function estimateCloneBytes(value, transferred = () => false) {
20
+ const out = { cloneBytes: 0, transferBytes: 0, sharedBytes: 0 };
21
+ const seen = new Set();
22
+ const stack = [value];
23
+ while (stack.length > 0) {
24
+ const v = stack.pop();
25
+ switch (typeof v) {
26
+ case 'string':
27
+ out.cloneBytes += v.length;
28
+ continue;
29
+ case 'number':
30
+ case 'bigint':
31
+ out.cloneBytes += 8;
32
+ continue;
33
+ case 'boolean':
34
+ out.cloneBytes += 1;
35
+ continue;
36
+ case 'object': break;
37
+ default: continue;
38
+ }
39
+ if (v === null || seen.has(v))
40
+ continue;
41
+ seen.add(v);
42
+ if (ArrayBuffer.isView(v)) {
43
+ stack.push(v.buffer);
44
+ continue;
45
+ }
46
+ if (typeof SharedArrayBuffer !== 'undefined' && v instanceof SharedArrayBuffer) {
47
+ out.sharedBytes += v.byteLength;
48
+ continue;
49
+ }
50
+ if (v instanceof ArrayBuffer) {
51
+ if (transferred(v))
52
+ out.transferBytes += v.byteLength;
53
+ else
54
+ out.cloneBytes += v.byteLength;
55
+ continue;
56
+ }
57
+ if (Array.isArray(v)) {
58
+ for (let i = 0; i < v.length; i++)
59
+ stack.push(v[i]);
60
+ continue;
61
+ }
62
+ if (v instanceof Map) {
63
+ for (const [k, e] of v)
64
+ stack.push(k, e);
65
+ continue;
66
+ }
67
+ if (v instanceof Set) {
68
+ for (const e of v)
69
+ stack.push(e);
70
+ continue;
71
+ }
72
+ if (v instanceof Date) {
73
+ out.cloneBytes += 8;
74
+ continue;
75
+ }
76
+ for (const key of Object.keys(v)) {
77
+ out.cloneBytes += key.length;
78
+ stack.push(v[key]);
79
+ }
80
+ }
81
+ return out;
82
+ }
83
+ export function accountWorkerMessages(worker, label, registry = perfCounters) {
84
+ if (!registry.enabled)
85
+ return worker;
86
+ // A pooled worker (#7036) changes role between loads, so it passes a getter.
87
+ const prefixOf = typeof label === 'function' ? () => `msg.${label()}` : () => `msg.${label}`;
88
+ const nativePost = worker.postMessage.bind(worker);
89
+ const counted = (message, transfer) => {
90
+ if (!isTraceControl(message)) {
91
+ const list = transferSet(transfer);
92
+ const est = estimateCloneBytes(message, (b) => list.has(b));
93
+ // A transfer list can move a buffer that is not reachable from the
94
+ // payload. Count the list itself before postMessage detaches it (#6993).
95
+ let transferBytes = 0;
96
+ for (const value of list)
97
+ if (value instanceof ArrayBuffer)
98
+ transferBytes += value.byteLength;
99
+ const prefix = prefixOf();
100
+ registry.add(`${prefix}.out.count`);
101
+ registry.add(`${prefix}.out.cloneBytes`, est.cloneBytes);
102
+ registry.add(`${prefix}.out.transferBytes`, transferBytes);
103
+ registry.add(`${prefix}.out.sharedBytes`, est.sharedBytes);
104
+ }
105
+ nativePost(message, transfer);
106
+ };
107
+ worker.postMessage = counted;
108
+ worker.addEventListener?.('message', ({ data }) => {
109
+ if (isTraceControl(data))
110
+ return;
111
+ // A received ArrayBuffer may have been transferred or cloned; this side
112
+ // cannot tell, so every buffer lands in bufferBytes, the rest in cloneBytes.
113
+ const est = estimateCloneBytes(data, () => true);
114
+ const prefix = prefixOf();
115
+ registry.add(`${prefix}.in.count`);
116
+ registry.add(`${prefix}.in.cloneBytes`, est.cloneBytes);
117
+ registry.add(`${prefix}.in.bufferBytes`, est.transferBytes);
118
+ registry.add(`${prefix}.in.sharedBytes`, est.sharedBytes);
119
+ });
120
+ return worker;
121
+ }
122
+ /** Attach sender-side accounting once tracing is enabled in a worker (#6993).
123
+ * Publish increments before each output: the host may terminate this realm
124
+ * immediately on receipt of a terminal message. Trace-control posts bypass
125
+ * accounting and flushing, so publishing counters cannot recurse. */
126
+ export function accountWorkerOutput(endpoint, registry, flush) {
127
+ const nativePost = endpoint.postMessage.bind(endpoint);
128
+ const port = accountWorkerMessages({
129
+ postMessage(message, transfer) {
130
+ if (!isTraceControl(message))
131
+ flush();
132
+ nativePost(message, transfer);
133
+ },
134
+ }, 'worker', registry);
135
+ endpoint.postMessage = port.postMessage;
136
+ }
137
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { TraceAttrValue } from './types.js';
2
+ /** Namespace for every User Timing entry this package writes. */
3
+ export declare const MEASURE_PREFIX = "ifc:";
4
+ /** Where finished spans are mirrored (the browser's User Timing buffer by default). */
5
+ export interface PerfSink {
6
+ measure(name: string, start: number, end: number, detail: Record<string, TraceAttrValue>): void;
7
+ }
8
+ /**
9
+ * Mirror spans into `performance.measure`. Prefers the instance method but
10
+ * falls back to `Performance.prototype.measure`: the viewer's DEV bootstrap
11
+ * deliberately nulls the INSTANCE property so React's dev render tracker stays
12
+ * off (apps/viewer/AGENTS.md), and the prototype is untouched by that.
13
+ */
14
+ export declare function createPerfSink(perf?: Performance | undefined): PerfSink | null;
15
+ //# sourceMappingURL=perf-sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-sink.d.ts","sourceRoot":"","sources":["../src/perf-sink.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,iEAAiE;AACjE,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC,uFAAuF;AACvF,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC;CACjG;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAE,WAAW,GAAG,SAAkC,GAAG,QAAQ,GAAG,IAAI,CAqBtG"}
@@ -0,0 +1,37 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /** Namespace for every User Timing entry this package writes. */
5
+ export const MEASURE_PREFIX = 'ifc:';
6
+ /**
7
+ * Mirror spans into `performance.measure`. Prefers the instance method but
8
+ * falls back to `Performance.prototype.measure`: the viewer's DEV bootstrap
9
+ * deliberately nulls the INSTANCE property so React's dev render tracker stays
10
+ * off (apps/viewer/AGENTS.md), and the prototype is untouched by that.
11
+ */
12
+ export function createPerfSink(perf = globalThis.performance) {
13
+ if (!perf)
14
+ return null;
15
+ const proto = typeof Performance !== 'undefined' ? Performance.prototype : undefined;
16
+ const instance = perf;
17
+ const measure = typeof instance.measure === 'function' ? instance.measure : proto?.measure;
18
+ if (typeof measure !== 'function')
19
+ return null;
20
+ let warned = false;
21
+ return {
22
+ measure(name, start, end, detail) {
23
+ // User Timing rejects negative timestamps and end < start.
24
+ const s = Math.max(0, start);
25
+ try {
26
+ measure.call(perf, name, { start: s, end: Math.max(s, end), detail });
27
+ }
28
+ catch (err) {
29
+ if (!warned) {
30
+ warned = true;
31
+ console.warn('[load-trace] performance.measure failed; span tree still recorded', err);
32
+ }
33
+ }
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=perf-sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf-sink.js","sourceRoot":"","sources":["../src/perf-sink.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAI/D,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AASrC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgC,UAAU,CAAC,WAAW;IACnF,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,KAAK,GAAG,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAE,WAAW,CAAC,SAAgD,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7H,MAAM,QAAQ,GAAG,IAA0C,CAAC;IAC5D,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC;IAC3F,IAAI,OAAO,OAAO,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,OAAO;QACL,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM;YAC9B,2DAA2D;YAC3D,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YAC7B,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YACxE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,GAAG,IAAI,CAAC;oBACd,OAAO,CAAC,IAAI,CAAC,mEAAmE,EAAE,GAAG,CAAC,CAAC;gBACzF,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/tree.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { LoadTraceSnapshot, TraceSpan } from './types.js';
2
+ export interface SpanTreeNode {
3
+ span: TraceSpan;
4
+ children: SpanTreeNode[];
5
+ }
6
+ /**
7
+ * Nest a snapshot's flat span list by `parentId`. Returns the root's direct
8
+ * children, each list ordered by start time (ties keep recording order).
9
+ */
10
+ export declare function buildSpanTree(snapshot: LoadTraceSnapshot): SpanTreeNode[];
11
+ //# sourceMappingURL=tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,iBAAiB,GAAG,YAAY,EAAE,CAczE"}
package/dist/tree.js ADDED
@@ -0,0 +1,25 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Nest a snapshot's flat span list by `parentId`. Returns the root's direct
6
+ * children, each list ordered by start time (ties keep recording order).
7
+ */
8
+ export function buildSpanTree(snapshot) {
9
+ const nodes = new Map();
10
+ for (const span of snapshot.spans)
11
+ nodes.set(span.id, { span, children: [] });
12
+ const roots = [];
13
+ for (const node of nodes.values()) {
14
+ const parent = node.span.parentId === null ? undefined : nodes.get(node.span.parentId);
15
+ (parent ? parent.children : roots).push(node);
16
+ }
17
+ const order = (list) => {
18
+ list.sort((a, b) => a.span.start - b.span.start || a.span.id - b.span.id);
19
+ for (const n of list)
20
+ order(n.children);
21
+ };
22
+ order(roots);
23
+ return roots;
24
+ }
25
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAS/D;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,QAA2B;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvF,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,IAAoB,EAAQ,EAAE;QAC3C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1E,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC,CAAC;IACF,KAAK,CAAC,KAAK,CAAC,CAAC;IACb,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,70 @@
1
+ import type { FrameSummary } from './frames.js';
2
+ /** Attribute values that survive structured clone, JSON and the User Timing `detail`. */
3
+ export type TraceAttrValue = string | number | boolean;
4
+ export type TraceAttrs = Readonly<Record<string, TraceAttrValue>>;
5
+ /**
6
+ * Per-load attributes (charter #6954). `journey` is the charter journey id
7
+ * (`J1` cold open, `J2` warm/cache open, `J4` federated add, ...).
8
+ */
9
+ export interface LoadTraceAttributes {
10
+ journey?: string;
11
+ modelKind?: 'primary' | 'federated';
12
+ cacheTier?: string;
13
+ loadPath?: string;
14
+ workerCount?: number;
15
+ [key: string]: TraceAttrValue | undefined;
16
+ }
17
+ /**
18
+ * One span on the load timeline. Times are milliseconds on the MAIN thread's
19
+ * `performance.now()` clock; worker spans are shifted onto it at merge time.
20
+ */
21
+ export interface TraceSpan {
22
+ id: number;
23
+ name: string;
24
+ /** `main`, or the worker label given to `enableWorkerTrace` (e.g. `geom-0`). */
25
+ thread: string;
26
+ start: number;
27
+ /** `null` while the span is still open. */
28
+ end: number | null;
29
+ /** Parent span id; `null` means the load root. */
30
+ parentId: number | null;
31
+ /** Milestones start at the load root, so their duration is time-to-milestone. */
32
+ milestone?: true;
33
+ attrs?: Record<string, TraceAttrValue>;
34
+ }
35
+ /** JSON-safe copy of one load's span tree (`spans` is flat; `parentId` links it). */
36
+ export interface LoadTraceSnapshot {
37
+ loadId: string;
38
+ attrs: LoadTraceAttributes;
39
+ /** Main-thread `performance.timeOrigin`, so absolute times can be recovered. */
40
+ timeOrigin: number;
41
+ start: number;
42
+ end: number | null;
43
+ spans: TraceSpan[];
44
+ /**
45
+ * Structural counters (#6957) moved while this load was the latest one:
46
+ * from its start until the next load starts (or the snapshot is taken),
47
+ * plus every counter its workers posted back. Absent when counters are off.
48
+ */
49
+ counters?: Record<string, number>;
50
+ /** The worker share of `counters`, per worker thread label. */
51
+ workerCounters?: Record<string, Record<string, number>>;
52
+ /** Long-frame summary over the same window; absent without a frame monitor. */
53
+ mainThread?: FrameSummary;
54
+ }
55
+ /** A finished span recorded inside a worker, on that worker's own clock. */
56
+ export interface WorkerSpan {
57
+ name: string;
58
+ start: number;
59
+ end: number;
60
+ attrs?: Record<string, TraceAttrValue>;
61
+ }
62
+ /** What a worker posts back: its spans plus the clock origin needed to align them. */
63
+ export interface WorkerTracePayload {
64
+ thread: string;
65
+ timeOrigin: number;
66
+ spans: WorkerSpan[];
67
+ /** Counter increments in the worker since its previous payload (#6957). */
68
+ counters?: Record<string, number>;
69
+ }
70
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACvD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iFAAiF;IACjF,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACxC;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,mBAAmB,CAAC;IAC3B,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,+EAA+E;IAC/E,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B;AAED,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACxC;AAED,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;+DAE+D"}
@@ -0,0 +1,44 @@
1
+ import type { TraceAttrs } from './types.js';
2
+ import { type PerfCounterRegistry } from './counters.js';
3
+ import { type MessageEndpoint } from './messages.js';
4
+ import { type TraceSpansMessage } from './worker.js';
5
+ /**
6
+ * Spans for the phases INSIDE one long worker handler (#6979), where
7
+ * `createWorkerTraceHost`'s one-span-per-message is too coarse. The parser
8
+ * worker runs a whole parse in a single `parse` message and the host
9
+ * terminates it the moment `complete` arrives, so a span posted after the
10
+ * handler settles would die with the worker: `flush()` posts what has
11
+ * finished so far, and the worker calls it before any message after which it
12
+ * may be terminated.
13
+ *
14
+ * Enabled by the same `enableWorkerTrace` request as the host. Until then
15
+ * every method is a null check, so an untraced load pays nothing measurable.
16
+ */
17
+ export interface WorkerPhaseTrace {
18
+ readonly enabled: boolean;
19
+ /** Feed each inbound message first; `true` = it was the enable request (consumed). */
20
+ accept(data: unknown): boolean;
21
+ /** Open a span; `-1` when disabled. */
22
+ begin(name: string, attrs?: TraceAttrs): number;
23
+ end(token: number, attrs?: TraceAttrs): void;
24
+ /** Time `fn`; a returned promise ends the span when it settles. Returns `fn`'s result unchanged. */
25
+ span<T>(name: string, fn: () => T, attrs?: TraceAttrs): T;
26
+ /**
27
+ * Sequential phases: end the previous `step` span and open `name` (or just
28
+ * end it, for `null`). A repeated name keeps the open span, so a progress
29
+ * callback that reports one phase many times can drive this directly.
30
+ */
31
+ step(name: string | null): void;
32
+ /** Post every finished span now (nothing when there are none). */
33
+ flush(): void;
34
+ }
35
+ export interface WorkerPhaseTraceOptions {
36
+ post: (message: TraceSpansMessage) => void;
37
+ now?: () => number;
38
+ timeOrigin?: number;
39
+ counters?: PerfCounterRegistry;
40
+ /** Worker global; sender accounting starts only after the enable request. */
41
+ endpoint?: () => MessageEndpoint;
42
+ }
43
+ export declare function createWorkerPhaseTrace(options: WorkerPhaseTraceOptions): WorkerPhaseTrace;
44
+ //# sourceMappingURL=worker-phases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-phases.d.ts","sourceRoot":"","sources":["../src/worker-phases.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAIL,KAAK,iBAAiB,EAEvB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,sFAAsF;IACtF,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAC/B,uCAAuC;IACvC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAChD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7C,oGAAoG;IACpG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC;IAC1D;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAChC,kEAAkE;IAClE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,eAAe,CAAC;CAClC;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CA2DzF"}
@@ -0,0 +1,78 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { perfCounters } from './counters.js';
5
+ import { accountWorkerOutput } from './messages.js';
6
+ import { createWorkerSpanRecorder, TRACE_ENABLE_MESSAGE, TRACE_SPANS_MESSAGE, } from './worker.js';
7
+ export function createWorkerPhaseTrace(options) {
8
+ let recorder = null;
9
+ let stepName = null;
10
+ let stepToken = -1;
11
+ const counters = options.counters ?? perfCounters;
12
+ let countedEndpoint;
13
+ const trace = {
14
+ get enabled() { return recorder !== null; },
15
+ accept(data) {
16
+ if (data?.type !== TRACE_ENABLE_MESSAGE)
17
+ return false;
18
+ const thread = data.thread;
19
+ recorder = createWorkerSpanRecorder(typeof thread === 'string' ? thread : 'worker', options.now, options.timeOrigin);
20
+ stepName = null;
21
+ stepToken = -1;
22
+ counters.enable();
23
+ counters.drain();
24
+ const endpoint = options.endpoint?.();
25
+ if (endpoint && endpoint !== countedEndpoint) {
26
+ accountWorkerOutput(endpoint, counters, () => trace.flush());
27
+ countedEndpoint = endpoint;
28
+ }
29
+ return true;
30
+ },
31
+ begin: (name, attrs) => recorder?.begin(name, attrs) ?? -1,
32
+ end: (token, attrs) => { if (token >= 0)
33
+ recorder?.end(token, attrs); },
34
+ span(name, fn, attrs) {
35
+ if (!recorder)
36
+ return fn();
37
+ const token = recorder.begin(name, attrs);
38
+ let result;
39
+ try {
40
+ result = fn();
41
+ }
42
+ catch (err) {
43
+ trace.end(token, { error: true });
44
+ throw err;
45
+ }
46
+ if (isThenable(result)) {
47
+ // Observes settlement only; the caller still owns the returned promise.
48
+ result.then(() => trace.end(token), () => trace.end(token, { error: true }));
49
+ }
50
+ else {
51
+ trace.end(token);
52
+ }
53
+ return result;
54
+ },
55
+ step(name) {
56
+ if (!recorder || name === stepName)
57
+ return;
58
+ trace.end(stepToken);
59
+ stepName = name;
60
+ stepToken = name === null ? -1 : recorder.begin(name);
61
+ },
62
+ flush() {
63
+ if (!recorder)
64
+ return;
65
+ const payload = recorder.drain();
66
+ const moved = counters.drain();
67
+ if (!payload && !moved)
68
+ return;
69
+ const out = payload ?? { thread: recorder.thread, timeOrigin: options.timeOrigin ?? performance.timeOrigin, spans: [] };
70
+ options.post({ type: TRACE_SPANS_MESSAGE, payload: moved ? { ...out, counters: moved } : out });
71
+ },
72
+ };
73
+ return trace;
74
+ }
75
+ function isThenable(value) {
76
+ return typeof value === 'object' && value !== null && typeof value.then === 'function';
77
+ }
78
+ //# sourceMappingURL=worker-phases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-phases.js","sourceRoot":"","sources":["../src/worker-phases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { type PerfCounterRegistry } from './counters.js';
2
+ import { type MessageEndpoint } from './messages.js';
3
+ import type { LoadTrace } from './load-trace.js';
4
+ import type { TraceAttrs, WorkerTracePayload } from './types.js';
5
+ /** Main -> worker: start recording, labelling spans with `thread`. */
6
+ export declare const TRACE_ENABLE_MESSAGE = "load-trace:enable";
7
+ /** Worker -> main: finished spans, see `WorkerTracePayload`. */
8
+ export declare const TRACE_SPANS_MESSAGE = "load-trace:spans";
9
+ export interface TraceEnableMessage {
10
+ type: typeof TRACE_ENABLE_MESSAGE;
11
+ thread: string;
12
+ }
13
+ export interface TraceSpansMessage {
14
+ type: typeof TRACE_SPANS_MESSAGE;
15
+ payload: WorkerTracePayload;
16
+ }
17
+ export interface WorkerSpanRecorder {
18
+ readonly thread: string;
19
+ begin(name: string, attrs?: TraceAttrs): number;
20
+ end(token: number, attrs?: TraceAttrs): void;
21
+ /** Finish named work before publishing an event that makes the host terminate us. */
22
+ endNamed(name: string): void;
23
+ /** Finished spans since the last drain, or `null` when there are none. */
24
+ drain(): WorkerTracePayload | null;
25
+ }
26
+ /**
27
+ * Records spans on the worker's own clock. `timeOrigin` travels with every
28
+ * drained payload so the main thread can shift the spans onto its clock: a
29
+ * worker's `performance.now()` is relative to the WORKER's creation time.
30
+ */
31
+ export declare function createWorkerSpanRecorder(thread: string, now?: () => number, timeOrigin?: number): WorkerSpanRecorder;
32
+ export interface WorkerTraceHostOptions {
33
+ /** Request message `type` -> span name. Unlisted types are never traced. */
34
+ spanNames: Readonly<Record<string, string>>;
35
+ /** Types traced only on their first occurrence (e.g. the first geometry chunk). */
36
+ onceTypes?: readonly string[];
37
+ post: (message: TraceSpansMessage) => void;
38
+ now?: () => number;
39
+ timeOrigin?: number;
40
+ /**
41
+ * This worker's counter registry (#6957), switched on with tracing. Its
42
+ * increments ride back with the spans after every handler, so per-chunk
43
+ * work (wasm copies, ...) reaches the load even for untraced message types.
44
+ */
45
+ counters?: PerfCounterRegistry;
46
+ /** Worker global, resolved lazily after tracing is enabled. Counts actual
47
+ * outbound transfer lists as msg.worker.out.* in this thread's counters. */
48
+ endpoint?: () => MessageEndpoint;
49
+ }
50
+ /**
51
+ * Wrap a worker's message dispatch. Until the main thread sends
52
+ * `TRACE_ENABLE_MESSAGE` the wrapper is one null check and a direct call, so
53
+ * an untraced load pays nothing measurable. Once enabled, each listed message
54
+ * type becomes one span, posted back as soon as its handler settles, and any
55
+ * handler that moved a counter posts those increments (#6957).
56
+ */
57
+ /**
58
+ * The wrapped dispatch, plus `flush()`: post what is pending NOW. A worker the
59
+ * main thread terminates on a message it is about to send (the pre-pass
60
+ * worker on its final event) calls it first, or the counters die with it.
61
+ */
62
+ export type WorkerTraceHost = ((data: unknown, run: () => Promise<void>) => Promise<void>) & {
63
+ /** Drain finished work; optionally finish all open spans with the given name before a terminal event. */
64
+ flush(completedSpan?: string): void;
65
+ };
66
+ export declare function createWorkerTraceHost(options: WorkerTraceHostOptions): WorkerTraceHost;
67
+ /** Ask `worker` to record spans for `trace` (no-op when tracing is off). Returns `worker`. */
68
+ export declare function enableWorkerTrace<W extends {
69
+ postMessage(message: unknown): void;
70
+ }>(worker: W, trace: LoadTrace, thread: string): W;
71
+ export declare function isTraceSpansMessage(message: unknown): message is TraceSpansMessage;
72
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAc,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE7E,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AACxD,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAAG,IAAI,EAAE,OAAO,oBAAoB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACzF,MAAM,WAAW,iBAAiB;IAAG,IAAI,EAAE,OAAO,mBAAmB,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE;AAEpG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAChD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7C,qFAAqF;IACrF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0EAA0E;IAC1E,KAAK,IAAI,kBAAkB,GAAG,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAM,MAAgC,EAC3C,UAAU,GAAE,MAA+B,GAC1C,kBAAkB,CAkCpB;AAED,MAAM,WAAW,sBAAsB;IACrC,4EAA4E;IAC5E,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5C,mFAAmF;IACnF,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B;gFAC4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,eAAe,CAAC;CAClC;AAED;;;;;;GAMG;AACH;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG;IAC3F,yGAAyG;IACzG,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAgDtF;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,EACjF,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAC1C,CAAC,CAGH;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,iBAAiB,CAElF"}