@continuum-dev/runtime 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.
- package/LICENSE +21 -0
- package/README.md +403 -0
- package/index.d.ts +5 -0
- package/index.d.ts.map +1 -0
- package/index.js +4 -0
- package/lib/context.d.ts +128 -0
- package/lib/context.d.ts.map +1 -0
- package/lib/context.js +305 -0
- package/lib/reconcile.d.ts +17 -0
- package/lib/reconcile.d.ts.map +1 -0
- package/lib/reconcile.js +76 -0
- package/lib/reconciliation/collection-resolver.d.ts +13 -0
- package/lib/reconciliation/collection-resolver.d.ts.map +1 -0
- package/lib/reconciliation/collection-resolver.js +283 -0
- package/lib/reconciliation/component-resolver.d.ts +10 -0
- package/lib/reconciliation/component-resolver.d.ts.map +1 -0
- package/lib/reconciliation/differ.d.ts +12 -0
- package/lib/reconciliation/differ.d.ts.map +1 -0
- package/lib/reconciliation/differ.js +102 -0
- package/lib/reconciliation/migrator.d.ts +13 -0
- package/lib/reconciliation/migrator.d.ts.map +1 -0
- package/lib/reconciliation/migrator.js +92 -0
- package/lib/reconciliation/node-resolver.d.ts +10 -0
- package/lib/reconciliation/node-resolver.d.ts.map +1 -0
- package/lib/reconciliation/node-resolver.js +190 -0
- package/lib/reconciliation/state-builder.d.ts +13 -0
- package/lib/reconciliation/state-builder.d.ts.map +1 -0
- package/lib/reconciliation/state-builder.js +211 -0
- package/lib/reconciliation/validator.d.ts +14 -0
- package/lib/reconciliation/validator.d.ts.map +1 -0
- package/lib/reconciliation/validator.js +100 -0
- package/lib/reconciliation/view-traversal.d.ts +14 -0
- package/lib/reconciliation/view-traversal.d.ts.map +1 -0
- package/lib/reconciliation/view-traversal.js +72 -0
- package/lib/types.d.ts +153 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +1 -0
- package/package.json +44 -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,403 @@
|
|
|
1
|
+
# ♾️ @continuum-dev/runtime
|
|
2
|
+
|
|
3
|
+
**The State Reconciliation Engine for Generative UI.** Saving state is easy. Reconciling state across unpredictable AI mutations is hard.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/@continuum-dev%2Fruntime)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## The Problem: AI Forces Reconciliation
|
|
9
|
+
|
|
10
|
+
In a traditional application, state management is trivial: a field has a static ID, and you map a value to it.
|
|
11
|
+
|
|
12
|
+
But in **Generative UI**, the AI doesn't just change data-it changes *structure*.
|
|
13
|
+
|
|
14
|
+
Imagine an AI agent renders a UI for a user. The user starts filling out a text field (`id: "field_123"`). Halfway through, the AI decides to "improve" the layout. It streams an updated UI that moves the field into a new grid, changes the container type, and renames the ID to `id: "grid_item_456"`.
|
|
15
|
+
|
|
16
|
+
Standard frameworks drop the old node, mount the new node, and the user's input is destroyed. The data wasn't lost because of a bad state store; it was orphaned because **the map between the state and the UI was mutated**.
|
|
17
|
+
|
|
18
|
+
To fix this, you don't need a better state manager. You need a reconciliation engine.
|
|
19
|
+
|
|
20
|
+
## The Solution
|
|
21
|
+
|
|
22
|
+
**Continuum Runtime** is a pure, stateless reconciliation engine designed specifically to solve the continuity problem in AI-generated interfaces.
|
|
23
|
+
|
|
24
|
+
Given a `priorView`, a `newView`, and the `priorData`, the engine performs deterministic semantic diffing. It matches nodes by stable keys (even when IDs change), executes data migrations, handles deep nesting restructures, and outputs a perfectly reconciled state ready for rendering.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @continuum-dev/runtime
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Core Capabilities
|
|
32
|
+
|
|
33
|
+
* 🧠 **Semantic Reconciliation:** AI changed the node IDs? Wrapped them in a new `Row` or `Grid`? Continuum reconciles data via stable semantic keys, ensuring state survives massive layout overhauls.
|
|
34
|
+
* 🛡️ **Detached State Retention:** If the AI temporarily removes a field, Continuum doesn't throw the data away. It caches it as an "orphaned" value and automatically restores it if the AI brings the field back in a future turn.
|
|
35
|
+
* 🔄 **Data Migrations:** Upgrading a simple text `field` to a complex `collection`? Provide migration strategies to transform data payloads seamlessly across view transitions.
|
|
36
|
+
* ⚛️ **Pure & Framework Agnostic:** 100% pure TypeScript. Zero I/O side-effects. Use it to power the reconciliation layer of your React, Angular, Vue, or Vanilla JS agents.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
Here is how Continuum reconciles state when an AI completely restructures a UI mid-session.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { reconcile } from '@continuum-dev/runtime';
|
|
46
|
+
|
|
47
|
+
// 1. The old view and the user's current data
|
|
48
|
+
const priorView = {
|
|
49
|
+
viewId: 'v1',
|
|
50
|
+
version: '1.0',
|
|
51
|
+
nodes: [{ id: 'random_id_1', key: 'user_email', type: 'field' }]
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const priorData = {
|
|
55
|
+
values: {
|
|
56
|
+
'random_id_1': { value: 'alice@example.com' } // The user typed this!
|
|
57
|
+
},
|
|
58
|
+
lineage: { timestamp: Date.now(), sessionId: 'session_123', viewId: 'v1', viewVersion: '1.0' }
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// 2. The AI generates a totally new layout, burying the field in a group and changing the ID.
|
|
62
|
+
const newView = {
|
|
63
|
+
viewId: 'v2',
|
|
64
|
+
version: '2.0',
|
|
65
|
+
nodes: [{
|
|
66
|
+
id: 'layout_group',
|
|
67
|
+
type: 'group',
|
|
68
|
+
children: [{ id: 'new_id_99', key: 'user_email', type: 'field' }]
|
|
69
|
+
}]
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// 3. Reconcile the AI's structural mutation! 🪄
|
|
73
|
+
const { reconciledState, diffs, issues, resolutions } = reconcile(
|
|
74
|
+
newView,
|
|
75
|
+
priorView,
|
|
76
|
+
priorData
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Continuum reconciled the state using the stable 'user_email' key.
|
|
80
|
+
// Your data survived the AI's restructure!
|
|
81
|
+
console.log(reconciledState.values['layout_group/new_id_99'].value);
|
|
82
|
+
// Output: 'alice@example.com'
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Deep Dive: The Reconciliation Pipeline
|
|
87
|
+
|
|
88
|
+
When you call `reconcile()`, the runtime executes a strict, deterministic pipeline to figure out exactly what the AI did:
|
|
89
|
+
|
|
90
|
+
1. **Context Indexing:** Recursively indexes both views into lookup maps by `id` and `key`, using scoped nested paths plus dot-suffix key matching to detect structural shifts.
|
|
91
|
+
2. **Node Resolution:** Evaluates every node in the new view:
|
|
92
|
+
* **Carry:** Type and Hash match; data moves forward effortlessly.
|
|
93
|
+
* **Migrate:** Hash changed; trigger explicit or registry-based migration strategies to reshape the data payload.
|
|
94
|
+
* **Detach:** Type mismatch (e.g., a text input became a button); old data is safely stored in `detachedValues` rather than discarded.
|
|
95
|
+
* **Restore:** A newly generated node matches the key/type of a previously detached value, reconciling the old data back to life.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
3. **Collection Mapping:** Normalizes arrays, enforcing `minItems`/`maxItems` constraints and remapping nested template paths if the AI restructures list items.
|
|
99
|
+
4. **Validation:** Runs lightweight constraints checks (`min`, `max`, `pattern`, `required`) to immediately surface data issues caused by the AI's new schema.
|
|
100
|
+
|
|
101
|
+
## Advanced: Migration Strategies
|
|
102
|
+
|
|
103
|
+
Sometimes an AI doesn't just move a node; it fundamentally changes its data structure. Continuum allows you to define explicit migration strategies during reconciliation.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const result = reconcile(newView, priorView, priorData, {
|
|
107
|
+
migrationStrategies: {
|
|
108
|
+
// If the AI changes the 'status' node schema, run this transformation
|
|
109
|
+
'status': (nodeId, priorNode, newNode, priorValue) => {
|
|
110
|
+
const typedValue = priorValue as { value: string };
|
|
111
|
+
return { value: typedValue.value.toUpperCase() };
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
// Or pass a registry of chainable strategies defined by the view AST
|
|
115
|
+
strategyRegistry: {
|
|
116
|
+
'string-to-array': myStringToArrayFunction
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## The Continuum Ecosystem
|
|
125
|
+
|
|
126
|
+
This package is the core engine, but the Continuum SDK provides dedicated framework bindings so you don't have to wire this up manually:
|
|
127
|
+
|
|
128
|
+
* `@continuum-dev/session` - Stateful manager for conversational UI streams.
|
|
129
|
+
* `@continuum-dev/react` - React bindings and component renderer.
|
|
130
|
+
* `@continuum-dev/angular` - Angular bindings and directives.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Public API Reference
|
|
135
|
+
|
|
136
|
+
The runtime barrel file exports:
|
|
137
|
+
|
|
138
|
+
- `./lib/reconcile.js`
|
|
139
|
+
- `./lib/types.js`
|
|
140
|
+
- `./lib/context.js`
|
|
141
|
+
- `./lib/reconciliation/validator.js`
|
|
142
|
+
|
|
143
|
+
Most integrations only need `reconcile()`, but advanced applications can use the lower-level APIs for debugging, preflight checks, and custom matching workflows.
|
|
144
|
+
|
|
145
|
+
### 1) Core Orchestration
|
|
146
|
+
|
|
147
|
+
#### `reconcile()`
|
|
148
|
+
|
|
149
|
+
Primary runtime entrypoint. Compares a new view against prior view/data and returns reconciled state plus reconciliation metadata.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
function reconcile(
|
|
153
|
+
newView: ViewDefinition,
|
|
154
|
+
priorView: ViewDefinition | null,
|
|
155
|
+
priorData: DataSnapshot | null,
|
|
156
|
+
options?: ReconciliationOptions
|
|
157
|
+
): ReconciliationResult;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Use case:
|
|
161
|
+
- Call this on every new AI-generated view push to preserve user state across structural mutations.
|
|
162
|
+
- Use the returned `diffs`, `issues`, and `resolutions` to power devtools, telemetry, or UI diagnostics.
|
|
163
|
+
|
|
164
|
+
### 2) Core Types and Interfaces
|
|
165
|
+
|
|
166
|
+
Exported from `src/lib/types.ts`.
|
|
167
|
+
|
|
168
|
+
#### `ReconciliationOptions`
|
|
169
|
+
|
|
170
|
+
Controls runtime behavior and migration extension points.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
interface ReconciliationOptions {
|
|
174
|
+
allowPartialRestore?: boolean;
|
|
175
|
+
allowBlindCarry?: boolean;
|
|
176
|
+
migrationStrategies?: Record<string, MigrationStrategy>;
|
|
177
|
+
strategyRegistry?: Record<string, MigrationStrategy>;
|
|
178
|
+
clock?: () => number;
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Use case:
|
|
183
|
+
- Enable `allowBlindCarry` when you have prior data but no prior view AST.
|
|
184
|
+
- Provide `migrationStrategies` or `strategyRegistry` when node schemas evolve across view versions.
|
|
185
|
+
- Provide `clock` in tests for deterministic lineage timestamps.
|
|
186
|
+
|
|
187
|
+
#### `ReconciliationResult`
|
|
188
|
+
|
|
189
|
+
Full output payload from `reconcile()`.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
interface ReconciliationResult {
|
|
193
|
+
reconciledState: DataSnapshot;
|
|
194
|
+
diffs: StateDiff[];
|
|
195
|
+
issues: ReconciliationIssue[];
|
|
196
|
+
resolutions: ReconciliationResolution[];
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Use case:
|
|
201
|
+
- Persist `reconciledState` as your canonical session state.
|
|
202
|
+
- Use `issues` as immediate feedback when AI-generated schemas introduce invalid data.
|
|
203
|
+
|
|
204
|
+
#### `StateDiff`
|
|
205
|
+
|
|
206
|
+
Represents a single change produced during reconciliation.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
interface StateDiff {
|
|
210
|
+
nodeId: string;
|
|
211
|
+
type: ViewDiff;
|
|
212
|
+
oldValue?: unknown;
|
|
213
|
+
newValue?: unknown;
|
|
214
|
+
reason?: string;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Use case:
|
|
219
|
+
- Render "what changed" timelines in developer tooling.
|
|
220
|
+
- Trigger downstream automation only for specific diff types (for example, `migrated` or `removed`).
|
|
221
|
+
|
|
222
|
+
#### `ReconciliationResolution`
|
|
223
|
+
|
|
224
|
+
Per-node trace of how a value was resolved.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
interface ReconciliationResolution {
|
|
228
|
+
nodeId: string;
|
|
229
|
+
priorId: string | null;
|
|
230
|
+
matchedBy: 'id' | 'key' | null;
|
|
231
|
+
priorType: string | null;
|
|
232
|
+
newType: string;
|
|
233
|
+
resolution: DataResolution;
|
|
234
|
+
priorValue: unknown;
|
|
235
|
+
reconciledValue: unknown;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Use case:
|
|
240
|
+
- Explain why a node was carried, migrated, detached, added, or restored.
|
|
241
|
+
- Audit matching reliability (`matchedBy: 'id' | 'key'`) over real AI traffic.
|
|
242
|
+
|
|
243
|
+
#### `ReconciliationIssue`
|
|
244
|
+
|
|
245
|
+
Structured issue emitted by reconciliation and validation.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
interface ReconciliationIssue {
|
|
249
|
+
severity: IssueSeverity;
|
|
250
|
+
nodeId?: string;
|
|
251
|
+
message: string;
|
|
252
|
+
code: IssueCode;
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Use case:
|
|
257
|
+
- Surface warning/error banners in debugging UIs.
|
|
258
|
+
- Build alerting and metrics keyed by `code` and `severity`.
|
|
259
|
+
|
|
260
|
+
#### `MigrationStrategy`
|
|
261
|
+
|
|
262
|
+
User-implemented function for transforming payloads when a node schema changes.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
type MigrationStrategy = (
|
|
266
|
+
nodeId: string,
|
|
267
|
+
priorNode: ViewNode,
|
|
268
|
+
newNode: ViewNode,
|
|
269
|
+
priorValue: unknown
|
|
270
|
+
) => unknown;
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Use case:
|
|
274
|
+
- Convert old value shapes into new structures when AI changes a node contract.
|
|
275
|
+
- Keep business-critical data continuity through deliberate, explicit transformations.
|
|
276
|
+
|
|
277
|
+
Note:
|
|
278
|
+
- `NodeResolutionAccumulator` is also exported but is primarily an internal accumulator used by the reconciliation loop.
|
|
279
|
+
|
|
280
|
+
### 3) Context and Matching Utilities (Advanced)
|
|
281
|
+
|
|
282
|
+
Exported from `src/lib/context.ts`.
|
|
283
|
+
|
|
284
|
+
#### `buildReconciliationContext()`
|
|
285
|
+
|
|
286
|
+
Indexes new and prior views into id/key lookup maps used by matching and resolution.
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
function buildReconciliationContext(
|
|
290
|
+
newView: ViewDefinition,
|
|
291
|
+
priorView: ViewDefinition | null
|
|
292
|
+
): ReconciliationContext;
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Use case:
|
|
296
|
+
- Build custom reconciliation inspectors or diagnostics outside the main pipeline.
|
|
297
|
+
- Precompute matching context once when running specialized custom workflows.
|
|
298
|
+
|
|
299
|
+
#### `findPriorNode()`
|
|
300
|
+
|
|
301
|
+
Finds the best prior-view match for a new node using scoped id, key, and suffix fallback rules.
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
function findPriorNode(
|
|
305
|
+
ctx: ReconciliationContext,
|
|
306
|
+
newNode: ViewNode
|
|
307
|
+
): ViewNode | null;
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Use case:
|
|
311
|
+
- Debug matching behavior node-by-node.
|
|
312
|
+
- Validate whether your semantic key strategy is stable across generated layouts.
|
|
313
|
+
|
|
314
|
+
#### `buildPriorValueLookupByIdAndKey()`
|
|
315
|
+
|
|
316
|
+
Creates a lookup map that carries prior values by id and semantic key remapping.
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
function buildPriorValueLookupByIdAndKey(
|
|
320
|
+
priorData: DataSnapshot,
|
|
321
|
+
ctx: ReconciliationContext
|
|
322
|
+
): Map<string, unknown>;
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Use case:
|
|
326
|
+
- Reuse runtime value remapping logic in custom reconciliation or simulation tooling.
|
|
327
|
+
|
|
328
|
+
#### `determineNodeMatchStrategy()`
|
|
329
|
+
|
|
330
|
+
Returns how a new node matched to prior state (`id`, `key`, or `null`).
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
function determineNodeMatchStrategy(
|
|
334
|
+
ctx: ReconciliationContext,
|
|
335
|
+
newNode: ViewNode,
|
|
336
|
+
priorNode: ViewNode | null
|
|
337
|
+
): 'id' | 'key' | null;
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Use case:
|
|
341
|
+
- Instrument matching quality and identify over-reliance on id-only carries.
|
|
342
|
+
|
|
343
|
+
#### `resolvePriorSnapshotId()`
|
|
344
|
+
|
|
345
|
+
Resolves a snapshot key to a unique scoped prior node id when possible.
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
function resolvePriorSnapshotId(
|
|
349
|
+
ctx: ReconciliationContext,
|
|
350
|
+
priorId: string
|
|
351
|
+
): string | null;
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Use case:
|
|
355
|
+
- Normalize snapshot keys before custom diffing, reconciliation, or migration passes.
|
|
356
|
+
|
|
357
|
+
#### `findNewNodeByPriorNode()`
|
|
358
|
+
|
|
359
|
+
Maps a prior node forward to its best new-view candidate by key.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
function findNewNodeByPriorNode(
|
|
363
|
+
ctx: ReconciliationContext,
|
|
364
|
+
priorNode: ViewNode
|
|
365
|
+
): ViewNode | null;
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Use case:
|
|
369
|
+
- Build forward-mapping analyzers for node removals, moves, and restores.
|
|
370
|
+
|
|
371
|
+
#### `collectDuplicateIssues()`
|
|
372
|
+
|
|
373
|
+
Scans a view tree for duplicate ids/keys and returns structured issues.
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
function collectDuplicateIssues(nodes: ViewNode[]): ReconciliationIssue[];
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Use case:
|
|
380
|
+
- Run preflight validation on AI-generated views before invoking `reconcile()`.
|
|
381
|
+
|
|
382
|
+
### 4) Validation
|
|
383
|
+
|
|
384
|
+
Exported from `src/lib/reconciliation/validator.ts`.
|
|
385
|
+
|
|
386
|
+
#### `validateNodeValue()`
|
|
387
|
+
|
|
388
|
+
Validates a node value against view constraints (`required`, numeric bounds, length bounds, pattern).
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
function validateNodeValue(
|
|
392
|
+
node: ViewNode,
|
|
393
|
+
state: NodeValue | undefined
|
|
394
|
+
): ReconciliationIssue[];
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Use case:
|
|
398
|
+
- Reuse runtime-consistent validation semantics in custom form flows and pre-submit checks.
|
|
399
|
+
- Re-validate values independently when users edit data outside the default reconciliation loop.
|
|
400
|
+
|
|
401
|
+
## License
|
|
402
|
+
|
|
403
|
+
MIT © CooperContinuum
|
package/index.d.ts
ADDED
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/runtime/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mCAAmC,CAAC"}
|
package/index.js
ADDED
package/lib/context.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { type DataSnapshot, type ViewDefinition, type ViewNode } from '@continuum/contract';
|
|
2
|
+
import type { ReconciliationIssue } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Scans a view tree for duplicate ids/keys and traversal-level issues.
|
|
5
|
+
*
|
|
6
|
+
* Useful for preflight checks when validating generated views before running
|
|
7
|
+
* full reconciliation.
|
|
8
|
+
*
|
|
9
|
+
* @param nodes Root nodes from a view definition.
|
|
10
|
+
* @returns Aggregated issues for duplicates and traversal anomalies.
|
|
11
|
+
*/
|
|
12
|
+
export declare function collectDuplicateIssues(nodes: ViewNode[]): ReconciliationIssue[];
|
|
13
|
+
export interface ReconciliationContext {
|
|
14
|
+
/**
|
|
15
|
+
* New view currently being reconciled.
|
|
16
|
+
*/
|
|
17
|
+
newView: ViewDefinition;
|
|
18
|
+
/**
|
|
19
|
+
* Prior view used for matching, if available.
|
|
20
|
+
*/
|
|
21
|
+
priorView: ViewDefinition | null;
|
|
22
|
+
/**
|
|
23
|
+
* New-view node lookup keyed by scoped node id.
|
|
24
|
+
*/
|
|
25
|
+
newById: Map<string, ViewNode>;
|
|
26
|
+
/**
|
|
27
|
+
* New-view node lookup keyed by semantic key (scoped when needed).
|
|
28
|
+
*/
|
|
29
|
+
newByKey: Map<string, ViewNode>;
|
|
30
|
+
/**
|
|
31
|
+
* Prior-view node lookup keyed by scoped node id.
|
|
32
|
+
*/
|
|
33
|
+
priorById: Map<string, ViewNode>;
|
|
34
|
+
/**
|
|
35
|
+
* Prior-view node lookup keyed by semantic key (scoped when needed).
|
|
36
|
+
*/
|
|
37
|
+
priorByKey: Map<string, ViewNode>;
|
|
38
|
+
/**
|
|
39
|
+
* New-view indexed ids grouped by raw node id.
|
|
40
|
+
*/
|
|
41
|
+
newIdsByRawId: Map<string, string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Prior-view indexed ids grouped by raw node id.
|
|
44
|
+
*/
|
|
45
|
+
priorIdsByRawId: Map<string, string[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Stable mapping from new node objects to scoped node ids.
|
|
48
|
+
*/
|
|
49
|
+
newNodeIds: WeakMap<ViewNode, string>;
|
|
50
|
+
/**
|
|
51
|
+
* Stable mapping from prior node objects to scoped node ids.
|
|
52
|
+
*/
|
|
53
|
+
priorNodeIds: WeakMap<ViewNode, string>;
|
|
54
|
+
/**
|
|
55
|
+
* Frequency table for keys in the new view.
|
|
56
|
+
*/
|
|
57
|
+
newKeyCounts: Map<string, number>;
|
|
58
|
+
/**
|
|
59
|
+
* Frequency table for keys in the prior view.
|
|
60
|
+
*/
|
|
61
|
+
priorKeyCounts: Map<string, number>;
|
|
62
|
+
/**
|
|
63
|
+
* Issues captured while indexing both view trees.
|
|
64
|
+
*/
|
|
65
|
+
issues: ReconciliationIssue[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Builds lookup maps used by the reconciliation matching pipeline.
|
|
69
|
+
*
|
|
70
|
+
* Context indexes scoped ids and semantic keys so downstream logic can resolve
|
|
71
|
+
* carry/migrate/restore decisions in O(1) lookups.
|
|
72
|
+
*
|
|
73
|
+
* @param newView Target view for this reconciliation cycle.
|
|
74
|
+
* @param priorView Previous view to compare against, if available.
|
|
75
|
+
* @returns Indexed reconciliation context consumed by resolver stages.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildReconciliationContext(newView: ViewDefinition, priorView: ViewDefinition | null): ReconciliationContext;
|
|
78
|
+
/**
|
|
79
|
+
* Resolves the best prior-view match for a node in the new view.
|
|
80
|
+
*
|
|
81
|
+
* Matching is attempted in deterministic order: scoped id, semantic key,
|
|
82
|
+
* dot-suffix key fallback, and unique raw-id fallback.
|
|
83
|
+
*
|
|
84
|
+
* @param ctx Reconciliation context with prior/new indexes.
|
|
85
|
+
* @param newNode Node from the new view to resolve against prior view.
|
|
86
|
+
* @returns Matched prior node, or null when no candidate can be resolved.
|
|
87
|
+
*/
|
|
88
|
+
export declare function findPriorNode(ctx: ReconciliationContext, newNode: ViewNode): ViewNode | null;
|
|
89
|
+
/**
|
|
90
|
+
* Builds a value lookup that supports id-based and key-based carry.
|
|
91
|
+
*
|
|
92
|
+
* The returned map contains direct prior ids plus remapped ids when semantic
|
|
93
|
+
* key matches indicate the value should move to a different node id.
|
|
94
|
+
*
|
|
95
|
+
* @param priorData Previous data snapshot.
|
|
96
|
+
* @param ctx Reconciliation context built from prior/new views.
|
|
97
|
+
* @returns Value lookup used by node resolution.
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildPriorValueLookupByIdAndKey(priorData: DataSnapshot, ctx: ReconciliationContext): Map<string, unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* Reports whether a match was resolved by id or by semantic key.
|
|
102
|
+
*
|
|
103
|
+
* This metadata is attached to reconciliation resolution records so consumers
|
|
104
|
+
* can explain why a value was carried/migrated/restored.
|
|
105
|
+
*
|
|
106
|
+
* @param ctx Reconciliation context with id/key indexes.
|
|
107
|
+
* @param newNode Node from the new view.
|
|
108
|
+
* @param priorNode Prior match candidate for the new node.
|
|
109
|
+
* @returns `id`, `key`, or null when no prior node exists.
|
|
110
|
+
*/
|
|
111
|
+
export declare function determineNodeMatchStrategy(ctx: ReconciliationContext, newNode: ViewNode, priorNode: ViewNode | null): 'id' | 'key' | null;
|
|
112
|
+
/**
|
|
113
|
+
* Resolves a snapshot value key to a unique scoped prior node id.
|
|
114
|
+
*
|
|
115
|
+
* @param ctx Reconciliation context with prior-view indexes.
|
|
116
|
+
* @param priorId Snapshot key from `priorData.values`.
|
|
117
|
+
* @returns Scoped prior id when uniquely resolvable; otherwise null.
|
|
118
|
+
*/
|
|
119
|
+
export declare function resolvePriorSnapshotId(ctx: ReconciliationContext, priorId: string): string | null;
|
|
120
|
+
/**
|
|
121
|
+
* Finds the best new-view node candidate for a given prior node by key.
|
|
122
|
+
*
|
|
123
|
+
* @param ctx Reconciliation context with new-view key indexes.
|
|
124
|
+
* @param priorNode Prior-view node to map forward.
|
|
125
|
+
* @returns Matching new-view node, or null when no key-compatible node exists.
|
|
126
|
+
*/
|
|
127
|
+
export declare function findNewNodeByPriorNode(ctx: ReconciliationContext, priorNode: ViewNode): ViewNode | null;
|
|
128
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../packages/runtime/src/lib/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAA+B,MAAM,qBAAqB,CAAC;AACzH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGtD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,mBAAmB,EAAE,CA+B/E;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,EAAE,cAAc,CAAC;IACxB;;OAEG;IACH,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/B;;OAEG;IACH,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChC;;OAEG;IACH,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjC;;OAEG;IACH,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAClC;;OAEG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACrC;;OAEG;IACH,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACtC;;OAEG;IACH,YAAY,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC;;OAEG;IACH,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;;OAEG;IACH,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;OAEG;IACH,MAAM,EAAE,mBAAmB,EAAE,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,cAAc,GAAG,IAAI,GAC/B,qBAAqB,CAuDvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,qBAAqB,EAC1B,OAAO,EAAE,QAAQ,GAChB,QAAQ,GAAG,IAAI,CA4CjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,YAAY,EACvB,GAAG,EAAE,qBAAqB,GACzB,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,qBAAqB,EAC1B,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,QAAQ,GAAG,IAAI,GACzB,IAAI,GAAG,KAAK,GAAG,IAAI,CAsBrB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,qBAAqB,EAC1B,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAEf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,qBAAqB,EAC1B,SAAS,EAAE,QAAQ,GAClB,QAAQ,GAAG,IAAI,CAUjB"}
|