arborkit 1.0.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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/dist/addressing.d.ts +22 -0
- package/dist/addressing.js +56 -0
- package/dist/addressing.js.map +1 -0
- package/dist/ag-ui.d.ts +45 -0
- package/dist/ag-ui.js +50 -0
- package/dist/ag-ui.js.map +1 -0
- package/dist/arbor.d.ts +73 -0
- package/dist/arbor.js +1542 -0
- package/dist/arbor.js.map +1 -0
- package/dist/artifact-tree.d.ts +66 -0
- package/dist/artifact-tree.js +285 -0
- package/dist/artifact-tree.js.map +1 -0
- package/dist/clock.d.ts +15 -0
- package/dist/clock.js +23 -0
- package/dist/clock.js.map +1 -0
- package/dist/decompose.d.ts +18 -0
- package/dist/decompose.js +22 -0
- package/dist/decompose.js.map +1 -0
- package/dist/delta-storage.d.ts +55 -0
- package/dist/delta-storage.js +106 -0
- package/dist/delta-storage.js.map +1 -0
- package/dist/delta.d.ts +55 -0
- package/dist/delta.js +740 -0
- package/dist/delta.js.map +1 -0
- package/dist/embedding-port.d.ts +17 -0
- package/dist/embedding-port.js +21 -0
- package/dist/embedding-port.js.map +1 -0
- package/dist/embedding-text.d.ts +14 -0
- package/dist/embedding-text.js +21 -0
- package/dist/embedding-text.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/event-log.d.ts +75 -0
- package/dist/event-log.js +82 -0
- package/dist/event-log.js.map +1 -0
- package/dist/file-storage.d.ts +22 -0
- package/dist/file-storage.js +42 -0
- package/dist/file-storage.js.map +1 -0
- package/dist/ids.d.ts +17 -0
- package/dist/ids.js +22 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +1826 -0
- package/dist/index.js.map +1 -0
- package/dist/json-edit.d.ts +18 -0
- package/dist/json-edit.js +85 -0
- package/dist/json-edit.js.map +1 -0
- package/dist/jsonpointer.d.ts +14 -0
- package/dist/jsonpointer.js +33 -0
- package/dist/jsonpointer.js.map +1 -0
- package/dist/mutator.d.ts +59 -0
- package/dist/mutator.js +244 -0
- package/dist/mutator.js.map +1 -0
- package/dist/navigator.d.ts +85 -0
- package/dist/navigator.js +192 -0
- package/dist/navigator.js.map +1 -0
- package/dist/path-glob.d.ts +13 -0
- package/dist/path-glob.js +40 -0
- package/dist/path-glob.js.map +1 -0
- package/dist/registry-validator.d.ts +15 -0
- package/dist/registry-validator.js +11 -0
- package/dist/registry-validator.js.map +1 -0
- package/dist/replay.d.ts +38 -0
- package/dist/replay.js +183 -0
- package/dist/replay.js.map +1 -0
- package/dist/semantic-index.d.ts +88 -0
- package/dist/semantic-index.js +226 -0
- package/dist/semantic-index.js.map +1 -0
- package/dist/storage.d.ts +39 -0
- package/dist/storage.js +378 -0
- package/dist/storage.js.map +1 -0
- package/dist/toolset.d.ts +78 -0
- package/dist/toolset.js +306 -0
- package/dist/toolset.js.map +1 -0
- package/dist/type-aware-decision.d.ts +8 -0
- package/dist/type-aware-decision.js +17 -0
- package/dist/type-aware-decision.js.map +1 -0
- package/dist/type-registry.d.ts +20 -0
- package/dist/type-registry.js +17 -0
- package/dist/type-registry.js.map +1 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/vector-index-port.d.ts +34 -0
- package/dist/vector-index-port.js +49 -0
- package/dist/vector-index-port.js.map +1 -0
- package/dist/zod-adapter.d.ts +13 -0
- package/dist/zod-adapter.js +34 -0
- package/dist/zod-adapter.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NodeId, ArbNode } from './types.js';
|
|
2
|
+
import { TreeDeps, ArtifactTree } from './artifact-tree.js';
|
|
3
|
+
import { MutationEvent, EventLog } from './event-log.js';
|
|
4
|
+
import { VectorIndexEntry, VectorIndexPort } from './vector-index-port.js';
|
|
5
|
+
import './ids.js';
|
|
6
|
+
import './clock.js';
|
|
7
|
+
import './decompose.js';
|
|
8
|
+
|
|
9
|
+
/** A JSON-serializable snapshot of an entire artifact: tree + event-log + vectors.
|
|
10
|
+
* v2 adds `baseSeq` (the event-log compaction floor); v1 files restore with floor 0. */
|
|
11
|
+
interface StoredArtifact {
|
|
12
|
+
version: 1 | 2;
|
|
13
|
+
rootId: NodeId;
|
|
14
|
+
nodes: ArbNode[];
|
|
15
|
+
events: MutationEvent[];
|
|
16
|
+
/** Absolute seq of the oldest retained event (compaction floor). Absent in v1 → 0. */
|
|
17
|
+
baseSeq?: number;
|
|
18
|
+
vectors: VectorIndexEntry[];
|
|
19
|
+
}
|
|
20
|
+
/** Persists/loads a StoredArtifact. Adapters: MemoryStorage, FileStorage (and DB-backed later). */
|
|
21
|
+
interface StoragePort {
|
|
22
|
+
save(artifact: StoredArtifact): Promise<void>;
|
|
23
|
+
load(): Promise<StoredArtifact | null>;
|
|
24
|
+
}
|
|
25
|
+
/** Dump the live components into a StoredArtifact (v2). */
|
|
26
|
+
declare function serializeArtifact(tree: ArtifactTree, log: EventLog, vectors: VectorIndexPort): Promise<StoredArtifact>;
|
|
27
|
+
/** Rebuild a fresh tree + log from a StoredArtifact, and upsert its vectors into `vectors`. */
|
|
28
|
+
declare function restoreArtifact(stored: StoredArtifact, deps: TreeDeps, vectors: VectorIndexPort): Promise<{
|
|
29
|
+
tree: ArtifactTree;
|
|
30
|
+
log: EventLog;
|
|
31
|
+
}>;
|
|
32
|
+
/** In-memory StoragePort: holds a deep-cloned bundle. */
|
|
33
|
+
declare class MemoryStorage implements StoragePort {
|
|
34
|
+
private stored;
|
|
35
|
+
save(artifact: StoredArtifact): Promise<void>;
|
|
36
|
+
load(): Promise<StoredArtifact | null>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { MemoryStorage, type StoragePort, type StoredArtifact, restoreArtifact, serializeArtifact };
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
// src/decompose.ts
|
|
2
|
+
function kindOf(value, opaque) {
|
|
3
|
+
if (opaque) return "leaf";
|
|
4
|
+
return Array.isArray(value) ? "array" : "object";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// src/errors.ts
|
|
8
|
+
var ArborError = class extends Error {
|
|
9
|
+
constructor(code, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.name = new.target.name;
|
|
13
|
+
}
|
|
14
|
+
code;
|
|
15
|
+
};
|
|
16
|
+
var InvalidOpError = class extends ArborError {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super("INVALID_OP", message);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/artifact-tree.ts
|
|
23
|
+
var ArtifactTree = class _ArtifactTree {
|
|
24
|
+
constructor(deps) {
|
|
25
|
+
this.deps = deps;
|
|
26
|
+
}
|
|
27
|
+
deps;
|
|
28
|
+
nodes = /* @__PURE__ */ new Map();
|
|
29
|
+
rootId;
|
|
30
|
+
/** Lazily built per-parent key→childId maps for O(1) object-child lookup.
|
|
31
|
+
* A cache OUTSIDE the nodes (ArbNode/StoredArtifact stay byte-identical);
|
|
32
|
+
* invalidated on every child-set change, rebuilt on read. Arrays never
|
|
33
|
+
* populate it (index lookup is already O(1)). */
|
|
34
|
+
keyMaps = /* @__PURE__ */ new Map();
|
|
35
|
+
static fromJson(json, deps) {
|
|
36
|
+
const tree = new _ArtifactTree(deps);
|
|
37
|
+
tree.rootId = tree.build(json, null, null);
|
|
38
|
+
return tree;
|
|
39
|
+
}
|
|
40
|
+
build(value, parentId, key, type) {
|
|
41
|
+
const opaque = this.deps.decision.isOpaque(value, type);
|
|
42
|
+
const kind = kindOf(value, opaque);
|
|
43
|
+
const id = this.deps.idGen.next();
|
|
44
|
+
const node = {
|
|
45
|
+
id,
|
|
46
|
+
parentId,
|
|
47
|
+
key,
|
|
48
|
+
kind,
|
|
49
|
+
content: kind === "leaf" ? value : null,
|
|
50
|
+
childIds: [],
|
|
51
|
+
meta: { version: 0, updatedAt: this.deps.clock.now(), embedding: { state: "none" } }
|
|
52
|
+
};
|
|
53
|
+
if (type !== void 0) node.type = type;
|
|
54
|
+
this.nodes.set(id, node);
|
|
55
|
+
if (kind === "object") {
|
|
56
|
+
for (const [k, v] of Object.entries(value)) {
|
|
57
|
+
node.childIds.push(this.build(v, id, k));
|
|
58
|
+
}
|
|
59
|
+
} else if (kind === "array") {
|
|
60
|
+
value.forEach((v, i) => {
|
|
61
|
+
node.childIds.push(this.build(v, id, i));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return id;
|
|
65
|
+
}
|
|
66
|
+
get(id) {
|
|
67
|
+
return this.nodes.get(id);
|
|
68
|
+
}
|
|
69
|
+
root() {
|
|
70
|
+
return this.nodes.get(this.rootId);
|
|
71
|
+
}
|
|
72
|
+
rootIdValue() {
|
|
73
|
+
return this.rootId;
|
|
74
|
+
}
|
|
75
|
+
/** O(1) child lookup: arrays by index, objects via a lazily built key map. */
|
|
76
|
+
childByKey(parentId, key) {
|
|
77
|
+
const parent = this.nodes.get(parentId);
|
|
78
|
+
if (!parent) return void 0;
|
|
79
|
+
if (parent.kind === "array") {
|
|
80
|
+
if (!/^(0|[1-9]\d*)$/.test(key)) return void 0;
|
|
81
|
+
const i = Number(key);
|
|
82
|
+
if (i >= parent.childIds.length) return void 0;
|
|
83
|
+
return this.nodes.get(parent.childIds[i]);
|
|
84
|
+
}
|
|
85
|
+
let map = this.keyMaps.get(parentId);
|
|
86
|
+
if (!map) {
|
|
87
|
+
map = /* @__PURE__ */ new Map();
|
|
88
|
+
for (const cid2 of parent.childIds) map.set(String(this.nodes.get(cid2).key), cid2);
|
|
89
|
+
this.keyMaps.set(parentId, map);
|
|
90
|
+
}
|
|
91
|
+
const cid = map.get(key);
|
|
92
|
+
return cid === void 0 ? void 0 : this.nodes.get(cid);
|
|
93
|
+
}
|
|
94
|
+
children(id) {
|
|
95
|
+
const n = this.nodes.get(id);
|
|
96
|
+
if (!n) return [];
|
|
97
|
+
return n.childIds.map((cid) => this.nodes.get(cid));
|
|
98
|
+
}
|
|
99
|
+
has(id) {
|
|
100
|
+
return this.nodes.has(id);
|
|
101
|
+
}
|
|
102
|
+
size() {
|
|
103
|
+
return this.nodes.size;
|
|
104
|
+
}
|
|
105
|
+
/** Reconstruct the JSON value rooted at `id` (defaults to the tree root). */
|
|
106
|
+
toJson(id = this.rootId) {
|
|
107
|
+
const n = this.nodes.get(id);
|
|
108
|
+
if (!n) throw new Error(`Unknown node: ${id}`);
|
|
109
|
+
if (n.kind === "leaf") return n.content;
|
|
110
|
+
if (n.kind === "array") return n.childIds.map((cid) => this.toJson(cid));
|
|
111
|
+
const obj = {};
|
|
112
|
+
for (const cid of n.childIds) {
|
|
113
|
+
const c = this.nodes.get(cid);
|
|
114
|
+
obj[String(c.key)] = this.toJson(cid);
|
|
115
|
+
}
|
|
116
|
+
return obj;
|
|
117
|
+
}
|
|
118
|
+
/** Replace the subtree value at `id` in place, keeping the node's id/key/parentId.
|
|
119
|
+
* `clearType` explicitly un-types the node (used by type-aware revert). */
|
|
120
|
+
replaceValue(id, value, type, clearType = false) {
|
|
121
|
+
const node = this.nodes.get(id);
|
|
122
|
+
if (!node) throw new InvalidOpError(`Unknown node: ${id}`);
|
|
123
|
+
this.deleteDescendants(id);
|
|
124
|
+
this.keyMaps.delete(id);
|
|
125
|
+
const opaque = this.deps.decision.isOpaque(value, type);
|
|
126
|
+
const kind = kindOf(value, opaque);
|
|
127
|
+
node.kind = kind;
|
|
128
|
+
node.content = kind === "leaf" ? value : null;
|
|
129
|
+
node.childIds = [];
|
|
130
|
+
if (clearType) node.type = void 0;
|
|
131
|
+
else if (type !== void 0) node.type = type;
|
|
132
|
+
if (kind === "object") {
|
|
133
|
+
for (const [k, v] of Object.entries(value)) {
|
|
134
|
+
node.childIds.push(this.build(v, id, k));
|
|
135
|
+
}
|
|
136
|
+
} else if (kind === "array") {
|
|
137
|
+
value.forEach((v, i) => {
|
|
138
|
+
node.childIds.push(this.build(v, id, i));
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Recursively remove all descendants of `id` from the node map (keeps `id` itself). */
|
|
143
|
+
deleteDescendants(id) {
|
|
144
|
+
const node = this.nodes.get(id);
|
|
145
|
+
if (!node) return;
|
|
146
|
+
for (const cid of node.childIds) {
|
|
147
|
+
this.deleteDescendants(cid);
|
|
148
|
+
this.nodes.delete(cid);
|
|
149
|
+
this.keyMaps.delete(cid);
|
|
150
|
+
}
|
|
151
|
+
node.childIds = [];
|
|
152
|
+
}
|
|
153
|
+
/** Deep, independent copy of the tree state for transaction rollback.
|
|
154
|
+
* `restore` consumes the snapshot; do not reuse it afterwards. */
|
|
155
|
+
snapshot() {
|
|
156
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
157
|
+
for (const [id, node] of this.nodes) {
|
|
158
|
+
nodes.set(id, structuredClone(node));
|
|
159
|
+
}
|
|
160
|
+
return { nodes, rootId: this.rootId };
|
|
161
|
+
}
|
|
162
|
+
/** Replace the tree state with a previously taken snapshot. The snapshot's
|
|
163
|
+
* nodes are adopted BY REFERENCE — restore consumes the snapshot; do not
|
|
164
|
+
* reuse it afterwards. */
|
|
165
|
+
restore(snap) {
|
|
166
|
+
this.nodes.clear();
|
|
167
|
+
this.keyMaps.clear();
|
|
168
|
+
for (const [id, node] of snap.nodes) {
|
|
169
|
+
this.nodes.set(id, node);
|
|
170
|
+
}
|
|
171
|
+
this.rootId = snap.rootId;
|
|
172
|
+
}
|
|
173
|
+
/** Insert a decomposed `value` as a child of `parentId`. For objects `keyOrIndex` is the string key; for arrays it is the insert index. Returns the new child's id. */
|
|
174
|
+
insertChild(parentId, keyOrIndex, value, type) {
|
|
175
|
+
const parent = this.nodes.get(parentId);
|
|
176
|
+
if (!parent) throw new InvalidOpError(`Unknown node: ${parentId}`);
|
|
177
|
+
if (parent.kind === "object") {
|
|
178
|
+
if (typeof keyOrIndex !== "string") {
|
|
179
|
+
throw new InvalidOpError("object insert requires a string key");
|
|
180
|
+
}
|
|
181
|
+
if (parent.childIds.some((cid2) => this.nodes.get(cid2).key === keyOrIndex)) {
|
|
182
|
+
throw new InvalidOpError(`key already exists: ${keyOrIndex}`);
|
|
183
|
+
}
|
|
184
|
+
const cid = this.build(value, parentId, keyOrIndex, type);
|
|
185
|
+
parent.childIds.push(cid);
|
|
186
|
+
this.keyMaps.delete(parentId);
|
|
187
|
+
return cid;
|
|
188
|
+
}
|
|
189
|
+
if (parent.kind === "array") {
|
|
190
|
+
if (typeof keyOrIndex !== "number") {
|
|
191
|
+
throw new InvalidOpError("array insert requires a numeric index");
|
|
192
|
+
}
|
|
193
|
+
const at = Math.max(0, Math.min(keyOrIndex, parent.childIds.length));
|
|
194
|
+
const cid = this.build(value, parentId, at, type);
|
|
195
|
+
parent.childIds.splice(at, 0, cid);
|
|
196
|
+
this.renumberArray(parentId);
|
|
197
|
+
return cid;
|
|
198
|
+
}
|
|
199
|
+
throw new InvalidOpError("cannot insert into a leaf node");
|
|
200
|
+
}
|
|
201
|
+
/** Remove `childId` (and its subtree) from `parentId`. Renumbers array siblings. */
|
|
202
|
+
removeChild(parentId, childId) {
|
|
203
|
+
const parent = this.nodes.get(parentId);
|
|
204
|
+
if (!parent) throw new InvalidOpError(`Unknown node: ${parentId}`);
|
|
205
|
+
const idx = parent.childIds.indexOf(childId);
|
|
206
|
+
if (idx < 0) throw new InvalidOpError(`${childId} is not a child of ${parentId}`);
|
|
207
|
+
this.deleteDescendants(childId);
|
|
208
|
+
this.nodes.delete(childId);
|
|
209
|
+
this.keyMaps.delete(childId);
|
|
210
|
+
parent.childIds.splice(idx, 1);
|
|
211
|
+
this.keyMaps.delete(parentId);
|
|
212
|
+
if (parent.kind === "array") this.renumberArray(parentId);
|
|
213
|
+
}
|
|
214
|
+
/** Move `id` under `newParentId` at `keyOrIndex`, preserving `id`. Renumbers affected arrays.
|
|
215
|
+
* ALL validation happens before any mutation — a rejected move leaves the tree untouched. */
|
|
216
|
+
moveNode(id, newParentId, keyOrIndex) {
|
|
217
|
+
const node = this.nodes.get(id);
|
|
218
|
+
if (!node) throw new InvalidOpError(`Unknown node: ${id}`);
|
|
219
|
+
if (node.parentId === null) throw new InvalidOpError("cannot move the root");
|
|
220
|
+
const newParent = this.nodes.get(newParentId);
|
|
221
|
+
if (!newParent) throw new InvalidOpError(`Unknown node: ${newParentId}`);
|
|
222
|
+
if (newParent.kind === "leaf") throw new InvalidOpError("cannot move into a leaf node");
|
|
223
|
+
let anc = newParentId;
|
|
224
|
+
while (anc !== null) {
|
|
225
|
+
if (anc === id) throw new InvalidOpError("cannot move a node into itself or its own subtree");
|
|
226
|
+
anc = this.nodes.get(anc)?.parentId ?? null;
|
|
227
|
+
}
|
|
228
|
+
if (newParent.kind === "object") {
|
|
229
|
+
if (typeof keyOrIndex !== "string") throw new InvalidOpError("object move requires a string key");
|
|
230
|
+
if (newParent.childIds.some((cid) => cid !== id && this.nodes.get(cid).key === keyOrIndex)) {
|
|
231
|
+
throw new InvalidOpError(`key already exists: ${keyOrIndex}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const oldParent = this.nodes.get(node.parentId);
|
|
235
|
+
const oldIdx = oldParent.childIds.indexOf(id);
|
|
236
|
+
oldParent.childIds.splice(oldIdx, 1);
|
|
237
|
+
this.keyMaps.delete(oldParent.id);
|
|
238
|
+
this.keyMaps.delete(newParentId);
|
|
239
|
+
if (oldParent.kind === "array") this.renumberArray(oldParent.id);
|
|
240
|
+
if (newParent.kind === "object") {
|
|
241
|
+
node.parentId = newParentId;
|
|
242
|
+
node.key = keyOrIndex;
|
|
243
|
+
newParent.childIds.push(id);
|
|
244
|
+
} else {
|
|
245
|
+
const at = typeof keyOrIndex === "number" ? Math.max(0, Math.min(keyOrIndex, newParent.childIds.length)) : newParent.childIds.length;
|
|
246
|
+
node.parentId = newParentId;
|
|
247
|
+
newParent.childIds.splice(at, 0, id);
|
|
248
|
+
this.renumberArray(newParentId);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** Set each array child's `key` to its current position. */
|
|
252
|
+
renumberArray(parentId) {
|
|
253
|
+
const parent = this.nodes.get(parentId);
|
|
254
|
+
if (!parent) return;
|
|
255
|
+
parent.childIds.forEach((cid, i) => {
|
|
256
|
+
this.nodes.get(cid).key = i;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/** All nodes in the tree (for serialization). */
|
|
260
|
+
allNodes() {
|
|
261
|
+
return [...this.nodes.values()];
|
|
262
|
+
}
|
|
263
|
+
/** Rebuild a tree from previously serialized nodes, preserving their ids. */
|
|
264
|
+
static fromStored(nodes, rootId, deps) {
|
|
265
|
+
const tree = new _ArtifactTree(deps);
|
|
266
|
+
for (const node of nodes) tree.nodes.set(node.id, node);
|
|
267
|
+
tree.rootId = rootId;
|
|
268
|
+
return tree;
|
|
269
|
+
}
|
|
270
|
+
/** All transitive descendant ids of `id` (depth-first), excluding `id` itself. */
|
|
271
|
+
descendantIds(id) {
|
|
272
|
+
const out = [];
|
|
273
|
+
const node = this.nodes.get(id);
|
|
274
|
+
if (!node) return out;
|
|
275
|
+
for (const cid of node.childIds) {
|
|
276
|
+
out.push(cid);
|
|
277
|
+
out.push(...this.descendantIds(cid));
|
|
278
|
+
}
|
|
279
|
+
return out;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/event-log.ts
|
|
284
|
+
var EventLog = class _EventLog {
|
|
285
|
+
events = [];
|
|
286
|
+
baseSeq = 0;
|
|
287
|
+
// count of compacted-away events; events[0].seq === baseSeq
|
|
288
|
+
append(event) {
|
|
289
|
+
const full = { ...event, seq: this.baseSeq + this.events.length };
|
|
290
|
+
this.events.push(full);
|
|
291
|
+
return full;
|
|
292
|
+
}
|
|
293
|
+
entries() {
|
|
294
|
+
return this.events;
|
|
295
|
+
}
|
|
296
|
+
/** Absolute seq of the oldest retained event (0 until compaction). Versions below
|
|
297
|
+
* this have been compacted away and are no longer reconstructable. */
|
|
298
|
+
baseSeqValue() {
|
|
299
|
+
return this.baseSeq;
|
|
300
|
+
}
|
|
301
|
+
/** The event at absolute `seq`, or undefined if compacted away / past the end. */
|
|
302
|
+
at(seq) {
|
|
303
|
+
const i = seq - this.baseSeq;
|
|
304
|
+
return i >= 0 && i < this.events.length ? this.events[i] : void 0;
|
|
305
|
+
}
|
|
306
|
+
since(seq) {
|
|
307
|
+
return this.events.filter((e) => e.seq >= seq);
|
|
308
|
+
}
|
|
309
|
+
/** Absolute next-seq / current version (unchanged across compaction). */
|
|
310
|
+
length() {
|
|
311
|
+
return this.baseSeq + this.events.length;
|
|
312
|
+
}
|
|
313
|
+
/** Drop events past absolute `length` — used to roll back a failed transaction.
|
|
314
|
+
* Throws below the compaction floor: that history is gone and the log cannot roll
|
|
315
|
+
* back past it — `compactTo` must never run inside a transaction. */
|
|
316
|
+
truncateTo(length) {
|
|
317
|
+
if (length < this.baseSeq) {
|
|
318
|
+
throw new InvalidOpError(
|
|
319
|
+
`cannot truncate to ${length}: events before ${this.baseSeq} were compacted away (compactTo must not run inside a transaction)`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
this.events.length = length - this.baseSeq;
|
|
323
|
+
}
|
|
324
|
+
/** Compaction: drop every retained event with seq < `floorSeq` (history before it
|
|
325
|
+
* becomes unreconstructable). `floorSeq` is clamped to [baseSeq, length()].
|
|
326
|
+
* Returns the number of events dropped. Must NOT be called inside a Mutator
|
|
327
|
+
* transaction (rollback would need the dropped events). */
|
|
328
|
+
compactTo(floorSeq) {
|
|
329
|
+
const floor = Math.max(this.baseSeq, Math.min(floorSeq, this.length()));
|
|
330
|
+
const drop = floor - this.baseSeq;
|
|
331
|
+
if (drop > 0) {
|
|
332
|
+
this.events.splice(0, drop);
|
|
333
|
+
this.baseSeq = floor;
|
|
334
|
+
}
|
|
335
|
+
return drop;
|
|
336
|
+
}
|
|
337
|
+
/** Rebuild a log from previously serialized events, preserving their seq + the
|
|
338
|
+
* compaction floor. */
|
|
339
|
+
static fromStored(events, baseSeq = 0) {
|
|
340
|
+
const log = new _EventLog();
|
|
341
|
+
log.baseSeq = baseSeq;
|
|
342
|
+
for (const e of events) log.events.push({ ...e });
|
|
343
|
+
return log;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// src/storage.ts
|
|
348
|
+
async function serializeArtifact(tree, log, vectors) {
|
|
349
|
+
return {
|
|
350
|
+
version: 2,
|
|
351
|
+
rootId: tree.rootIdValue(),
|
|
352
|
+
nodes: tree.allNodes(),
|
|
353
|
+
events: [...log.entries()],
|
|
354
|
+
baseSeq: log.baseSeqValue(),
|
|
355
|
+
vectors: await vectors.entries()
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
async function restoreArtifact(stored, deps, vectors) {
|
|
359
|
+
const tree = ArtifactTree.fromStored(stored.nodes, stored.rootId, deps);
|
|
360
|
+
const log = EventLog.fromStored(stored.events, stored.baseSeq ?? 0);
|
|
361
|
+
await vectors.upsert(stored.vectors);
|
|
362
|
+
return { tree, log };
|
|
363
|
+
}
|
|
364
|
+
var MemoryStorage = class {
|
|
365
|
+
stored = null;
|
|
366
|
+
async save(artifact) {
|
|
367
|
+
this.stored = structuredClone(artifact);
|
|
368
|
+
}
|
|
369
|
+
async load() {
|
|
370
|
+
return this.stored ? structuredClone(this.stored) : null;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
export {
|
|
374
|
+
MemoryStorage,
|
|
375
|
+
restoreArtifact,
|
|
376
|
+
serializeArtifact
|
|
377
|
+
};
|
|
378
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/decompose.ts","../src/errors.ts","../src/artifact-tree.ts","../src/event-log.ts","../src/storage.ts"],"sourcesContent":["import type { Json, NodeKind } from \"./types\";\r\n\r\n/** Policy deciding whether a value is stored whole (opaque leaf) or split into child nodes. */\r\nexport interface DecomposeDecision {\r\n /** `type` is the optional registered node type (used by the by-type override in a later milestone). */\r\n isOpaque(value: Json, type?: string): boolean;\r\n}\r\n\r\n/** UTF-8 byte length of the JSON serialization of a value. */\r\nexport function byteSize(value: Json): number {\r\n return Buffer.byteLength(JSON.stringify(value), \"utf8\");\r\n}\r\n\r\n/** Structural kind of a value given whether it is being stored opaquely. */\r\nexport function kindOf(value: Json, opaque: boolean): NodeKind {\r\n if (opaque) return \"leaf\";\r\n return Array.isArray(value) ? \"array\" : \"object\";\r\n}\r\n\r\n/**\r\n * Default policy: scalars are always opaque leaves; containers stay opaque\r\n * while their serialized size is within `maxOpaqueBytes`, otherwise they split.\r\n */\r\nexport function sizeBasedDecision(maxOpaqueBytes: number): DecomposeDecision {\r\n return {\r\n isOpaque(value: Json): boolean {\r\n if (value === null || typeof value !== \"object\") return true;\r\n return byteSize(value) <= maxOpaqueBytes;\r\n },\r\n };\r\n}\r\n","import type { NodeId } from \"./types\";\r\n\r\n/** A reference to a node: by stable id or by JSON Pointer path. */\r\nexport type Ref = { id: NodeId } | { path: string };\r\n\r\nexport class ArborError extends Error {\r\n constructor(\r\n public readonly code: string,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = new.target.name;\r\n }\r\n}\r\n\r\nexport class NodeNotFoundError extends ArborError {\r\n constructor(public readonly ref: Ref) {\r\n super(\"NODE_NOT_FOUND\", `Node not found: ${JSON.stringify(ref)}`);\r\n }\r\n}\r\n\r\nexport class ScopeViolationError extends ArborError {\r\n constructor(\r\n public readonly targetPath: string,\r\n public readonly scope: string,\r\n ) {\r\n super(\"SCOPE_VIOLATION\", `Access outside scope: ${targetPath} (scope: ${scope})`);\r\n }\r\n}\r\n\r\nexport class StaleVersionError extends ArborError {\r\n constructor(\r\n public readonly id: NodeId,\r\n public readonly expected: number,\r\n public readonly actual: number,\r\n ) {\r\n super(\"STALE_VERSION\", `Stale version for ${id}: expected ${expected}, actual ${actual}`);\r\n }\r\n}\r\n\r\nexport class InvalidOpError extends ArborError {\r\n constructor(message: string) {\r\n super(\"INVALID_OP\", message);\r\n }\r\n}\r\n\r\nexport class ValidationError extends ArborError {\r\n constructor(\r\n public readonly type: string | undefined,\r\n public readonly details: string,\r\n ) {\r\n super(\"VALIDATION_ERROR\", `Validation failed${type ? ` for type ${type}` : \"\"}: ${details}`);\r\n }\r\n}\r\n","import type { ArbNode, Json, NodeId } from \"./types\";\r\nimport type { IdGen } from \"./ids\";\r\nimport type { Clock } from \"./clock\";\r\nimport { type DecomposeDecision, kindOf } from \"./decompose\";\r\nimport { InvalidOpError } from \"./errors\";\r\n\r\nexport interface TreeDeps {\r\n idGen: IdGen;\r\n clock: Clock;\r\n decision: DecomposeDecision;\r\n}\r\n\r\nexport interface TreeSnapshot {\r\n nodes: Map<NodeId, ArbNode>;\r\n rootId: NodeId;\r\n}\r\n\r\nexport class ArtifactTree {\r\n private readonly nodes = new Map<NodeId, ArbNode>();\r\n private rootId!: NodeId;\r\n /** Lazily built per-parent key→childId maps for O(1) object-child lookup.\r\n * A cache OUTSIDE the nodes (ArbNode/StoredArtifact stay byte-identical);\r\n * invalidated on every child-set change, rebuilt on read. Arrays never\r\n * populate it (index lookup is already O(1)). */\r\n private readonly keyMaps = new Map<NodeId, Map<string, NodeId>>();\r\n\r\n private constructor(private readonly deps: TreeDeps) {}\r\n\r\n static fromJson(json: Json, deps: TreeDeps): ArtifactTree {\r\n const tree = new ArtifactTree(deps);\r\n tree.rootId = tree.build(json, null, null);\r\n return tree;\r\n }\r\n\r\n private build(value: Json, parentId: NodeId | null, key: string | number | null, type?: string): NodeId {\r\n const opaque = this.deps.decision.isOpaque(value, type);\r\n const kind = kindOf(value, opaque);\r\n const id = this.deps.idGen.next();\r\n const node: ArbNode = {\r\n id,\r\n parentId,\r\n key,\r\n kind,\r\n content: kind === \"leaf\" ? value : null,\r\n childIds: [],\r\n meta: { version: 0, updatedAt: this.deps.clock.now(), embedding: { state: \"none\" } },\r\n };\r\n if (type !== undefined) node.type = type;\r\n this.nodes.set(id, node);\r\n\r\n if (kind === \"object\") {\r\n for (const [k, v] of Object.entries(value as Record<string, Json>)) {\r\n node.childIds.push(this.build(v, id, k));\r\n }\r\n } else if (kind === \"array\") {\r\n (value as Json[]).forEach((v, i) => {\r\n node.childIds.push(this.build(v, id, i));\r\n });\r\n }\r\n return id;\r\n }\r\n\r\n get(id: NodeId): ArbNode | undefined {\r\n return this.nodes.get(id);\r\n }\r\n\r\n root(): ArbNode {\r\n return this.nodes.get(this.rootId)!;\r\n }\r\n\r\n rootIdValue(): NodeId {\r\n return this.rootId;\r\n }\r\n\r\n /** O(1) child lookup: arrays by index, objects via a lazily built key map. */\r\n childByKey(parentId: NodeId, key: string): ArbNode | undefined {\r\n const parent = this.nodes.get(parentId);\r\n if (!parent) return undefined;\r\n if (parent.kind === \"array\") {\r\n if (!/^(0|[1-9]\\d*)$/.test(key)) return undefined; // canonical RFC 6901 index: digits only, no leading zeros\r\n const i = Number(key);\r\n if (i >= parent.childIds.length) return undefined;\r\n return this.nodes.get(parent.childIds[i]);\r\n }\r\n let map = this.keyMaps.get(parentId);\r\n if (!map) {\r\n map = new Map();\r\n for (const cid of parent.childIds) map.set(String(this.nodes.get(cid)!.key), cid);\r\n this.keyMaps.set(parentId, map);\r\n }\r\n const cid = map.get(key);\r\n return cid === undefined ? undefined : this.nodes.get(cid);\r\n }\r\n\r\n children(id: NodeId): ArbNode[] {\r\n const n = this.nodes.get(id);\r\n if (!n) return [];\r\n return n.childIds.map((cid) => this.nodes.get(cid)!);\r\n }\r\n\r\n has(id: NodeId): boolean {\r\n return this.nodes.has(id);\r\n }\r\n\r\n size(): number {\r\n return this.nodes.size;\r\n }\r\n\r\n /** Reconstruct the JSON value rooted at `id` (defaults to the tree root). */\r\n toJson(id: NodeId = this.rootId): Json {\r\n const n = this.nodes.get(id);\r\n if (!n) throw new Error(`Unknown node: ${id}`);\r\n if (n.kind === \"leaf\") return n.content;\r\n if (n.kind === \"array\") return n.childIds.map((cid) => this.toJson(cid));\r\n const obj: Record<string, Json> = {};\r\n for (const cid of n.childIds) {\r\n const c = this.nodes.get(cid)!;\r\n obj[String(c.key)] = this.toJson(cid);\r\n }\r\n return obj;\r\n }\r\n\r\n /** Replace the subtree value at `id` in place, keeping the node's id/key/parentId.\r\n * `clearType` explicitly un-types the node (used by type-aware revert). */\r\n replaceValue(id: NodeId, value: Json, type?: string, clearType = false): void {\r\n const node = this.nodes.get(id);\r\n if (!node) throw new InvalidOpError(`Unknown node: ${id}`);\r\n this.deleteDescendants(id);\r\n this.keyMaps.delete(id);\r\n const opaque = this.deps.decision.isOpaque(value, type);\r\n const kind = kindOf(value, opaque);\r\n node.kind = kind;\r\n node.content = kind === \"leaf\" ? value : null;\r\n node.childIds = [];\r\n if (clearType) node.type = undefined;\r\n else if (type !== undefined) node.type = type;\r\n if (kind === \"object\") {\r\n for (const [k, v] of Object.entries(value as Record<string, Json>)) {\r\n node.childIds.push(this.build(v, id, k));\r\n }\r\n } else if (kind === \"array\") {\r\n (value as Json[]).forEach((v, i) => {\r\n node.childIds.push(this.build(v, id, i));\r\n });\r\n }\r\n }\r\n\r\n /** Recursively remove all descendants of `id` from the node map (keeps `id` itself). */\r\n private deleteDescendants(id: NodeId): void {\r\n const node = this.nodes.get(id);\r\n if (!node) return;\r\n for (const cid of node.childIds) {\r\n this.deleteDescendants(cid);\r\n this.nodes.delete(cid);\r\n this.keyMaps.delete(cid);\r\n }\r\n node.childIds = [];\r\n }\r\n\r\n /** Deep, independent copy of the tree state for transaction rollback.\r\n * `restore` consumes the snapshot; do not reuse it afterwards. */\r\n snapshot(): TreeSnapshot {\r\n const nodes = new Map<NodeId, ArbNode>();\r\n for (const [id, node] of this.nodes) {\r\n nodes.set(id, structuredClone(node));\r\n }\r\n return { nodes, rootId: this.rootId };\r\n }\r\n\r\n /** Replace the tree state with a previously taken snapshot. The snapshot's\r\n * nodes are adopted BY REFERENCE — restore consumes the snapshot; do not\r\n * reuse it afterwards. */\r\n restore(snap: TreeSnapshot): void {\r\n this.nodes.clear();\r\n this.keyMaps.clear();\r\n for (const [id, node] of snap.nodes) {\r\n this.nodes.set(id, node);\r\n }\r\n this.rootId = snap.rootId;\r\n }\r\n\r\n /** Insert a decomposed `value` as a child of `parentId`. For objects `keyOrIndex` is the string key; for arrays it is the insert index. Returns the new child's id. */\r\n insertChild(parentId: NodeId, keyOrIndex: string | number, value: Json, type?: string): NodeId {\r\n const parent = this.nodes.get(parentId);\r\n if (!parent) throw new InvalidOpError(`Unknown node: ${parentId}`);\r\n if (parent.kind === \"object\") {\r\n if (typeof keyOrIndex !== \"string\") {\r\n throw new InvalidOpError(\"object insert requires a string key\");\r\n }\r\n if (parent.childIds.some((cid) => this.nodes.get(cid)!.key === keyOrIndex)) {\r\n throw new InvalidOpError(`key already exists: ${keyOrIndex}`);\r\n }\r\n const cid = this.build(value, parentId, keyOrIndex, type);\r\n parent.childIds.push(cid);\r\n this.keyMaps.delete(parentId);\r\n return cid;\r\n }\r\n if (parent.kind === \"array\") {\r\n if (typeof keyOrIndex !== \"number\") {\r\n throw new InvalidOpError(\"array insert requires a numeric index\");\r\n }\r\n const at = Math.max(0, Math.min(keyOrIndex, parent.childIds.length));\r\n const cid = this.build(value, parentId, at, type);\r\n parent.childIds.splice(at, 0, cid);\r\n this.renumberArray(parentId);\r\n return cid;\r\n }\r\n throw new InvalidOpError(\"cannot insert into a leaf node\");\r\n }\r\n\r\n /** Remove `childId` (and its subtree) from `parentId`. Renumbers array siblings. */\r\n removeChild(parentId: NodeId, childId: NodeId): void {\r\n const parent = this.nodes.get(parentId);\r\n if (!parent) throw new InvalidOpError(`Unknown node: ${parentId}`);\r\n const idx = parent.childIds.indexOf(childId);\r\n if (idx < 0) throw new InvalidOpError(`${childId} is not a child of ${parentId}`);\r\n this.deleteDescendants(childId);\r\n this.nodes.delete(childId);\r\n this.keyMaps.delete(childId);\r\n parent.childIds.splice(idx, 1);\r\n this.keyMaps.delete(parentId);\r\n if (parent.kind === \"array\") this.renumberArray(parentId);\r\n }\r\n\r\n /** Move `id` under `newParentId` at `keyOrIndex`, preserving `id`. Renumbers affected arrays.\r\n * ALL validation happens before any mutation — a rejected move leaves the tree untouched. */\r\n moveNode(id: NodeId, newParentId: NodeId, keyOrIndex: string | number): void {\r\n const node = this.nodes.get(id);\r\n if (!node) throw new InvalidOpError(`Unknown node: ${id}`);\r\n if (node.parentId === null) throw new InvalidOpError(\"cannot move the root\");\r\n const newParent = this.nodes.get(newParentId);\r\n if (!newParent) throw new InvalidOpError(`Unknown node: ${newParentId}`);\r\n if (newParent.kind === \"leaf\") throw new InvalidOpError(\"cannot move into a leaf node\");\r\n // Moving into itself or its own subtree would create a parent-chain cycle:\r\n // toJson silently drops the subtree and pathOf never terminates.\r\n let anc: NodeId | null = newParentId;\r\n while (anc !== null) {\r\n if (anc === id) throw new InvalidOpError(\"cannot move a node into itself or its own subtree\");\r\n anc = this.nodes.get(anc)?.parentId ?? null;\r\n }\r\n if (newParent.kind === \"object\") {\r\n if (typeof keyOrIndex !== \"string\") throw new InvalidOpError(\"object move requires a string key\");\r\n // Mirrors insertChild: a duplicate key silently shadows the existing child.\r\n if (newParent.childIds.some((cid) => cid !== id && this.nodes.get(cid)!.key === keyOrIndex)) {\r\n throw new InvalidOpError(`key already exists: ${keyOrIndex}`);\r\n }\r\n }\r\n\r\n const oldParent = this.nodes.get(node.parentId)!;\r\n const oldIdx = oldParent.childIds.indexOf(id);\r\n oldParent.childIds.splice(oldIdx, 1);\r\n this.keyMaps.delete(oldParent.id);\r\n this.keyMaps.delete(newParentId);\r\n if (oldParent.kind === \"array\") this.renumberArray(oldParent.id);\r\n\r\n if (newParent.kind === \"object\") {\r\n node.parentId = newParentId;\r\n node.key = keyOrIndex;\r\n newParent.childIds.push(id);\r\n } else {\r\n const at = typeof keyOrIndex === \"number\" ? Math.max(0, Math.min(keyOrIndex, newParent.childIds.length)) : newParent.childIds.length;\r\n node.parentId = newParentId;\r\n newParent.childIds.splice(at, 0, id);\r\n this.renumberArray(newParentId);\r\n }\r\n }\r\n\r\n /** Set each array child's `key` to its current position. */\r\n private renumberArray(parentId: NodeId): void {\r\n const parent = this.nodes.get(parentId);\r\n if (!parent) return;\r\n parent.childIds.forEach((cid, i) => {\r\n this.nodes.get(cid)!.key = i;\r\n });\r\n }\r\n\r\n /** All nodes in the tree (for serialization). */\r\n allNodes(): ArbNode[] {\r\n return [...this.nodes.values()];\r\n }\r\n\r\n /** Rebuild a tree from previously serialized nodes, preserving their ids. */\r\n static fromStored(nodes: ArbNode[], rootId: NodeId, deps: TreeDeps): ArtifactTree {\r\n const tree = new ArtifactTree(deps);\r\n for (const node of nodes) tree.nodes.set(node.id, node);\r\n tree.rootId = rootId;\r\n return tree;\r\n }\r\n\r\n /** All transitive descendant ids of `id` (depth-first), excluding `id` itself. */\r\n descendantIds(id: NodeId): NodeId[] {\r\n const out: NodeId[] = [];\r\n const node = this.nodes.get(id);\r\n if (!node) return out;\r\n for (const cid of node.childIds) {\r\n out.push(cid);\r\n out.push(...this.descendantIds(cid));\r\n }\r\n return out;\r\n }\r\n}\r\n","import type { Json, NodeId } from \"./types\";\r\nimport { InvalidOpError } from \"./errors\";\r\n\r\nexport type OpKind = \"set\" | \"insert\" | \"remove\" | \"move\";\r\n\r\n/**\r\n * A recorded mutation. Carries enough to reverse it later (M7 replay):\r\n * - set: before = old subtree value, after = new value\r\n * - insert: after = inserted value (inverse is remove of targetId)\r\n * - remove: before = removed subtree value (inverse is insert at parentId/key)\r\n * - move: from/to capture old and new (parentId, key)\r\n */\r\nexport interface MutationEvent {\r\n seq: number;\r\n kind: OpKind;\r\n targetId: NodeId;\r\n parentId: NodeId | null;\r\n key: string | number | null;\r\n before?: Json;\r\n after?: Json;\r\n from?: { parentId: NodeId | null; key: string | number | null };\r\n to?: { parentId: NodeId | null; key: string | number | null };\r\n /** JSON Pointer of the affected node (set/insert: its path; remove: its pre-removal path). */\r\n path?: string;\r\n /** move: source path (before the move). */\r\n fromPath?: string;\r\n /** move: destination path (after the move). */\r\n toPath?: string;\r\n /** set/remove: the node's type BEFORE the op; insert/set: `nodeType` = type AFTER.\r\n * `null` = explicitly untyped; ABSENT = pre-M10 event (unknown — replay keeps the current type). */\r\n nodeTypeBefore?: string | null;\r\n nodeType?: string | null;\r\n /** set/remove: the node's tags BEFORE the op ([] = untagged).\r\n * ABSENT = pre-M14 event (unknown — revert leaves the current tags alone). */\r\n tagsBefore?: string[];\r\n /** set/insert: the node's tags AFTER the op ([] = untagged). ABSENT = pre-M14. */\r\n tags?: string[];\r\n actor?: string;\r\n ts: number;\r\n}\r\n\r\n/** Append-only log of mutations with monotonic, absolute seq. Supports compaction:\r\n * events before `baseSeq` are dropped, but retained events keep their absolute seq\r\n * and `length()` stays the absolute next-seq, so versions never shift. */\r\nexport class EventLog {\r\n private readonly events: MutationEvent[] = [];\r\n private baseSeq = 0; // count of compacted-away events; events[0].seq === baseSeq\r\n\r\n append(event: Omit<MutationEvent, \"seq\">): MutationEvent {\r\n const full: MutationEvent = { ...event, seq: this.baseSeq + this.events.length };\r\n this.events.push(full);\r\n return full;\r\n }\r\n\r\n entries(): readonly MutationEvent[] {\r\n return this.events;\r\n }\r\n\r\n /** Absolute seq of the oldest retained event (0 until compaction). Versions below\r\n * this have been compacted away and are no longer reconstructable. */\r\n baseSeqValue(): number {\r\n return this.baseSeq;\r\n }\r\n\r\n /** The event at absolute `seq`, or undefined if compacted away / past the end. */\r\n at(seq: number): MutationEvent | undefined {\r\n const i = seq - this.baseSeq;\r\n return i >= 0 && i < this.events.length ? this.events[i] : undefined;\r\n }\r\n\r\n since(seq: number): MutationEvent[] {\r\n return this.events.filter((e) => e.seq >= seq);\r\n }\r\n\r\n /** Absolute next-seq / current version (unchanged across compaction). */\r\n length(): number {\r\n return this.baseSeq + this.events.length;\r\n }\r\n\r\n /** Drop events past absolute `length` — used to roll back a failed transaction.\r\n * Throws below the compaction floor: that history is gone and the log cannot roll\r\n * back past it — `compactTo` must never run inside a transaction. */\r\n truncateTo(length: number): void {\r\n if (length < this.baseSeq) {\r\n throw new InvalidOpError(\r\n `cannot truncate to ${length}: events before ${this.baseSeq} were compacted away (compactTo must not run inside a transaction)`,\r\n );\r\n }\r\n this.events.length = length - this.baseSeq;\r\n }\r\n\r\n /** Compaction: drop every retained event with seq < `floorSeq` (history before it\r\n * becomes unreconstructable). `floorSeq` is clamped to [baseSeq, length()].\r\n * Returns the number of events dropped. Must NOT be called inside a Mutator\r\n * transaction (rollback would need the dropped events). */\r\n compactTo(floorSeq: number): number {\r\n const floor = Math.max(this.baseSeq, Math.min(floorSeq, this.length()));\r\n const drop = floor - this.baseSeq;\r\n if (drop > 0) {\r\n this.events.splice(0, drop);\r\n this.baseSeq = floor;\r\n }\r\n return drop;\r\n }\r\n\r\n /** Rebuild a log from previously serialized events, preserving their seq + the\r\n * compaction floor. */\r\n static fromStored(events: MutationEvent[], baseSeq = 0): EventLog {\r\n const log = new EventLog();\r\n log.baseSeq = baseSeq;\r\n for (const e of events) log.events.push({ ...e });\r\n return log;\r\n }\r\n}\r\n","import type { ArbNode, NodeId } from \"./types\";\r\nimport { ArtifactTree, type TreeDeps } from \"./artifact-tree\";\r\nimport { EventLog, type MutationEvent } from \"./event-log\";\r\nimport type { VectorIndexPort, VectorIndexEntry } from \"./vector-index-port\";\r\n\r\n/** A JSON-serializable snapshot of an entire artifact: tree + event-log + vectors.\r\n * v2 adds `baseSeq` (the event-log compaction floor); v1 files restore with floor 0. */\r\nexport interface StoredArtifact {\r\n version: 1 | 2;\r\n rootId: NodeId;\r\n nodes: ArbNode[];\r\n events: MutationEvent[];\r\n /** Absolute seq of the oldest retained event (compaction floor). Absent in v1 → 0. */\r\n baseSeq?: number;\r\n vectors: VectorIndexEntry[];\r\n}\r\n\r\n/** Persists/loads a StoredArtifact. Adapters: MemoryStorage, FileStorage (and DB-backed later). */\r\nexport interface StoragePort {\r\n save(artifact: StoredArtifact): Promise<void>;\r\n load(): Promise<StoredArtifact | null>;\r\n}\r\n\r\n/** Dump the live components into a StoredArtifact (v2). */\r\nexport async function serializeArtifact(\r\n tree: ArtifactTree,\r\n log: EventLog,\r\n vectors: VectorIndexPort,\r\n): Promise<StoredArtifact> {\r\n return {\r\n version: 2,\r\n rootId: tree.rootIdValue(),\r\n nodes: tree.allNodes(),\r\n events: [...log.entries()],\r\n baseSeq: log.baseSeqValue(),\r\n vectors: await vectors.entries(),\r\n };\r\n}\r\n\r\n/** Rebuild a fresh tree + log from a StoredArtifact, and upsert its vectors into `vectors`. */\r\nexport async function restoreArtifact(\r\n stored: StoredArtifact,\r\n deps: TreeDeps,\r\n vectors: VectorIndexPort,\r\n): Promise<{ tree: ArtifactTree; log: EventLog }> {\r\n const tree = ArtifactTree.fromStored(stored.nodes, stored.rootId, deps);\r\n const log = EventLog.fromStored(stored.events, stored.baseSeq ?? 0);\r\n await vectors.upsert(stored.vectors);\r\n return { tree, log };\r\n}\r\n\r\n/** In-memory StoragePort: holds a deep-cloned bundle. */\r\nexport class MemoryStorage implements StoragePort {\r\n private stored: StoredArtifact | null = null;\r\n\r\n async save(artifact: StoredArtifact): Promise<void> {\r\n this.stored = structuredClone(artifact);\r\n }\r\n\r\n async load(): Promise<StoredArtifact | null> {\r\n return this.stored ? structuredClone(this.stored) : null;\r\n }\r\n}\r\n"],"mappings":"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","names":["cid"]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ArtifactTree } from './artifact-tree.js';
|
|
2
|
+
import { Addressing } from './addressing.js';
|
|
3
|
+
import { MutationEvent, EventLog } from './event-log.js';
|
|
4
|
+
import { Mutator } from './mutator.js';
|
|
5
|
+
import { SearchOpts, SearchResult, SemanticIndex } from './semantic-index.js';
|
|
6
|
+
import { DescribeOpts, DescribeResult, GetOpts, GetResult, FindSelector, FindOpts, FindResult } from './navigator.js';
|
|
7
|
+
import { Ref } from './errors.js';
|
|
8
|
+
import { Json, NodeId } from './types.js';
|
|
9
|
+
import './ids.js';
|
|
10
|
+
import './clock.js';
|
|
11
|
+
import './decompose.js';
|
|
12
|
+
import './type-registry.js';
|
|
13
|
+
import './embedding-port.js';
|
|
14
|
+
import './vector-index-port.js';
|
|
15
|
+
|
|
16
|
+
/** Every toolset call returns this — errors are structured, never thrown across the agent boundary. */
|
|
17
|
+
type ToolResult<T> = {
|
|
18
|
+
ok: true;
|
|
19
|
+
value: T;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
error: {
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
type PatchOp = {
|
|
28
|
+
op: "set";
|
|
29
|
+
value: Json;
|
|
30
|
+
ifVersion?: number;
|
|
31
|
+
} | {
|
|
32
|
+
op: "insert";
|
|
33
|
+
key: string | number;
|
|
34
|
+
value: Json;
|
|
35
|
+
ifVersion?: number;
|
|
36
|
+
} | {
|
|
37
|
+
op: "remove";
|
|
38
|
+
ifVersion?: number;
|
|
39
|
+
} | {
|
|
40
|
+
op: "move";
|
|
41
|
+
to: Ref;
|
|
42
|
+
key: string | number;
|
|
43
|
+
ifVersion?: number;
|
|
44
|
+
};
|
|
45
|
+
interface ToolsetDeps {
|
|
46
|
+
tree: ArtifactTree;
|
|
47
|
+
addressing: Addressing;
|
|
48
|
+
log: EventLog;
|
|
49
|
+
mutator: Mutator;
|
|
50
|
+
index?: SemanticIndex;
|
|
51
|
+
}
|
|
52
|
+
interface ToolsetBinding {
|
|
53
|
+
owner?: string;
|
|
54
|
+
/** JSON Pointer prefix: writes must be at or under it (enforced by the Mutator). */
|
|
55
|
+
writeScope?: string;
|
|
56
|
+
/** JSON Pointer prefix: reads are confined to it. */
|
|
57
|
+
readScope?: string;
|
|
58
|
+
}
|
|
59
|
+
interface PatchResult {
|
|
60
|
+
id: NodeId;
|
|
61
|
+
path: string;
|
|
62
|
+
/** The affected node's version AFTER the op (remove: the parent's). Feed into the next ifVersion. */
|
|
63
|
+
version: number;
|
|
64
|
+
}
|
|
65
|
+
interface Toolset {
|
|
66
|
+
describe(ref?: Ref, opts?: DescribeOpts): Promise<ToolResult<DescribeResult>>;
|
|
67
|
+
get(ref: Ref, opts?: GetOpts): Promise<ToolResult<GetResult>>;
|
|
68
|
+
find(selector: FindSelector, opts?: FindOpts): Promise<ToolResult<FindResult>>;
|
|
69
|
+
search(query: string, opts?: SearchOpts): Promise<ToolResult<SearchResult>>;
|
|
70
|
+
patch(ref: Ref, op: PatchOp): Promise<ToolResult<PatchResult>>;
|
|
71
|
+
history(ref?: Ref, opts?: {
|
|
72
|
+
limit?: number;
|
|
73
|
+
}): Promise<ToolResult<MutationEvent[]>>;
|
|
74
|
+
}
|
|
75
|
+
/** A scoped, agent-facing bundle of tools returning structured results. */
|
|
76
|
+
declare function makeToolset(deps: ToolsetDeps, binding?: ToolsetBinding): Toolset;
|
|
77
|
+
|
|
78
|
+
export { type PatchOp, type PatchResult, type ToolResult, type Toolset, type ToolsetBinding, type ToolsetDeps, makeToolset };
|