@tracelane/core 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICE ADDED
@@ -0,0 +1,11 @@
1
+ rrweb-stack
2
+ Copyright 2026 Cubenest
3
+
4
+ This product includes software developed by:
5
+ - PostHog (https://posthog.com) — vendored rrweb fork:
6
+ @posthog/rrweb 0.0.34 (MIT License)
7
+ @posthog/rrweb-types 0.0.24 (MIT License)
8
+ Forked from upstream rrweb-io/rrweb 2.0.0-alpha.17.
9
+
10
+ This product is licensed under the Apache License, Version 2.0.
11
+ See the LICENSE file for details.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @tracelane/core
2
+
3
+ Framework-agnostic recording engine for [`tracelane`](https://github.com/Cubenest/rrweb-stack/tree/main/packages/tracelane-core). Wraps a per-framework `browser` object behind a common `BrowserExecutor` interface, injects the rrweb capture bundle, drains in-page events to Node, and builds the buffer that the report packages render.
4
+
5
+ Not intended for direct consumption — depend on a product package (`@tracelane/wdio`, `@tracelane/playwright`, `@tracelane/cypress`) instead.
6
+
7
+ ## What's in here
8
+
9
+ - `BrowserExecutor` — the framework-agnostic surface that adapters implement (see [ADR-0004](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0004-p1-wdio-service-not-reporter.md)).
10
+ - Recorder controller — in-page buffer install + Node-polled drain (see [ADR-0006](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0006-p1-in-page-buffer-node-polled.md)).
11
+ - Navigation re-injection with a 250ms cooldown + `tracelane.nav` boundary events.
12
+ - Mode switch (`'failed' | 'all'`) and a 25 MB FullSnapshot-preserving report-size guard (see [ADR-0005](https://github.com/Cubenest/rrweb-stack/blob/main/prds/adrs/0005-p1-failed-only-self-contained-html.md)).
13
+
14
+ Built on [`@cubenest/rrweb-core`](https://github.com/Cubenest/rrweb-stack/tree/main/packages/rrweb-core).
15
+
16
+ ## License
17
+
18
+ Apache-2.0. Contributions require a [DCO](https://developercertificate.org/) sign-off (`git commit -s`).
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The framework-agnostic surface that wraps a per-framework `browser`/`page`
3
+ * object. The wdio / playwright / cypress adapters implement this in their own
4
+ * packages (ADR-0004); `@tracelane/core` only ever talks to a `BrowserExecutor`,
5
+ * never to a concrete framework driver.
6
+ *
7
+ * Modeled on WebdriverIO's `browser` API (P1 PRD §A.4 / §A.5), which is the
8
+ * lowest-common-denominator across the three target frameworks.
9
+ */
10
+ export interface BrowserExecutor {
11
+ /**
12
+ * Run `fn` in the page (browser) context and resolve with its return value.
13
+ *
14
+ * The function body is `.toString()`-serialized and evaluated in the page, so
15
+ * (per PRD §A.4) it MUST be self-contained: closures over Node-side variables
16
+ * are silently dropped — always pass values explicitly via `...args`. The
17
+ * return value must be JSON-serializable (no functions, DOM nodes, or
18
+ * circular references).
19
+ */
20
+ execute<T>(fn: (...args: unknown[]) => T, ...args: unknown[]): Promise<T>;
21
+ /**
22
+ * Run an async `fn` in the page context. The injected function receives a
23
+ * trailing `done` callback as its last argument (WebDriver async-script
24
+ * semantics); it resolves the returned promise when `done(value)` is called.
25
+ */
26
+ executeAsync<T>(fn: (...args: unknown[]) => void, ...args: unknown[]): Promise<T>;
27
+ /**
28
+ * Send a Chrome DevTools Protocol command on the active connection
29
+ * (PRD §A.5). Requires a CDP-capable transport (e.g. `@wdio/devtools-service`
30
+ * for WebdriverIO). Resolves with the raw CDP result.
31
+ */
32
+ cdp(domain: string, command: string, params?: Record<string, unknown>): Promise<unknown>;
33
+ /**
34
+ * Subscribe to a CDP event (e.g. `'Network.responseReceived'`) on the same
35
+ * connection used by {@link BrowserExecutor.cdp}.
36
+ */
37
+ on(event: string, handler: (params: unknown) => void): void;
38
+ }
39
+ //# sourceMappingURL=browser-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-executor.d.ts","sourceRoot":"","sources":["../src/browser-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;OAQG;IACH,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE1E;;;;OAIG;IACH,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAElF;;;;OAIG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzF;;;OAGG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;CAC7D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=browser-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-executor.js","sourceRoot":"","sources":["../src/browser-executor.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ export type { BrowserExecutor } from './browser-executor.js';
2
+ export { createRecorder, DEFAULT_COOLDOWN_MS, DEFAULT_DRAIN_INTERVAL_MS } from './recorder.js';
3
+ export type { FinalizeResult, Recorder, RecorderOptions, TestOutcome } from './recorder.js';
4
+ export type { ConsolePluginOptions } from './page-script.js';
5
+ export { DEFAULT_MODE, resolveMode } from './mode.js';
6
+ export type { Mode } from './mode.js';
7
+ export { MAX_REPORT_BYTES, PRUNE_EVENT_TAG, pruneToSizeBudget, serializedSize, } from './size-guard.js';
8
+ export type { PruneEventPayload, PruneResult } from './size-guard.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC/F,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5F,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACtD,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Public API surface for @tracelane/core.
2
+ // Recorder controller — in-page buffer install + Node-polled drain (ADR-0006).
3
+ export { createRecorder, DEFAULT_COOLDOWN_MS, DEFAULT_DRAIN_INTERVAL_MS } from './recorder.js';
4
+ // Capture mode switch (ADR-0005).
5
+ export { DEFAULT_MODE, resolveMode } from './mode.js';
6
+ // 25 MB report-size guard with FullSnapshot-preserving prune (ADR-0005).
7
+ export { MAX_REPORT_BYTES, PRUNE_EVENT_TAG, pruneToSizeBudget, serializedSize, } from './size-guard.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAK1C,+EAA+E;AAC/E,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAI/F,kCAAkC;AAClC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGtD,yEAAyE;AACzE,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,cAAc,GACf,MAAM,iBAAiB,CAAC"}
package/dist/mode.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Capture mode (ADR-0005).
3
+ *
4
+ * - `'failed'` (default): events buffer in memory during the test; on pass the
5
+ * buffer is discarded, on failure a report is built.
6
+ * - `'all'`: a report is built regardless of outcome (visual-regression
7
+ * workflows), available behind `TRACELANE_MODE=all`.
8
+ */
9
+ export type Mode = 'failed' | 'all';
10
+ /** The default capture mode (ADR-0005). */
11
+ export declare const DEFAULT_MODE: Mode;
12
+ /**
13
+ * Resolve the effective mode. The `TRACELANE_MODE` env var, when set to a valid
14
+ * value, overrides the config; an invalid env value is ignored. Falls back to
15
+ * the config mode, then {@link DEFAULT_MODE}.
16
+ */
17
+ export declare function resolveMode(configMode?: Mode): Mode;
18
+ //# sourceMappingURL=mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEpC,2CAA2C;AAC3C,eAAO,MAAM,YAAY,EAAE,IAAe,CAAC;AAgB3C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAInD"}
package/dist/mode.js ADDED
@@ -0,0 +1,26 @@
1
+ /** The default capture mode (ADR-0005). */
2
+ export const DEFAULT_MODE = 'failed';
3
+ function isMode(value) {
4
+ return value === 'failed' || value === 'all';
5
+ }
6
+ /**
7
+ * Read `TRACELANE_MODE` from the environment without a hard dependency on
8
+ * `@types/node` (this package stays framework- and platform-light). Resolves
9
+ * `process.env` defensively so it's a no-op in a browser-like context.
10
+ */
11
+ function readModeEnv() {
12
+ const proc = globalThis.process;
13
+ return proc?.env?.TRACELANE_MODE;
14
+ }
15
+ /**
16
+ * Resolve the effective mode. The `TRACELANE_MODE` env var, when set to a valid
17
+ * value, overrides the config; an invalid env value is ignored. Falls back to
18
+ * the config mode, then {@link DEFAULT_MODE}.
19
+ */
20
+ export function resolveMode(configMode) {
21
+ const fromEnv = readModeEnv();
22
+ if (isMode(fromEnv))
23
+ return fromEnv;
24
+ return configMode ?? DEFAULT_MODE;
25
+ }
26
+ //# sourceMappingURL=mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.js","sourceRoot":"","sources":["../src/mode.ts"],"names":[],"mappings":"AAUA,2CAA2C;AAC3C,MAAM,CAAC,MAAM,YAAY,GAAS,QAAQ,CAAC;AAE3C,SAAS,MAAM,CAAC,KAAyB;IACvC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,KAAK,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW;IAClB,MAAM,IAAI,GAAI,UAAyE,CAAC,OAAO,CAAC;IAChG,OAAO,IAAI,EAAE,GAAG,EAAE,cAAc,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,UAAiB;IAC3C,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,IAAI,MAAM,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACpC,OAAO,UAAU,IAAI,YAAY,CAAC;AACpC,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Page-context scripts, written as self-contained functions so a
3
+ * {@link BrowserExecutor} can `.toString()`-serialize and run them in the
4
+ * browser (PRD §A.4). They MUST NOT close over any Node-side variable — every
5
+ * input arrives via an explicit argument, every output is JSON-serializable.
6
+ *
7
+ * The recorder controller (Node side) is the only caller; these are kept in a
8
+ * separate module so the serialized source stays small and reviewable.
9
+ */
10
+ /** Options forwarded into the in-page rrweb console plugin (PRD §D.3). */
11
+ export interface ConsolePluginOptions {
12
+ level?: string[];
13
+ lengthThreshold?: number;
14
+ stringifyOptions?: {
15
+ stringLengthLimit?: number;
16
+ numOfKeysLimit?: number;
17
+ depthOfLimit?: number;
18
+ };
19
+ }
20
+ /** Console-plugin defaults from PRD §D.3. */
21
+ export declare const DEFAULT_CONSOLE_PLUGIN_OPTIONS: ConsolePluginOptions;
22
+ /**
23
+ * The in-page init routine (PRD §D.3 + ADR-0006). Idempotent across calls via a
24
+ * monotonic `__tracelane__sessionId` stamp and a cooldown guard so hash-only /
25
+ * HMR navigations don't double-init the recorder (the cooldown / re-injection
26
+ * semantics are exercised in Task 2.4).
27
+ *
28
+ * Assumes `window.rrweb` is already defined (the recorder injects the rrweb
29
+ * bundle string first). Returns the active session id so the Node side can
30
+ * confirm whether a (re-)init actually took effect.
31
+ */
32
+ export declare function tracelaneInitScript(cooldownMs: number, consoleOptions: ConsolePluginOptions): number;
33
+ /**
34
+ * Read-and-clear drain (PRD §A.4 / §D.3). Returns the buffered events and resets
35
+ * the page buffer so the next drain doesn't double-count.
36
+ */
37
+ export declare function tracelaneDrainScript(): unknown[];
38
+ /**
39
+ * Append a `tracelane.nav` boundary marker (ADR-0006 / PRD §D.5) via rrweb's
40
+ * canonical custom-event API so the merged stream still has a navigation marker
41
+ * the player can render. No-op if rrweb isn't present.
42
+ */
43
+ export declare function tracelaneNavScript(url: string, ts: number): void;
44
+ //# sourceMappingURL=page-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-script.d.ts","sourceRoot":"","sources":["../src/page-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,0EAA0E;AAC1E,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE;QACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED,6CAA6C;AAC7C,eAAO,MAAM,8BAA8B,EAAE,oBAI5C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,oBAAoB,GACnC,MAAM,CA8CR;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,EAAE,CAKhD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAKhE"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Page-context scripts, written as self-contained functions so a
3
+ * {@link BrowserExecutor} can `.toString()`-serialize and run them in the
4
+ * browser (PRD §A.4). They MUST NOT close over any Node-side variable — every
5
+ * input arrives via an explicit argument, every output is JSON-serializable.
6
+ *
7
+ * The recorder controller (Node side) is the only caller; these are kept in a
8
+ * separate module so the serialized source stays small and reviewable.
9
+ */
10
+ /** Console-plugin defaults from PRD §D.3. */
11
+ export const DEFAULT_CONSOLE_PLUGIN_OPTIONS = {
12
+ level: ['info', 'log', 'warn', 'error'],
13
+ lengthThreshold: 10000,
14
+ stringifyOptions: { stringLengthLimit: 1000, numOfKeysLimit: 100, depthOfLimit: 1 },
15
+ };
16
+ /**
17
+ * The in-page init routine (PRD §D.3 + ADR-0006). Idempotent across calls via a
18
+ * monotonic `__tracelane__sessionId` stamp and a cooldown guard so hash-only /
19
+ * HMR navigations don't double-init the recorder (the cooldown / re-injection
20
+ * semantics are exercised in Task 2.4).
21
+ *
22
+ * Assumes `window.rrweb` is already defined (the recorder injects the rrweb
23
+ * bundle string first). Returns the active session id so the Node side can
24
+ * confirm whether a (re-)init actually took effect.
25
+ */
26
+ export function tracelaneInitScript(cooldownMs, consoleOptions) {
27
+ const w = window;
28
+ const now = Date.now();
29
+ // Cooldown: a very recent init means this is a hash/HMR re-render, not a real
30
+ // navigation — skip to avoid double-recording (ADR-0006).
31
+ if (w.__tracelane__inited !== undefined && now - w.__tracelane__inited < cooldownMs) {
32
+ return w.__tracelane__sessionId ?? 0;
33
+ }
34
+ w.__tracelane__inited = now;
35
+ w.__tracelane__sessionId = (w.__tracelane__sessionId ?? 0) + 1;
36
+ w.__tracelane__events = w.__tracelane__events ?? [];
37
+ if (w.rrweb !== undefined) {
38
+ // Tear down any prior recorder before starting a fresh one (re-injection).
39
+ if (typeof w.__tracelane__stop === 'function') {
40
+ try {
41
+ w.__tracelane__stop();
42
+ }
43
+ catch {
44
+ // ignore teardown errors from a destroyed page context
45
+ }
46
+ }
47
+ const stop = w.rrweb.record({
48
+ emit(event) {
49
+ // Never call console.* here — the console plugin patches console and
50
+ // guards recursion (PRD §D.4).
51
+ w.__tracelane__events.push(event);
52
+ },
53
+ plugins: [w.rrweb.getRecordConsolePlugin(consoleOptions)],
54
+ });
55
+ w.__tracelane__stop = typeof stop === 'function' ? stop : undefined;
56
+ }
57
+ return w.__tracelane__sessionId;
58
+ }
59
+ /**
60
+ * Read-and-clear drain (PRD §A.4 / §D.3). Returns the buffered events and resets
61
+ * the page buffer so the next drain doesn't double-count.
62
+ */
63
+ export function tracelaneDrainScript() {
64
+ const w = window;
65
+ const out = w.__tracelane__events ?? [];
66
+ w.__tracelane__events = [];
67
+ return out;
68
+ }
69
+ /**
70
+ * Append a `tracelane.nav` boundary marker (ADR-0006 / PRD §D.5) via rrweb's
71
+ * canonical custom-event API so the merged stream still has a navigation marker
72
+ * the player can render. No-op if rrweb isn't present.
73
+ */
74
+ export function tracelaneNavScript(url, ts) {
75
+ const w = window;
76
+ w.rrweb?.record?.addCustomEvent?.('tracelane.nav', { url, ts });
77
+ }
78
+ //# sourceMappingURL=page-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-script.js","sourceRoot":"","sources":["../src/page-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,6CAA6C;AAC7C,MAAM,CAAC,MAAM,8BAA8B,GAAyB;IAClE,KAAK,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC;IACvC,eAAe,EAAE,KAAK;IACtB,gBAAgB,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,EAAE;CACpF,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAkB,EAClB,cAAoC;IAEpC,MAAM,CAAC,GAAG,MAWT,CAAC;IAEF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,8EAA8E;IAC9E,0DAA0D;IAC1D,IAAI,CAAC,CAAC,mBAAmB,KAAK,SAAS,IAAI,GAAG,GAAG,CAAC,CAAC,mBAAmB,GAAG,UAAU,EAAE,CAAC;QACpF,OAAO,CAAC,CAAC,sBAAsB,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,CAAC,CAAC,mBAAmB,GAAG,GAAG,CAAC;IAC5B,CAAC,CAAC,sBAAsB,GAAG,CAAC,CAAC,CAAC,sBAAsB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/D,CAAC,CAAC,mBAAmB,GAAG,CAAC,CAAC,mBAAmB,IAAI,EAAE,CAAC;IAEpD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1B,2EAA2E;QAC3E,IAAI,OAAO,CAAC,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACH,CAAC,CAAC,iBAAiB,EAAE,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,uDAAuD;YACzD,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;YAC1B,IAAI,CAAC,KAAc;gBACjB,qEAAqE;gBACrE,+BAA+B;gBAC9B,CAAC,CAAC,mBAAiC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnD,CAAC;YACD,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;SAC1D,CAAC,CAAC;QACH,CAAC,CAAC,iBAAiB,GAAG,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAE,IAAmB,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,CAAC;IAED,OAAO,CAAC,CAAC,sBAAsB,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,CAAC,GAAG,MAAwD,CAAC;IACnE,MAAM,GAAG,GAAG,CAAC,CAAC,mBAAmB,IAAI,EAAE,CAAC;IACxC,CAAC,CAAC,mBAAmB,GAAG,EAAE,CAAC;IAC3B,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,EAAU;IACxD,MAAM,CAAC,GAAG,MAET,CAAC;IACF,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { eventWithTime } from '@cubenest/rrweb-core';
2
+ import type { BrowserExecutor } from './browser-executor.js';
3
+ import { type Mode } from './mode.js';
4
+ import { type ConsolePluginOptions } from './page-script.js';
5
+ /** Default re-injection cooldown in ms (ADR-0006). */
6
+ export declare const DEFAULT_COOLDOWN_MS = 250;
7
+ /** Default Node-side poll interval in ms (ADR-0006). */
8
+ export declare const DEFAULT_DRAIN_INTERVAL_MS = 5000;
9
+ export interface RecorderOptions {
10
+ /** The framework-agnostic driver (ADR-0004). */
11
+ executor: BrowserExecutor;
12
+ /**
13
+ * The rrweb UMD bundle source that defines `window.rrweb` (with `record` and
14
+ * `getRecordConsolePlugin`). Supplied by the consuming adapter — `@tracelane/core`
15
+ * is bundle-source-agnostic and never imports rrweb for in-page injection.
16
+ */
17
+ rrwebBundle: string;
18
+ /** Node-side drain poll interval (default 5000). */
19
+ drainIntervalMs?: number;
20
+ /** Re-injection cooldown guard (default 250). */
21
+ cooldownMs?: number;
22
+ /** Options forwarded to the in-page console plugin. */
23
+ consolePluginOptions?: ConsolePluginOptions;
24
+ /**
25
+ * Capture mode (ADR-0005). Default `'failed'`. The `TRACELANE_MODE` env var
26
+ * overrides this at {@link Recorder.finalize} time.
27
+ */
28
+ mode?: Mode;
29
+ }
30
+ /** Outcome handed to {@link Recorder.finalize}. */
31
+ export interface TestOutcome {
32
+ /** Whether the test passed. */
33
+ passed: boolean;
34
+ }
35
+ /** Decision returned by {@link Recorder.finalize}. */
36
+ export interface FinalizeResult {
37
+ /** Whether a report should be built for this test (ADR-0005). */
38
+ shouldBuildReport: boolean;
39
+ /** The events to build the report from (empty when discarded). */
40
+ events: eventWithTime[];
41
+ }
42
+ export interface Recorder {
43
+ /** Inject the rrweb bundle, install the in-page buffer, and start polling. */
44
+ start(): Promise<void>;
45
+ /**
46
+ * Re-inject after a navigation (ADR-0006). The in-page cooldown guard
47
+ * suppresses double-init on hash-only / HMR navigations; when a real re-init
48
+ * takes effect (the monotonic session id advances) a `tracelane.nav` boundary
49
+ * event is appended. Returns `true` if a re-init actually happened.
50
+ */
51
+ reinject(url: string): Promise<boolean>;
52
+ /** Read+clear the page buffer, merge into the Node buffer, return the batch. */
53
+ drain(): Promise<eventWithTime[]>;
54
+ /** Stop polling and perform a final drain. */
55
+ stop(): Promise<void>;
56
+ /**
57
+ * End the capture (ADR-0005): stop polling, drain any pending in-page events,
58
+ * then apply the mode policy. In `'failed'` mode a passing test discards the
59
+ * buffer and reports nothing; a failing test (or `'all'` mode) keeps the
60
+ * buffer and signals that a report should be built. `TRACELANE_MODE` overrides
61
+ * the configured mode here.
62
+ */
63
+ finalize(outcome: TestOutcome): Promise<FinalizeResult>;
64
+ /** The merged Node-side event buffer (live reference). */
65
+ getBuffer(): eventWithTime[];
66
+ }
67
+ export declare function createRecorder(options: RecorderOptions): Recorder;
68
+ //# sourceMappingURL=recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,KAAK,IAAI,EAAe,MAAM,WAAW,CAAC;AACnD,OAAO,EACL,KAAK,oBAAoB,EAK1B,MAAM,kBAAkB,CAAC;AAE1B,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,wDAAwD;AACxD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,+BAA+B;IAC/B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kEAAkE;IAClE,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,8EAA8E;IAC9E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,gFAAgF;IAChF,KAAK,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAClC,8CAA8C;IAC9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxD,0DAA0D;IAC1D,SAAS,IAAI,aAAa,EAAE,CAAC;CAC9B;AASD,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CA+FjE"}
@@ -0,0 +1,88 @@
1
+ import { resolveMode } from './mode.js';
2
+ import { DEFAULT_CONSOLE_PLUGIN_OPTIONS, tracelaneDrainScript, tracelaneInitScript, tracelaneNavScript, } from './page-script.js';
3
+ /** Default re-injection cooldown in ms (ADR-0006). */
4
+ export const DEFAULT_COOLDOWN_MS = 250;
5
+ /** Default Node-side poll interval in ms (ADR-0006). */
6
+ export const DEFAULT_DRAIN_INTERVAL_MS = 5000;
7
+ /** Inject + eval the rrweb bundle string in the page (defines `window.rrweb`). */
8
+ function injectBundleScript(bundle) {
9
+ // window.eval runs the bundle in global page scope (so `window.rrweb` becomes
10
+ // a real global), which is the intended injection behavior in the page context.
11
+ window.eval(bundle);
12
+ }
13
+ export function createRecorder(options) {
14
+ const { executor, rrwebBundle, drainIntervalMs = DEFAULT_DRAIN_INTERVAL_MS, cooldownMs = DEFAULT_COOLDOWN_MS, consolePluginOptions = DEFAULT_CONSOLE_PLUGIN_OPTIONS, mode: configMode, } = options;
15
+ const buffer = [];
16
+ let pollTimer;
17
+ let started = false;
18
+ // Last session id we've observed from the page; advances only when an init
19
+ // actually takes effect (i.e. wasn't suppressed by the cooldown guard).
20
+ let lastSessionId = 0;
21
+ /** Run the init script in-page and return the active session id. */
22
+ async function runInit() {
23
+ return executor.execute(tracelaneInitScript, cooldownMs, consolePluginOptions);
24
+ }
25
+ async function inject() {
26
+ await executor.execute(injectBundleScript, rrwebBundle);
27
+ lastSessionId = await runInit();
28
+ }
29
+ async function reinject(url) {
30
+ // Re-eval the bundle (the page may have been torn down by navigation), then
31
+ // re-run init. The cooldown guard inside the init script decides whether a
32
+ // fresh recorder actually starts.
33
+ await executor.execute(injectBundleScript, rrwebBundle);
34
+ const sessionId = await runInit();
35
+ if (sessionId <= lastSessionId) {
36
+ // Suppressed by cooldown — no navigation boundary to record.
37
+ return false;
38
+ }
39
+ lastSessionId = sessionId;
40
+ await executor.execute(tracelaneNavScript, url, Date.now());
41
+ return true;
42
+ }
43
+ async function drain() {
44
+ const batch = (await executor.execute(tracelaneDrainScript));
45
+ if (batch && batch.length > 0) {
46
+ buffer.push(...batch);
47
+ return batch;
48
+ }
49
+ return [];
50
+ }
51
+ async function start() {
52
+ if (started)
53
+ return;
54
+ started = true;
55
+ await inject();
56
+ pollTimer = setInterval(() => {
57
+ void drain();
58
+ }, drainIntervalMs);
59
+ }
60
+ async function stop() {
61
+ if (pollTimer !== undefined) {
62
+ clearInterval(pollTimer);
63
+ pollTimer = undefined;
64
+ }
65
+ started = false;
66
+ await drain();
67
+ }
68
+ async function finalize(outcome) {
69
+ await stop();
70
+ const mode = resolveMode(configMode);
71
+ const shouldBuildReport = mode === 'all' || !outcome.passed;
72
+ if (!shouldBuildReport) {
73
+ // Discard: passing test in 'failed' mode keeps near-zero artifact cost.
74
+ buffer.length = 0;
75
+ return { shouldBuildReport: false, events: [] };
76
+ }
77
+ return { shouldBuildReport: true, events: buffer };
78
+ }
79
+ return {
80
+ start,
81
+ reinject,
82
+ drain,
83
+ stop,
84
+ finalize,
85
+ getBuffer: () => buffer,
86
+ };
87
+ }
88
+ //# sourceMappingURL=recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { eventWithTime } from '@cubenest/rrweb-core';
2
+ /**
3
+ * Hard cap on the serialized events size (ADR-0005): 25 MB. Calibrated against
4
+ * GitHub Actions artifact limits and rrweb's typical compressed size for
5
+ * ~5-minute interactive tests; anything larger is slow to open in a browser.
6
+ */
7
+ export declare const MAX_REPORT_BYTES: number;
8
+ /** Tag of the custom rrweb event emitted when a prune fires (ADR-0005). */
9
+ export declare const PRUNE_EVENT_TAG = "tracelane.events-pruned";
10
+ /** UTF-8 byte length of the JSON serialization of `events`. */
11
+ export declare function serializedSize(events: readonly eventWithTime[]): number;
12
+ /** Payload of the {@link PRUNE_EVENT_TAG} marker. */
13
+ export interface PruneEventPayload {
14
+ /** Number of IncrementalSnapshot events dropped to fit the budget. */
15
+ droppedCount: number;
16
+ /** The byte budget the prune targeted. */
17
+ maxBytes: number;
18
+ }
19
+ export interface PruneResult {
20
+ /** The (possibly pruned) events, including the prune marker when fired. */
21
+ events: eventWithTime[];
22
+ /** Whether any events were dropped. */
23
+ pruned: boolean;
24
+ /** How many IncrementalSnapshot events were dropped. */
25
+ droppedCount: number;
26
+ }
27
+ /**
28
+ * Prune `events` to fit `maxBytes` (ADR-0005), dropping the OLDEST
29
+ * IncrementalSnapshot (type 3) events first while preserving FullSnapshot /
30
+ * Meta / Custom / Plugin. Surviving events keep their relative order. When any
31
+ * event is dropped, a single {@link PRUNE_EVENT_TAG} custom event is appended
32
+ * so the report can surface a "events pruned to fit budget" banner.
33
+ *
34
+ * If dropping every IncrementalSnapshot still doesn't fit (preserved events
35
+ * alone exceed the budget), it keeps the preserved events — replay correctness
36
+ * wins over the byte cap, and the prune marker still records what happened.
37
+ */
38
+ export declare function pruneToSizeBudget(events: readonly eventWithTime[], maxBytes?: number): PruneResult;
39
+ //# sourceMappingURL=size-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"size-guard.d.ts","sourceRoot":"","sources":["../src/size-guard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAmB,CAAC;AAEjD,2EAA2E;AAC3E,eAAO,MAAM,eAAe,4BAA4B,CAAC;AAiBzD,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAEvE;AAED,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,QAAQ,GAAE,MAAyB,GAClC,WAAW,CA2Bb"}
@@ -0,0 +1,70 @@
1
+ import { EventType } from '@cubenest/rrweb-core';
2
+ /**
3
+ * Hard cap on the serialized events size (ADR-0005): 25 MB. Calibrated against
4
+ * GitHub Actions artifact limits and rrweb's typical compressed size for
5
+ * ~5-minute interactive tests; anything larger is slow to open in a browser.
6
+ */
7
+ export const MAX_REPORT_BYTES = 25 * 1024 * 1024;
8
+ /** Tag of the custom rrweb event emitted when a prune fires (ADR-0005). */
9
+ export const PRUNE_EVENT_TAG = 'tracelane.events-pruned';
10
+ /**
11
+ * Event types that must survive a prune (ADR-0005): FullSnapshot checkpoints
12
+ * are mandatory for replay, and Meta / Custom / Plugin carry structural and
13
+ * panel context. Only IncrementalSnapshot (the largest, lowest-value-per-byte
14
+ * category) is droppable.
15
+ */
16
+ const PRESERVED_TYPES = new Set([
17
+ EventType.FullSnapshot, // 2
18
+ EventType.Meta, // 4
19
+ EventType.Custom, // 5
20
+ EventType.Plugin, // 6
21
+ ]);
22
+ const encoder = new TextEncoder();
23
+ /** UTF-8 byte length of the JSON serialization of `events`. */
24
+ export function serializedSize(events) {
25
+ return encoder.encode(JSON.stringify(events)).length;
26
+ }
27
+ function makePruneEvent(payload) {
28
+ return {
29
+ type: EventType.Custom,
30
+ data: { tag: PRUNE_EVENT_TAG, payload },
31
+ timestamp: Date.now(),
32
+ };
33
+ }
34
+ /**
35
+ * Prune `events` to fit `maxBytes` (ADR-0005), dropping the OLDEST
36
+ * IncrementalSnapshot (type 3) events first while preserving FullSnapshot /
37
+ * Meta / Custom / Plugin. Surviving events keep their relative order. When any
38
+ * event is dropped, a single {@link PRUNE_EVENT_TAG} custom event is appended
39
+ * so the report can surface a "events pruned to fit budget" banner.
40
+ *
41
+ * If dropping every IncrementalSnapshot still doesn't fit (preserved events
42
+ * alone exceed the budget), it keeps the preserved events — replay correctness
43
+ * wins over the byte cap, and the prune marker still records what happened.
44
+ */
45
+ export function pruneToSizeBudget(events, maxBytes = MAX_REPORT_BYTES) {
46
+ if (serializedSize(events) <= maxBytes) {
47
+ return { events: [...events], pruned: false, droppedCount: 0 };
48
+ }
49
+ // Indices of droppable (IncrementalSnapshot) events, oldest first. The input
50
+ // is already in chronological order, so array order == chronological order.
51
+ const droppableIndices = [];
52
+ events.forEach((e, i) => {
53
+ if (!PRESERVED_TYPES.has(e.type))
54
+ droppableIndices.push(i);
55
+ });
56
+ const dropped = new Set();
57
+ for (const index of droppableIndices) {
58
+ if (serializedSize(events.filter((_, i) => !dropped.has(i))) <= maxBytes)
59
+ break;
60
+ dropped.add(index);
61
+ }
62
+ const droppedCount = dropped.size;
63
+ const survivors = events.filter((_, i) => !dropped.has(i));
64
+ if (droppedCount === 0) {
65
+ return { events: survivors, pruned: false, droppedCount: 0 };
66
+ }
67
+ survivors.push(makePruneEvent({ droppedCount, maxBytes }));
68
+ return { events: survivors, pruned: true, droppedCount };
69
+ }
70
+ //# sourceMappingURL=size-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"size-guard.js","sourceRoot":"","sources":["../src/size-guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,eAAe,GAA2B,IAAI,GAAG,CAAC;IACtD,SAAS,CAAC,YAAY,EAAE,IAAI;IAC5B,SAAS,CAAC,IAAI,EAAE,IAAI;IACpB,SAAS,CAAC,MAAM,EAAE,IAAI;IACtB,SAAS,CAAC,MAAM,EAAE,IAAI;CACvB,CAAC,CAAC;AAEH,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC,+DAA+D;AAC/D,MAAM,UAAU,cAAc,CAAC,MAAgC;IAC7D,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AACvD,CAAC;AAmBD,SAAS,cAAc,CAAC,OAA0B;IAChD,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,MAAM;QACtB,IAAI,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE;QACvC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACM,CAAC;AAChC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAgC,EAChC,WAAmB,gBAAgB;IAEnC,IAAI,cAAc,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACvC,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IACjE,CAAC;IAED,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,IAAI,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ;YAAE,MAAM;QAChF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAClC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3D,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IAC/D,CAAC;IAED,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAC3D,CAAC"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@tracelane/core",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Framework-agnostic rrweb recording engine for tracelane. Wraps a per-framework browser object behind a common BrowserExecutor interface.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist", "NOTICE", "README.md"],
16
+ "scripts": {
17
+ "build": "tsc -p tsconfig.json",
18
+ "typecheck": "tsc -p tsconfig.json --noEmit",
19
+ "test": "vitest run --passWithNoTests"
20
+ },
21
+ "dependencies": {
22
+ "@cubenest/rrweb-core": "workspace:*"
23
+ },
24
+ "devDependencies": {
25
+ "jsdom": "^25.0.1"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "provenance": true
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/Cubenest/rrweb-stack.git",
34
+ "directory": "packages/tracelane-core"
35
+ },
36
+ "homepage": "https://github.com/Cubenest/rrweb-stack/tree/main/packages/tracelane-core#readme"
37
+ }