@brett_lamy/docstream 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +36 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/replay/NativeReplay.d.ts +2 -2
  6. package/dist/replay/NativeReplay.d.ts.map +1 -1
  7. package/dist/replay/NativeReplay.js +3 -1
  8. package/dist/replay/NativeReplay.js.map +1 -1
  9. package/dist/replay/ReplayPreview.d.ts +3 -3
  10. package/dist/replay/ReplayPreview.d.ts.map +1 -1
  11. package/dist/replay/ReplayPreview.js.map +1 -1
  12. package/dist/replay/ReplayStage.d.ts +10 -0
  13. package/dist/replay/ReplayStage.d.ts.map +1 -0
  14. package/dist/replay/ReplayStage.js +19 -0
  15. package/dist/replay/ReplayStage.js.map +1 -0
  16. package/dist/replay/ReplayStageImpl.d.ts +5 -0
  17. package/dist/replay/ReplayStageImpl.d.ts.map +1 -0
  18. package/dist/replay/ReplayStageImpl.js +249 -0
  19. package/dist/replay/ReplayStageImpl.js.map +1 -0
  20. package/dist/replay/index.d.ts +6 -0
  21. package/dist/replay/index.d.ts.map +1 -1
  22. package/dist/replay/index.js +3 -0
  23. package/dist/replay/index.js.map +1 -1
  24. package/dist/replay/markers.d.ts +55 -0
  25. package/dist/replay/markers.d.ts.map +1 -0
  26. package/dist/replay/markers.js +177 -0
  27. package/dist/replay/markers.js.map +1 -0
  28. package/dist/replay/sanitize.d.ts +2 -2
  29. package/dist/replay/sanitize.d.ts.map +1 -1
  30. package/dist/replay/sanitize.js +1 -1
  31. package/dist/replay/sanitize.js.map +1 -1
  32. package/dist/replay/stage-types.d.ts +65 -0
  33. package/dist/replay/stage-types.d.ts.map +1 -0
  34. package/dist/replay/stage-types.js +2 -0
  35. package/dist/replay/stage-types.js.map +1 -0
  36. package/dist/replay/types.d.ts +39 -0
  37. package/dist/replay/types.d.ts.map +1 -0
  38. package/dist/replay/types.js +36 -0
  39. package/dist/replay/types.js.map +1 -0
  40. package/package.json +6 -2
  41. package/src/index.ts +1 -0
  42. package/src/replay/NativeReplay.tsx +6 -2
  43. package/src/replay/ReplayPreview.tsx +6 -6
  44. package/src/replay/ReplayStage.tsx +28 -0
  45. package/src/replay/ReplayStageImpl.tsx +281 -0
  46. package/src/replay/index.ts +6 -0
  47. package/src/replay/markers.ts +213 -0
  48. package/src/replay/sanitize.ts +2 -3
  49. package/src/replay/stage-types.ts +63 -0
  50. package/src/replay/types.ts +46 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../src/replay/markers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAA;AAE5G,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAA;IACb,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAA;IAChB,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAA;AAEtF,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,gBAAgB,CAAA;IACtB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC3B,MAAM,EAAE,kBAAkB,EAAE,CAAA;CAC7B;AAiBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,UAAU,CA2BxE;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,YAAY,EAAE,CA6D/E;AAED,gGAAgG;AAChG,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,kBAAkB,CA6BxF;AAED,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAexG"}
