@brightbeamai/chap-coordinator 0.3.3 → 0.3.5

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 (47) hide show
  1. package/README.md +54 -8
  2. package/dist/{api-59aU01Bs.d.cts → api-CjZQnj63.d.cts} +128 -14
  3. package/dist/{api-CxciGFHA.d.ts → api-DG0VFGVr.d.ts} +128 -14
  4. package/dist/api.cjs +3 -1
  5. package/dist/api.cjs.map +1 -1
  6. package/dist/api.d.cts +1 -1
  7. package/dist/api.d.ts +1 -1
  8. package/dist/api.js +3 -1
  9. package/dist/api.js.map +1 -1
  10. package/dist/index.cjs +1578 -276
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +20 -6
  13. package/dist/index.d.ts +20 -6
  14. package/dist/index.js +1577 -276
  15. package/dist/index.js.map +1 -1
  16. package/dist/jsonrpc.cjs +1 -0
  17. package/dist/jsonrpc.cjs.map +1 -1
  18. package/dist/jsonrpc.d.cts +1 -0
  19. package/dist/jsonrpc.d.ts +1 -0
  20. package/dist/jsonrpc.js +1 -0
  21. package/dist/jsonrpc.js.map +1 -1
  22. package/dist/methods.cjs.map +1 -1
  23. package/dist/methods.d.cts +36 -3
  24. package/dist/methods.d.ts +36 -3
  25. package/dist/patch.cjs +195 -61
  26. package/dist/patch.cjs.map +1 -1
  27. package/dist/patch.d.cts +16 -2
  28. package/dist/patch.d.ts +16 -2
  29. package/dist/patch.js +194 -61
  30. package/dist/patch.js.map +1 -1
  31. package/dist/storage/sqlite.cjs +81 -19
  32. package/dist/storage/sqlite.cjs.map +1 -1
  33. package/dist/storage/sqlite.d.cts +19 -9
  34. package/dist/storage/sqlite.d.ts +19 -9
  35. package/dist/storage/sqlite.js +81 -19
  36. package/dist/storage/sqlite.js.map +1 -1
  37. package/dist/storage/store.cjs +20 -15
  38. package/dist/storage/store.cjs.map +1 -1
  39. package/dist/storage/store.d.cts +24 -6
  40. package/dist/storage/store.d.ts +24 -6
  41. package/dist/storage/store.js +20 -15
  42. package/dist/storage/store.js.map +1 -1
  43. package/dist/types.cjs.map +1 -1
  44. package/dist/types.d.cts +56 -1
  45. package/dist/types.d.ts +56 -1
  46. package/dist/types.js.map +1 -1
  47. package/package.json +2 -2
package/dist/patch.js CHANGED
@@ -107,77 +107,116 @@ function deepEqual(a, b) {
107
107
  }
108
108
  return true;
109
109
  }
