@bimetal/devtools 0.15.10 → 0.16.7

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 (40) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/causation-forest.d.ts +60 -0
  3. package/dist/causation-forest.d.ts.map +1 -0
  4. package/dist/causation-forest.js +191 -0
  5. package/dist/causation-forest.js.map +1 -0
  6. package/dist/index.d.ts +50 -8
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +36 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/inspectable-json.d.ts +71 -0
  11. package/dist/inspectable-json.d.ts.map +1 -0
  12. package/dist/inspectable-json.js +225 -0
  13. package/dist/inspectable-json.js.map +1 -0
  14. package/dist/observed-bridge.d.ts +71 -0
  15. package/dist/observed-bridge.d.ts.map +1 -0
  16. package/dist/observed-bridge.js +165 -0
  17. package/dist/observed-bridge.js.map +1 -0
  18. package/dist/observed-helpers.d.ts +88 -0
  19. package/dist/observed-helpers.d.ts.map +1 -0
  20. package/dist/observed-helpers.js +76 -0
  21. package/dist/observed-helpers.js.map +1 -0
  22. package/dist/registry.d.ts +42 -1
  23. package/dist/registry.d.ts.map +1 -1
  24. package/dist/registry.js +84 -0
  25. package/dist/registry.js.map +1 -1
  26. package/dist/replay-engine.d.ts +128 -0
  27. package/dist/replay-engine.d.ts.map +1 -0
  28. package/dist/replay-engine.js +199 -0
  29. package/dist/replay-engine.js.map +1 -0
  30. package/dist/snapshot-export.d.ts +73 -0
  31. package/dist/snapshot-export.d.ts.map +1 -0
  32. package/dist/snapshot-export.js +119 -0
  33. package/dist/snapshot-export.js.map +1 -0
  34. package/dist/time-travel.d.ts +62 -0
  35. package/dist/time-travel.d.ts.map +1 -0
  36. package/dist/time-travel.js +140 -0
  37. package/dist/time-travel.js.map +1 -0
  38. package/dist/types.d.ts +121 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +2 -2