@@ -0,0 +1,177 @@
1
+ /* Pure readers over an rrweb event list — no rrweb import, so custom player chrome (a
2
+ scrubber with event markers, a cursor overlay) can be drawn before the player chunk loads. */
3
+ import { ReplayEventType, ReplayIncrementalSource, ReplayMouseInteraction } from './types.js';
4
+ function sorted(events) {
5
+ return [...events].sort((a, b) => a.timestamp - b.timestamp);
6
+ }
7
+ function record(value) {
8
+ return value && typeof value === 'object' ? value : {};
9
+ }
10
+ function text(value) {
11
+ if (typeof value === 'string')
12
+ return value;
13
+ if (typeof value === 'number')
14
+ return String(value);
15
+ if (Array.isArray(value))
16
+ return value.map((v) => (typeof v === 'string' ? v.replace(/^"|"$/g, '') : JSON.stringify(v))).join(' ');
17
+ return undefined;
18
+ }
19
+ export function getReplayMeta(events) {
20
+ const list = sorted(events);
21
+ const start = list[0]?.timestamp ?? 0;
22
+ const end = list[list.length - 1]?.timestamp ?? start;
23
+ let width = 0;
24
+ let height = 0;
25
+ let href;
26
+ let playable = false;
27
+ for (const event of list) {
28
+ const data = record(event.data);
29
+ if (event.type === ReplayEventType.FullSnapshot)
30
+ playable = true;
31
+ if (event.type === ReplayEventType.Meta) {
32
+ if (!width && typeof data.width === 'number')
33
+ width = data.width;
34
+ if (!height && typeof data.height === 'number')
35
+ height = data.height;
36
+ if (!href && typeof data.href === 'string')
37
+ href = data.href;
38
+ }
39
+ else if (!width &&
40
+ event.type === ReplayEventType.IncrementalSnapshot &&
41
+ data.source === ReplayIncrementalSource.ViewportResize &&
42
+ typeof data.width === 'number' &&
43
+ typeof data.height === 'number') {
44
+ width = data.width;
45
+ height = data.height;
46
+ }
47
+ }
48
+ return { start, end, duration: end - start, width, height, href, playable };
49
+ }
50
+ const ERROR_TAGS = new Set(['error', 'exception', 'console.error', 'uncaught']);
51
+ const NETWORK_TAGS = new Set(['network', 'network.error', 'request.failed', 'fetch.failed']);
52
+ const NAVIGATION_TAGS = new Set(['navigation', 'navigate', 'route']);
53
+ /**
54
+ * Timeline markers for a scrubber: clicks, console errors (rrweb console plugin), failed
55
+ * requests (rrweb network plugin, status 0 or ≥ 400), page navigations (Meta events after the
56
+ * first) and Custom events (`record.addCustomEvent(tag, payload)`; tags `error`, `network`,
57
+ * `navigation` map to those kinds, anything else is `custom` labelled by `payload.label`).
58
+ */
59
+ export function getReplayMarkers(events) {
60
+ const list = sorted(events);
61
+ const start = list[0]?.timestamp ?? 0;
62
+ const markers = [];
63
+ let seenMeta = false;
64
+ const push = (marker) => markers.push({ ...marker, id: `${marker.kind}-${markers.length}` });
65
+ for (const event of list) {
66
+ const time = event.timestamp - start;
67
+ const data = record(event.data);
68
+ switch (event.type) {
69
+ case ReplayEventType.IncrementalSnapshot: {
70
+ if (data.source === ReplayIncrementalSource.MouseInteraction &&
71
+ (data.type === ReplayMouseInteraction.Click || data.type === ReplayMouseInteraction.DblClick) &&
72
+ typeof data.x === 'number' &&
73
+ typeof data.y === 'number') {
74
+ push({ kind: 'click', time, label: data.type === ReplayMouseInteraction.DblClick ? 'Double-click' : 'Click', x: data.x, y: data.y });
75
+ }
76
+ break;
77
+ }
78
+ case ReplayEventType.Meta: {
79
+ if (seenMeta && typeof data.href === 'string')
80
+ push({ kind: 'navigation', time, label: 'Navigated', detail: data.href });
81
+ seenMeta = true;
82
+ break;
83
+ }
84
+ case ReplayEventType.Plugin: {
85
+ const payload = record(data.payload);
86
+ if (data.plugin === 'rrweb/console@1' && (payload.level === 'error' || payload.level === 'assert')) {
87
+ push({ kind: 'error', time, label: 'Console error', detail: text(payload.payload) });
88
+ }
89
+ else if (data.plugin === 'rrweb/network@1' && Array.isArray(payload.requests)) {
90
+ for (const raw of payload.requests) {
91
+ const request = record(raw);
92
+ const status = typeof request.status === 'number' ? request.status : typeof request.responseStatus === 'number' ? request.responseStatus : undefined;
93
+ if (status === 0 || (status !== undefined && status >= 400) || request.error) {
94
+ const method = typeof request.method === 'string' ? request.method : 'GET';
95
+ push({ kind: 'network', time, label: `${method} ${status || 'failed'}`, detail: text(request.url ?? request.name) });
96
+ }
97
+ }
98
+ }
99
+ break;
100
+ }
101
+ case ReplayEventType.Custom: {
102
+ const tag = typeof data.tag === 'string' ? data.tag : 'custom';
103
+ const payload = record(data.payload);
104
+ const detail = text(payload.detail ?? payload.message ?? payload.url);
105
+ const label = text(payload.label);
106
+ if (ERROR_TAGS.has(tag))
107
+ push({ kind: 'error', time, label: label ?? 'Error', detail });
108
+ else if (NETWORK_TAGS.has(tag)) {
109
+ const status = text(payload.status);
110
+ const method = text(payload.method) ?? 'GET';
111
+ push({ kind: 'network', time, label: label ?? `${method} ${status ?? 'failed'}`, detail });
112
+ }
113
+ else if (NAVIGATION_TAGS.has(tag))
114
+ push({ kind: 'navigation', time, label: label ?? 'Navigated', detail });
115
+ else
116
+ push({ kind: 'custom', time, label: label ?? tag, detail });
117
+ break;
118
+ }
119
+ }
120
+ }
121
+ return markers;
122
+ }
123
+ /** The recorded pointer path (mouse/touch moves and drags) and click points, in viewport px. */
124
+ export function getReplayPointerTrack(events) {
125
+ const list = sorted(events);
126
+ const start = list[0]?.timestamp ?? 0;
127
+ const moves = [];
128
+ const clicks = [];
129
+ for (const event of list) {
130
+ if (event.type !== ReplayEventType.IncrementalSnapshot)
131
+ continue;
132
+ const data = record(event.data);
133
+ const source = data.source;
134
+ if ((source === ReplayIncrementalSource.MouseMove ||
135
+ source === ReplayIncrementalSource.TouchMove ||
136
+ source === ReplayIncrementalSource.Drag) &&
137
+ Array.isArray(data.positions)) {
138
+ for (const raw of data.positions) {
139
+ const p = record(raw);
140
+ if (typeof p.x !== 'number' || typeof p.y !== 'number')
141
+ continue;
142
+ const offset = typeof p.timeOffset === 'number' ? p.timeOffset : 0;
143
+ moves.push({ time: Math.max(0, event.timestamp - start + offset), x: p.x, y: p.y });
144
+ }
145
+ }
146
+ else if (source === ReplayIncrementalSource.MouseInteraction && typeof data.x === 'number' && typeof data.y === 'number') {
147
+ const point = { time: event.timestamp - start, x: data.x, y: data.y };
148
+ moves.push(point);
149
+ if (data.type === ReplayMouseInteraction.Click || data.type === ReplayMouseInteraction.DblClick)
150
+ clicks.push(point);
151
+ }
152
+ }
153
+ moves.sort((a, b) => a.time - b.time);
154
+ return { moves, clicks };
155
+ }
156
+ /** The pointer position at `time` (linear between recorded points), or null before the first. */
157
+ export function replayPointerAt(track, time) {
158
+ const { moves } = track;
159
+ if (!moves.length || time < moves[0].time)
160
+ return null;
161
+ let lo = 0;
162
+ let hi = moves.length - 1;
163
+ if (time >= moves[hi].time)
164
+ return { x: moves[hi].x, y: moves[hi].y };
165
+ while (hi - lo > 1) {
166
+ const mid = (lo + hi) >> 1;
167
+ if (moves[mid].time <= time)
168
+ lo = mid;
169
+ else
170
+ hi = mid;
171
+ }
172
+ const a = moves[lo];
173
+ const b = moves[hi];
174
+ const k = b.time === a.time ? 1 : (time - a.time) / (b.time - a.time);
175
+ return { x: a.x + (b.x - a.x) * k, y: a.y + (b.y - a.y) * k };
176
+ }
177
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../src/replay/markers.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { eventWithTime } from 'rrweb';
1
+ import { type ReplayEvent } from './types.js';
2
2
  /** Prevent a replay iframe from making the viewer fetch localhost/private assets. */
