@continuum-dev/session 0.1.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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +306 -0
  3. package/index.d.ts +3 -0
  4. package/index.d.ts.map +1 -0
  5. package/index.js +2 -0
  6. package/lib/session/action-manager.d.ts +7 -0
  7. package/lib/session/action-manager.d.ts.map +1 -0
  8. package/lib/session/checkpoint-manager.d.ts +39 -0
  9. package/lib/session/checkpoint-manager.d.ts.map +1 -0
  10. package/lib/session/checkpoint-manager.js +106 -0
  11. package/lib/session/destroyer.d.ts +12 -0
  12. package/lib/session/destroyer.d.ts.map +1 -0
  13. package/lib/session/destroyer.js +22 -0
  14. package/lib/session/event-log.d.ts +13 -0
  15. package/lib/session/event-log.d.ts.map +1 -0
  16. package/lib/session/event-log.js +126 -0
  17. package/lib/session/intent-manager.d.ts +34 -0
  18. package/lib/session/intent-manager.d.ts.map +1 -0
  19. package/lib/session/intent-manager.js +67 -0
  20. package/lib/session/listeners.d.ts +49 -0
  21. package/lib/session/listeners.d.ts.map +1 -0
  22. package/lib/session/listeners.js +77 -0
  23. package/lib/session/persistence.d.ts +14 -0
  24. package/lib/session/persistence.d.ts.map +1 -0
  25. package/lib/session/persistence.js +99 -0
  26. package/lib/session/schema-pusher.d.ts +4 -0
  27. package/lib/session/schema-pusher.d.ts.map +1 -0
  28. package/lib/session/serializer.d.ts +25 -0
  29. package/lib/session/serializer.d.ts.map +1 -0
  30. package/lib/session/serializer.js +126 -0
  31. package/lib/session/session-state.d.ts +65 -0
  32. package/lib/session/session-state.d.ts.map +1 -0
  33. package/lib/session/session-state.js +79 -0
  34. package/lib/session/view-pusher.d.ts +13 -0
  35. package/lib/session/view-pusher.d.ts.map +1 -0
  36. package/lib/session/view-pusher.js +104 -0
  37. package/lib/session.d.ts +33 -0
  38. package/lib/session.d.ts.map +1 -0
  39. package/lib/session.js +275 -0
  40. package/lib/types.d.ts +265 -0
  41. package/lib/types.d.ts.map +1 -0
  42. package/lib/types.js +1 -0
  43. package/package.json +46 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CooperContinuum
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,306 @@
1
+ # ♾️ @continuum-dev/session
2
+
3
+ **The Stateful Ledger for Generative UI.** Give your AI agents memory, conflict resolution, and time-travel capabilities.
4
+
5
+ [![npm version](https://badge.fury.io/js/@continuum-dev%2Fsession.svg)](https://badge.fury.io/js/@continuum-dev%2Fsession)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## The Problem: The User and the AI are Fighting
9
+
10
+ Building a multi-turn Generative UI introduces a massive state management problem: **concurrency and conflict**.
11
+
12
+ - **Data Clobbering:** The user is typing in a text field, but the AI suddenly pushes an updated view and tries to overwrite their in-progress input.
13
+ - **Hallucinations:** The AI generates a broken layout or removes critical UI. You need deterministic undo.
14
+ - **Persistence:** A user closes the tab and returns later. You need exact rehydration of generated view, user state, and timeline.
15
+
16
+ Standard state managers are optimized for deterministic single-author UIs. They struggle when the UI schema itself mutates over time.
17
+
18
+ ## The Solution
19
+
20
+ **Continuum Session** is a stateful lifecycle manager built on top of `@continuum-dev/runtime`. It converts a stream of AI view mutations and user interactions into a structured, event-sourced session ledger.
21
+
22
+ It tracks event history, manages checkpoints, protects dirty user input with proposals, and serializes the entire session into a portable blob for resumable experiences.
23
+
24
+ ```bash
25
+ npm install @continuum-dev/session
26
+ ```
27
+
28
+ ## Core Capabilities
29
+
30
+ - ⏱️ **Time-Travel (Undo/Rewind):** Auto-checkpoints on `pushView()`, plus manual checkpoints and rewind support.
31
+ - 🛡️ **Conflict Resolution (Proposals):** Dirty values are protected from AI overwrites by staging proposed values.
32
+ - 💾 **Portable Persistence:** `serialize()` and `deserialize()` capture and restore full session state.
33
+ - ⚡ **Action Registry:** Register and dispatch typed action handlers by intent id.
34
+ - 📖 **Event-Sourced Timeline:** Interactions, intents, checkpoint boundaries, and reconciliation diagnostics are preserved.
35
+
36
+ ---
37
+
38
+ ## Quick Start
39
+
40
+ ```typescript
41
+ import { createSession } from '@continuum-dev/session';
42
+
43
+ const session = createSession();
44
+
45
+ session.pushView({
46
+ viewId: 'agent-form',
47
+ version: '1.0',
48
+ nodes: [{ id: 'field_1', key: 'username', type: 'field' }]
49
+ });
50
+
51
+ session.updateState('field_1', { value: 'Alice' });
52
+
53
+ const snapshot = session.getSnapshot();
54
+ console.log(snapshot?.data.values['field_1'].value); // 'Alice'
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Public API Reference
60
+
61
+ The package exports:
62
+
63
+ - `./lib/session.js`
64
+ - `./lib/types.js`
65
+
66
+ ### 1) Initialization and Lifecycle
67
+
68
+ #### `createSession(options?)`
69
+
70
+ Creates a fresh session ledger.
71
+
72
+ ```typescript
73
+ function createSession(options?: SessionOptions): Session;
74
+ ```
75
+
76
+ #### `deserialize(data, options?)`
77
+
78
+ Restores a previously serialized session blob.
79
+
80
+ ```typescript
81
+ function deserialize(data: unknown, options?: SessionOptions): Session;
82
+ ```
83
+
84
+ #### `hydrateOrCreate(options?)`
85
+
86
+ Creates from persisted storage when available, otherwise creates a new session.
87
+
88
+ ```typescript
89
+ function hydrateOrCreate(options?: SessionOptions): Session;
90
+ ```
91
+
92
+ Persistence note:
93
+
94
+ - When `options.persistence` is provided, snapshot writes are debounced by 200ms.
95
+ - `SessionPersistenceOptions.maxBytes` enforces a payload size cap before writes.
96
+ - `SessionPersistenceOptions.onError` receives `size_limit` and `storage_error` events.
97
+ - Browser `storage` events are consumed for cross-tab session synchronization.
98
+
99
+ #### `sessionFactory`
100
+
101
+ DI-friendly factory object for session creation and deserialization.
102
+
103
+ ```typescript
104
+ const sessionFactory: SessionFactory = { createSession, deserialize };
105
+ ```
106
+
107
+ #### Subscriptions
108
+
109
+ Listen to state and issue updates.
110
+
111
+ ```typescript
112
+ const stopSnapshot = session.onSnapshot((snapshot) => {
113
+ // update UI
114
+ });
115
+
116
+ const stopIssues = session.onIssues((issues) => {
117
+ // handle warnings/errors
118
+ });
119
+ ```
120
+
121
+ ### 2) View and State Updates
122
+
123
+ #### `session.pushView(view)`
124
+
125
+ Pushes a new AI-generated view, runs reconciliation, updates detached values, marks stale pending intents on version change, and creates an auto-checkpoint.
126
+
127
+ ```typescript
128
+ session.pushView({ viewId: 'form', version: '2.0', nodes: [] });
129
+ ```
130
+
131
+ #### `session.updateState(nodeId, payload)`
132
+
133
+ Records a data update interaction for a node.
134
+
135
+ ```typescript
136
+ session.updateState('email', { value: 'test@example.com', isDirty: true });
137
+ ```
138
+
139
+ #### `session.recordIntent(interaction)`
140
+
141
+ Records a raw interaction event.
142
+
143
+ ```typescript
144
+ session.recordIntent({
145
+ nodeId: 'email',
146
+ type: 'data-update',
147
+ payload: { value: 'test@example.com' }
148
+ });
149
+ ```
150
+
151
+ #### Viewport APIs
152
+
153
+ ```typescript
154
+ session.updateViewportState('table_1', { scrollY: 320, isFocused: true });
155
+ const viewport = session.getViewportState('table_1');
156
+ ```
157
+
158
+ ### 3) Anti-Clobbering with Proposals
159
+
160
+ When a node has dirty user state and AI proposes a new value, Continuum stages the proposal instead of overwriting immediately.
161
+
162
+ #### `session.proposeValue(nodeId, value, source?)`
163
+
164
+ ```typescript
165
+ session.proposeValue('email', { value: 'ai_guess@example.com' }, 'ai-agent');
166
+ ```
167
+
168
+ #### `session.getPendingProposals()`
169
+
170
+ ```typescript
171
+ const proposals = session.getPendingProposals();
172
+ ```
173
+
174
+ #### `session.acceptProposal(nodeId)` / `session.rejectProposal(nodeId)`
175
+
176
+ ```typescript
177
+ session.acceptProposal('email');
178
+ session.rejectProposal('email');
179
+ ```
180
+
181
+ ### 4) Time Travel with Checkpoints
182
+
183
+ #### `session.checkpoint()`
184
+
185
+ Manually captures a checkpoint snapshot.
186
+
187
+ ```typescript
188
+ const cp = session.checkpoint();
189
+ ```
190
+
191
+ #### `session.rewind(checkpointId)`
192
+
193
+ Rewinds to a checkpoint id and truncates checkpoint history after that point.
194
+
195
+ ```typescript
196
+ session.rewind(cp.checkpointId);
197
+ ```
198
+
199
+ #### `session.restoreFromCheckpoint(checkpoint)`
200
+
201
+ Restores to a checkpoint object without truncating the checkpoint stack.
202
+
203
+ ```typescript
204
+ session.restoreFromCheckpoint(cp);
205
+ ```
206
+
207
+ #### `session.getCheckpoints()`
208
+
209
+ ```typescript
210
+ const checkpoints = session.getCheckpoints();
211
+ ```
212
+
213
+ ### 5) Intents and Event Sourcing
214
+
215
+ #### `session.submitIntent(intent)`
216
+
217
+ Queue a pending user intent for AI/backend processing.
218
+
219
+ ```typescript
220
+ session.submitIntent({
221
+ nodeId: 'submit_btn',
222
+ intentName: 'execute_search',
223
+ payload: { term: 'Continuum' }
224
+ });
225
+ ```
226
+
227
+ #### `session.getPendingIntents()`, `session.validateIntent(intentId)`, `session.cancelIntent(intentId)`
228
+
229
+ ```typescript
230
+ const intents = session.getPendingIntents();
231
+ session.validateIntent(intents[0].intentId);
232
+ session.cancelIntent(intents[0].intentId);
233
+ ```
234
+
235
+ #### `session.getEventLog()`
236
+
237
+ ```typescript
238
+ const log = session.getEventLog();
239
+ ```
240
+
241
+ ### 6) Actions Registry
242
+
243
+ Register local handlers for semantic intent ids and dispatch them with current session snapshot data.
244
+
245
+ ```typescript
246
+ session.registerAction('fetch_data', { label: 'Fetch Data' }, async ({ snapshot, nodeId }) => {
247
+ void snapshot;
248
+ void nodeId;
249
+ });
250
+
251
+ await session.dispatchAction('fetch_data', 'btn_1');
252
+ ```
253
+
254
+ Also available:
255
+
256
+ - `session.unregisterAction(intentId)`
257
+ - `session.getRegisteredActions()`
258
+
259
+ ### 7) Teardown, Persistence, and Maintenance
260
+
261
+ ```typescript
262
+ const blob = session.serialize();
263
+ const detached = session.getDetachedValues();
264
+ session.purgeDetachedValues();
265
+
266
+ session.reset();
267
+ const final = session.destroy();
268
+ ```
269
+
270
+ Persistence behavior:
271
+
272
+ - `serialize()` returns a JSON-safe payload with `formatVersion: 1`.
273
+ - Automatic persistence writes are debounced (200ms) to reduce storage churn.
274
+ - If `maxBytes` is exceeded, the write is skipped and `onError` is invoked.
275
+ - Remote storage updates can rehydrate in-memory state for cross-tab continuity.
276
+
277
+ `destroy()` returns:
278
+
279
+ ```typescript
280
+ { issues: ReconciliationIssue[] }
281
+ ```
282
+
283
+ ### 8) Core Types
284
+
285
+ Primary exported types from `src/lib/types.ts`:
286
+
287
+ - `Session`
288
+ - `SessionOptions`
289
+ - `SessionFactory`
290
+ - `SessionPersistenceOptions`
291
+ - `SessionPersistenceStorage`
292
+
293
+ ---
294
+
295
+ ## Architecture Context
296
+
297
+ `@continuum-dev/session` handles stateful timeline management and lifecycle orchestration. It delegates structural data reconciliation to `@continuum-dev/runtime` whenever a new view is pushed.
298
+
299
+ Framework bindings can wrap this package for UI-first usage:
300
+
301
+ - `@continuum-dev/react`
302
+ - `@continuum-dev/angular`
303
+
304
+ ## License
305
+
306
+ MIT © CooperContinuum
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './lib/session.js';
2
+ export * from './lib/types.js';
3
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/session/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC"}
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './lib/session.js';
2
+ export * from './lib/types.js';
@@ -0,0 +1,7 @@
1
+ import type { PendingAction } from '@continuum/contract';
2
+ import type { SessionState } from './session-state.js';
3
+ export declare function submitAction(internal: SessionState, partial: Omit<PendingAction, 'id' | 'createdAt' | 'status' | 'schemaVersion'>): void;
4
+ export declare function validateAction(internal: SessionState, actionId: string): boolean;
5
+ export declare function cancelAction(internal: SessionState, actionId: string): boolean;
6
+ export declare function markAllPendingActionsAsStale(internal: SessionState): void;
7
+ //# sourceMappingURL=action-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-manager.d.ts","sourceRoot":"","sources":["../../../../../packages/session/src/lib/session/action-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,GAAG,eAAe,CAAC,GAC5E,IAAI,CAiBN;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKhF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK9E;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAMzE"}
@@ -0,0 +1,39 @@
1
+ import type { Checkpoint } from '@continuum/contract';
2
+ import type { SessionState } from './session-state.js';
3
+ /**
4
+ * Deep clones checkpoint payloads to prevent accidental shared mutations.
5
+ *
6
+ * @param value Snapshot value to clone.
7
+ * @returns Structured cloned value.
8
+ */
9
+ export declare function cloneCheckpointSnapshot<T>(value: T): T;
10
+ /**
11
+ * Creates an automatic checkpoint from current snapshot state.
12
+ *
13
+ * Auto checkpoints are pruned first when exceeding `maxCheckpoints`.
14
+ *
15
+ * @param internal Mutable internal session state.
16
+ */
17
+ export declare function autoCheckpoint(internal: SessionState): void;
18
+ /**
19
+ * Creates a manual checkpoint and appends it to the checkpoint stack.
20
+ *
21
+ * @param internal Mutable internal session state.
22
+ * @returns The created checkpoint.
23
+ */
24
+ export declare function createManualCheckpoint(internal: SessionState): Checkpoint;
25
+ /**
26
+ * Restores state from a checkpoint without truncating checkpoint history.
27
+ *
28
+ * @param internal Mutable internal session state.
29
+ * @param cp Checkpoint to restore from.
30
+ */
31
+ export declare function restoreFromCheckpoint(internal: SessionState, cp: Checkpoint): void;
32
+ /**
33
+ * Rewinds state to a checkpoint id and truncates checkpoints after it.
34
+ *
35
+ * @param internal Mutable internal session state.
36
+ * @param checkpointId Target checkpoint id.
37
+ */
38
+ export declare function rewind(internal: SessionState, checkpointId: string): void;
39
+ //# sourceMappingURL=checkpoint-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint-manager.d.ts","sourceRoot":"","sources":["../../../../../packages/session/src/lib/session/checkpoint-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOvD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAqB3D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,YAAY,GAAG,UAAU,CAezE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,CAalF;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAkBzE"}
@@ -0,0 +1,106 @@
1
+ import { generateId } from './session-state.js';
2
+ import { buildSnapshotFromCurrentState, notifySnapshotAndIssueListeners, } from './listeners.js';
3
+ /**
4
+ * Deep clones checkpoint payloads to prevent accidental shared mutations.
5
+ *
6
+ * @param value Snapshot value to clone.
7
+ * @returns Structured cloned value.
8
+ */
9
+ export function cloneCheckpointSnapshot(value) {
10
+ return structuredClone(value);
11
+ }
12
+ /**
13
+ * Creates an automatic checkpoint from current snapshot state.
14
+ *
15
+ * Auto checkpoints are pruned first when exceeding `maxCheckpoints`.
16
+ *
17
+ * @param internal Mutable internal session state.
18
+ */
19
+ export function autoCheckpoint(internal) {
20
+ const snapshot = buildSnapshotFromCurrentState(internal);
21
+ if (!snapshot)
22
+ return;
23
+ const id = generateId('cp', internal.clock);
24
+ internal.checkpoints.push({
25
+ checkpointId: id,
26
+ sessionId: internal.sessionId,
27
+ snapshot: cloneCheckpointSnapshot(snapshot),
28
+ eventIndex: internal.eventLog.length,
29
+ timestamp: internal.clock(),
30
+ trigger: 'auto',
31
+ });
32
+ if (internal.checkpoints.length > internal.maxCheckpoints) {
33
+ const overflow = internal.checkpoints.length - internal.maxCheckpoints;
34
+ for (let index = 0; index < overflow; index += 1) {
35
+ const removableIndex = internal.checkpoints.findIndex((checkpoint) => checkpoint.trigger === 'auto');
36
+ if (removableIndex === -1)
37
+ break;
38
+ internal.checkpoints.splice(removableIndex, 1);
39
+ }
40
+ }
41
+ }
42
+ /**
43
+ * Creates a manual checkpoint and appends it to the checkpoint stack.
44
+ *
45
+ * @param internal Mutable internal session state.
46
+ * @returns The created checkpoint.
47
+ */
48
+ export function createManualCheckpoint(internal) {
49
+ const snapshot = buildSnapshotFromCurrentState(internal);
50
+ if (!snapshot) {
51
+ throw new Error('Cannot create checkpoint before pushing a view');
52
+ }
53
+ const checkpoint = {
54
+ checkpointId: generateId('cp', internal.clock),
55
+ sessionId: internal.sessionId,
56
+ snapshot: cloneCheckpointSnapshot(snapshot),
57
+ eventIndex: internal.eventLog.length,
58
+ timestamp: internal.clock(),
59
+ trigger: 'manual',
60
+ };
61
+ internal.checkpoints.push(checkpoint);
62
+ return checkpoint;
63
+ }
64
+ /**
65
+ * Restores state from a checkpoint without truncating checkpoint history.
66
+ *
67
+ * @param internal Mutable internal session state.
68
+ * @param cp Checkpoint to restore from.
69
+ */
70
+ export function restoreFromCheckpoint(internal, cp) {
71
+ if (internal.destroyed)
72
+ return;
73
+ internal.currentView = cloneCheckpointSnapshot(cp.snapshot.view);
74
+ internal.currentData = cloneCheckpointSnapshot(cp.snapshot.data);
75
+ internal.priorView = null;
76
+ internal.eventLog = internal.eventLog.slice(0, cp.eventIndex);
77
+ internal.issues = [];
78
+ internal.diffs = [];
79
+ internal.resolutions = [];
80
+ internal.pendingIntents = [];
81
+ notifySnapshotAndIssueListeners(internal);
82
+ }
83
+ /**
84
+ * Rewinds state to a checkpoint id and truncates checkpoints after it.
85
+ *
86
+ * @param internal Mutable internal session state.
87
+ * @param checkpointId Target checkpoint id.
88
+ */
89
+ export function rewind(internal, checkpointId) {
90
+ if (internal.destroyed)
91
+ return;
92
+ const idx = internal.checkpoints.findIndex((cp) => cp.checkpointId === checkpointId);
93
+ if (idx === -1)
94
+ throw new Error(`Checkpoint ${checkpointId} not found`);
95
+ const cp = internal.checkpoints[idx];
96
+ internal.checkpoints = internal.checkpoints.slice(0, idx + 1);
97
+ internal.currentView = cloneCheckpointSnapshot(cp.snapshot.view);
98
+ internal.currentData = cloneCheckpointSnapshot(cp.snapshot.data);
99
+ internal.priorView = null;
100
+ internal.eventLog = internal.eventLog.slice(0, cp.eventIndex);
101
+ internal.issues = [];
102
+ internal.diffs = [];
103
+ internal.resolutions = [];
104
+ internal.pendingIntents = [];
105
+ notifySnapshotAndIssueListeners(internal);
106
+ }
@@ -0,0 +1,12 @@
1
+ import type { ReconciliationIssue } from '@continuum/runtime';
2
+ import type { SessionState } from './session-state.js';
3
+ /**
4
+ * Destroys a session and clears all mutable state containers.
5
+ *
6
+ * @param internal Mutable internal session state.
7
+ * @returns Final snapshot of issues captured before teardown.
8
+ */
9
+ export declare function teardownSessionAndClearState(internal: SessionState): {
10
+ issues: ReconciliationIssue[];
11
+ };
12
+ //# sourceMappingURL=destroyer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"destroyer.d.ts","sourceRoot":"","sources":["../../../../../packages/session/src/lib/session/destroyer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,mBAAmB,EAAE,CAAA;CAAE,CAetG"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Destroys a session and clears all mutable state containers.
3
+ *
4
+ * @param internal Mutable internal session state.
5
+ * @returns Final snapshot of issues captured before teardown.
6
+ */
7
+ export function teardownSessionAndClearState(internal) {
8
+ internal.destroyed = true;
9
+ internal.currentView = null;
10
+ internal.currentData = null;
11
+ internal.priorView = null;
12
+ internal.eventLog = [];
13
+ internal.pendingIntents = [];
14
+ internal.checkpoints = [];
15
+ const result = { issues: [...internal.issues] };
16
+ internal.issues = [];
17
+ internal.diffs = [];
18
+ internal.resolutions = [];
19
+ internal.snapshotListeners.clear();
20
+ internal.issueListeners.clear();
21
+ return result;
22
+ }
@@ -0,0 +1,13 @@
1
+ import type { Interaction } from '@continuum/contract';
2
+ import type { SessionState } from './session-state.js';
3
+ /**
4
+ * Records an interaction event and applies its payload to current data state.
5
+ *
6
+ * The function updates event log, value lineage, optional validation issues,
7
+ * and latest auto-checkpoint snapshot.
8
+ *
9
+ * @param internal Mutable internal session state.
10
+ * @param partial Interaction payload without generated metadata.
11
+ */
12
+ export declare function recordIntent(internal: SessionState, partial: Omit<Interaction, 'interactionId' | 'timestamp' | 'sessionId' | 'viewVersion'>): void;
13
+ //# sourceMappingURL=event-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-log.d.ts","sourceRoot":"","sources":["../../../../../packages/session/src/lib/session/event-log.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,WAAW,EAA2B,MAAM,qBAAqB,CAAC;AAE1F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAsDvD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,eAAe,GAAG,WAAW,GAAG,WAAW,GAAG,aAAa,CAAC,GACtF,IAAI,CAiFN"}
@@ -0,0 +1,126 @@
1
+ import { getChildNodes, ISSUE_CODES, ISSUE_SEVERITY, isInteractionType } from '@continuum/contract';
2
+ import { generateId } from './session-state.js';
3
+ import { buildSnapshotFromCurrentState, notifySnapshotAndIssueListeners } from './listeners.js';
4
+ import { cloneCheckpointSnapshot } from './checkpoint-manager.js';
5
+ import { validateNodeValue } from '@continuum/runtime';
6
+ function collectNodesByCanonicalId(nodes) {
7
+ const byId = new Map();
8
+ const walk = (items, parentPath) => {
9
+ for (const node of items) {
10
+ const nodeId = parentPath.length > 0 ? `${parentPath}/${node.id}` : node.id;
11
+ byId.set(nodeId, node);
12
+ const children = getChildNodes(node);
13
+ if (children.length > 0) {
14
+ walk(children, nodeId);
15
+ }
16
+ }
17
+ };
18
+ walk(nodes, '');
19
+ return byId;
20
+ }
21
+ function resolveNodeLookupEntry(nodes, requestedId) {
22
+ const canonicalMap = collectNodesByCanonicalId(nodes);
23
+ const direct = canonicalMap.get(requestedId);
24
+ if (direct) {
25
+ return { canonicalId: requestedId, node: direct };
26
+ }
27
+ const matches = [];
28
+ const walk = (items, parentPath) => {
29
+ for (const node of items) {
30
+ const canonicalId = parentPath.length > 0 ? `${parentPath}/${node.id}` : node.id;
31
+ if (node.id === requestedId) {
32
+ matches.push({ canonicalId, node });
33
+ }
34
+ const children = getChildNodes(node);
35
+ if (children.length > 0) {
36
+ walk(children, canonicalId);
37
+ }
38
+ }
39
+ };
40
+ walk(nodes, '');
41
+ if (matches.length === 1) {
42
+ return matches[0];
43
+ }
44
+ return null;
45
+ }
46
+ /**
47
+ * Records an interaction event and applies its payload to current data state.
48
+ *
49
+ * The function updates event log, value lineage, optional validation issues,
50
+ * and latest auto-checkpoint snapshot.
51
+ *
52
+ * @param internal Mutable internal session state.
53
+ * @param partial Interaction payload without generated metadata.
54
+ */
55
+ export function recordIntent(internal, partial) {
56
+ if (internal.destroyed || !internal.currentData || !internal.currentView)
57
+ return;
58
+ if (!isInteractionType(partial.type)) {
59
+ throw new Error(`Invalid interaction type: ${String(partial.type)}`);
60
+ }
61
+ const now = internal.clock();
62
+ const id = generateId('int', internal.clock);
63
+ const interaction = {
64
+ interactionId: id,
65
+ sessionId: internal.sessionId,
66
+ viewVersion: internal.currentView.version,
67
+ timestamp: now,
68
+ nodeId: partial.nodeId,
69
+ type: partial.type,
70
+ payload: partial.payload,
71
+ };
72
+ internal.eventLog.push(interaction);
73
+ if (internal.eventLog.length > internal.maxEventLogSize) {
74
+ internal.eventLog.splice(0, internal.eventLog.length - internal.maxEventLogSize);
75
+ }
76
+ const resolvedEntry = resolveNodeLookupEntry(internal.currentView.nodes, partial.nodeId);
77
+ if (!resolvedEntry) {
78
+ internal.issues = [
79
+ ...internal.issues,
80
+ {
81
+ severity: ISSUE_SEVERITY.WARNING,
82
+ nodeId: partial.nodeId,
83
+ message: `Node ${partial.nodeId} not found in current view`,
84
+ code: ISSUE_CODES.UNKNOWN_NODE,
85
+ },
86
+ ];
87
+ notifySnapshotAndIssueListeners(internal);
88
+ return;
89
+ }
90
+ const { canonicalId, node } = resolvedEntry;
91
+ internal.currentData = {
92
+ ...internal.currentData,
93
+ values: {
94
+ ...internal.currentData.values,
95
+ [canonicalId]: partial.payload,
96
+ },
97
+ lineage: {
98
+ ...internal.currentData.lineage,
99
+ timestamp: now,
100
+ lastInteractionId: id,
101
+ },
102
+ valueLineage: {
103
+ ...internal.currentData.valueLineage,
104
+ [canonicalId]: {
105
+ lastUpdated: now,
106
+ lastInteractionId: id,
107
+ },
108
+ },
109
+ };
110
+ if (internal.validateOnUpdate) {
111
+ const validationIssues = validateNodeValue(node, partial.payload);
112
+ if (validationIssues.length > 0) {
113
+ internal.issues = [...internal.issues, ...validationIssues];
114
+ }
115
+ }
116
+ const lastAutoCheckpoint = [...internal.checkpoints]
117
+ .reverse()
118
+ .find((checkpoint) => checkpoint.trigger === 'auto');
119
+ if (lastAutoCheckpoint) {
120
+ const snapshot = buildSnapshotFromCurrentState(internal);
121
+ if (snapshot) {
122
+ lastAutoCheckpoint.snapshot = cloneCheckpointSnapshot(snapshot);
123
+ }
124
+ }
125
+ notifySnapshotAndIssueListeners(internal);
126
+ }