@agentshouse/mdruntime 0.1.0-ca9afacd5aebd → 0.1.0-cb0eb0d87ff49
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/AGENTS.md +114 -123
- package/dist/adapter.d.ts +26 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attempt.d.ts.map +1 -1
- package/dist/attempt.js +14 -1
- package/dist/bootstrap.d.ts +2 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +28 -6
- package/dist/change-set.d.ts +2 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +34 -9
- package/dist/chat/acts.d.ts +3 -4
- package/dist/chat/acts.d.ts.map +1 -1
- package/dist/chat/acts.js +10 -10
- package/dist/chat/list.d.ts +6 -0
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +6 -3
- package/dist/chat/markdown.d.ts +2 -1
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +7 -6
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +2 -2
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +25 -50
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +2 -2
- package/dist/chat/style.js +3 -3
- package/dist/client.d.ts +7 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -1
- package/dist/collection.d.ts +17 -3
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +230 -19
- package/dist/connection.d.ts +8 -5
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +858 -301
- package/dist/conversation.d.ts +12 -7
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +258 -130
- package/dist/entry.d.ts +17 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +156 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/host.d.ts +4 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +9 -5
- package/dist/identity.d.ts +2 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +6 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/memory.d.ts +1 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +10 -1
- package/dist/model.d.ts +1 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +9 -0
- package/dist/observation.d.ts +5 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +22 -11
- package/dist/outcome.d.ts +4 -0
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +1 -0
- package/dist/overlay.d.ts +1 -0
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +33 -54
- package/dist/query.d.ts +28 -3
- package/dist/query.d.ts.map +1 -1
- package/dist/reach.js +2 -2
- package/dist/react.d.ts +3 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/read-side.d.ts +13 -6
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +282 -83
- package/dist/recovery.d.ts +25 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +197 -0
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +11 -10
- package/dist/replica.d.ts +1 -1
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +49 -32
- package/dist/rows.d.ts +4 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +2 -12
- package/dist/save.d.ts +6 -2
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +42 -18
- package/dist/search.d.ts +28 -6
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +109 -44
- package/dist/shared.d.ts +11 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +368 -0
- package/dist/store.d.ts +35 -10
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +275 -74
- package/dist/turn.d.ts +7 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +45 -0
- package/package.json +12 -8
package/dist/read-side.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, inArray, or, toArray, } from '@tanstack/db';
|
|
2
|
-
import { ARRAY_INDEX, pointerSegments } from '@agentshouse/mdmodel';
|
|
3
|
-
import { rowsOf } from './observation.js';
|
|
2
|
+
import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
|
|
4
3
|
import { documentKey } from './rows.js';
|
|
4
|
+
import { after, inUnits, ROW_WEIGHT } from './turn.js';
|
|
5
5
|
function every(expressions) {
|
|
6
6
|
const [first, second, ...rest] = expressions;
|
|
7
7
|
return second === undefined ? first : and(first, second, ...rest);
|
|
@@ -101,14 +101,11 @@ function filtered(q, rows, plan) {
|
|
|
101
101
|
}
|
|
102
102
|
return query;
|
|
103
103
|
}
|
|
104
|
-
function
|
|
105
|
-
return
|
|
104
|
+
function bodied(shape) {
|
|
105
|
+
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
106
106
|
}
|
|
107
107
|
function documents(q, rows, plan, shape) {
|
|
108
108
|
let query = filtered(q, rows, plan);
|
|
109
|
-
if (shape.sections !== null || shape.preamble || shape.markdown) {
|
|
110
|
-
query = query.leftJoin({ b: rows }, ({ d, b }) => eq(d.body, b.key));
|
|
111
|
-
}
|
|
112
109
|
for (const { key, options } of shape.orderBy) {
|
|
113
110
|
query = query.orderBy(({ d }) => keyed(key, d), options);
|
|
114
111
|
}
|
|
@@ -117,16 +114,13 @@ function documents(q, rows, plan, shape) {
|
|
|
117
114
|
}
|
|
118
115
|
if (plan.limit !== null)
|
|
119
116
|
query = query.limit(plan.limit);
|
|
120
|
-
return query.select(({ d
|
|
117
|
+
return query.select(({ d }) => ({
|
|
121
118
|
room: d.room,
|
|
122
119
|
path: d.path,
|
|
123
120
|
type: d.type,
|
|
124
121
|
binary: d.binary,
|
|
125
122
|
...(shape.fields === null ? {} : { fields: d.fields }),
|
|
126
|
-
...(shape
|
|
127
|
-
...(Array.isArray(shape.sections) ? named('section', shape.sections, b.sections) : {}),
|
|
128
|
-
...(shape.preamble ? { preamble: b.preamble } : {}),
|
|
129
|
-
...(shape.markdown ? { markdown: b.markdown } : {}),
|
|
123
|
+
...(bodied(shape) ? { text: d.text } : {}),
|
|
130
124
|
...(shape.backlinks ? { backlinks: toArray(backlinked(q, rows, d, plan.reach)) } : {}),
|
|
131
125
|
...(shape.change ? { change: d.change } : {}),
|
|
132
126
|
}));
|
|
@@ -172,13 +166,22 @@ function built(rows, plan) {
|
|
|
172
166
|
},
|
|
173
167
|
};
|
|
174
168
|
}
|
|
175
|
-
function
|
|
176
|
-
return Object.fromEntries(entries.filter(([, value]) => value !== undefined));
|
|
177
|
-
}
|
|
178
|
-
function part(row, prefix, names, whole) {
|
|
169
|
+
function sectionsOf(sections, names) {
|
|
179
170
|
if (names === 'all')
|
|
180
|
-
return
|
|
181
|
-
return
|
|
171
|
+
return sections;
|
|
172
|
+
return Object.fromEntries(names.flatMap((name) => (Object.hasOwn(sections, name) ? [[name, sections[name]]] : [])));
|
|
173
|
+
}
|
|
174
|
+
function bodyOf(row, key, bodies) {
|
|
175
|
+
const text = row.text;
|
|
176
|
+
if (text === null)
|
|
177
|
+
return null;
|
|
178
|
+
const held = bodies?.get(key);
|
|
179
|
+
if (held !== undefined && held.text === text)
|
|
180
|
+
return held.document;
|
|
181
|
+
const read = parse(text, row.path);
|
|
182
|
+
const document = read.ok ? read.document : null;
|
|
183
|
+
bodies?.set(key, { text, document });
|
|
184
|
+
return document;
|
|
182
185
|
}
|
|
183
186
|
function masked(pointers) {
|
|
184
187
|
const root = { whole: false, members: new Map() };
|
|
@@ -229,7 +232,7 @@ function cut(value, mask) {
|
|
|
229
232
|
function selected(fields, pointers) {
|
|
230
233
|
return (cut(fields, masked(pointers)) ?? {});
|
|
231
234
|
}
|
|
232
|
-
function documentOf(row, shape) {
|
|
235
|
+
function documentOf(key, row, shape, bodies) {
|
|
233
236
|
if (row.type === null) {
|
|
234
237
|
const binary = row.binary;
|
|
235
238
|
return {
|
|
@@ -238,6 +241,7 @@ function documentOf(row, shape) {
|
|
|
238
241
|
...(binary === null ? {} : { binaryFile: binary }),
|
|
239
242
|
};
|
|
240
243
|
}
|
|
244
|
+
const body = bodied(shape) ? bodyOf(row, key, bodies) : null;
|
|
241
245
|
return {
|
|
242
246
|
room: row.room,
|
|
243
247
|
path: row.path,
|
|
@@ -249,13 +253,11 @@ function documentOf(row, shape) {
|
|
|
249
253
|
? row.fields
|
|
250
254
|
: selected(row.fields, shape.fields),
|
|
251
255
|
}),
|
|
252
|
-
...(shape.sections === null
|
|
256
|
+
...(shape.sections === null || body === null
|
|
253
257
|
? {}
|
|
254
|
-
: {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
...(shape.preamble ? { preamble: row.preamble } : {}),
|
|
258
|
-
...(shape.markdown ? { markdown: row.markdown } : {}),
|
|
258
|
+
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
259
|
+
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
260
|
+
...(shape.markdown && body !== null ? { markdown: body.content } : {}),
|
|
259
261
|
...(shape.backlinks
|
|
260
262
|
? {
|
|
261
263
|
backlinks: row.backlinks.map(({ path, type, field }) => ({ path, type, field })),
|
|
@@ -264,30 +266,40 @@ function documentOf(row, shape) {
|
|
|
264
266
|
...(shape.change ? { change: row.change } : {}),
|
|
265
267
|
};
|
|
266
268
|
}
|
|
267
|
-
function
|
|
269
|
+
function weight(row) {
|
|
270
|
+
return ROW_WEIGHT + (typeof row?.text === 'string' ? row.text.length : 0);
|
|
271
|
+
}
|
|
272
|
+
function rowsOfPlan(plan, entries, bodies, each = (row) => row) {
|
|
268
273
|
const shape = plan.shape;
|
|
269
274
|
if (shape.kind === 'documents') {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
const documents = [];
|
|
276
|
+
return after(inUnits(entries, ([key, row]) => {
|
|
277
|
+
documents.push(each(documentOf(key, row, shape, bodies)));
|
|
278
|
+
return weight(row);
|
|
279
|
+
}), () => ({ rows: { documents }, keys: entries.map(([key]) => key) }));
|
|
274
280
|
}
|
|
275
281
|
const bounded = shape.orderBy.length === 0 && plan.limit !== null ? entries.slice(0, plan.limit) : entries;
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
};
|
|
282
|
+
const groups = [];
|
|
283
|
+
return after(inUnits(bounded, ([, row]) => {
|
|
284
|
+
groups.push(each({
|
|
285
|
+
key: shape.groupBy.map((_, index) => row[`key${index}`]),
|
|
286
|
+
count: row.count,
|
|
287
|
+
}));
|
|
288
|
+
return ROW_WEIGHT;
|
|
289
|
+
}), () => ({ rows: { groups }, keys: bounded.map(([key]) => key) }));
|
|
285
290
|
}
|
|
286
291
|
export function reconciled(previous, next) {
|
|
287
|
-
const before =
|
|
288
|
-
const
|
|
292
|
+
const before = previous.result;
|
|
293
|
+
const after = next.result;
|
|
294
|
+
if ('pending' in before || 'pending' in after) {
|
|
295
|
+
if (deepEquals(before, after))
|
|
296
|
+
return null;
|
|
297
|
+
return { keyed: next, message: next };
|
|
298
|
+
}
|
|
299
|
+
const earlier = 'groups' in before ? before.groups : before.documents;
|
|
300
|
+
const held = new Map(previous.keys.map((key, index) => [key, earlier[index]]));
|
|
289
301
|
const changed = [];
|
|
290
|
-
const rows =
|
|
302
|
+
const rows = ('groups' in after ? after.groups : after.documents).map((row, index) => {
|
|
291
303
|
const key = next.keys[index];
|
|
292
304
|
const kept = held.get(key);
|
|
293
305
|
if (kept !== undefined && deepEquals(kept, row))
|
|
@@ -296,22 +308,37 @@ export function reconciled(previous, next) {
|
|
|
296
308
|
return row;
|
|
297
309
|
});
|
|
298
310
|
const present = new Set(next.keys);
|
|
299
|
-
const survivors = previous.keys.filter((key) => present.has(key));
|
|
300
311
|
const removed = previous.keys.filter((key) => !present.has(key));
|
|
301
|
-
const moved =
|
|
302
|
-
|
|
303
|
-
if (changed.length === 0 && removed.length === 0 && !moved)
|
|
312
|
+
const moved = reordered(previous.keys, next.keys, present);
|
|
313
|
+
const covered = !deepEquals(before.coverage, after.coverage);
|
|
314
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered)
|
|
304
315
|
return null;
|
|
305
316
|
return {
|
|
306
317
|
keyed: {
|
|
307
|
-
result: 'groups' in
|
|
308
|
-
? { groups: rows }
|
|
309
|
-
: { documents: rows },
|
|
318
|
+
result: 'groups' in after
|
|
319
|
+
? { groups: rows, coverage: after.coverage }
|
|
320
|
+
: { documents: rows, coverage: after.coverage },
|
|
310
321
|
keys: next.keys,
|
|
311
322
|
},
|
|
312
|
-
|
|
323
|
+
message: {
|
|
324
|
+
changed,
|
|
325
|
+
removed,
|
|
326
|
+
order: moved ? next.keys : null,
|
|
327
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
328
|
+
},
|
|
313
329
|
};
|
|
314
330
|
}
|
|
331
|
+
function reordered(previous, next, present) {
|
|
332
|
+
let at = 0;
|
|
333
|
+
for (const key of previous) {
|
|
334
|
+
if (!present.has(key))
|
|
335
|
+
continue;
|
|
336
|
+
if (next[at] !== key)
|
|
337
|
+
return true;
|
|
338
|
+
at += 1;
|
|
339
|
+
}
|
|
340
|
+
return at !== next.length;
|
|
341
|
+
}
|
|
315
342
|
export function createReadSide() {
|
|
316
343
|
configureIndexDevMode({ enabled: false });
|
|
317
344
|
let sync;
|
|
@@ -331,23 +358,139 @@ export function createReadSide() {
|
|
|
331
358
|
});
|
|
332
359
|
const shared = new Map();
|
|
333
360
|
function searched(row) {
|
|
334
|
-
const
|
|
361
|
+
const read = row.text === null ? null : parse(row.text, row.path);
|
|
362
|
+
if (read === null || !read.ok)
|
|
363
|
+
return null;
|
|
335
364
|
return {
|
|
336
365
|
room: row.room,
|
|
337
366
|
path: row.path,
|
|
338
367
|
type: row.type,
|
|
339
368
|
fields: row.fields,
|
|
340
|
-
preamble:
|
|
341
|
-
sections:
|
|
369
|
+
preamble: read.document.preamble,
|
|
370
|
+
sections: read.document.sections,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function kept(entry, key, row) {
|
|
374
|
+
const held = entry.rows.get(key);
|
|
375
|
+
if (held !== undefined && deepEquals(held, row))
|
|
376
|
+
return;
|
|
377
|
+
entry.rows.set(key, row);
|
|
378
|
+
}
|
|
379
|
+
function rebuilt(entry) {
|
|
380
|
+
return after(rowsOfPlan(entry.plan, [...entry.built.live.entries()], entry.bodies), ({ rows: found, keys }) => {
|
|
381
|
+
const present = new Set(keys);
|
|
382
|
+
for (const key of [...entry.rows.keys()])
|
|
383
|
+
if (!present.has(key))
|
|
384
|
+
entry.rows.delete(key);
|
|
385
|
+
for (const key of [...entry.bodies.keys()]) {
|
|
386
|
+
if (!present.has(key))
|
|
387
|
+
entry.bodies.delete(key);
|
|
388
|
+
}
|
|
389
|
+
keys.forEach((key, index) => kept(entry, key, rowAt(found, index)));
|
|
390
|
+
return keys;
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
function refreshed(entry, dirty) {
|
|
394
|
+
const shape = entry.plan.shape;
|
|
395
|
+
const keys = [...entry.built.live.keys()];
|
|
396
|
+
const named = new Set([...dirty, ...keys.filter((key) => !entry.rows.has(key))]);
|
|
397
|
+
const values = [...named].map((key) => [key, entry.built.live.get(key)]);
|
|
398
|
+
const done = inUnits(values, ([key, value]) => {
|
|
399
|
+
if (value !== undefined)
|
|
400
|
+
kept(entry, key, documentOf(key, value, shape, entry.bodies));
|
|
401
|
+
else {
|
|
402
|
+
entry.rows.delete(key);
|
|
403
|
+
entry.bodies.delete(key);
|
|
404
|
+
}
|
|
405
|
+
return weight(value);
|
|
406
|
+
});
|
|
407
|
+
return after(done, () => keys);
|
|
408
|
+
}
|
|
409
|
+
function current(entry, keys, coverage) {
|
|
410
|
+
const found = keys.map((key) => entry.rows.get(key));
|
|
411
|
+
return {
|
|
412
|
+
result: entry.plan.shape.kind === 'groups'
|
|
413
|
+
? { groups: found, coverage }
|
|
414
|
+
: { documents: found, coverage },
|
|
415
|
+
keys,
|
|
342
416
|
};
|
|
343
417
|
}
|
|
344
|
-
function
|
|
345
|
-
const
|
|
346
|
-
|
|
418
|
+
function delivered(entry, next) {
|
|
419
|
+
const previous = entry.result;
|
|
420
|
+
entry.result = next;
|
|
421
|
+
const message = previous === undefined ? null : changedFrom(previous, next);
|
|
422
|
+
if (message !== null)
|
|
423
|
+
for (const listener of entry.ready)
|
|
424
|
+
listener(message);
|
|
425
|
+
for (const listener of entry.fresh) {
|
|
426
|
+
entry.ready.add(listener);
|
|
427
|
+
listener(next);
|
|
428
|
+
}
|
|
429
|
+
entry.fresh.clear();
|
|
430
|
+
}
|
|
431
|
+
function run(entry) {
|
|
432
|
+
while (entry.listeners.size > 0 &&
|
|
433
|
+
(entry.whole || entry.dirty.size > 0 || entry.stale || entry.fresh.size > 0)) {
|
|
434
|
+
entry.stale = false;
|
|
435
|
+
const cover = entry.cover();
|
|
436
|
+
if ('pending' in cover) {
|
|
437
|
+
entry.whole = true;
|
|
438
|
+
entry.dirty.clear();
|
|
439
|
+
entry.rows.clear();
|
|
440
|
+
entry.bodies.clear();
|
|
441
|
+
delivered(entry, { result: cover, keys: [] });
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
const whole = entry.whole ||
|
|
445
|
+
entry.plan.shape.kind === 'groups' ||
|
|
446
|
+
entry.result === undefined ||
|
|
447
|
+
'pending' in entry.result.result;
|
|
448
|
+
const dirty = entry.dirty;
|
|
449
|
+
entry.dirty = new Set();
|
|
450
|
+
entry.whole = false;
|
|
451
|
+
const keys = whole ? rebuilt(entry) : refreshed(entry, dirty);
|
|
452
|
+
if (keys instanceof Promise) {
|
|
453
|
+
void keys
|
|
454
|
+
.then((found) => {
|
|
455
|
+
if (entry.listeners.size > 0)
|
|
456
|
+
delivered(entry, current(entry, found, cover.coverage));
|
|
457
|
+
run(entry);
|
|
458
|
+
})
|
|
459
|
+
.catch(() => {
|
|
460
|
+
entry.running = false;
|
|
461
|
+
});
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
delivered(entry, current(entry, keys, cover.coverage));
|
|
465
|
+
}
|
|
466
|
+
entry.running = false;
|
|
467
|
+
}
|
|
468
|
+
function wake(entry) {
|
|
469
|
+
if (entry.subscription === null || entry.running)
|
|
347
470
|
return;
|
|
348
|
-
entry.
|
|
349
|
-
|
|
350
|
-
|
|
471
|
+
entry.running = true;
|
|
472
|
+
try {
|
|
473
|
+
run(entry);
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
entry.running = false;
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function recover() {
|
|
481
|
+
for (const entry of shared.values()) {
|
|
482
|
+
if (entry.result === undefined || entry.subscription === null)
|
|
483
|
+
continue;
|
|
484
|
+
const cover = entry.cover();
|
|
485
|
+
const before = entry.result.result;
|
|
486
|
+
const held = 'pending' in cover
|
|
487
|
+
? deepEquals(before, cover)
|
|
488
|
+
: 'coverage' in before && deepEquals(before.coverage, cover.coverage);
|
|
489
|
+
if (held)
|
|
490
|
+
continue;
|
|
491
|
+
entry.stale = true;
|
|
492
|
+
wake(entry);
|
|
493
|
+
}
|
|
351
494
|
}
|
|
352
495
|
return {
|
|
353
496
|
apply(set) {
|
|
@@ -358,38 +501,52 @@ export function createReadSide() {
|
|
|
358
501
|
for (const row of change.after)
|
|
359
502
|
next.set(row.key, row);
|
|
360
503
|
}
|
|
361
|
-
if (next.size
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
504
|
+
if (next.size > 0) {
|
|
505
|
+
sync.begin();
|
|
506
|
+
for (const [key, row] of next) {
|
|
507
|
+
if (row === null) {
|
|
508
|
+
if (rows.has(key))
|
|
509
|
+
sync.write({ type: 'delete', key });
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
sync.write({ type: rows.has(key) ? 'update' : 'insert', value: row });
|
|
369
513
|
}
|
|
370
|
-
sync.
|
|
514
|
+
sync.commit();
|
|
371
515
|
}
|
|
372
|
-
|
|
516
|
+
recover();
|
|
373
517
|
},
|
|
374
|
-
|
|
518
|
+
recover,
|
|
519
|
+
async query(plan, at) {
|
|
375
520
|
const { live: collection, dispose } = built(rows, plan);
|
|
376
521
|
try {
|
|
377
522
|
await collection.toArrayWhenReady();
|
|
378
|
-
|
|
523
|
+
const entries = [...collection.entries()];
|
|
524
|
+
const held = at();
|
|
525
|
+
const found = await rowsOfPlan(plan, entries, null, structuredClone);
|
|
526
|
+
return { rows: found.rows, at: held };
|
|
379
527
|
}
|
|
380
528
|
finally {
|
|
381
529
|
dispose();
|
|
382
530
|
}
|
|
383
531
|
},
|
|
384
|
-
watch(plan, identity,
|
|
532
|
+
watch(plan, identity, cover, deliverTo) {
|
|
385
533
|
let entry = shared.get(identity);
|
|
386
534
|
if (entry === undefined) {
|
|
387
535
|
const created = {
|
|
388
536
|
plan,
|
|
389
537
|
built: built(rows, plan),
|
|
538
|
+
cover,
|
|
390
539
|
result: undefined,
|
|
540
|
+
rows: new Map(),
|
|
541
|
+
bodies: new Map(),
|
|
391
542
|
listeners: new Set(),
|
|
543
|
+
fresh: new Set(),
|
|
544
|
+
ready: new Set(),
|
|
392
545
|
subscription: null,
|
|
546
|
+
whole: true,
|
|
547
|
+
dirty: new Set(),
|
|
548
|
+
stale: false,
|
|
549
|
+
running: false,
|
|
393
550
|
};
|
|
394
551
|
created.built.live.on('status:change', ({ status }) => {
|
|
395
552
|
if (status === 'cleaned-up' && created.listeners.size === 0)
|
|
@@ -399,29 +556,40 @@ export function createReadSide() {
|
|
|
399
556
|
entry = created;
|
|
400
557
|
}
|
|
401
558
|
const observed = entry;
|
|
402
|
-
observed.listeners.add(
|
|
559
|
+
observed.listeners.add(deliverTo);
|
|
403
560
|
observed.built.live.onFirstReady(() => {
|
|
404
|
-
if (!observed.listeners.has(
|
|
561
|
+
if (!observed.listeners.has(deliverTo))
|
|
405
562
|
return;
|
|
563
|
+
observed.fresh.add(deliverTo);
|
|
406
564
|
if (observed.subscription === null) {
|
|
407
|
-
observed.
|
|
408
|
-
observed.subscription = observed.built.live.subscribeChanges(() => {
|
|
409
|
-
|
|
565
|
+
observed.whole = true;
|
|
566
|
+
observed.subscription = observed.built.live.subscribeChanges((changes) => {
|
|
567
|
+
if (observed.plan.shape.kind === 'groups')
|
|
568
|
+
observed.whole = true;
|
|
569
|
+
else
|
|
570
|
+
for (const { key } of changes)
|
|
571
|
+
observed.dirty.add(key);
|
|
572
|
+
wake(observed);
|
|
410
573
|
}, { includeInitialState: false });
|
|
411
574
|
}
|
|
412
|
-
|
|
575
|
+
wake(observed);
|
|
413
576
|
});
|
|
414
577
|
return () => {
|
|
415
|
-
|
|
578
|
+
observed.fresh.delete(deliverTo);
|
|
579
|
+
observed.ready.delete(deliverTo);
|
|
580
|
+
if (!observed.listeners.delete(deliverTo) || observed.listeners.size > 0)
|
|
416
581
|
return;
|
|
417
582
|
observed.subscription?.unsubscribe();
|
|
418
583
|
observed.subscription = null;
|
|
584
|
+
observed.result = undefined;
|
|
585
|
+
observed.rows.clear();
|
|
586
|
+
observed.bodies.clear();
|
|
587
|
+
observed.dirty.clear();
|
|
588
|
+
observed.whole = true;
|
|
419
589
|
};
|
|
420
590
|
},
|
|
421
|
-
|
|
422
|
-
return rows.toArray
|
|
423
|
-
.filter((row) => row.kind === 'document' && row.type !== null)
|
|
424
|
-
.map(searched);
|
|
591
|
+
room(room) {
|
|
592
|
+
return rows.toArray.flatMap((row) => row.kind !== 'document' || row.type === null || row.room !== room ? [] : [row.path]);
|
|
425
593
|
},
|
|
426
594
|
document(room, path) {
|
|
427
595
|
const row = rows.get(documentKey(room, path));
|
|
@@ -430,6 +598,8 @@ export function createReadSide() {
|
|
|
430
598
|
close() {
|
|
431
599
|
for (const entry of shared.values()) {
|
|
432
600
|
entry.listeners.clear();
|
|
601
|
+
entry.fresh.clear();
|
|
602
|
+
entry.ready.clear();
|
|
433
603
|
entry.subscription?.unsubscribe();
|
|
434
604
|
entry.built.dispose();
|
|
435
605
|
}
|
|
@@ -438,3 +608,32 @@ export function createReadSide() {
|
|
|
438
608
|
},
|
|
439
609
|
};
|
|
440
610
|
}
|
|
611
|
+
function changedFrom(previous, next) {
|
|
612
|
+
const before = previous.result;
|
|
613
|
+
const after = next.result;
|
|
614
|
+
if ('pending' in before || 'pending' in after)
|
|
615
|
+
return deepEquals(before, after) ? null : next;
|
|
616
|
+
const earlier = 'groups' in before ? before.groups : before.documents;
|
|
617
|
+
const held = new Map(previous.keys.map((key, index) => [key, earlier[index]]));
|
|
618
|
+
const later = 'groups' in after ? after.groups : after.documents;
|
|
619
|
+
const changed = [];
|
|
620
|
+
next.keys.forEach((key, index) => {
|
|
621
|
+
if (held.get(key) !== later[index])
|
|
622
|
+
changed.push([key, later[index]]);
|
|
623
|
+
});
|
|
624
|
+
const present = new Set(next.keys);
|
|
625
|
+
const removed = previous.keys.filter((key) => !present.has(key));
|
|
626
|
+
const moved = reordered(previous.keys, next.keys, present);
|
|
627
|
+
const covered = !deepEquals(before.coverage, after.coverage);
|
|
628
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered)
|
|
629
|
+
return null;
|
|
630
|
+
return {
|
|
631
|
+
changed,
|
|
632
|
+
removed,
|
|
633
|
+
order: moved ? next.keys : null,
|
|
634
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function rowAt(rows, index) {
|
|
638
|
+
return 'groups' in rows ? rows.groups[index] : rows.documents[index];
|
|
639
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type Attempted = 'done' | 'again' | {
|
|
2
|
+
readonly at: number;
|
|
3
|
+
};
|
|
4
|
+
export type Attempt = (signal: AbortSignal) => Promise<Attempted>;
|
|
5
|
+
export type AttemptOptions = {
|
|
6
|
+
readonly at?: number;
|
|
7
|
+
readonly again?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type RecoveryOptions = {
|
|
10
|
+
readonly firstMs?: number;
|
|
11
|
+
readonly ceilingMs?: number;
|
|
12
|
+
readonly attempts?: number;
|
|
13
|
+
readonly inFlight?: number;
|
|
14
|
+
};
|
|
15
|
+
export type Recovery = {
|
|
16
|
+
attempt(intent: string, run: Attempt, options?: AttemptOptions): Promise<boolean>;
|
|
17
|
+
pending(intent: string): boolean;
|
|
18
|
+
resume(): void;
|
|
19
|
+
cancel(intent: string): void;
|
|
20
|
+
scope(inFlight?: number): Recovery;
|
|
21
|
+
retire(): Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
export declare function retryDelay(tries: number, firstMs?: number, ceilingMs?: number): number;
|
|
24
|
+
export declare function createRecovery(options?: RecoveryOptions): Recovery;
|
|
25
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB,CAAC;AAsBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,SAAW,EAAE,SAAS,SAAa,GAAG,MAAM,CAG5F;AAuKD,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAStE"}
|