3
- export declare function sanitizeReplayEvent(event: eventWithTime): eventWithTime;
3
+ export declare function sanitizeReplayEvent<T extends ReplayEvent>(event: T): T;
4
4
  //# sourceMappingURL=sanitize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/replay/sanitize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAyD3C,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAuBvE"}
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/replay/sanitize.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8E,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAyDvH,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAuBtE"}
@@ -1,4 +1,4 @@
1
- import { EventType, IncrementalSource } from 'rrweb';
1
+ import { ReplayEventType as EventType, ReplayIncrementalSource as IncrementalSource } from './types.js';
2
2
  const TRANSPARENT_PIXEL = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
3
3
  const EMPTY_STYLESHEET = 'data:text/css,';
4
4
  function isLocalNetworkUrl(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/replay/sanitize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/replay/sanitize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { CSSProperties, ReactNode } from 'react';
2
+ import type { ReplayEvent } from './types.js';
3
+ /** Imperative control over a mounted `ReplayStage`. Times are ms from the recording's start. */
4
+ export interface ReplayStageHandle {
5
+ /** Play from `offset` (default: the current time; from 0 when at the end). */
6
+ play(offset?: number): void;
7
+ /** Pause, optionally jumping to `offset`. */
8
+ pause(offset?: number): void;
9
+ /** Jump to `ms`, keeping the play state. */
10
+ seek(ms: number): void;
11
+ toggle(): void;
12
+ getCurrentTime(): number;
13
+ getDuration(): number;
14
+ isPlaying(): boolean;
15
+ }
16
+ export interface ReplayStageLayout {
17
+ /** Recorded viewport size (px). */
18
+ width: number;
19
+ height: number;
20
+ /** How much the viewport is scaled to fit the stage box. */
21
+ scale: number;
22
+ /** Where the scaled viewport sits inside the box. */
23
+ offsetX: number;
24
+ offsetY: number;
25
+ }
26
+ export interface ReplayStageProps {
27
+ /** rrweb events; appended events are fed to the running player. */
28
+ events: ReplayEvent[];
29
+ autoplay?: boolean;
30
+ loop?: boolean;
31
+ /** Playback rate (1 = real time). */
32
+ speed?: number;
33
+ /** Fast-forward idle stretches longer than 5s (rrweb `skipInactive`). Default true. */
34
+ skipInactive?: boolean;
35
+ /** Where the paused player starts (ms). */
36
+ initialTime?: number;
37
+ /**
38
+ * `width` (default): scale down to the box width, height follows the recording's aspect.
39
+ * `contain`: fill the box (give it a height) and letterbox the viewport inside it.
40
+ */
41
+ fit?: 'width' | 'contain';
42
+ /** rrweb's own cursor. Hide it when drawing a pointer overlay. Default true. */
43
+ showMouse?: boolean;
44
+ /**
45
+ * Rendered over the page, in recorded-viewport coordinates (a box of `width × height` px
46
+ * scaled with the page). `var(--replay-scale)` holds the scale, so an overlay can
47
+ * counter-scale elements that should keep their on-screen size.
48
+ */
49
+ overlay?: ReactNode;
50
+ className?: string;
51
+ style?: CSSProperties;
52
+ /** Shown while the player chunk (rrweb) loads. */
53
+ fallback?: ReactNode;
54
+ onReady?(info: {
55
+ duration: number;
56
+ width: number;
57
+ height: number;
58
+ }): void;
59
+ /** Every frame while playing, and after each seek/pause. */
60
+ onTimeUpdate?(ms: number): void;
61
+ onPlayingChange?(playing: boolean): void;
62
+ onFinish?(): void;
63
+ onLayout?(layout: ReplayStageLayout): void;
64
+ }
65
+ //# sourceMappingURL=stage-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-types.d.ts","sourceRoot":"","sources":["../../src/replay/stage-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,6CAA6C;IAC7C,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,4CAA4C;IAC5C,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,IAAI,IAAI,CAAA;IACd,cAAc,IAAI,MAAM,CAAA;IACxB,WAAW,IAAI,MAAM,CAAA;IACrB,SAAS,IAAI,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uFAAuF;IACvF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACzB,gFAAgF;IAChF,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,OAAO,CAAC,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACzE,4DAA4D;IAC5D,YAAY,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,eAAe,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACxC,QAAQ,CAAC,IAAI,IAAI,CAAA;IACjB,QAAQ,CAAC,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAA;CAC3C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stage-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-types.js","sourceRoot":"","sources":["../../src/replay/stage-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
1
+ /** One recorded rrweb event. rrweb's own `eventWithTime` is assignable to it. */
2
+ export interface ReplayEvent {
3
+ type: number;
4
+ data: unknown;
5
+ timestamp: number;
6
+ delay?: number;
7
+ }
8
+ /** rrweb `EventType`. */
9
+ export declare const ReplayEventType: {
10
+ readonly DomContentLoaded: 0;
11
+ readonly Load: 1;
12
+ readonly FullSnapshot: 2;
13
+ readonly IncrementalSnapshot: 3;
14
+ readonly Meta: 4;
15
+ readonly Custom: 5;
16
+ readonly Plugin: 6;
17
+ };
18
+ /** rrweb `IncrementalSource` (the subset the helpers read). */
19
+ export declare const ReplayIncrementalSource: {
20
+ readonly Mutation: 0;
21
+ readonly MouseMove: 1;
22
+ readonly MouseInteraction: 2;
23
+ readonly Scroll: 3;
24
+ readonly ViewportResize: 4;
25
+ readonly Input: 5;
26
+ readonly TouchMove: 6;
27
+ readonly Drag: 12;
28
+ };
29
+ /** rrweb `MouseInteractions` (the subset the helpers read). */
30
+ export declare const ReplayMouseInteraction: {
31
+ readonly MouseUp: 0;
32
+ readonly MouseDown: 1;
33
+ readonly Click: 2;
34
+ readonly ContextMenu: 3;
35
+ readonly DblClick: 4;
36
+ readonly TouchStart: 7;
37
+ readonly TouchEnd: 9;
38
+ };
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/replay/types.ts"],"names":[],"mappings":"AAKA,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,yBAAyB;AACzB,eAAO,MAAM,eAAe;;;;;;;;CAQlB,CAAA;AAEV,+DAA+D;AAC/D,eAAO,MAAM,uBAAuB;;;;;;;;;CAS1B,CAAA;AAEV,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB;;;;;;;;CAQzB,CAAA"}
@@ -0,0 +1,36 @@
1
+ /* rrweb's event shape and the numeric enums the replay helpers read, declared locally so the
2
+ eager replay modules (and their published declarations) never import rrweb. rrweb is an
3
+ optional peer: only the lazily loaded player chunk (`ReplayStageImpl` / `NativeReplay`)
4
+ imports it, so hosts that never play native events don't need it installed. */
5
+ /** rrweb `EventType`. */
6
+ export const ReplayEventType = {
7
+ DomContentLoaded: 0,
8
+ Load: 1,
9
+ FullSnapshot: 2,
10
+ IncrementalSnapshot: 3,
11
+ Meta: 4,
12
+ Custom: 5,
13
+ Plugin: 6,
14
+ };
15
+ /** rrweb `IncrementalSource` (the subset the helpers read). */
16
+ export const ReplayIncrementalSource = {
17
+ Mutation: 0,
18
+ MouseMove: 1,
19
+ MouseInteraction: 2,
20
+ Scroll: 3,
21
+ ViewportResize: 4,
22
+ Input: 5,
23
+ TouchMove: 6,
24
+ Drag: 12,
25
+ };
26
+ /** rrweb `MouseInteractions` (the subset the helpers read). */
27
+ export const ReplayMouseInteraction = {
28
+ MouseUp: 0,
29
+ MouseDown: 1,
30
+ Click: 2,
31
+ ContextMenu: 3,
32
+ DblClick: 4,
33
+ TouchStart: 7,
34
+ TouchEnd: 9,
35
+ };
36
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/replay/types.ts"],"names":[],"mappings":"AAAA;;;iFAGiF;AAUjF,yBAAyB;AACzB,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,gBAAgB,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC;IACP,YAAY,EAAE,CAAC;IACf,mBAAmB,EAAE,CAAC;IACtB,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACD,CAAA;AAEV,+DAA+D;AAC/D,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,QAAQ,EAAE,CAAC;IACX,SAAS,EAAE,CAAC;IACZ,gBAAgB,EAAE,CAAC;IACnB,MAAM,EAAE,CAAC;IACT,cAAc,EAAE,CAAC;IACjB,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,CAAC;IACZ,IAAI,EAAE,EAAE;CACA,CAAA;AAEV,+DAA+D;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,OAAO,EAAE,CAAC;IACV,SAAS,EAAE,CAAC;IACZ,KAAK,EAAE,CAAC;IACR,WAAW,EAAE,CAAC;IACd,QAAQ,EAAE,CAAC;IACX,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,CAAC;CACH,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brett_lamy/docstream",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "GitBook-aware readonly markdown and AI stream renderer.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -113,7 +113,6 @@
113
113
  "lucide-react": "^1.17.0",
114
114
  "mermaid": "^11.15.0",
115
115
  "react-aria": "^3.52.1",
116
- "rrweb": "2.0.0-alpha.20",
117
116
  "yaml": "^2.9.0"
118
117
  },
