@reactra/replay 0.1.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/index.d.ts +225 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +344 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Akhil Shastri and the Reactra contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @reactra/replay
|
|
2
|
+
|
|
3
|
+
> Reactra session replay — recording, streaming, re-drive, and diffing of Reactra app state.
|
|
4
|
+
|
|
5
|
+
**Alpha** — published under the npm dist-tag `alpha`. APIs may change before 1.0.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @reactra/replay@alpha
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Part of [Reactra](https://github.com/akhilshastri/reactra) — a compiler-first,
|
|
12
|
+
React-19-compatible framework. See the [documentation](https://reactra-docs.vercel.app) to get
|
|
13
|
+
started.
|
|
14
|
+
|
|
15
|
+
## License
|
|
16
|
+
|
|
17
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { ReplayEvent, SessionBundle } from "@reactra/behaviours/replayable";
|
|
2
|
+
export type { ActionEvent, GapEvent, MountEvent, ReplayEvent, ResourceEvent, SessionBundle, StateSnapshotEvent, } from "@reactra/behaviours/replayable";
|
|
3
|
+
/**
|
|
4
|
+
* Keys whose value changed between two folded states (shallow `!==` on
|
|
5
|
+
* JSON-stable values — the same comparison the recorder's deltas use).
|
|
6
|
+
* Keys present only in `cur` count as changed; keys only in `prev` don't
|
|
7
|
+
* (the panel highlights what the current stop introduced).
|
|
8
|
+
*
|
|
9
|
+
* Promoted here from `@reactra/replay-devtools/helpers` (diff plan §2) so
|
|
10
|
+
* `diffBundles` can use it without inverting the dependency graph.
|
|
11
|
+
* `@reactra/replay-devtools` re-exports this symbol to preserve all existing
|
|
12
|
+
* import paths (COND-2).
|
|
13
|
+
*/
|
|
14
|
+
export declare const diffKeys: (prev: Record<string, unknown> | undefined, cur: Record<string, unknown>) => string[];
|
|
15
|
+
/** One key that differs between two aligned states. */
|
|
16
|
+
export interface KeyDiff {
|
|
17
|
+
/** The state key. */
|
|
18
|
+
key: string;
|
|
19
|
+
/**
|
|
20
|
+
* The value in bundle A at this ordinal.
|
|
21
|
+
*
|
|
22
|
+
* `undefined` means the key was absent on that side — indistinguishable
|
|
23
|
+
* from a recorded literal `undefined` (moot under JSON, which drops
|
|
24
|
+
* `undefined` values). Known v1 limitation (diff plan §4 COND-5).
|
|
25
|
+
*/
|
|
26
|
+
valueA: unknown;
|
|
27
|
+
/**
|
|
28
|
+
* The value in bundle B at this ordinal.
|
|
29
|
+
* See `valueA` note on `undefined` semantics.
|
|
30
|
+
*/
|
|
31
|
+
valueB: unknown;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Symmetric key diff: keys changed between `a` and `b`, keys only in `a`,
|
|
35
|
+
* and keys only in `b`.
|
|
36
|
+
*
|
|
37
|
+
* Unlike `diffKeys`, which reports from `cur`'s perspective only, this
|
|
38
|
+
* variant is symmetric — suitable for cross-bundle comparison where neither
|
|
39
|
+
* side is the "authoritative" current state.
|
|
40
|
+
*/
|
|
41
|
+
export declare const diffKeysSymmetric: (a: Record<string, unknown> | undefined, b: Record<string, unknown> | undefined) => {
|
|
42
|
+
changed: KeyDiff[];
|
|
43
|
+
onlyA: string[];
|
|
44
|
+
onlyB: string[];
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Headless navigator over a recorded `SessionBundle` (Replay spec §5). Pure data
|
|
48
|
+
* — no React state setters, no live re-drive (RLIM-07). For tests, devtools
|
|
49
|
+
* timeline panels, and bug-report inspection.
|
|
50
|
+
*/
|
|
51
|
+
export declare class ReplayPlayer {
|
|
52
|
+
/** The recorded bundle under navigation. (Declared explicitly, not as a constructor parameter property — Node strip-only mode rejects parameter properties, and package source must import under plain Node per CLAUDE.md §6 / Runtime §2.) */
|
|
53
|
+
private readonly bundle;
|
|
54
|
+
/** All snapshots, in recorded order — built once for O(log n) navigation (v1.6). */
|
|
55
|
+
private readonly snapshots;
|
|
56
|
+
/** Whether the bundle uses delta semantics ("2.1"); "2.0" reads as all-keyframes (§3). */
|
|
57
|
+
private readonly deltaAware;
|
|
58
|
+
constructor(bundle: SessionBundle);
|
|
59
|
+
/**
|
|
60
|
+
* SINGLE-INSTANCE fold (§5): the state of the instance owning the nearest
|
|
61
|
+
* snapshot ≤ `timestamp`. Correct as a whole-app answer only for
|
|
62
|
+
* single-component bundles — multi-component consumers use `statesAt`
|
|
63
|
+
* (v1.8 normative note). v1.6: binary-search the nearest snapshot ≤ t,
|
|
64
|
+
* walk back to that INSTANCE's nearest keyframe, and fold its deltas
|
|
65
|
+
* forward to t — O(log n + fold-depth), fold-depth bounded by
|
|
66
|
+
* `keyframeEvery`. A "2.0" bundle (every snapshot full) folds trivially.
|
|
67
|
+
*/
|
|
68
|
+
stateAt(timestamp: number): Record<string, unknown>;
|
|
69
|
+
/**
|
|
70
|
+
* MULTI-INSTANCE fold (§5 v1.8): every recorded instance's state as of
|
|
71
|
+
* `timestamp`. Keys are FULL componentIds — instance suffix included
|
|
72
|
+
* (`"Board#1"`); callers feeding `applyReplayState` (which targets a
|
|
73
|
+
* component NAME) strip the suffix themselves. Per instance the fold
|
|
74
|
+
* starts at its latest keyframe ≤ t (§3 semantics); instances with no
|
|
75
|
+
* snapshot ≤ t are absent. One pass over the snapshot index — keyframes
|
|
76
|
+
* reset the fold, deltas merge into it, exactly the recorder's semantics.
|
|
77
|
+
*/
|
|
78
|
+
statesAt(timestamp: number): Map<string, Record<string, unknown>>;
|
|
79
|
+
/**
|
|
80
|
+
* The navigable URL recorded as of `timestamp` (§5) — sugar over `statesAt`,
|
|
81
|
+
* which folds the synthetic `Route#1` snapshot exactly like any instance.
|
|
82
|
+
* `undefined` when the session carries no route snapshot ≤ t.
|
|
83
|
+
*/
|
|
84
|
+
routeAt(timestamp: number): string | undefined;
|
|
85
|
+
/** Events within `[from, to]` (inclusive) — for stepping a timeline UI (§5). */
|
|
86
|
+
eventsBetween(from: number, to: number): ReplayEvent[];
|
|
87
|
+
/** All recorded events, in order. */
|
|
88
|
+
get events(): readonly ReplayEvent[];
|
|
89
|
+
/** Total session duration in ms. */
|
|
90
|
+
get duration(): number;
|
|
91
|
+
/**
|
|
92
|
+
* Fold all instance states up to and including the snapshot at position
|
|
93
|
+
* `endIndex` in the global snapshot array. Used by `diffBundles` to pair
|
|
94
|
+
* snapshots by ordinal rather than timestamp, so that same-timestamp
|
|
95
|
+
* clustered commits are each treated as a distinct ordinal (B1 fix).
|
|
96
|
+
*
|
|
97
|
+
* Not exposed in the public API — consumers use `statesAt(timestamp)`.
|
|
98
|
+
*/
|
|
99
|
+
statesUpToIndex(endIndex: number): Map<string, Record<string, unknown>>;
|
|
100
|
+
/** Binary search: the index of the last snapshot with `timestamp ≤ t`, or -1. */
|
|
101
|
+
private lastSnapshotIndexAtOrBefore;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Filter options for `diffBundles`. v1 supports a single `componentName`
|
|
105
|
+
* filter (the component base name, without the `#N` instance suffix — e.g.
|
|
106
|
+
* `"PulseDashboard"` matches `"PulseDashboard#1"`, `"PulseDashboard#2"`, …).
|
|
107
|
+
* Omit to diff all instances present in either bundle.
|
|
108
|
+
*/
|
|
109
|
+
export interface BundleDiffOptions {
|
|
110
|
+
/**
|
|
111
|
+
* Restrict the diff to instances whose componentId starts with this name.
|
|
112
|
+
*
|
|
113
|
+
* - Pass a base name (`"Board"`) to include all instances: `Board#1`,
|
|
114
|
+
* `Board#2`, etc. (`componentId.startsWith("Board#")`).
|
|
115
|
+
* - Pass a full componentId (`"Board#2"`) to target exactly one instance
|
|
116
|
+
* (`componentId === "Board#2"`).
|
|
117
|
+
* - Omit to diff all instances present in either bundle.
|
|
118
|
+
*/
|
|
119
|
+
componentName?: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The diff at one aligned ordinal for a component instance (diff plan §4).
|
|
123
|
+
* `changedKeys` is empty when both states agree at this stop.
|
|
124
|
+
*/
|
|
125
|
+
export interface AlignedStop {
|
|
126
|
+
/** 0-based pair index — A's k-th snapshot ↔ B's k-th. */
|
|
127
|
+
ordinal: number;
|
|
128
|
+
/** Offset from bundle A's `startTime` (ms). */
|
|
129
|
+
offsetA: number;
|
|
130
|
+
/** Offset from bundle B's `startTime` (ms). */
|
|
131
|
+
offsetB: number;
|
|
132
|
+
/**
|
|
133
|
+
* Global snapshot-array index in bundle A used for this ordinal.
|
|
134
|
+
* Pass to `ReplayPlayer.statesUpToIndex` to read the exact state
|
|
135
|
+
* the diff engine saw — avoids same-timestamp ambiguity.
|
|
136
|
+
*/
|
|
137
|
+
globalIndexA: number;
|
|
138
|
+
/** Global snapshot-array index in bundle B for this ordinal. */
|
|
139
|
+
globalIndexB: number;
|
|
140
|
+
/**
|
|
141
|
+
* Per-key diffs at this ordinal (empty array = states agree).
|
|
142
|
+
* Keys absent in both sides are not listed.
|
|
143
|
+
*/
|
|
144
|
+
changedKeys: KeyDiff[];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The full diff for one component instance across both bundles (diff plan §4).
|
|
148
|
+
*
|
|
149
|
+
* When `presence === "both"`, `stops` contains all aligned (paired) ordinals;
|
|
150
|
+
* `unpairedA`/`unpairedB` hold the leftover stop offsets on the longer side.
|
|
151
|
+
* When `presence !== "both"`, `stops` is empty.
|
|
152
|
+
*/
|
|
153
|
+
export interface InstanceDiff {
|
|
154
|
+
/** Full componentId including instance suffix (e.g. `"Board#1"`). */
|
|
155
|
+
componentId: string;
|
|
156
|
+
/** Whether this instance was observed in both bundles, or only one. */
|
|
157
|
+
presence: "both" | "onlyA" | "onlyB";
|
|
158
|
+
/** Aligned stop diffs; populated only when `presence === "both"`. */
|
|
159
|
+
stops: AlignedStop[];
|
|
160
|
+
/**
|
|
161
|
+
* When `presence === "both"`: offsets (relative to A's `startTime`, ms) for
|
|
162
|
+
* A's snapshots beyond the paired region (the tail past `min(countA, countB)`).
|
|
163
|
+
* When `presence === "onlyA"`: ALL of A's snapshot offsets (no paired region
|
|
164
|
+
* exists). Empty when `presence === "onlyB"`.
|
|
165
|
+
*/
|
|
166
|
+
unpairedA: number[];
|
|
167
|
+
/**
|
|
168
|
+
* When `presence === "both"`: offsets (relative to B's `startTime`, ms) for
|
|
169
|
+
* B's snapshots beyond the paired region.
|
|
170
|
+
* When `presence === "onlyB"`: ALL of B's snapshot offsets.
|
|
171
|
+
* Empty when `presence === "onlyA"`.
|
|
172
|
+
*/
|
|
173
|
+
unpairedB: number[];
|
|
174
|
+
/**
|
|
175
|
+
* Action names fired in A but not B (min-truncated ordinal comparison —
|
|
176
|
+
* no LCS; diff plan §3 deferred).
|
|
177
|
+
*/
|
|
178
|
+
actionsOnlyA: string[];
|
|
179
|
+
/**
|
|
180
|
+
* Action names fired in B but not A.
|
|
181
|
+
*/
|
|
182
|
+
actionsOnlyB: string[];
|
|
183
|
+
/**
|
|
184
|
+
* Resource names fetched in A but not B.
|
|
185
|
+
*/
|
|
186
|
+
resourcesOnlyA: string[];
|
|
187
|
+
/**
|
|
188
|
+
* Resource names fetched in B but not A.
|
|
189
|
+
*/
|
|
190
|
+
resourcesOnlyB: string[];
|
|
191
|
+
}
|
|
192
|
+
/** The top-level output of `diffBundles`. */
|
|
193
|
+
export interface DiffResult {
|
|
194
|
+
/** One entry per distinct componentId present in either bundle. */
|
|
195
|
+
instances: InstanceDiff[];
|
|
196
|
+
/**
|
|
197
|
+
* Total count of aligned stop pairs where at least one key differs.
|
|
198
|
+
* 0 = the two sessions agree on all observed state.
|
|
199
|
+
*/
|
|
200
|
+
divergentStopCount: number;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Compare two recorded `SessionBundle`s and return the structured diff
|
|
204
|
+
* (diff plan §4).
|
|
205
|
+
*
|
|
206
|
+
* Alignment: per-instance event ordinal — A's k-th snapshot ↔ B's k-th.
|
|
207
|
+
* Correct when both sessions ran the same component through a comparable
|
|
208
|
+
* commit sequence. Pairs truncate at `min(stopsA, stopsB)`; the unpaired
|
|
209
|
+
* tail is reported as `unpairedA`/`unpairedB`.
|
|
210
|
+
*
|
|
211
|
+
* Version-aware: reads state through `ReplayPlayer.statesAt` so 2.0
|
|
212
|
+
* (all-keyframe) and 2.1 (delta) bundles fold to equal states when the
|
|
213
|
+
* recorded logical state is the same (diff plan §6 scenario 6).
|
|
214
|
+
*
|
|
215
|
+
* Pure — no React, no DOM, no module singletons (COND-4). Safe to call in
|
|
216
|
+
* tests, workers, and server-side code.
|
|
217
|
+
*
|
|
218
|
+
* @remarks v1 aligns by per-instance snapshot ordinal only. The following are
|
|
219
|
+
* explicitly deferred (Replay §5): LCS/fuzzy action-sequence alignment,
|
|
220
|
+
* multi-instance fuzzy matching (e.g. `Board#2` ↔ `Board#3`), normalized-time
|
|
221
|
+
* or route-aware alignment, deep nested-value diff (beyond shallow JSON
|
|
222
|
+
* compare), three-way diff, and drive-in-compare.
|
|
223
|
+
*/
|
|
224
|
+
export declare const diffBundles: (a: SessionBundle, b: SessionBundle, opts?: BundleDiffOptions) => DiffResult;
|
|
225
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// @reactra/replay — headless session playback (Replay spec §5).
|
|
2
|
+
//
|
|
3
|
+
// Owner spec: reactra-replay-spec.md §5; package placement per Runtime v1 §2's
|
|
4
|
+
// boundary rule — anything compiler-emitted code calls (the channel,
|
|
5
|
+
// `configureReplay`, `finalizeReplay`, `useReplayCapture`, the live re-drive)
|
|
6
|
+
// lives in `@reactra/behaviours/replayable`; this package only CONSUMES a
|
|
7
|
+
// recorded `SessionBundle`: the headless `ReplayPlayer` (tests, devtools,
|
|
8
|
+
// bug-report inspection — pure data, no React). Live re-drive is the
|
|
9
|
+
// behaviours-side `applyReplayState` (Replay §5 v1.4); the recording-format
|
|
10
|
+
// types are owned by the recorder module and re-exported here for convenience.
|
|
11
|
+
/**
|
|
12
|
+
* Keys whose value changed between two folded states (shallow `!==` on
|
|
13
|
+
* JSON-stable values — the same comparison the recorder's deltas use).
|
|
14
|
+
* Keys present only in `cur` count as changed; keys only in `prev` don't
|
|
15
|
+
* (the panel highlights what the current stop introduced).
|
|
16
|
+
*
|
|
17
|
+
* Promoted here from `@reactra/replay-devtools/helpers` (diff plan §2) so
|
|
18
|
+
* `diffBundles` can use it without inverting the dependency graph.
|
|
19
|
+
* `@reactra/replay-devtools` re-exports this symbol to preserve all existing
|
|
20
|
+
* import paths (COND-2).
|
|
21
|
+
*/
|
|
22
|
+
export const diffKeys = (prev, cur) => {
|
|
23
|
+
if (!prev)
|
|
24
|
+
return Object.keys(cur);
|
|
25
|
+
return Object.keys(cur).filter((k) => !(k in prev) || JSON.stringify(prev[k]) !== JSON.stringify(cur[k]));
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Symmetric key diff: keys changed between `a` and `b`, keys only in `a`,
|
|
29
|
+
* and keys only in `b`.
|
|
30
|
+
*
|
|
31
|
+
* Unlike `diffKeys`, which reports from `cur`'s perspective only, this
|
|
32
|
+
* variant is symmetric — suitable for cross-bundle comparison where neither
|
|
33
|
+
* side is the "authoritative" current state.
|
|
34
|
+
*/
|
|
35
|
+
export const diffKeysSymmetric = (a, b) => {
|
|
36
|
+
const aKeys = new Set(a ? Object.keys(a) : []);
|
|
37
|
+
const bKeys = new Set(b ? Object.keys(b) : []);
|
|
38
|
+
const changed = [];
|
|
39
|
+
const onlyA = [];
|
|
40
|
+
const onlyB = [];
|
|
41
|
+
for (const k of aKeys) {
|
|
42
|
+
if (!bKeys.has(k)) {
|
|
43
|
+
onlyA.push(k);
|
|
44
|
+
}
|
|
45
|
+
else if (JSON.stringify(a[k]) !== JSON.stringify(b[k])) {
|
|
46
|
+
changed.push({ key: k, valueA: a[k], valueB: b[k] });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const k of bKeys) {
|
|
50
|
+
if (!aKeys.has(k))
|
|
51
|
+
onlyB.push(k);
|
|
52
|
+
}
|
|
53
|
+
return { changed, onlyA, onlyB };
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Headless navigator over a recorded `SessionBundle` (Replay spec §5). Pure data
|
|
57
|
+
* — no React state setters, no live re-drive (RLIM-07). For tests, devtools
|
|
58
|
+
* timeline panels, and bug-report inspection.
|
|
59
|
+
*/
|
|
60
|
+
export class ReplayPlayer {
|
|
61
|
+
/** The recorded bundle under navigation. (Declared explicitly, not as a constructor parameter property — Node strip-only mode rejects parameter properties, and package source must import under plain Node per CLAUDE.md §6 / Runtime §2.) */
|
|
62
|
+
bundle;
|
|
63
|
+
/** All snapshots, in recorded order — built once for O(log n) navigation (v1.6). */
|
|
64
|
+
snapshots;
|
|
65
|
+
/** Whether the bundle uses delta semantics ("2.1"); "2.0" reads as all-keyframes (§3). */
|
|
66
|
+
deltaAware;
|
|
67
|
+
constructor(bundle) {
|
|
68
|
+
this.bundle = bundle;
|
|
69
|
+
this.snapshots = bundle.events.filter((e) => e.type === "state_snapshot");
|
|
70
|
+
this.deltaAware = bundle.version === "2.1";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* SINGLE-INSTANCE fold (§5): the state of the instance owning the nearest
|
|
74
|
+
* snapshot ≤ `timestamp`. Correct as a whole-app answer only for
|
|
75
|
+
* single-component bundles — multi-component consumers use `statesAt`
|
|
76
|
+
* (v1.8 normative note). v1.6: binary-search the nearest snapshot ≤ t,
|
|
77
|
+
* walk back to that INSTANCE's nearest keyframe, and fold its deltas
|
|
78
|
+
* forward to t — O(log n + fold-depth), fold-depth bounded by
|
|
79
|
+
* `keyframeEvery`. A "2.0" bundle (every snapshot full) folds trivially.
|
|
80
|
+
*/
|
|
81
|
+
stateAt(timestamp) {
|
|
82
|
+
const idx = this.lastSnapshotIndexAtOrBefore(timestamp);
|
|
83
|
+
if (idx < 0)
|
|
84
|
+
return {};
|
|
85
|
+
const target = this.snapshots[idx];
|
|
86
|
+
if (!this.deltaAware)
|
|
87
|
+
return target.state;
|
|
88
|
+
// Walk back to the instance's keyframe (first-per-instance is guaranteed).
|
|
89
|
+
let start = idx;
|
|
90
|
+
while (start >= 0) {
|
|
91
|
+
const s = this.snapshots[start];
|
|
92
|
+
if (s.componentId === target.componentId && s.keyframe)
|
|
93
|
+
break;
|
|
94
|
+
start--;
|
|
95
|
+
}
|
|
96
|
+
if (start < 0)
|
|
97
|
+
return target.state; // defensive: no keyframe found — treat as full
|
|
98
|
+
const folded = { ...this.snapshots[start].state };
|
|
99
|
+
for (let i = start + 1; i <= idx; i++) {
|
|
100
|
+
const s = this.snapshots[i];
|
|
101
|
+
if (s.componentId !== target.componentId)
|
|
102
|
+
continue;
|
|
103
|
+
Object.assign(folded, s.state);
|
|
104
|
+
}
|
|
105
|
+
return folded;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* MULTI-INSTANCE fold (§5 v1.8): every recorded instance's state as of
|
|
109
|
+
* `timestamp`. Keys are FULL componentIds — instance suffix included
|
|
110
|
+
* (`"Board#1"`); callers feeding `applyReplayState` (which targets a
|
|
111
|
+
* component NAME) strip the suffix themselves. Per instance the fold
|
|
112
|
+
* starts at its latest keyframe ≤ t (§3 semantics); instances with no
|
|
113
|
+
* snapshot ≤ t are absent. One pass over the snapshot index — keyframes
|
|
114
|
+
* reset the fold, deltas merge into it, exactly the recorder's semantics.
|
|
115
|
+
*/
|
|
116
|
+
statesAt(timestamp) {
|
|
117
|
+
const out = new Map();
|
|
118
|
+
const end = this.lastSnapshotIndexAtOrBefore(timestamp);
|
|
119
|
+
for (let i = 0; i <= end; i++) {
|
|
120
|
+
const s = this.snapshots[i];
|
|
121
|
+
// A "2.0" bundle has no keyframe flags — every snapshot is full state.
|
|
122
|
+
if (!this.deltaAware || s.keyframe)
|
|
123
|
+
out.set(s.componentId, { ...s.state });
|
|
124
|
+
else
|
|
125
|
+
out.set(s.componentId, { ...(out.get(s.componentId) ?? {}), ...s.state });
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The navigable URL recorded as of `timestamp` (§5) — sugar over `statesAt`,
|
|
131
|
+
* which folds the synthetic `Route#1` snapshot exactly like any instance.
|
|
132
|
+
* `undefined` when the session carries no route snapshot ≤ t.
|
|
133
|
+
*/
|
|
134
|
+
routeAt(timestamp) {
|
|
135
|
+
const route = this.statesAt(timestamp).get("Route#1");
|
|
136
|
+
return route?.path;
|
|
137
|
+
}
|
|
138
|
+
/** Events within `[from, to]` (inclusive) — for stepping a timeline UI (§5). */
|
|
139
|
+
eventsBetween(from, to) {
|
|
140
|
+
return this.bundle.events.filter((e) => e.timestamp >= from && e.timestamp <= to);
|
|
141
|
+
}
|
|
142
|
+
/** All recorded events, in order. */
|
|
143
|
+
get events() {
|
|
144
|
+
return this.bundle.events;
|
|
145
|
+
}
|
|
146
|
+
/** Total session duration in ms. */
|
|
147
|
+
get duration() {
|
|
148
|
+
return this.bundle.duration;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Fold all instance states up to and including the snapshot at position
|
|
152
|
+
* `endIndex` in the global snapshot array. Used by `diffBundles` to pair
|
|
153
|
+
* snapshots by ordinal rather than timestamp, so that same-timestamp
|
|
154
|
+
* clustered commits are each treated as a distinct ordinal (B1 fix).
|
|
155
|
+
*
|
|
156
|
+
* Not exposed in the public API — consumers use `statesAt(timestamp)`.
|
|
157
|
+
*/
|
|
158
|
+
statesUpToIndex(endIndex) {
|
|
159
|
+
const out = new Map();
|
|
160
|
+
for (let i = 0; i <= endIndex && i < this.snapshots.length; i++) {
|
|
161
|
+
const s = this.snapshots[i];
|
|
162
|
+
if (!this.deltaAware || s.keyframe)
|
|
163
|
+
out.set(s.componentId, { ...s.state });
|
|
164
|
+
else
|
|
165
|
+
out.set(s.componentId, { ...(out.get(s.componentId) ?? {}), ...s.state });
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
/** Binary search: the index of the last snapshot with `timestamp ≤ t`, or -1. */
|
|
170
|
+
lastSnapshotIndexAtOrBefore(t) {
|
|
171
|
+
let lo = 0;
|
|
172
|
+
let hi = this.snapshots.length - 1;
|
|
173
|
+
let ans = -1;
|
|
174
|
+
while (lo <= hi) {
|
|
175
|
+
const mid = (lo + hi) >> 1;
|
|
176
|
+
if (this.snapshots[mid].timestamp <= t) {
|
|
177
|
+
ans = mid;
|
|
178
|
+
lo = mid + 1;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
hi = mid - 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return ans;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Per-instance snapshot records in RECORDED ORDER: `{ globalIndex, timestamp }`.
|
|
189
|
+
* `globalIndex` is the position in the bundle's full snapshot array (all instances).
|
|
190
|
+
*
|
|
191
|
+
* Intentionally keeps duplicates: if two snapshots for the same instance land
|
|
192
|
+
* at the same millisecond (clustered commits), each is a distinct entry.
|
|
193
|
+
* Using a Set of unique timestamps would drop one, shifting all subsequent
|
|
194
|
+
* ordinals and pairing A's k-th snapshot against B's (k-1)-th → phantom
|
|
195
|
+
* divergences. The pairing contract is per snapshot-sequence position, not
|
|
196
|
+
* per unique timestamp.
|
|
197
|
+
*/
|
|
198
|
+
const instanceSnapshotEntries = (bundle, id) => {
|
|
199
|
+
const out = [];
|
|
200
|
+
let gi = 0;
|
|
201
|
+
for (const e of bundle.events) {
|
|
202
|
+
if (e.type === "state_snapshot") {
|
|
203
|
+
if (e.componentId === id)
|
|
204
|
+
out.push({ globalIndex: gi, timestamp: e.timestamp });
|
|
205
|
+
gi++;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
};
|
|
210
|
+
/** All action names for one instance, in recorded order (with duplicates). */
|
|
211
|
+
const instanceActionNames = (bundle, id) => bundle.events
|
|
212
|
+
.filter((e) => e.type === "action" && e.componentId === id)
|
|
213
|
+
.map((e) => e.name);
|
|
214
|
+
/** All resource names for one instance, in recorded order (with duplicates). */
|
|
215
|
+
const instanceResourceNames = (bundle, id) => bundle.events
|
|
216
|
+
.filter((e) => e.type === "resource" && e.componentId === id)
|
|
217
|
+
.map((e) => e.name);
|
|
218
|
+
/** Items in `a` that are absent from `b` (min-truncated, positional). */
|
|
219
|
+
const onlyIn = (a, b) => {
|
|
220
|
+
const count = new Map();
|
|
221
|
+
for (const x of b)
|
|
222
|
+
count.set(x, (count.get(x) ?? 0) + 1);
|
|
223
|
+
const result = [];
|
|
224
|
+
for (const x of a) {
|
|
225
|
+
const rem = count.get(x) ?? 0;
|
|
226
|
+
if (rem === 0)
|
|
227
|
+
result.push(x);
|
|
228
|
+
else
|
|
229
|
+
count.set(x, rem - 1);
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Compare two recorded `SessionBundle`s and return the structured diff
|
|
235
|
+
* (diff plan §4).
|
|
236
|
+
*
|
|
237
|
+
* Alignment: per-instance event ordinal — A's k-th snapshot ↔ B's k-th.
|
|
238
|
+
* Correct when both sessions ran the same component through a comparable
|
|
239
|
+
* commit sequence. Pairs truncate at `min(stopsA, stopsB)`; the unpaired
|
|
240
|
+
* tail is reported as `unpairedA`/`unpairedB`.
|
|
241
|
+
*
|
|
242
|
+
* Version-aware: reads state through `ReplayPlayer.statesAt` so 2.0
|
|
243
|
+
* (all-keyframe) and 2.1 (delta) bundles fold to equal states when the
|
|
244
|
+
* recorded logical state is the same (diff plan §6 scenario 6).
|
|
245
|
+
*
|
|
246
|
+
* Pure — no React, no DOM, no module singletons (COND-4). Safe to call in
|
|
247
|
+
* tests, workers, and server-side code.
|
|
248
|
+
*
|
|
249
|
+
* @remarks v1 aligns by per-instance snapshot ordinal only. The following are
|
|
250
|
+
* explicitly deferred (Replay §5): LCS/fuzzy action-sequence alignment,
|
|
251
|
+
* multi-instance fuzzy matching (e.g. `Board#2` ↔ `Board#3`), normalized-time
|
|
252
|
+
* or route-aware alignment, deep nested-value diff (beyond shallow JSON
|
|
253
|
+
* compare), three-way diff, and drive-in-compare.
|
|
254
|
+
*/
|
|
255
|
+
export const diffBundles = (a, b, opts) => {
|
|
256
|
+
const playerA = new ReplayPlayer(a);
|
|
257
|
+
const playerB = new ReplayPlayer(b);
|
|
258
|
+
// Collect all componentIds in either bundle.
|
|
259
|
+
const allIds = new Set();
|
|
260
|
+
for (const e of a.events)
|
|
261
|
+
allIds.add(e.componentId);
|
|
262
|
+
for (const e of b.events)
|
|
263
|
+
allIds.add(e.componentId);
|
|
264
|
+
// Apply componentName filter if requested.
|
|
265
|
+
const ids = opts?.componentName
|
|
266
|
+
? [...allIds].filter((id) => id === opts.componentName ||
|
|
267
|
+
id.startsWith(opts.componentName + "#"))
|
|
268
|
+
: [...allIds];
|
|
269
|
+
const instances = [];
|
|
270
|
+
let divergentStopCount = 0;
|
|
271
|
+
for (const id of ids) {
|
|
272
|
+
// B1 fix: align by per-instance SNAPSHOT SEQUENCE (globalIndex position),
|
|
273
|
+
// not by a Set of unique timestamps. Same-ms clustered commits each count
|
|
274
|
+
// as a distinct ordinal. Reading state via statesUpToIndex(globalIndex)
|
|
275
|
+
// folds exactly the snapshots recorded up to that position, so the state
|
|
276
|
+
// after the k-th snapshot is correct even when k and k+1 share a timestamp.
|
|
277
|
+
const entriesA = instanceSnapshotEntries(a, id);
|
|
278
|
+
const entriesB = instanceSnapshotEntries(b, id);
|
|
279
|
+
const inA = entriesA.length > 0;
|
|
280
|
+
const inB = entriesB.length > 0;
|
|
281
|
+
const presence = inA && inB ? "both" : inA ? "onlyA" : "onlyB";
|
|
282
|
+
const actionsA = instanceActionNames(a, id);
|
|
283
|
+
const actionsB = instanceActionNames(b, id);
|
|
284
|
+
const resourcesA = instanceResourceNames(a, id);
|
|
285
|
+
const resourcesB = instanceResourceNames(b, id);
|
|
286
|
+
if (presence !== "both") {
|
|
287
|
+
instances.push({
|
|
288
|
+
componentId: id,
|
|
289
|
+
presence,
|
|
290
|
+
stops: [],
|
|
291
|
+
unpairedA: presence === "onlyA"
|
|
292
|
+
? entriesA.map((e) => e.timestamp - a.startTime)
|
|
293
|
+
: [],
|
|
294
|
+
unpairedB: presence === "onlyB"
|
|
295
|
+
? entriesB.map((e) => e.timestamp - b.startTime)
|
|
296
|
+
: [],
|
|
297
|
+
actionsOnlyA: onlyIn(actionsA, actionsB),
|
|
298
|
+
actionsOnlyB: onlyIn(actionsB, actionsA),
|
|
299
|
+
resourcesOnlyA: onlyIn(resourcesA, resourcesB),
|
|
300
|
+
resourcesOnlyB: onlyIn(resourcesB, resourcesA),
|
|
301
|
+
});
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const pairCount = Math.min(entriesA.length, entriesB.length);
|
|
305
|
+
const alignedStops = [];
|
|
306
|
+
for (let k = 0; k < pairCount; k++) {
|
|
307
|
+
const eA = entriesA[k];
|
|
308
|
+
const eB = entriesB[k];
|
|
309
|
+
const offsetA = eA.timestamp - a.startTime;
|
|
310
|
+
const offsetB = eB.timestamp - b.startTime;
|
|
311
|
+
// Read folded state up to this snapshot's global index (version-aware).
|
|
312
|
+
// Using statesUpToIndex rather than statesAt(timestamp) avoids the
|
|
313
|
+
// same-timestamp ambiguity: two snapshots at t=100 each have a distinct
|
|
314
|
+
// globalIndex, so statesUpToIndex folds exactly the right prefix.
|
|
315
|
+
const stateA = playerA.statesUpToIndex(eA.globalIndex).get(id);
|
|
316
|
+
const stateB = playerB.statesUpToIndex(eB.globalIndex).get(id);
|
|
317
|
+
const { changed, onlyA, onlyB } = diffKeysSymmetric(stateA, stateB);
|
|
318
|
+
const changedKeys = [
|
|
319
|
+
...changed,
|
|
320
|
+
...onlyA.map((key) => ({ key, valueA: stateA[key], valueB: undefined })),
|
|
321
|
+
...onlyB.map((key) => ({ key, valueA: undefined, valueB: stateB[key] })),
|
|
322
|
+
];
|
|
323
|
+
if (changedKeys.length > 0)
|
|
324
|
+
divergentStopCount++;
|
|
325
|
+
// Invariant: alignedStops[k].ordinal === k (the compare UI indexes stops
|
|
326
|
+
// by selectedOrdinal — inst.stops[selectedOrdinal] must be the right pair;
|
|
327
|
+
// maintained by always pushing in order with ordinal: k).
|
|
328
|
+
alignedStops.push({ ordinal: k, offsetA, offsetB, globalIndexA: eA.globalIndex, globalIndexB: eB.globalIndex, changedKeys });
|
|
329
|
+
}
|
|
330
|
+
instances.push({
|
|
331
|
+
componentId: id,
|
|
332
|
+
presence,
|
|
333
|
+
stops: alignedStops,
|
|
334
|
+
unpairedA: entriesA.slice(pairCount).map((e) => e.timestamp - a.startTime),
|
|
335
|
+
unpairedB: entriesB.slice(pairCount).map((e) => e.timestamp - b.startTime),
|
|
336
|
+
actionsOnlyA: onlyIn(actionsA, actionsB),
|
|
337
|
+
actionsOnlyB: onlyIn(actionsB, actionsA),
|
|
338
|
+
resourcesOnlyA: onlyIn(resourcesA, resourcesB),
|
|
339
|
+
resourcesOnlyB: onlyIn(resourcesB, resourcesA),
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return { instances, divergentStopCount };
|
|
343
|
+
};
|
|
344
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reactra/replay",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@reactra/behaviours": "^0.1.0-alpha.0"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"react": "^19.2.0"
|
|
17
|
+
},
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"tag": "alpha",
|
|
25
|
+
"provenance": false
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/akhilshastri/reactra.git",
|
|
30
|
+
"directory": "packages/replay"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://reactra-docs.vercel.app",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.18"
|
|
36
|
+
},
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"description": "Reactra session replay — recording, streaming, re-drive, and diffing of Reactra app state."
|
|
39
|
+
}
|