@@ -0,0 +1,128 @@
1
+ /**
2
+ * `createReplayEngine` — pure synchrone Fold-Function über einen Stream.
3
+ *
4
+ * Architektur-Verträge aus `design/calendar/16-replay-target-architecture.md`:
5
+ * - **Passive Beobachtung**: Engine ruft NIEMALS `eventStore.append`,
6
+ * `store.dispatch` oder `broker.publish`. Architecture-Test verriegelt.
7
+ * - **subscribe-first + buffer + read + drain**: kein Race-Window zwischen
8
+ * Catch-up und Live-Tail (v0.15.7-Lehre).
9
+ * - **Sticky Error**: bei `apply`-Wurf wird `status = 'error'` und KEIN
10
+ * weiteres Event mehr gefolded — sonst entstände Fold auf staleren
11
+ * Zustand und der User sähe vorgegaukelten Aggregat-Stand.
12
+ * - **Recovery nur via `dispose()` + neuer `createReplayEngine`-Call.**
13
+ * Engine kennt keine `clearError`-Operation.
14
+ *
15
+ * Implementations-Notes:
16
+ * - `handleEvent` ist **synchron** (Architektur-Vertrag).
17
+ * - Initial-Read ist async (das ist OK — `read()` ist der einzige
18
+ * await; danach läuft alles synchron).
19
+ * - `snapshot`-Getter ist reference-stable bis zur nächsten Mutation
20
+ * (analog Devtools-Registry, useSyncExternalStore-Vertrag).
21
+ */
22
+ import type { DomainEvent, Unsubscribe } from '@bimetal/event-sourcing';
23
+ import type { EventStoreLike } from './types.js';
24
+ /**
25
+ * Domain-agnostisches Aggregate-Interface. Bewusst KEIN `handle(state, command)`
26
+ * — Command-Semantik gehört nicht in den Devtools-Vertrag.
27
+ */
28
+ export interface AggregateLike<S> {
29
+ readonly initialState: S;
30
+ apply(state: S, event: DomainEvent): S;
31
+ }
32
+ export interface ProjectionLike<R> {
33
+ readonly initialState: R;
34
+ apply(readModel: R, event: DomainEvent): R;
35
+ readonly version: number;
36
+ }
37
+ /**
38
+ * Strukturelles SnapshotStore-Interface. Optional — Engine liest nur,
39
+ * schreibt NIE rein. Aktuell für Konsistenz-Checks reserviert (in v0.16.0
40
+ * noch nicht genutzt, kommt mit späterer Slice).
41
+ */
42
+ export interface SnapshotStoreLike {
43
+ load(streamId: string): Promise<SnapshotLike | null>;
44
+ }
45
+ export interface SnapshotLike {
46
+ readonly state: unknown;
47
+ readonly projectionVersion: number;
48
+ readonly version: number;
49
+ readonly timestamp: number;
50
+ }
51
+ export interface ReplayTargetHandle<S = unknown, R = unknown> {
52
+ readonly id: string;
53
+ readonly name: string;
54
+ readonly eventStore: EventStoreLike;
55
+ readonly streamId: string;
56
+ readonly aggregate: AggregateLike<S>;
57
+ readonly projection: ProjectionLike<R>;
58
+ readonly snapshotStore?: SnapshotStoreLike;
59
+ }
60
+ export interface ReplayEngineSnapshot<S, R> {
61
+ readonly state: S;
62
+ readonly readModel: R;
63
+ /** `envelope.version` des zuletzt erfolgreich applied Events; `0` bei initialState. */
64
+ readonly lastAppliedVersion: number;
65
+ readonly status: 'replaying' | 'live' | 'error';
66
+ /**
67
+ * Bei `status='error'`: gesetzt, mit `event` als der Event, der den
68
+ * Fehler auslöste — oder als der zuletzt blockierte Event (nach
69
+ * status='error' aktualisiert sich `event` mit jedem eingehenden,
70
+ * nicht-applied Event, damit der User „letzter blockierter Event war v9"
71
+ * sehen kann). `event=null` zeigt: Fehler kam aus `read()`, nicht aus `apply()`.
72
+ */
73
+ readonly lastError?: {
74
+ readonly event: DomainEvent | null;
75
+ readonly error: Error;
76
+ };
77
+ readonly lastAppliedEventId?: string;
78
+ /**
79
+ * `metadata.schemaVersion` des zuletzt erfolgreich applied Events.
80
+ * Konsumenten-UI vergleicht das mit der `projection.version` aus dem
81
+ * Handle, um Schema-Drift sichtbar zu machen (v0.16-Architektur-Doc
82
+ * Leitplanke 6).
83
+ */
84
+ readonly lastAppliedSchemaVersion?: number;
85
+ /** Events nach `status='error'` eingegangen, aber NICHT applied. */
86
+ readonly blockedEventCount: number;
87
+ }
88
+ export interface ReplayEngine<S, R> {
89
+ /** Reference-stable bis zur nächsten Mutation. */
90
+ readonly snapshot: ReplayEngineSnapshot<S, R>;
91
+ subscribe(listener: () => void): Unsubscribe;
92
+ /** Cleanup: stoppt Live-Subscriber, befreit Listener. */
93
+ dispose(): void;
94
+ }
95
+ /**
96
+ * Initialer Snapshot für eine fresh angesetzte Replay-Sequenz. `status`
97
+ * beginnt bei `'replaying'`; der Konsument (Engine ODER TimeTravelController)
98
+ * entscheidet selbst, wann er auf `'live'`/`'ready'` umschaltet.
99
+ */
100
+ export declare function createInitialSnapshot<S, R>(aggregate: AggregateLike<S>, projection: ProjectionLike<R>): ReplayEngineSnapshot<S, R>;
101
+ /**
102
+ * Pure synchroner Fold von genau einem Event auf einen Snapshot.
103
+ *
104
+ * **Atomarer Commit:** beide `apply`-Aufrufe (aggregate + projection)
105
+ * laufen ZUERST in lokale `next*`-Variablen. Erst wenn BEIDE
106
+ * erfolgreich sind, wird das neue Snapshot-Objekt mit allen Tracking-
107
+ * Feldern (`lastAppliedVersion`, `lastAppliedEventId`,
108
+ * `lastAppliedSchemaVersion`) zurückgegeben. Wirft `projection.apply`,
109
+ * ist `nextState` lokal verloren, der Input-`snap` bleibt unverändert —
110
+ * Half-Update unmöglich.
111
+ *
112
+ * **Sticky-Error:** Bei Input-`snap.status === 'error'` wird der Event
113
+ * NICHT applied. Output trägt `blockedEventCount + 1` und ein
114
+ * aktualisiertes `lastError.event` (auf den jeweils zuletzt blockierten
115
+ * Event), damit der User „letzter blockierter Event war v9" sehen kann.
116
+ *
117
+ * **Nichts read/subscribe-bezogenes.** Reine Funktion: Snapshot rein,
118
+ * Event rein, Snapshot raus. Live-Engine UND TimeTravelController
119
+ * konsumieren dieselbe Funktion.
120
+ */
121
+ export declare function applyEventToSnapshot<S, R>(snap: ReplayEngineSnapshot<S, R>, event: DomainEvent, aggregate: AggregateLike<S>, projection: ProjectionLike<R>): ReplayEngineSnapshot<S, R>;
122
+ /**
123
+ * Convenience: Reduce über ein Array von Events — equivalent zu
124
+ * sequenziellem `applyEventToSnapshot`. Pure Funktion, keine Side-Effects.
125
+ */
126
+ export declare function foldEvents<S, R>(events: readonly DomainEvent[], aggregate: AggregateLike<S>, projection: ProjectionLike<R>, initial: ReplayEngineSnapshot<S, R>): ReplayEngineSnapshot<S, R>;
127
+ export declare function createReplayEngine<S, R>(handle: ReplayTargetHandle<S, R>): ReplayEngine<S, R>;
128
+ //# sourceMappingURL=replay-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-engine.d.ts","sourceRoot":"","sources":["../src/replay-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;IACzB,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,GAAG,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;IACzB,KAAK,CAAC,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,GAAG,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IAC1D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;IAChD;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC;IACnF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAC3C,oEAAoE;IACpE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,CAAC;IAChC,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;IAC7C,yDAAyD;IACzD,OAAO,IAAI,IAAI,CAAC;CACjB;AAID;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,CAAC,EACxC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAC3B,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,GAC5B,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAQ5B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,CAAC,EACvC,IAAI,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,EAChC,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAC3B,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,GAC5B,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CA4B5B;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAC7B,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAC3B,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,EAC7B,OAAO,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAClC,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAM5B;AAID,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EACrC,MAAM,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC/B,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAyGpB"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * `createReplayEngine` — pure synchrone Fold-Function über einen Stream.
3
+ *
4
+ * Architektur-Verträge aus `design/calendar/16-replay-target-architecture.md`:
5
+ * - **Passive Beobachtung**: Engine ruft NIEMALS `eventStore.append`,
6
+ * `store.dispatch` oder `broker.publish`. Architecture-Test verriegelt.
7
+ * - **subscribe-first + buffer + read + drain**: kein Race-Window zwischen
8
+ * Catch-up und Live-Tail (v0.15.7-Lehre).
9
+ * - **Sticky Error**: bei `apply`-Wurf wird `status = 'error'` und KEIN
10
+ * weiteres Event mehr gefolded — sonst entstände Fold auf staleren
11
+ * Zustand und der User sähe vorgegaukelten Aggregat-Stand.
12
+ * - **Recovery nur via `dispose()` + neuer `createReplayEngine`-Call.**
13
+ * Engine kennt keine `clearError`-Operation.
14
+ *
15
+ * Implementations-Notes:
16
+ * - `handleEvent` ist **synchron** (Architektur-Vertrag).
17
+ * - Initial-Read ist async (das ist OK — `read()` ist der einzige
18
+ * await; danach läuft alles synchron).
19
+ * - `snapshot`-Getter ist reference-stable bis zur nächsten Mutation
20
+ * (analog Devtools-Registry, useSyncExternalStore-Vertrag).
21
+ */
22
+ // ── Pure Fold-Core (v0.16.2 Sub 1) ────────────────────────────────
23
+ /**
24
+ * Initialer Snapshot für eine fresh angesetzte Replay-Sequenz. `status`
25
+ * beginnt bei `'replaying'`; der Konsument (Engine ODER TimeTravelController)
26
+ * entscheidet selbst, wann er auf `'live'`/`'ready'` umschaltet.
27
+ */
28
+ export function createInitialSnapshot(aggregate, projection) {
29
+ return {
30
+ state: aggregate.initialState,
31
+ readModel: projection.initialState,
32
+ lastAppliedVersion: 0,
33
+ status: 'replaying',
34
+ blockedEventCount: 0,
35
+ };
36
+ }
37
+ /**
38
+ * Pure synchroner Fold von genau einem Event auf einen Snapshot.
39
+ *
40
+ * **Atomarer Commit:** beide `apply`-Aufrufe (aggregate + projection)
41
+ * laufen ZUERST in lokale `next*`-Variablen. Erst wenn BEIDE
42
+ * erfolgreich sind, wird das neue Snapshot-Objekt mit allen Tracking-
43
+ * Feldern (`lastAppliedVersion`, `lastAppliedEventId`,
44
+ * `lastAppliedSchemaVersion`) zurückgegeben. Wirft `projection.apply`,
45
+ * ist `nextState` lokal verloren, der Input-`snap` bleibt unverändert —
46
+ * Half-Update unmöglich.
47
+ *
48
+ * **Sticky-Error:** Bei Input-`snap.status === 'error'` wird der Event
49
+ * NICHT applied. Output trägt `blockedEventCount + 1` und ein
50
+ * aktualisiertes `lastError.event` (auf den jeweils zuletzt blockierten
51
+ * Event), damit der User „letzter blockierter Event war v9" sehen kann.
52
+ *
53
+ * **Nichts read/subscribe-bezogenes.** Reine Funktion: Snapshot rein,
54
+ * Event rein, Snapshot raus. Live-Engine UND TimeTravelController
55
+ * konsumieren dieselbe Funktion.
56
+ */
57
+ export function applyEventToSnapshot(snap, event, aggregate, projection) {
58
+ if (snap.status === 'error') {
59
+ return {
60
+ ...snap,
61
+ blockedEventCount: snap.blockedEventCount + 1,
62
+ lastError: snap.lastError
63
+ ? { event, error: snap.lastError.error }
64
+ : snap.lastError,
65
+ };
66
+ }
67
+ try {
68
+ const nextState = aggregate.apply(snap.state, event);
69
+ const nextReadModel = projection.apply(snap.readModel, event);
70
+ return {
71
+ ...snap,
72
+ state: nextState,
73
+ readModel: nextReadModel,
74
+ lastAppliedVersion: event.envelope.version,
75
+ lastAppliedEventId: event.envelope.id,
76
+ lastAppliedSchemaVersion: event.metadata.schemaVersion,
77
+ };
78
+ }
79
+ catch (error) {
80
+ return {
81
+ ...snap,
82
+ status: 'error',
83
+ lastError: { event, error: error instanceof Error ? error : new Error(String(error)) },
84
+ };
85
+ }
86
+ }
87
+ /**
88
+ * Convenience: Reduce über ein Array von Events — equivalent zu
89
+ * sequenziellem `applyEventToSnapshot`. Pure Funktion, keine Side-Effects.
90
+ */
91
+ export function foldEvents(events, aggregate, projection, initial) {
92
+ let current = initial;
93
+ for (const event of events) {
94
+ current = applyEventToSnapshot(current, event, aggregate, projection);
95
+ }
96
+ return current;
97
+ }
98
+ // ── Factory ───────────────────────────────────────────────────────
99
+ export function createReplayEngine(handle) {
100
+ let current = createInitialSnapshot(handle.aggregate, handle.projection);
101
+ const listeners = new Set();
102
+ function invalidate() {
103
+ for (const l of listeners) {
104
+ try {
105
+ l();
106
+ }
107
+ catch (err) {
108
+ // eslint-disable-next-line no-console
109
+ console.error('[@bimetal/devtools] ReplayEngine listener threw:', err);
110
+ }
111
+ }
112
+ }
113
+ function applyEvent(event) {
114
+ current = applyEventToSnapshot(current, event, handle.aggregate, handle.projection);
115
+ invalidate();
116
+ }
117
+ // ── Lifecycle: subscribe-first + buffer + read + drain ──────────
118
+ const pendingBuffer = [];
119
+ let drained = false;
120
+ let disposed = false;
121
+ // 1. ZUERST subscribe — Events ab jetzt landen im Buffer (bis drained=true).
122
+ const off = handle.eventStore.subscribe((event) => {
123
+ if (disposed)
124
+ return;
125
+ if (!drained) {
126
+ pendingBuffer.push(event);
127
+ return;
128
+ }
129
+ applyEvent(event);
130
+ }, handle.streamId);
131
+ // 2. Dann read() für historische Events. Async — danach drain mit
132
+ // envelope.version-basierter Dedup gegen den pendingBuffer.
133
+ void (async () => {
134
+ try {
135
+ const initial = await handle.eventStore.read(handle.streamId);
136
+ if (disposed)
137
+ return;
138
+ // 3. Fold initial
139
+ for (const event of initial) {
140
+ applyEvent(event);
141
+ }
142
+ // 4. Drain pendingBuffer mit Versions-Dedup gegen initial. Garantie:
143
+ // jedes Event aus dem Stream wird genau einmal applied.
144
+ const lastInitialVersion = initial.length > 0
145
+ ? initial[initial.length - 1].envelope.version
146
+ : 0;
147
+ for (const event of pendingBuffer) {
148
+ if (event.envelope.version > lastInitialVersion) {
149
+ applyEvent(event);
150
+ }
151
+ }
152
+ pendingBuffer.length = 0;
153
+ drained = true;
154
+ // 5. Status-Übergang: 'replaying' → 'live' nur wenn kein apply-Fehler
155
+ // während des initial-Folds ausgelöst wurde.
156
+ if (current.status === 'replaying') {
157
+ current = { ...current, status: 'live' };
158
+ invalidate();
159
+ }
160
+ }
161
+ catch (error) {
162
+ // read() wirft → Error-State, off() bleibt aktiv damit Engine
163
+ // sichtbar bleibt; dispose() vom Konsumenten räumt auf.
164
+ current = {
165
+ ...current,
166
+ status: 'error',
167
+ lastError: { event: null, error: error instanceof Error ? error : new Error(String(error)) },
168
+ };
169
+ drained = true; // verhindere Buffer-Akkumulation bei Folge-Events
170
+ pendingBuffer.length = 0;
171
+ invalidate();
172
+ }
173
+ })();
174
+ return {
175
+ /**
176
+ * Reference-stable: `current` ist das Snapshot-Objekt; jede Mutation
177
+ * ersetzt es vollständig durch ein neues. Konsument vergleicht per
178
+ * Identität (=`===`), keine Deep-Eq nötig.
179
+ */
180
+ get snapshot() {
181
+ return current;
182
+ },
183
+ subscribe(listener) {
184
+ listeners.add(listener);
185
+ return () => {
186
+ listeners.delete(listener);
187
+ };
188
+ },
189
+ dispose() {
190
+ if (disposed)
191
+ return;
192
+ disposed = true;
193
+ off();
194
+ listeners.clear();
195
+ pendingBuffer.length = 0;
196
+ },
197
+ };
198
+ }
199
+ //# sourceMappingURL=replay-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-engine.js","sourceRoot":"","sources":["../src/replay-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * v0.16.6 — DevtoolsSnapshot Export/Import.
3
+ *
4
+ * Captured Stream + State exportierbar als versioniertes JSON-Dokument;
5
+ * Loader-API für späteren Sandbox-Replay. Sandbox-UI ist NICHT v0.16.6
6
+ * (eigene Slice).
7
+ *
8
+ * **Verträge (Pilot-Verriegelungen 2026-06-09):**
9
+ * - `exportSnapshot(state, { exportedAt })` — Konsument liefert die
10
+ * Zeit. Helper ist pure: keine Zeit-/Zufalls-/Konsolen-APIs. Die
11
+ * konkreten verbotenen Identifier sind im Architecture-Test
12
+ * verriegelt; diese Quelle vermeidet sie auch in Kommentaren, damit
13
+ * der Source-Scan einfach bleibt. Der State wird beim Erzeugen via
14
+ * `toInspectableJson` normalisiert + detached — Mutationen am Live-
15
+ * State treffen den Snapshot nicht (Sub-1-Pilot-Schärfung).
16
+ * - `serializeSnapshot(snap)` — reines `JSON.stringify`. Snapshot ist
17
+ * bereits JSON-normalisiert beim Erzeugen, daher kein Pipeline-Schritt
18
+ * mehr. Lossy-Charakter (Map/Set/BigInt → JSON-Form, zyklische
19
+ * Strukturen → marker) gehört zum Snapshot-Format selbst, nicht zu
20
+ * Serialize: Sandbox-Replay ist Diagnose, kein Round-Trip-Beweis.
21
+ * - `parseSnapshot(json)` — strukturelle Validierung. Wirft bei
22
+ * JSON-Parse-Fehler, Nicht-Objekt-Top-Level, falscher `version`,
23
+ * nicht-finite `exportedAt`, fehlendem `state` oder fehlenden/
24
+ * falsch-typisierten Pflicht-State-Feldern. Tiefe Domain-Schema-
25
+ * Validierung NICHT in v0.16.6.
26
+ */
27
+ import type { DevtoolsState } from './types.js';
28
+ /**
29
+ * Format-Version als zentrale Konstante. Pilot-Verriegelung 2026-06-09
30
+ * (Sub-1-Schärfung): Architecture-Test prüft den Wert dieser Konstante
31
+ * direkt, statt freie `version:`-Literale im Source zu scannen. Eine
32
+ * spätere Slice, die das Format ändert, MUSS die Konstante hier ändern
33
+ * — und nichts anderes funktioniert ohne diese Stelle anzufassen.
34
+ */
35
+ export declare const SNAPSHOT_VERSION: "1.0.0";
36
+ export interface DevtoolsSnapshot {
37
+ readonly version: typeof SNAPSHOT_VERSION;
38
+ readonly exportedAt: number;
39
+ /**
40
+ * JSON-normalisierte Form von `DevtoolsState`. Lossy: Map→Object,
41
+ * Date→ISO, BigInt→string, Function→null, Cycle→marker. Strukturell
42
+ * äquivalent zum übergebenen State, aber NICHT typ-identisch und
43
+ * **detached** — Mutationen am ursprünglichen State treffen den
44
+ * Snapshot nicht.
45
+ */
46
+ readonly state: DevtoolsState;
47
+ }
48
+ export interface ExportSnapshotOptions {
49
+ readonly exportedAt: number;
50
+ }
51
+ /**
52
+ * Versioniertes Snapshot-Dokument bauen. Pilot-Schärfung Sub 1
53
+ * (HIGH-Finding): der State wird beim Erzeugen via `toInspectableJson`
54
+ * normalisiert + detached. Damit ist `serializeSnapshot()` nur noch
55
+ * `JSON.stringify`, und ein Konsument, der nach `exportSnapshot()` die
56
+ * Registry weiter-mutiert, beeinflusst den Snapshot nicht.
57
+ *
58
+ * Pure: deterministisch über das `state`-Argument, `exportedAt` vom
59
+ * Konsumenten (kein Date-/Random-/Konsolen-API).
60
+ */
61
+ export declare function exportSnapshot(state: DevtoolsState, options: ExportSnapshotOptions): DevtoolsSnapshot;
62
+ /**
63
+ * JSON-String erzeugen. Snapshot ist bereits JSON-normalisiert (siehe
64
+ * `exportSnapshot`), daher reicht `JSON.stringify`.
65
+ */
66
+ export declare function serializeSnapshot(snap: DevtoolsSnapshot): string;
67
+ /**
68
+ * Strukturelle Validierung: Top-Level-Form, `version`, `exportedAt`,
69
+ * `state` und die sieben benannten Array-Felder. Wirft mit konkreter
70
+ * Message pro Verletzung.
71
+ */
72
+ export declare function parseSnapshot(json: string): DevtoolsSnapshot;
73
+ //# sourceMappingURL=snapshot-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-export.d.ts","sourceRoot":"","sources":["../src/snapshot-export.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAG,OAAgB,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,gBAAgB,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,qBAAqB,GAC7B,gBAAgB,CAMlB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAEhE;AAiBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAqC5D"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * v0.16.6 — DevtoolsSnapshot Export/Import.
3
+ *
4
+ * Captured Stream + State exportierbar als versioniertes JSON-Dokument;
5
+ * Loader-API für späteren Sandbox-Replay. Sandbox-UI ist NICHT v0.16.6
6
+ * (eigene Slice).
7
+ *
8
+ * **Verträge (Pilot-Verriegelungen 2026-06-09):**
9
+ * - `exportSnapshot(state, { exportedAt })` — Konsument liefert die
10
+ * Zeit. Helper ist pure: keine Zeit-/Zufalls-/Konsolen-APIs. Die
11
+ * konkreten verbotenen Identifier sind im Architecture-Test
12
+ * verriegelt; diese Quelle vermeidet sie auch in Kommentaren, damit
13
+ * der Source-Scan einfach bleibt. Der State wird beim Erzeugen via
14
+ * `toInspectableJson` normalisiert + detached — Mutationen am Live-
15
+ * State treffen den Snapshot nicht (Sub-1-Pilot-Schärfung).
16
+ * - `serializeSnapshot(snap)` — reines `JSON.stringify`. Snapshot ist
17
+ * bereits JSON-normalisiert beim Erzeugen, daher kein Pipeline-Schritt
18
+ * mehr. Lossy-Charakter (Map/Set/BigInt → JSON-Form, zyklische
19
+ * Strukturen → marker) gehört zum Snapshot-Format selbst, nicht zu
20
+ * Serialize: Sandbox-Replay ist Diagnose, kein Round-Trip-Beweis.
21
+ * - `parseSnapshot(json)` — strukturelle Validierung. Wirft bei
22
+ * JSON-Parse-Fehler, Nicht-Objekt-Top-Level, falscher `version`,
23
+ * nicht-finite `exportedAt`, fehlendem `state` oder fehlenden/
24
+ * falsch-typisierten Pflicht-State-Feldern. Tiefe Domain-Schema-
25
+ * Validierung NICHT in v0.16.6.
26
+ */
27
+ import { toInspectableJson } from './inspectable-json.js';
28
+ /**
29
+ * Format-Version als zentrale Konstante. Pilot-Verriegelung 2026-06-09
30
+ * (Sub-1-Schärfung): Architecture-Test prüft den Wert dieser Konstante
31
+ * direkt, statt freie `version:`-Literale im Source zu scannen. Eine
32
+ * spätere Slice, die das Format ändert, MUSS die Konstante hier ändern
33
+ * — und nichts anderes funktioniert ohne diese Stelle anzufassen.
34
+ */
35
+ export const SNAPSHOT_VERSION = '1.0.0';
36
+ /**
37
+ * Versioniertes Snapshot-Dokument bauen. Pilot-Schärfung Sub 1
38
+ * (HIGH-Finding): der State wird beim Erzeugen via `toInspectableJson`
39
+ * normalisiert + detached. Damit ist `serializeSnapshot()` nur noch
40
+ * `JSON.stringify`, und ein Konsument, der nach `exportSnapshot()` die
41
+ * Registry weiter-mutiert, beeinflusst den Snapshot nicht.
42
+ *
43
+ * Pure: deterministisch über das `state`-Argument, `exportedAt` vom
44
+ * Konsumenten (kein Date-/Random-/Konsolen-API).
45
+ */
46
+ export function exportSnapshot(state, options) {
47
+ return {
48
+ version: SNAPSHOT_VERSION,
49
+ exportedAt: options.exportedAt,
50
+ state: toInspectableJson(state),
51
+ };
52
+ }
53
+ /**
54
+ * JSON-String erzeugen. Snapshot ist bereits JSON-normalisiert (siehe
55
+ * `exportSnapshot`), daher reicht `JSON.stringify`.
56
+ */
57
+ export function serializeSnapshot(snap) {
58
+ return JSON.stringify(snap, null, 2);
59
+ }
60
+ /**
61
+ * Liste der State-Felder, die `parseSnapshot` namentlich prüft. Pilot-
62
+ * Verriegelung: NICHT „irgendeine sieben Arrays" — exakt diese Namen.
63
+ * Reihenfolge ist die Reihenfolge der Throw-Tests.
64
+ */
65
+ const REQUIRED_STATE_ARRAY_FIELDS = [
66
+ 'events',
67
+ 'syncRecords',
68
+ 'bridgeRecords',
69
+ 'eventStores',
70
+ 'syncTransports',
71
+ 'replayTargets',
72
+ 'bridges',
73
+ ];
74
+ /**
75
+ * Strukturelle Validierung: Top-Level-Form, `version`, `exportedAt`,
76
+ * `state` und die sieben benannten Array-Felder. Wirft mit konkreter
77
+ * Message pro Verletzung.
78
+ */
79
+ export function parseSnapshot(json) {
80
+ let parsed;
81
+ try {
82
+ parsed = JSON.parse(json);
83
+ }
84
+ catch (err) {
85
+ throw new Error(`parseSnapshot: invalid JSON — ${err instanceof Error ? err.message : String(err)}`);
86
+ }
87
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
88
+ throw new Error('parseSnapshot: top-level must be a plain object');
89
+ }
90
+ const obj = parsed;
91
+ if (obj.version !== SNAPSHOT_VERSION) {
92
+ throw new Error(`parseSnapshot: unsupported version "${String(obj.version)}" — expected '${SNAPSHOT_VERSION}'`);
93
+ }
94
+ if (typeof obj.exportedAt !== 'number' || !Number.isFinite(obj.exportedAt)) {
95
+ throw new Error('parseSnapshot: exportedAt must be a finite number');
96
+ }
97
+ const state = obj.state;
98
+ if (state === null || typeof state !== 'object' || Array.isArray(state)) {
99
+ throw new Error('parseSnapshot: state must be a plain object');
100
+ }
101
+ const stateObj = state;
102
+ for (const fieldName of REQUIRED_STATE_ARRAY_FIELDS) {
103
+ if (!(fieldName in stateObj)) {
104
+ throw new Error(`parseSnapshot: state.${fieldName} is missing`);
105
+ }
106
+ if (!Array.isArray(stateObj[fieldName])) {
107
+ throw new Error(`parseSnapshot: state.${fieldName} must be an array (got ${typeOfDetailed(stateObj[fieldName])})`);
108
+ }
109
+ }
110
+ return parsed;
111
+ }
112
+ function typeOfDetailed(value) {
113
+ if (value === null)
114
+ return 'null';
115
+ if (Array.isArray(value))
116
+ return 'array';
117
+ return typeof value;
118
+ }
119
+ //# sourceMappingURL=snapshot-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-export.js","sourceRoot":"","sources":["../src/snapshot-export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `createTimeTravelController` — ephemerer Controller, baut View-State zu
3
+ * einer beliebigen Stream-Version (v0.16.2 Sub 3).
4
+ *
5
+ * Architektur-Verträge aus `design/calendar/16-replay-target-architecture.md`:
6
+ * - **Eigene Subscribe-Seite**, NICHT im `DevtoolsState`-Snapshot — Slider-
7
+ * Bewegungen churchen nicht die Always-On-Listener im Inspector.
8
+ * - **Pure Fold-Kern wiederverwendet**: nutzt `foldEvents` aus
9
+ * `replay-engine.ts`, KEINE eigene Engine. Live-Engine + Controller
10
+ * produzieren bit-identische Snapshots (Sub 1 verriegelt).
11
+ * - **Stale-Cancel via running-counter**: schnelle Slider-Drags droppen
12
+ * ältere `goTo`-Resolves silently.
13
+ * - **Kein Range-Read**: `eventStore.read(streamId)` komplett, filter
14
+ * via `event.envelope.version <= targetVersion`. Optimierung erst mit
15
+ * Snapshot-Reuse-Slice.
16
+ * - **dispose-safe**: alle Methoden silent no-op nach dispose.
17
+ * - **`view.status` ist eigenständig** ('idle' | 'loading' | 'ready' | 'error')
18
+ * — getrennt von `view.snapshot.status` (das ist der Fold-Status:
19
+ * 'replaying' / 'live' / 'error'). UI nimmt `view.status` für den
20
+ * Controller-Zustand, nicht den geschachtelten Snapshot-Status.
21
+ */
22
+ import type { Unsubscribe } from '@bimetal/event-sourcing';
23
+ import { type ReplayEngineSnapshot, type ReplayTargetHandle } from './replay-engine.js';
24
+ import { type JsonDiffEntry } from './inspectable-json.js';
25
+ export type TimeTravelStatus = 'idle' | 'loading' | 'ready' | 'error';
26
+ export interface TimeTravelView<S = unknown, R = unknown> {
27
+ readonly status: TimeTravelStatus;
28
+ /** Aktuelle Ziel-Version. Beim ersten `goTo(v)` wird das `v`. */
29
+ readonly cursor: number;
30
+ /** Snapshot zur `cursor`-Version. `state`/`readModel` ableitbar. */
31
+ readonly snapshot: ReplayEngineSnapshot<S, R>;
32
+ /** Höchste im Stream gesehene Version beim letzten erfolgreichen `goTo`. */
33
+ readonly maxVersion: number;
34
+ readonly lastError?: Error;
35
+ }
36
+ export interface TimeTravelController<S = unknown, R = unknown> {
37
+ /** Reference-stable bis zur nächsten Mutation. */
38
+ readonly view: TimeTravelView<S, R>;
39
+ subscribe(listener: () => void): Unsubscribe;
40
+ /**
41
+ * Setzt den Cursor und re-faltet den Stream bis zu dieser Version.
42
+ * Stale-Cancel: ein neuer `goTo`-Aufruf invalidiert ältere laufende
43
+ * Reads — wenn die ältere `read`-Promise später resolved, wird ihr
44
+ * Resultat verworfen.
45
+ */
46
+ goTo(version: number): Promise<void>;
47
+ /**
48
+ * Diff zwischen `view.snapshot.state` und einem Vergleichs-State.
49
+ * - Ohne Argument: gegen Live (= alle Events gefoldet).
50
+ * - Mit `compareToVersion`: gegen genau diese Version.
51
+ *
52
+ * Diff operiert auf `state` (nicht `readModel`) — der Aggregate-Stand
53
+ * ist der semantische Anker. UI kann zusätzlich `readModel`-Diff
54
+ * anzeigen, indem sie `jsonDiff(currentView.snapshot.readModel, ...)`
55
+ * direkt ruft; das ist Konsumenten-Konzern.
56
+ */
57
+ diff(compareToVersion?: number): Promise<readonly JsonDiffEntry[]>;
58
+ /** Cleanup. Idempotent. Nach `dispose()` no-op'en alle Methoden. */
59
+ dispose(): void;
60
+ }
61
+ export declare function createTimeTravelController<S, R>(handle: ReplayTargetHandle<S, R>): TimeTravelController<S, R>;
62
+ //# sourceMappingURL=time-travel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-travel.d.ts","sourceRoot":"","sources":["../src/time-travel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAErE,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEtE,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACtD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IAC5D,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpC,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;IAC7C;;;;;OAKG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC;;;;;;;;;OASG;IACH,IAAI,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IACnE,oEAAoE;IACpE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,CAAC,EAC7C,MAAM,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC/B,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CA8H5B"}