119
118
  "peerDependencies": {
@@ -121,6 +120,7 @@
121
120
  "@brett_lamy/viz-engine": ">=0.2.0",
122
121
  "react": ">=18",
123
122
  "react-dom": ">=18",
123
+ "rrweb": "2.0.0-alpha.20",
124
124
  "vite": ">=5"
125
125
  },
126
126
  "peerDependenciesMeta": {
@@ -132,6 +132,9 @@
132
132
  },
133
133
  "vite": {
134
134
  "optional": true
135
+ },
136
+ "rrweb": {
137
+ "optional": true
135
138
  }
136
139
  },
137
140
  "repository": {
@@ -145,6 +148,7 @@
145
148
  "@types/react": "^18.3.3",
146
149
  "@types/react-dom": "^18.3.7",
147
150
  "happy-dom": "^20.14.5",
151
+ "rrweb": "2.0.0-alpha.20",
148
152
  "typescript": "^5.5.4",
149
153
  "vite": "^7.0.0",
150
154
  "vitest": "^3.2.4"
package/src/index.ts CHANGED
@@ -76,6 +76,7 @@ export type {
76
76
  ReplayPreviewProps,
77
77
  ReplaySource,
78
78
  } from "./replay/ReplayPreview"
79
+ export type { ReplayEvent } from "./replay/types"
79
80
  export { isReplayQaUrl, normalizeReplayEmbedUrl } from "./replay/url"
80
81
  export { VideoEmbed } from "./video"
81
82
  export type { VideoEmbedProps } from "./video"
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { EventType, Replayer } from 'rrweb';
5
5
  import type { eventWithTime } from 'rrweb';
6
6
  import { sanitizeReplayEvent } from './sanitize';
7
+ import type { ReplayEvent } from './types';
7
8
  import 'rrweb/dist/style.css';
8
9
 
9
10
  const DEFAULT_SPEEDS = [0.5, 1, 2, 4, 8];
@@ -15,7 +16,7 @@ export default function NativeReplay({
15
16
  loop = false,
16
17
  speeds = DEFAULT_SPEEDS,
17
18
  }: {
18
- events: eventWithTime[];
19
+ events: ReplayEvent[];
19
20
  autoplay?: boolean;
20
21
  loop?: boolean;
21
22
  speeds?: number[];
@@ -36,7 +37,10 @@ export default function NativeReplay({
36
37
  const autoplayRef = useRef(autoplay);
37
38
 
38
39
  const prepared = useMemo(
39
- () => events.map((event) => sanitizeReplayEvent(event)).sort((a, b) => a.timestamp - b.timestamp),
40
+ () =>
41
+ events
42
+ .map((event) => sanitizeReplayEvent(event))
43
+ .sort((a, b) => a.timestamp - b.timestamp) as unknown as eventWithTime[],
40
44
  [events],
41
45
  );
42
46
  const hasSnapshot = prepared.some((event) => event.type === EventType.FullSnapshot);
@@ -1,5 +1,5 @@
1
1
  import { Suspense, lazy, useEffect, useState } from 'react';
2
- import type { eventWithTime } from 'rrweb';
2
+ import type { ReplayEvent } from './types';
3
3
  import { normalizeReplayEmbedUrl } from './url';
4
4
 
5
5
  export interface ReplayPageSource {
@@ -10,7 +10,7 @@ export interface ReplayPageSource {
10
10
 
11
11
  export interface ReplayEventsSource {
12
12
  kind: 'events';
13
- events: eventWithTime[];
13
+ events: ReplayEvent[];
14
14
  }
15
15
 
16
16
  export interface ReplayEventsUrlSource {
@@ -21,7 +21,7 @@ export interface ReplayEventsUrlSource {
21
21
 
22
22
  export type ReplaySource =
23
23
  | string
24
- | eventWithTime[]
24
+ | ReplayEvent[]
25
25
  | ReplayPageSource
26
26
  | ReplayEventsSource
27
27
  | ReplayEventsUrlSource;
@@ -84,7 +84,7 @@ function ReplayFrame({ source, title }: { source: ReplayPageSource; title?: stri
84
84
  // rrweb (and its stylesheet) load on first use of an event-based source.
85
85
  const LazyNativeReplay = lazy(() => import('./NativeReplay'));
86
86
 
87
- function NativeReplay(props: { events: eventWithTime[]; autoplay?: boolean; loop?: boolean; speeds?: number[] }) {
87
+ function NativeReplay(props: { events: ReplayEvent[]; autoplay?: boolean; loop?: boolean; speeds?: number[] }) {
88
88
  return (
89
89
  <Suspense fallback={<div className="replay-preview__empty">Loading replay…</div>}>
90
90
  <LazyNativeReplay {...props} />
@@ -103,7 +103,7 @@ function RemoteReplay({
103
103
  loop?: boolean;
104
104
  speeds?: number[];
105
105
  }) {
106
- const [events, setEvents] = useState<eventWithTime[] | null>(null);
106
+ const [events, setEvents] = useState<ReplayEvent[] | null>(null);
107
107
  const [error, setError] = useState<string | null>(null);
108
108
 
109
109
  useEffect(() => {
@@ -120,7 +120,7 @@ function RemoteReplay({
120
120
  ? (body as { events: unknown[] }).events
121
121
  : null;
122
122
  if (!value) throw new Error('Replay event endpoint must return an event array or { events }');
123
- return value as eventWithTime[];
123
+ return value as ReplayEvent[];
124
124
  })
125
125
  .then(setEvents)
126
126
  .catch((reason: unknown) => {
@@ -0,0 +1,28 @@
1
+ /* The eager half of the headless stage: a lazy boundary around `ReplayStageImpl`, so importing
2
+ `ReplayStage` never pulls in rrweb — it (and its stylesheet) load on first render. */
3
+ import { Suspense, forwardRef, lazy } from 'react'
4
+ import type { ReplayStageHandle, ReplayStageProps } from './stage-types'
5
+
6
+ const load = () => import('./ReplayStageImpl')
7
+ const LazyReplayStage = lazy(load)
8
+
9
+ /** Start loading the rrweb player chunk ahead of the first `ReplayStage` render. */
10
+ export function preloadReplayStage(): Promise<unknown> {
11
+ return load()
12
+ }
13
+
14
+ /**
15
+ * A headless rrweb player: the recorded page, fitted to its box, with no controls. Drive it
16
+ * through the ref (`play`, `pause`, `seek`, …) and the `onTimeUpdate` / `onPlayingChange`
17
+ * callbacks to build custom player chrome. Requires the optional `rrweb` peer.
18
+ */
19
+ export const ReplayStage = forwardRef<ReplayStageHandle, ReplayStageProps>(function ReplayStage(
20
+ { fallback = null, ...props },
21
+ ref,
22
+ ) {
23
+ return (
24
+ <Suspense fallback={fallback}>
25
+ <LazyReplayStage ref={ref} {...props} />
26
+ </Suspense>
27
+ )
28
+ })