110
- function applyOne(doc, op) {
110
+ function addAt(doc, path, value, size, copied = false, movedFrom) {
111
+ const parts = splitPath(path);
112
+ if (parts.length === 0) {
113
+ if (size) {
114
+ size.reset(value);
115
+ if (copied || movedFrom !== void 0) size.carry(size);
116
+ }
117
+ return value;
118
+ }
119
+ const [parent, key] = navigate(doc, parts);
120
+ let newKey;
121
+ if (Array.isArray(parent)) {
122
+ if (key === "-") {
123
+ parent.push(value);
124
+ } else {
125
+ const idx = key;
126
+ if (idx < 0 || idx > parent.length) {
127
+ throw new PatchError(`Index out of range for add: ${path}`);
128
+ }
129
+ parent.splice(idx, 0, value);
130
+ }
131
+ } else if (parent !== null && typeof parent === "object") {
132
+ if (Object.prototype.hasOwnProperty.call(parent, key)) size?.take(parent[key]);
133
+ else newKey = key;
134
+ parent[key] = value;
135
+ } else {
136
+ throw new PatchError(`Cannot add into ${jsonType(parent)}`);
137
+ }
138
+ if (size) {
139
+ if (movedFrom === void 0) {
140
+ const inserted = size.insert(value, parts.length, newKey);
141
+ if (copied) size.carry(inserted);
142
+ } else {
143
+ if (newKey !== void 0) size.bytes += utf8Length(newKey);
144
+ if (parts.length > movedFrom) {
145
+ const moved = sizeOf(value);
146
+ size.depth = Math.max(size.depth, parts.length + moved.depth);
147
+ size.carry(moved);
148
+ }
149
+ }
150
+ }
151
+ return doc;
152
+ }
153
+ function removeAt(doc, path, size, moved = false) {
154
+ const parts = splitPath(path);
155
+ if (parts.length === 0) throw new PatchError("Cannot remove root.");
156
+ const [parent, key] = navigate(doc, parts);
157
+ if (Array.isArray(parent)) {
158
+ const idx = key === "-" ? -1 : key;
159
+ if (idx < 0 || idx >= parent.length) {
160
+ throw new PatchError(`Index out of range for remove: ${path}`);
161
+ }
162
+ if (size && !moved) size.take(parent[idx]);
163
+ parent.splice(idx, 1);
164
+ } else if (parent !== null && typeof parent === "object") {
165
+ if (!Object.prototype.hasOwnProperty.call(parent, key)) {
166
+ throw new PatchError(`Path not found for remove: ${path}`);
167
+ }
168
+ if (size) {
169
+ if (moved) size.bytes -= utf8Length(key);
170
+ else size.take(parent[key], key);
171
+ }
172
+ delete parent[key];
173
+ } else {
174
+ throw new PatchError(`Cannot remove from ${jsonType(parent)}`);
175
+ }
176
+ return doc;
177
+ }
178
+ function applyOne(doc, op, size) {
111
179
  const kind = op.op;
112
180
  const path = op.path ?? "";
113
181
  if (kind === "add") {
114
182
  if (!("value" in op)) throw new PatchError("'add' requires 'value'");
115
- const parts = splitPath(path);
116
- if (parts.length === 0) return op.value;
117
- const [parent, key] = navigate(doc, parts);
118
- if (Array.isArray(parent)) {
119
- if (key === "-") {
120
- parent.push(op.value);
121
- } else {
122
- const idx = key;
123
- if (idx < 0 || idx > parent.length) {
124
- throw new PatchError(`Index out of range for add: ${path}`);
125
- }
126
- parent.splice(idx, 0, op.value);
127
- }
128
- } else if (parent !== null && typeof parent === "object") {
129
- parent[key] = op.value;
130
- } else {
131
- throw new PatchError(`Cannot add into ${jsonType(parent)}`);
132
- }
133
- return doc;
183
+ return addAt(doc, path, clone(op.value), size);
134
184
  }
135
185
  if (kind === "replace") {
136
186
  if (!("value" in op)) throw new PatchError("'replace' requires 'value'");
137
187
  const parts = splitPath(path);
138
- if (parts.length === 0) return op.value;
188
+ const value = clone(op.value);
189
+ if (parts.length === 0) {
190
+ size?.reset(value);
191
+ return value;
192
+ }
139
193
  const [parent, key] = navigate(doc, parts);
140
194
  if (Array.isArray(parent)) {
141
195
  const idx = key === "-" ? -1 : key;
142
196
  if (idx < 0 || idx >= parent.length) {
143
197
  throw new PatchError(`Path not found for replace: ${path}`);
144
198
  }
145
- parent[idx] = op.value;
199
+ size?.take(parent[idx]);
200
+ parent[idx] = value;
146
201
  } else if (parent !== null && typeof parent === "object") {
147
202
  if (!Object.prototype.hasOwnProperty.call(parent, key)) {
148
203
  throw new PatchError(`Path not found for replace: ${path}`);
149
204
  }
150
- parent[key] = op.value;
205
+ size?.take(parent[key]);
206
+ parent[key] = value;
151
207
  } else {
152
208
  throw new PatchError(`Cannot replace in ${jsonType(parent)}`);
153
209
  }
210
+ size?.insert(value, parts.length);
154
211
  return doc;
155
212
  }
156
213
  if (kind === "remove") {
157
- const parts = splitPath(path);
158
- if (parts.length === 0) throw new PatchError("Cannot remove root.");
159
- const [parent, key] = navigate(doc, parts);
160
- if (Array.isArray(parent)) {
161
- const idx = key === "-" ? -1 : key;
162
- if (idx < 0 || idx >= parent.length) {
163
- throw new PatchError(`Index out of range for remove: ${path}`);
164
- }
165
- parent.splice(idx, 1);
166
- } else if (parent !== null && typeof parent === "object") {
167
- if (!Object.prototype.hasOwnProperty.call(parent, key)) {
168
- throw new PatchError(`Path not found for remove: ${path}`);
169
- }
170
- delete parent[key];
171
- } else {
172
- throw new PatchError(`Cannot remove from ${jsonType(parent)}`);
173
- }
174
- return doc;
214
+ return removeAt(doc, path, size);
175
215
  }
176
216
  if (kind === "copy") {
177
217
  const src = op.from;
178
218
  if (src === void 0) throw new PatchError("'copy' requires 'from'");
179
- const value = clone(getAt(doc, src));
180
- return applyOne(doc, { op: "add", path, value });
219
+ return addAt(doc, path, clone(getAt(doc, src)), size, true);
181
220
  }
182
221
  if (kind === "move") {
183
222
  const src = op.from;
@@ -185,9 +224,9 @@ function applyOne(doc, op) {
185
224
  if (path.startsWith(src + "/")) {
186
225
  throw new PatchError("'move' cannot move a location into its own child.");
187
226
  }
188
- const value = clone(getAt(doc, src));
189
- doc = applyOne(doc, { op: "remove", path: src });
190
- return applyOne(doc, { op: "add", path, value });
227
+ const value = getAt(doc, src);
228
+ doc = removeAt(doc, src, size, true);
229
+ return addAt(doc, path, value, size, false, splitPath(src).length);
191
230
  }
192
231
  if (kind === "test") {
193
232
  if (!("value" in op)) throw new PatchError("'test' requires 'value'");
@@ -199,29 +238,122 @@ function applyOne(doc, op) {
199
238
  }
200
239
  var MAX_PATCH_OPS = 1e3;
201
240
  var MAX_DOCUMENT_NODES = 1e5;
202
- function nodeCount(value) {
203
- if (Array.isArray(value)) {
204
- let n = 1;
205
- for (const v of value) n += nodeCount(v);
206
- return n;
207
- }
208
- if (value !== null && typeof value === "object") {
209
- let n = 1;
210
- for (const v of Object.values(value)) n += nodeCount(v);
211
- return n;
212
- }
213
- return 1;
241
+ var MAX_DOCUMENT_DEPTH = 64;
242
+ var MAX_CARRIED_TIMES = 3;
243
+ var DEFAULT_MAX_PATCHED_BYTES = 1048576;
244
+ function utf8Length(s) {
245
+ const buffer = globalThis.Buffer;
246
+ if (typeof buffer === "function") {
247
+ return buffer.byteLength(s, "utf8");
248
+ }
249
+ let n = 0;
250
+ for (let i = 0; i < s.length; i++) {
251
+ const c = s.charCodeAt(i);
252
+ if (c < 128) n += 1;
253
+ else if (c < 2048) n += 2;
254
+ else if (c >= 55296 && c <= 56319 && i + 1 < s.length) {
255
+ const d = s.charCodeAt(i + 1);
256
+ if (d >= 56320 && d <= 57343) {
257
+ n += 4;
258
+ i++;
259
+ } else n += 3;
260
+ } else n += 3;
261
+ }
262
+ return n;
214
263
  }
215
- function applyJsonPatch(doc, ops) {
264
+ function sizeOf(value) {
265
+ if (value === null || typeof value !== "object") {
266
+ return { nodes: 1, bytes: typeof value === "string" ? utf8Length(value) : 0, depth: 1 };
267
+ }
268
+ let nodes = 0;
269
+ let bytes = 0;
270
+ let depth = 0;
271
+ const values = [value];
272
+ const levels = [1];
273
+ const visit = (x, level) => {
274
+ if (x !== null && typeof x === "object") {
275
+ values.push(x);
276
+ levels.push(level);
277
+ return;
278
+ }
279
+ nodes += 1;
280
+ if (level > depth) depth = level;
281
+ if (typeof x === "string") bytes += utf8Length(x);
282
+ };
283
+ while (values.length > 0) {
284
+ const v = values.pop();
285
+ const level = levels.pop();
286
+ nodes += 1;
287
+ if (level > depth) depth = level;
288
+ if (Array.isArray(v)) {
289
+ for (const x of v) visit(x, level + 1);
290
+ } else {
291
+ const o = v;
292
+ for (const k of Object.keys(o)) {
293
+ bytes += utf8Length(k);
294
+ visit(o[k], level + 1);
295
+ }
296
+ }
297
+ }
298
+ return { nodes, bytes, depth };
299
+ }
300
+ var Size = class {
301
+ nodes = 0;
302
+ bytes = 0;
303
+ depth = 0;
304
+ /** What copies, and moves to a deeper place or onto the whole document, have carried. */
305
+ carriedNodes = 0;
306
+ carriedBytes = 0;
307
+ constructor(doc) {
308
+ this.reset(doc);
309
+ }
310
+ reset(doc) {
311
+ const s = sizeOf(doc);
312
+ this.nodes = s.nodes;
313
+ this.bytes = s.bytes;
314
+ this.depth = s.depth;
315
+ }
316
+ /** `value` goes in `segments` levels below the top, under a new `key`. */
317
+ insert(value, segments, key) {
318
+ const s = sizeOf(value);
319
+ this.nodes += s.nodes;
320
+ this.bytes += s.bytes + (key === void 0 ? 0 : utf8Length(key));
321
+ this.depth = Math.max(this.depth, segments + s.depth);
322
+ return s;
323
+ }
324
+ carry(s) {
325
+ this.carriedNodes += s.nodes;
326
+ this.carriedBytes += s.bytes;
327
+ }
328
+ /** `value` comes out, with its `key`. */
329
+ take(value, key) {
330
+ const s = sizeOf(value);
331
+ this.nodes -= s.nodes;
332
+ this.bytes -= s.bytes + (key === void 0 ? 0 : utf8Length(key));
333
+ }
334
+ };
335
+ function applyJsonPatch(doc, ops, limits = {}) {
336
+ const maxBytes = limits.maxStringBytes ?? DEFAULT_MAX_PATCHED_BYTES;
216
337
  if (ops.length > MAX_PATCH_OPS) {
217
338
  throw new PatchError(`Patch has too many operations (${ops.length} > ${MAX_PATCH_OPS})`);
218
339
  }
219
340
  let out = JSON.parse(JSON.stringify(doc));
341
+ const size = new Size(out);
220
342
  for (const op of ops) {
221
- out = applyOne(out, op);
222
- if (nodeCount(out) > MAX_DOCUMENT_NODES) {
343
+ out = applyOne(out, op, size);
344
+ if (size.depth > MAX_DOCUMENT_DEPTH) size.depth = sizeOf(out).depth;
345
+ if (size.nodes > MAX_DOCUMENT_NODES) {
223
346
  throw new PatchError(`Patched document exceeds the node limit (${MAX_DOCUMENT_NODES})`);
224
347
  }
348
+ if (size.bytes > maxBytes) {
349
+ throw new PatchError(`Patched document exceeds the size limit (${maxBytes} bytes of strings and keys)`);
350
+ }
351
+ if (size.depth > MAX_DOCUMENT_DEPTH) {
352
+ throw new PatchError(`Patched document nests deeper than ${MAX_DOCUMENT_DEPTH} levels`);
353
+ }
354
+ if (size.carriedNodes > MAX_CARRIED_TIMES * MAX_DOCUMENT_NODES || size.carriedBytes > MAX_CARRIED_TIMES * maxBytes) {
355
+ throw new PatchError(`Patch copies or moves more than its limit (${MAX_CARRIED_TIMES * MAX_DOCUMENT_NODES} values or ${MAX_CARRIED_TIMES * maxBytes} bytes of strings and keys)`);
356
+ }
225
357
  }
226
358
  return out;
227
359
  }
@@ -260,6 +392,7 @@ function diffJsonPatch(from, to, basePath = "") {
260
392
  return ops;
261
393
  }
262
394
  export {
395
+ DEFAULT_MAX_PATCHED_BYTES,
263
396
  PatchError,
264
397
  applyJsonPatch,
265
398
  diffJsonPatch
package/dist/patch.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/patch.ts"],"sourcesContent":["/**\n * @brightbeamai/chap-coordinator/patch\n *\n * RFC 6902 JSON Patch implementation, plus a small diff computer that\n * produces a patch from two values. Used by the Coordinator for\n * `decide.override` apply and by the playground UI (in the browser) to\n * compute the patch from a textarea edit.\n *\n * Implements all six RFC 6902 operations (add, remove, replace, move,\n * copy, test) with semantics identical to the Python reference. Path\n * segments that enable prototype pollution in a JavaScript runtime\n * (`__proto__`, `constructor`, `prototype`) are rejected, so the two\n * references accept and reject exactly the same patches.\n */\n\nimport type { JsonPatchOp } from \"./types.js\";\n\nexport class PatchError extends Error {}\n\n// Rejected in every JSON Pointer segment: these enable prototype pollution.\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n// An array index is an RFC 6901 array index: \"0\" or a positive integer with no\n// leading zero. A shared strict rule keeps the two references in step -- JS\n// Number() and Python int() otherwise accept disjoint token sets (e.g. \"1e1\",\n// \"0x0a\", \"3.0\", \"\", \"1_0\"), which would apply the same patch differently.\nconst ARRAY_INDEX_RE = /^(0|[1-9][0-9]*)$/;\n\n// The JSON type name of a value. Python and JavaScript name their own types\n// differently (dict against object, list against object, str against string).\n// The refusal names the JSON type, which both references and the\n// specification share.\nfunction jsonType(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n switch (typeof value) {\n case \"object\": return \"object\";\n case \"string\": return \"string\";\n case \"number\": return \"number\";\n case \"boolean\": return \"boolean\";\n default: return \"null\";\n }\n}\n\nfunction arrayIndex(seg: string): number {\n if (!ARRAY_INDEX_RE.test(seg)) throw new PatchError(`Array index expected at ${JSON.stringify(seg)}`);\n return Number(seg);\n}\n\nfunction unescape(token: string): string {\n return token.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\nfunction splitPath(path: string): string[] {\n if (path === \"\") return [];\n if (!path.startsWith(\"/\")) {\n throw new PatchError(`JSON Pointer must start with '/': ${JSON.stringify(path)}`);\n }\n const segments = path.slice(1).split(\"/\").map(unescape);\n for (const seg of segments) {\n if (DANGEROUS_KEYS.has(seg)) {\n throw new PatchError(`Refusing unsafe path segment ${JSON.stringify(seg)}`);\n }\n }\n return segments;\n}\n\n/** Navigate to the parent of the target; return [parent, lastKey]. */\nfunction navigate(doc: any, parts: string[]): [any, string | number] {\n if (parts.length === 0) {\n throw new PatchError(\"Cannot operate on root with this helper.\");\n }\n let parent = doc;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (Array.isArray(parent)) {\n const idx = arrayIndex(part);\n if (idx >= parent.length) {\n throw new PatchError(`Index out of range at /${parts.slice(0, i + 1).join(\"/\")}`);\n }\n parent = parent[idx];\n } else if (parent !== null && typeof parent === \"object\") {\n if (!Object.prototype.hasOwnProperty.call(parent, part)) {\n throw new PatchError(`Path not found: /${parts.slice(0, i + 1).join(\"/\")}`);\n }\n parent = parent[part];\n } else {\n throw new PatchError(`Cannot traverse into ${jsonType(parent)} at ${JSON.stringify(part)}`);\n }\n }\n const last = parts[parts.length - 1];\n if (Array.isArray(parent)) {\n if (last === \"-\") return [parent, \"-\"];\n return [parent, arrayIndex(last)];\n }\n return [parent, last];\n}\n\nfunction getAt(doc: any, path: string): any {\n const parts = splitPath(path);\n if (parts.length === 0) return doc;\n const [parent, key] = navigate(doc, parts);\n if (Array.isArray(parent)) {\n if (key === \"-\") throw new PatchError(\"Cannot read '-' position.\");\n const idx = key as number;\n if (idx < 0 || idx >= parent.length) throw new PatchError(`Index out of range: ${path}`);\n return parent[idx];\n }\n if (\n parent === null ||\n typeof parent !== \"object\" ||\n !Object.prototype.hasOwnProperty.call(parent, key)\n ) {\n throw new PatchError(`Path not found: ${path}`);\n }\n return parent[key as string];\n}\n\nfunction clone<T>(v: T): T {\n return v === undefined ? v : (JSON.parse(JSON.stringify(v)) as T);\n}\n\nfunction deepEqual(a: any, b: any): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (typeof a !== typeof b) return false;\n if (typeof a !== \"object\") return a === b;\n const aArr = Array.isArray(a);\n if (aArr !== Array.isArray(b)) return false;\n if (aArr) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false;\n return true;\n }\n const ak = Object.keys(a);\n const bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (!Object.prototype.hasOwnProperty.call(b, k)) return false;\n if (!deepEqual(a[k], b[k])) return false;\n }\n return true;\n}\n\nfunction applyOne(doc: any, op: JsonPatchOp): any {\n const kind = op.op;\n const path = op.path ?? \"\";\n\n if (kind === \"add\") {\n if (!(\"value\" in op)) throw new PatchError(\"'add' requires 'value'\");\n const parts = splitPath(path);\n if (parts.length === 0) return op.value; // replace whole document\n const [parent, key] = navigate(doc, parts);\n if (Array.isArray(parent)) {\n if (key === \"-\") {\n parent.push(op.value);\n } else {\n const idx = key as number;\n if (idx < 0 || idx > parent.length) {\n throw new PatchError(`Index out of range for add: ${path}`);\n }\n parent.splice(idx, 0, op.value);\n }\n } else if (parent !== null && typeof parent === \"object\") {\n parent[key as string] = op.value;\n } else {\n throw new PatchError(`Cannot add into ${jsonType(parent)}`);\n }\n return doc;\n }\n\n if (kind === \"replace\") {\n if (!(\"value\" in op)) throw new PatchError(\"'replace' requires 'value'\");\n const parts = splitPath(path);\n if (parts.length === 0) return op.value;\n const [parent, key] = navigate(doc, parts);\n if (Array.isArray(parent)) {\n // RFC 6901: \"-\" names the position after the last element, which no\n // element occupies, so only \"add\" can use it. Left as a string it slips\n // through a numeric range check and writes a \"-\" property on the array.\n const idx = key === \"-\" ? -1 : (key as number);\n if (idx < 0 || idx >= parent.length) {\n throw new PatchError(`Path not found for replace: ${path}`);\n }\n parent[idx] = op.value;\n } else if (parent !== null && typeof parent === \"object\") {\n if (!Object.prototype.hasOwnProperty.call(parent, key)) {\n throw new PatchError(`Path not found for replace: ${path}`);\n }\n parent[key as string] = op.value;\n } else {\n throw new PatchError(`Cannot replace in ${jsonType(parent)}`);\n }\n return doc;\n }\n\n if (kind === \"remove\") {\n const parts = splitPath(path);\n if (parts.length === 0) throw new PatchError(\"Cannot remove root.\");\n const [parent, key] = navigate(doc, parts);\n if (Array.isArray(parent)) {\n // RFC 6901: \"-\" names no existing element, so it cannot be removed.\n // Left as a string it passes the numeric range check and then splice\n // coerces it to zero, removing the first element instead.\n const idx = key === \"-\" ? -1 : (key as number);\n if (idx < 0 || idx >= parent.length) {\n throw new PatchError(`Index out of range for remove: ${path}`);\n }\n parent.splice(idx, 1);\n } else if (parent !== null && typeof parent === \"object\") {\n if (!Object.prototype.hasOwnProperty.call(parent, key)) {\n throw new PatchError(`Path not found for remove: ${path}`);\n }\n delete parent[key as string];\n } else {\n throw new PatchError(`Cannot remove from ${jsonType(parent)}`);\n }\n return doc;\n }\n\n if (kind === \"copy\") {\n const src = op.from;\n if (src === undefined) throw new PatchError(\"'copy' requires 'from'\");\n const value = clone(getAt(doc, src));\n return applyOne(doc, { op: \"add\", path, value });\n }\n\n if (kind === \"move\") {\n const src = op.from;\n if (src === undefined) throw new PatchError(\"'move' requires 'from'\");\n // RFC 6902 4.4: 'from' MUST NOT be a proper prefix of 'path'.\n if (path.startsWith(src + \"/\")) {\n throw new PatchError(\"'move' cannot move a location into its own child.\");\n }\n const value = clone(getAt(doc, src));\n doc = applyOne(doc, { op: \"remove\", path: src });\n return applyOne(doc, { op: \"add\", path, value });\n }\n\n if (kind === \"test\") {\n if (!(\"value\" in op)) throw new PatchError(\"'test' requires 'value'\");\n const actual = getAt(doc, path);\n if (!deepEqual(actual, op.value)) throw new PatchError(`'test' failed at ${path}`);\n return doc;\n }\n\n throw new PatchError(`Unsupported op: ${JSON.stringify(kind)}`);\n}\n\nconst MAX_PATCH_OPS = 1000;\nconst MAX_DOCUMENT_NODES = 100_000;\n\nfunction nodeCount(value: unknown): number {\n if (Array.isArray(value)) {\n let n = 1;\n for (const v of value) n += nodeCount(v);\n return n;\n }\n if (value !== null && typeof value === \"object\") {\n let n = 1;\n for (const v of Object.values(value as Record<string, unknown>)) n += nodeCount(v);\n return n;\n }\n return 1;\n}\n\nexport function applyJsonPatch(doc: unknown, ops: JsonPatchOp[]): unknown {\n if (ops.length > MAX_PATCH_OPS) {\n throw new PatchError(`Patch has too many operations (${ops.length} > ${MAX_PATCH_OPS})`);\n }\n let out: any = JSON.parse(JSON.stringify(doc));\n for (const op of ops) {\n out = applyOne(out, op);\n if (nodeCount(out) > MAX_DOCUMENT_NODES) {\n throw new PatchError(`Patched document exceeds the node limit (${MAX_DOCUMENT_NODES})`);\n }\n }\n return out;\n}\n\n// ============================================================\n// diff: produce a patch that transforms `from` into `to`\n// ============================================================\n\n/**\n * Compute an RFC 6902 patch that, when applied to `from`, produces\n * `to`. Walks objects key-by-key; arrays use a shallow whole-array\n * replace if any element differs. Sufficient for the playground's\n * draft-edit use case where drafts are small JSON objects.\n */\nexport function diffJsonPatch(from: unknown, to: unknown, basePath = \"\"): JsonPatchOp[] {\n const ops: JsonPatchOp[] = [];\n\n if (typeof from !== typeof to || Array.isArray(from) !== Array.isArray(to)) {\n ops.push({ op: \"replace\", path: basePath || \"/\", value: to });\n return ops;\n }\n\n if (from === null || to === null || typeof from !== \"object\") {\n if (from !== to) {\n ops.push({ op: \"replace\", path: basePath || \"/\", value: to });\n }\n return ops;\n }\n\n if (Array.isArray(from) && Array.isArray(to)) {\n if (JSON.stringify(from) !== JSON.stringify(to)) {\n ops.push({ op: \"replace\", path: basePath || \"/\", value: to });\n }\n return ops;\n }\n\n const fromObj = from as Record<string, unknown>;\n const toObj = to as Record<string, unknown>;\n const allKeys = new Set([...Object.keys(fromObj), ...Object.keys(toObj)]);\n\n for (const key of allKeys) {\n const escaped = key.replace(/~/g, \"~0\").replace(/\\//g, \"~1\");\n const path = `${basePath}/${escaped}`;\n if (!(key in fromObj)) {\n ops.push({ op: \"add\", path, value: toObj[key] });\n } else if (!(key in toObj)) {\n ops.push({ op: \"remove\", path });\n } else {\n ops.push(...diffJsonPatch(fromObj[key], toObj[key], path));\n }\n }\n\n return ops;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/patch.ts"],"sourcesContent":["/**\n * @brightbeamai/chap-coordinator/patch\n *\n * RFC 6902 JSON Patch implementation, plus a small diff computer that\n * produces a patch from two values. Used by the Coordinator for\n * `decide.override` apply and by the playground UI (in the browser) to\n * compute the patch from a textarea edit.\n *\n * Implements all six RFC 6902 operations (add, remove, replace, move,\n * copy, test) with semantics identical to the Python reference. Path\n * segments that enable prototype pollution in a JavaScript runtime\n * (`__proto__`, `constructor`, `prototype`) are rejected, so the two\n * references accept and reject exactly the same patches.\n */\n\nimport type { JsonPatchOp } from \"./types.js\";\n\nexport class PatchError extends Error {}\n\n// Rejected in every JSON Pointer segment: these enable prototype pollution.\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n// An array index is an RFC 6901 array index: \"0\" or a positive integer with no\n// leading zero. A shared strict rule keeps the two references in step -- JS\n// Number() and Python int() otherwise accept disjoint token sets (e.g. \"1e1\",\n// \"0x0a\", \"3.0\", \"\", \"1_0\"), which would apply the same patch differently.\nconst ARRAY_INDEX_RE = /^(0|[1-9][0-9]*)$/;\n\n// The JSON type name of a value. Python and JavaScript name their own types\n// differently (dict against object, list against object, str against string).\n// The refusal names the JSON type, which both references and the\n// specification share.\nfunction jsonType(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n switch (typeof value) {\n case \"object\": return \"object\";\n case \"string\": return \"string\";\n case \"number\": return \"number\";\n case \"boolean\": return \"boolean\";\n default: return \"null\";\n }\n}\n\nfunction arrayIndex(seg: string): number {\n if (!ARRAY_INDEX_RE.test(seg)) throw new PatchError(`Array index expected at ${JSON.stringify(seg)}`);\n return Number(seg);\n}\n\nfunction unescape(token: string): string {\n return token.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\nfunction splitPath(path: string): string[] {\n if (path === \"\") return [];\n if (!path.startsWith(\"/\")) {\n throw new PatchError(`JSON Pointer must start with '/': ${JSON.stringify(path)}`);\n }\n const segments = path.slice(1).split(\"/\").map(unescape);\n for (const seg of segments) {\n if (DANGEROUS_KEYS.has(seg)) {\n throw new PatchError(`Refusing unsafe path segment ${JSON.stringify(seg)}`);\n }\n }\n return segments;\n}\n\n/** Navigate to the parent of the target; return [parent, lastKey]. */\nfunction navigate(doc: any, parts: string[]): [any, string | number] {\n if (parts.length === 0) {\n throw new PatchError(\"Cannot operate on root with this helper.\");\n }\n let parent = doc;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (Array.isArray(parent)) {\n const idx = arrayIndex(part);\n if (idx >= parent.length) {\n throw new PatchError(`Index out of range at /${parts.slice(0, i + 1).join(\"/\")}`);\n }\n parent = parent[idx];\n } else if (parent !== null && typeof parent === \"object\") {\n if (!Object.prototype.hasOwnProperty.call(parent, part)) {\n throw new PatchError(`Path not found: /${parts.slice(0, i + 1).join(\"/\")}`);\n }\n parent = parent[part];\n } else {\n throw new PatchError(`Cannot traverse into ${jsonType(parent)} at ${JSON.stringify(part)}`);\n }\n }\n const last = parts[parts.length - 1];\n if (Array.isArray(parent)) {\n if (last === \"-\") return [parent, \"-\"];\n return [parent, arrayIndex(last)];\n }\n return [parent, last];\n}\n\nfunction getAt(doc: any, path: string): any {\n const parts = splitPath(path);\n if (parts.length === 0) return doc;\n const [parent, key] = navigate(doc, parts);\n if (Array.isArray(parent)) {\n if (key === \"-\") throw new PatchError(\"Cannot read '-' position.\");\n const idx = key as number;\n if (idx < 0 || idx >= parent.length) throw new PatchError(`Index out of range: ${path}`);\n return parent[idx];\n }\n if (\n parent === null ||\n typeof parent !== \"object\" ||\n !Object.prototype.hasOwnProperty.call(parent, key)\n ) {\n throw new PatchError(`Path not found: ${path}`);\n }\n return parent[key as string];\n}\n\nfunction clone<T>(v: T): T {\n return v === undefined ? v : (JSON.parse(JSON.stringify(v)) as T);\n}\n\nfunction deepEqual(a: any, b: any): boolean {\n if (a === b) return true;\n if (a === null || b === null) return a === b;\n if (typeof a !== typeof b) return false;\n if (typeof a !== \"object\") return a === b;\n const aArr = Array.isArray(a);\n if (aArr !== Array.isArray(b)) return false;\n if (aArr) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false;\n return true;\n }\n const ak = Object.keys(a);\n const bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (!Object.prototype.hasOwnProperty.call(b, k)) return false;\n if (!deepEqual(a[k], b[k])) return false;\n }\n return true;\n}\n\n/**\n * Puts `value`, which the document owns from here on, at `path`. A copied\n * value counts towards what the patch carries. A moved value is counted\n * already: `movedFrom` is the number of segments of the place it left, and\n * only its key, a value it replaces and how deep it now reaches change the\n * count. A move to a deeper place, or onto the whole document, costs a pass\n * over the value, so it counts towards what the patch carries as well.\n */\nfunction addAt(doc: any, path: string, value: unknown, size: Size | undefined,\n copied = false, movedFrom?: number): any {\n const parts = splitPath(path);\n if (parts.length === 0) {\n if (size) {\n size.reset(value);\n if (copied || movedFrom !== undefined) size.carry(size);\n }\n return value; // replace whole document\n }\n const [parent, key] = navigate(doc, parts);\n let newKey: string | undefined;\n if (Array.isArray(parent)) {\n if (key === \"-\") {\n parent.push(value);\n } else {\n const idx = key as number;\n if (idx < 0 || idx > parent.length) {\n throw new PatchError(`Index out of range for add: ${path}`);\n }\n parent.splice(idx, 0, value);\n }\n } else if (parent !== null && typeof parent === \"object\") {\n if (Object.prototype.hasOwnProperty.call(parent, key)) size?.take(parent[key as string]);\n else newKey = key as string;\n parent[key as string] = value;\n } else {\n throw new PatchError(`Cannot add into ${jsonType(parent)}`);\n }\n if (size) {\n if (movedFrom === undefined) {\n const inserted = size.insert(value, parts.length, newKey);\n if (copied) size.carry(inserted);\n } else {\n if (newKey !== undefined) size.bytes += utf8Length(newKey);\n if (parts.length > movedFrom) {\n // A move to a deeper place costs a pass over the value, for its depth.\n const moved = sizeOf(value);\n size.depth = Math.max(size.depth, parts.length + moved.depth);\n size.carry(moved);\n }\n }\n }\n return doc;\n}\n\n/** Removes the value at `path`. A moved value goes back in elsewhere, so only its key leaves the count. */\nfunction removeAt(doc: any, path: string, size: Size | undefined, moved = false): any {\n const parts = splitPath(path);\n if (parts.length === 0) throw new PatchError(\"Cannot remove root.\");\n const [parent, key] = navigate(doc, parts);\n if (Array.isArray(parent)) {\n // RFC 6901: \"-\" names no existing element, so it cannot be removed.\n // Left as a string it passes the numeric range check and then splice\n // coerces it to zero, removing the first element instead.\n const idx = key === \"-\" ? -1 : (key as number);\n if (idx < 0 || idx >= parent.length) {\n throw new PatchError(`Index out of range for remove: ${path}`);\n }\n if (size && !moved) size.take(parent[idx]);\n parent.splice(idx, 1);\n } else if (parent !== null && typeof parent === \"object\") {\n if (!Object.prototype.hasOwnProperty.call(parent, key)) {\n throw new PatchError(`Path not found for remove: ${path}`);\n }\n if (size) {\n if (moved) size.bytes -= utf8Length(key as string);\n else size.take(parent[key as string], key as string);\n }\n delete parent[key as string];\n } else {\n throw new PatchError(`Cannot remove from ${jsonType(parent)}`);\n }\n return doc;\n}\n\nfunction applyOne(doc: any, op: JsonPatchOp, size?: Size): any {\n const kind = op.op;\n const path = op.path ?? \"\";\n\n if (kind === \"add\") {\n if (!(\"value\" in op)) throw new PatchError(\"'add' requires 'value'\");\n // A copy, so an operation on the inserted value leaves the patch as it\n // was sent.\n return addAt(doc, path, clone(op.value), size);\n }\n\n if (kind === \"replace\") {\n if (!(\"value\" in op)) throw new PatchError(\"'replace' requires 'value'\");\n const parts = splitPath(path);\n const value = clone(op.value);\n if (parts.length === 0) {\n size?.reset(value);\n return value;\n }\n const [parent, key] = navigate(doc, parts);\n if (Array.isArray(parent)) {\n // RFC 6901: \"-\" names the position after the last element, which no\n // element occupies, so only \"add\" can use it. Left as a string it slips\n // through a numeric range check and writes a \"-\" property on the array.\n const idx = key === \"-\" ? -1 : (key as number);\n if (idx < 0 || idx >= parent.length) {\n throw new PatchError(`Path not found for replace: ${path}`);\n }\n size?.take(parent[idx]);\n parent[idx] = value;\n } else if (parent !== null && typeof parent === \"object\") {\n if (!Object.prototype.hasOwnProperty.call(parent, key)) {\n throw new PatchError(`Path not found for replace: ${path}`);\n }\n size?.take(parent[key as string]);\n parent[key as string] = value;\n } else {\n throw new PatchError(`Cannot replace in ${jsonType(parent)}`);\n }\n size?.insert(value, parts.length);\n return doc;\n }\n\n if (kind === \"remove\") {\n return removeAt(doc, path, size);\n }\n\n if (kind === \"copy\") {\n const src = op.from;\n if (src === undefined) throw new PatchError(\"'copy' requires 'from'\");\n return addAt(doc, path, clone(getAt(doc, src)), size, true);\n }\n\n if (kind === \"move\") {\n const src = op.from;\n if (src === undefined) throw new PatchError(\"'move' requires 'from'\");\n // RFC 6902 4.4: 'from' MUST NOT be a proper prefix of 'path'.\n if (path.startsWith(src + \"/\")) {\n throw new PatchError(\"'move' cannot move a location into its own child.\");\n }\n // The remove detaches the value, so it moves as it is, uncopied.\n const value = getAt(doc, src);\n doc = removeAt(doc, src, size, true);\n return addAt(doc, path, value, size, false, splitPath(src).length);\n }\n\n if (kind === \"test\") {\n if (!(\"value\" in op)) throw new PatchError(\"'test' requires 'value'\");\n const actual = getAt(doc, path);\n if (!deepEqual(actual, op.value)) throw new PatchError(`'test' failed at ${path}`);\n return doc;\n }\n\n throw new PatchError(`Unsupported op: ${JSON.stringify(kind)}`);\n}\n\nconst MAX_PATCH_OPS = 1000;\nconst MAX_DOCUMENT_NODES = 100_000;\n\n/**\n * How deeply a patched document may nest, counting the document as level one,\n * as the coordinator counts a request's nesting. Each operation's value comes\n * within the request's limit, and a patch that puts one value inside another\n * would otherwise nest the document without limit.\n */\nconst MAX_DOCUMENT_DEPTH = 64;\n\n/**\n * How much a patch's copies, and its moves to a deeper place or onto the whole\n * document, may carry between them, as a multiple of the document's limits.\n * Each costs a pass over the value it carries, so a patch that copied the same\n * large value back and forth could otherwise keep a coordinator busy for\n * seconds.\n */\nconst MAX_CARRIED_TIMES = 3;\n\n/**\n * The default cap on the string and key text a patched document may hold, in\n * UTF-8 bytes: the default `max_envelope_bytes`. An artefact that arrives in a\n * request holds no more text than the request's size. The node cap alone does\n * not bound size: a `copy` of the root doubles the document, long strings\n * included, so fifteen of them grow 64 KiB to about 2 GiB within 100,000\n * nodes.\n */\nexport const DEFAULT_MAX_PATCHED_BYTES = 1_048_576;\n\n/** Limits a caller can set on {@link applyJsonPatch}. */\nexport interface PatchLimits {\n /** The most UTF-8 bytes of string and key text the result may hold. */\n maxStringBytes?: number;\n}\n\n/**\n * UTF-8 length of a string: Buffer's count where Node provides it, and the\n * same count by hand elsewhere, so the module runs anywhere. A lone surrogate\n * counts three bytes either way, as does Python's\n * `len(s.encode(\"utf-8\", \"surrogatepass\"))`, which keeps the two references\n * refusing the same patches.\n */\nfunction utf8Length(s: string): number {\n const buffer = (globalThis as { Buffer?: unknown }).Buffer;\n if (typeof buffer === \"function\") {\n return (buffer as unknown as { byteLength(s: string, encoding: \"utf8\"): number }).byteLength(s, \"utf8\");\n }\n let n = 0;\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i);\n if (c < 0x80) n += 1;\n else if (c < 0x800) n += 2;\n else if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {\n const d = s.charCodeAt(i + 1);\n if (d >= 0xdc00 && d <= 0xdfff) { n += 4; i++; } else n += 3;\n } else n += 3;\n }\n return n;\n}\n\ninterface Measured { nodes: number; bytes: number; depth: number }\n\n/**\n * A value's count of values, the UTF-8 bytes of its strings and keys, and its\n * depth, counting the value itself as level one, in one pass that does not\n * recurse.\n */\nfunction sizeOf(value: unknown): Measured {\n if (value === null || typeof value !== \"object\") {\n return { nodes: 1, bytes: typeof value === \"string\" ? utf8Length(value) : 0, depth: 1 };\n }\n let nodes = 0;\n let bytes = 0;\n let depth = 0;\n const values: object[] = [value];\n const levels: number[] = [1];\n // A scalar child is counted where it is found, which saves a trip through\n // the stack.\n const visit = (x: unknown, level: number): void => {\n if (x !== null && typeof x === \"object\") {\n values.push(x);\n levels.push(level);\n return;\n }\n nodes += 1;\n if (level > depth) depth = level;\n if (typeof x === \"string\") bytes += utf8Length(x);\n };\n while (values.length > 0) {\n const v = values.pop()!;\n const level = levels.pop()!;\n nodes += 1;\n if (level > depth) depth = level;\n if (Array.isArray(v)) {\n for (const x of v) visit(x, level + 1);\n } else {\n const o = v as Record<string, unknown>;\n for (const k of Object.keys(o)) {\n bytes += utf8Length(k);\n visit(o[k], level + 1);\n }\n }\n }\n return { nodes, bytes, depth };\n}\n\n/**\n * What the working document holds, kept in step by each operation. The values\n * and the bytes are exact: each operation counts what it puts in and what it\n * takes out, so a patch costs what it changes, whatever the document's size.\n * The depth is a bound, raised by what each operation puts in; it passes the\n * limit when the document does, or when the document started deeper.\n */\nclass Size implements Measured {\n nodes = 0;\n bytes = 0;\n depth = 0;\n /** What copies, and moves to a deeper place or onto the whole document, have carried. */\n carriedNodes = 0;\n carriedBytes = 0;\n\n constructor(doc: unknown) {\n this.reset(doc);\n }\n\n reset(doc: unknown): void {\n const s = sizeOf(doc);\n this.nodes = s.nodes;\n this.bytes = s.bytes;\n this.depth = s.depth;\n }\n\n /** `value` goes in `segments` levels below the top, under a new `key`. */\n insert(value: unknown, segments: number, key?: string): Measured {\n const s = sizeOf(value);\n this.nodes += s.nodes;\n this.bytes += s.bytes + (key === undefined ? 0 : utf8Length(key));\n this.depth = Math.max(this.depth, segments + s.depth);\n return s;\n }\n\n carry(s: Measured): void {\n this.carriedNodes += s.nodes;\n this.carriedBytes += s.bytes;\n }\n\n /** `value` comes out, with its `key`. */\n take(value: unknown, key?: string): void {\n const s = sizeOf(value);\n this.nodes -= s.nodes;\n this.bytes -= s.bytes + (key === undefined ? 0 : utf8Length(key));\n }\n}\n\nexport function applyJsonPatch(doc: unknown, ops: JsonPatchOp[], limits: PatchLimits = {}): unknown {\n const maxBytes = limits.maxStringBytes ?? DEFAULT_MAX_PATCHED_BYTES;\n if (ops.length > MAX_PATCH_OPS) {\n throw new PatchError(`Patch has too many operations (${ops.length} > ${MAX_PATCH_OPS})`);\n }\n let out: any = JSON.parse(JSON.stringify(doc));\n const size = new Size(out);\n for (const op of ops) {\n out = applyOne(out, op, size);\n if (size.depth > MAX_DOCUMENT_DEPTH) size.depth = sizeOf(out).depth;\n if (size.nodes > MAX_DOCUMENT_NODES) {\n throw new PatchError(`Patched document exceeds the node limit (${MAX_DOCUMENT_NODES})`);\n }\n if (size.bytes > maxBytes) {\n throw new PatchError(`Patched document exceeds the size limit (${maxBytes} bytes of strings and keys)`);\n }\n if (size.depth > MAX_DOCUMENT_DEPTH) {\n throw new PatchError(`Patched document nests deeper than ${MAX_DOCUMENT_DEPTH} levels`);\n }\n if (size.carriedNodes > MAX_CARRIED_TIMES * MAX_DOCUMENT_NODES || size.carriedBytes > MAX_CARRIED_TIMES * maxBytes) {\n throw new PatchError(`Patch copies or moves more than its limit (${MAX_CARRIED_TIMES * MAX_DOCUMENT_NODES} values or ${MAX_CARRIED_TIMES * maxBytes} bytes of strings and keys)`);\n }\n }\n return out;\n}\n\n// ============================================================\n// diff: produce a patch that transforms `from` into `to`\n// ============================================================\n\n/**\n * Compute an RFC 6902 patch that, when applied to `from`, produces\n * `to`. Walks objects key-by-key; arrays use a shallow whole-array\n * replace if any element differs. Sufficient for the playground's\n * draft-edit use case where drafts are small JSON objects.\n */\nexport function diffJsonPatch(from: unknown, to: unknown, basePath = \"\"): JsonPatchOp[] {\n const ops: JsonPatchOp[] = [];\n\n if (typeof from !== typeof to || Array.isArray(from) !== Array.isArray(to)) {\n ops.push({ op: \"replace\", path: basePath || \"/\", value: to });\n return ops;\n }\n\n if (from === null || to === null || typeof from !== \"object\") {\n if (from !== to) {\n ops.push({ op: \"replace\", path: basePath || \"/\", value: to });\n }\n return ops;\n }\n\n if (Array.isArray(from) && Array.isArray(to)) {\n if (JSON.stringify(from) !== JSON.stringify(to)) {\n ops.push({ op: \"replace\", path: basePath || \"/\", value: to });\n }\n return ops;\n }\n\n const fromObj = from as Record<string, unknown>;\n const toObj = to as Record<string, unknown>;\n const allKeys = new Set([...Object.keys(fromObj), ...Object.keys(toObj)]);\n\n for (const key of allKeys) {\n const escaped = key.replace(/~/g, \"~0\").replace(/\\//g, \"~1\");\n const path = `${basePath}/${escaped}`;\n if (!(key in fromObj)) {\n ops.push({ op: \"add\", path, value: toObj[key] });\n } else if (!(key in toObj)) {\n ops.push({ op: \"remove\", path });\n } else {\n ops.push(...diffJsonPatch(fromObj[key], toObj[key], path));\n }\n }\n\n return ops;\n}\n"],"mappings":"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","names":[]}
@@ -34,7 +34,8 @@ var COLLECTIONS = [
34
34
  "deliberations",
35
35
  "handoffs",
36
36
  "snapshots",
37
- "route_decisions"
37
+ "route_decisions",
38
+ "invitations"
38
39
  ];
39
40
  var IDEMPOTENCY_KEYS = "idempotency_keys";
40
41
  var StoreConflictError = class extends Error {
@@ -45,7 +46,7 @@ var StoreConflictError = class extends Error {
45
46
  base;
46
47
  constructor(workspace, held, base) {
47
48
  super(
48
- `The store holds ${held} audit entries for workspace ${workspace} and this write expected ${base}, so nothing was written. Another coordinator may be writing to this workspace, the store may have been changed outside this coordinator, or this coordinator could not load the workspace when it started. Restart this coordinator to reload.`
49
+ `The store holds ${held} audit entries for workspace ${workspace} and this write expected ${base}, so nothing was written. Another coordinator may be writing to this workspace, or the store may have been changed outside this coordinator. A coordinator reloads the workspace and answers the call as failed, and the call may be sent again.`
49
50
  );
50
51
  this.name = "StoreConflictError";
51
52
  this.workspace = workspace;
@@ -122,6 +123,54 @@ function assembleSnapshot(meta, entries, objects) {
122
123
 
123
124
  // src/storage/sqlite.ts
124
125
  var import_meta = {};
126
+ function fromNodeSqlite(db) {
127
+ const transaction = (fn) => {
128
+ const run = (begin) => (...args) => {
129
+ db.exec(begin);
130
+ try {
131
+ const result = fn(...args);
132
+ db.exec("COMMIT");
133
+ return result;
134
+ } catch (err) {
135
+ db.exec("ROLLBACK");
136
+ throw err;
137
+ }
138
+ };
139
+ return Object.assign(run("BEGIN"), { immediate: run("BEGIN IMMEDIATE") });
140
+ };
141
+ return {
142
+ exec: (sql) => db.exec(sql),
143
+ prepare: (sql) => db.prepare(sql),
144
+ pragma: (name, options) => {
145
+ const row = db.prepare(`PRAGMA ${name}`).get();
146
+ return options?.simple ? row ? Object.values(row)[0] : void 0 : row;
147
+ },
148
+ transaction,
149
+ close: () => db.close()
150
+ };
151
+ }
152
+ function openDatabase(path, driver) {
153
+ const failures = [];
154
+ const load = (name) => {
155
+ try {
156
+ return (0, import_node_module.createRequire)(typeof __filename === "string" ? __filename : import_meta.url)(name);
157
+ } catch (err) {
158
+ failures.push(`${name}: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`);
159
+ return void 0;
160
+ }
161
+ };
162
+ if (driver !== "node:sqlite") {
163
+ const Database = load("better-sqlite3");
164
+ if (Database) return new Database(path);
165
+ }
166
+ if (driver !== "better-sqlite3") {
167
+ const sqlite = load("node:sqlite");
168
+ if (sqlite) return fromNodeSqlite(new sqlite.DatabaseSync(path));
169
+ }
170
+ throw new Error(
171
+ `SqliteStore needs the \`better-sqlite3\` package, or Node 22.13 or later for node:sqlite. Install it with \`npm install better-sqlite3\` or use MemoryStore for non-persistent workloads (${failures.join("; ")})`
172
+ );
173
+ }
125
174
  var SCHEMA = `
126
175
  CREATE TABLE IF NOT EXISTS chap_meta (
127
176
  workspace TEXT PRIMARY KEY,
@@ -148,23 +197,15 @@ var LEGACY_VIEW = `CREATE VIEW IF NOT EXISTS ${LEGACY_TABLE} AS SELECT m.workspa
148
197
  "'$.audit', json((SELECT json_group_array(json(e.data)) FROM (SELECT data FROM chap_entries WHERE workspace = m.workspace ORDER BY seq) AS e))",
149
198
  ...[...COLLECTIONS, IDEMPOTENCY_KEYS].map((c) => `'$.${c}', json((SELECT json_group_object(id, json(data)) FROM chap_objects WHERE workspace = m.workspace AND collection = '${c}'))`)
150
199
  ].join(", ") + ") AS data, m.version AS version, m.updated_at AS updated_at FROM chap_meta AS m";
200
+ var LEGACY_VIEW_RECORDED = LEGACY_VIEW.replace("CREATE VIEW IF NOT EXISTS ", "CREATE VIEW ");
151
201
  var SqliteStore = class {
152
202
  db;
153
203
  stmt;
154
204
  txWrite;
155
205
  txDelete;
156
206
  constructor(path, options = {}) {
157
- let Database;
158
- try {
159
- const here = typeof __filename === "string" ? __filename : import_meta.url;
160
- Database = (0, import_node_module.createRequire)(here)("better-sqlite3");
161
- } catch (err) {
162
- const detail = err instanceof Error ? `: ${err.message.split("\n")[0]}` : "";
163
- throw new Error(
164
- `SqliteStore requires the \`better-sqlite3\` package. Install it with \`npm install better-sqlite3\` or use MemoryStore for non-persistent workloads${detail}`
165
- );
166
- }
167
- this.db = new Database(path);
207
+ this.db = openDatabase(path, options.driver);
208
+ this.db.pragma("busy_timeout = 5000");
168
209
  if (path !== ":memory:" && options.wal !== false) {
169
210
  this.db.pragma("journal_mode = WAL");
170
211
  }
@@ -188,7 +229,10 @@ var SqliteStore = class {
188
229
  dropObjects: prepare("DELETE FROM chap_objects WHERE workspace = ?"),
189
230
  metas: prepare("SELECT workspace, data, version, updated_at FROM chap_meta"),
190
231
  entries: prepare("SELECT workspace, seq, data FROM chap_entries ORDER BY workspace, seq"),
191
- objects: prepare("SELECT workspace, collection, id, data FROM chap_objects ORDER BY rowid")
232
+ objects: prepare("SELECT workspace, collection, id, data FROM chap_objects ORDER BY rowid"),
233
+ metaOf: prepare("SELECT workspace, data, version, updated_at FROM chap_meta WHERE workspace = ?"),
234
+ entriesOf: prepare("SELECT workspace, seq, data FROM chap_entries WHERE workspace = ? ORDER BY seq"),
235
+ objectsOf: prepare("SELECT workspace, collection, id, data FROM chap_objects WHERE workspace = ? ORDER BY rowid")
192
236
  };
193
237
  this.txWrite = this.db.transaction((change) => this.write(change));
194
238
  this.txDelete = this.db.transaction((id) => this.drop(id));
@@ -230,6 +274,8 @@ var SqliteStore = class {
230
274
  }
231
275
  this.db.exec("DROP INDEX IF EXISTS idx_chap_workspaces_updated_at");
232
276
  }
277
+ const view = this.db.prepare("SELECT type, sql FROM sqlite_master WHERE name = ?").get(LEGACY_TABLE);
278
+ if (view?.type === "view" && view.sql !== LEGACY_VIEW_RECORDED) this.db.exec(`DROP VIEW ${LEGACY_TABLE}`);
233
279
  if (!this.stmt.hasName.get(LEGACY_TABLE)) this.db.exec(LEGACY_VIEW);
234
280
  }).immediate();
235
281
  for (const id of unreadable) {
@@ -263,12 +309,28 @@ var SqliteStore = class {
263
309
  this.stmt.dropObjects.run(id);
264
310
  }
265
311
  load() {
266
- return this.db.transaction(() => this.readAll())();
312
+ return this.db.transaction(() => this.read(
313
+ this.stmt.metas.all(),
314
+ this.stmt.entries.all(),
315
+ this.stmt.objects.all()
316
+ ))();
317
+ }
318
+ loadOne(id) {
319
+ return this.db.transaction(() => this.read(
320
+ this.stmt.metaOf.all(id),
321
+ this.stmt.entriesOf.all(id),
322
+ this.stmt.objectsOf.all(id)
323
+ ))()[0];
324
+ }
325
+ version(id) {
326
+ if (!this.stmt.hasMeta.get(id)) return void 0;
327
+ const last = this.stmt.held.get(id)?.last;
328
+ return last === null || last === void 0 ? 0 : last + 1;
267
329
  }
268
- readAll() {
330
+ read(metas, entryRows, objectRows) {
269
331
  const broken = /* @__PURE__ */ new Map();
270
332
  const entries = /* @__PURE__ */ new Map();
271
- for (const r of this.stmt.entries.all()) {
333
+ for (const r of entryRows) {
272
334
  if (broken.has(r.workspace)) continue;
273
335
  let list = entries.get(r.workspace);
274
336
  if (!list) entries.set(r.workspace, list = []);
@@ -283,7 +345,7 @@ var SqliteStore = class {
283
345
  }
284
346
  }
285
347
  const objects = /* @__PURE__ */ new Map();
286
- for (const r of this.stmt.objects.all()) {
348
+ for (const r of objectRows) {
287
349
  if (broken.has(r.workspace)) continue;
288
350
  let list = objects.get(r.workspace);
289
351
  if (!list) objects.set(r.workspace, list = []);
@@ -294,7 +356,7 @@ var SqliteStore = class {
294
356
  }
295
357
  }
296
358
  const out = [];
297
- for (const r of this.stmt.metas.all()) {
359
+ for (const r of metas) {
298
360
  let meta;
299
361
  if (!broken.has(r.workspace)) {
300
362
  try {