@agentshouse/mdruntime 0.1.0-c5d3fa6fb67bc → 0.1.0-c6636685d0c4b
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 +205 -205
- package/README.md +8 -8
- package/dist/adapter.d.ts +36 -9
- package/dist/adapter.d.ts.map +1 -1
- package/dist/bootstrap.d.ts +2 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +1 -1
- package/dist/change-set.d.ts +5 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -1
- package/dist/chat/earlier.d.ts +10 -0
- package/dist/chat/earlier.d.ts.map +1 -0
- package/dist/chat/earlier.js +54 -0
- package/dist/chat/kit.js +1 -1
- package/dist/chat/list.d.ts +2 -1
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +45 -5
- package/dist/chat/markdown.d.ts +1 -0
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +78 -20
- package/dist/chat/message.d.ts +1 -0
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +12 -2
- package/dist/chat/messages.d.ts +2 -0
- package/dist/chat/messages.d.ts.map +1 -1
- package/dist/chat/messages.js +52 -20
- package/dist/chat/overlay.d.ts +1 -0
- package/dist/chat/overlay.d.ts.map +1 -1
- package/dist/chat/overlay.js +10 -0
- package/dist/chat/pieces.d.ts +62 -0
- package/dist/chat/pieces.d.ts.map +1 -0
- package/dist/chat/pieces.js +1110 -0
- package/dist/chat/provider.d.ts +1 -0
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +23 -14
- package/dist/chat/question.d.ts +1 -1
- package/dist/chat/question.d.ts.map +1 -1
- package/dist/chat/question.js +7 -18
- package/dist/chat/ready.d.ts +1 -1
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +1 -1
- package/dist/chat/steady.d.ts +3 -0
- package/dist/chat/steady.d.ts.map +1 -0
- package/dist/chat/steady.js +26 -0
- package/dist/chat/style.js +1 -1
- package/dist/chat.d.ts +2 -2
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/client.d.ts +6 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +16 -5
- package/dist/collection.d.ts +4 -3
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +128 -63
- package/dist/connection.d.ts +12 -4
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +577 -217
- package/dist/conversation.d.ts +12 -0
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +196 -41
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +2 -1
- package/dist/host.d.ts +5 -4
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +45 -5
- 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 +6 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +17 -11
- package/dist/observation.d.ts +2 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +9 -3
- package/dist/outcome.d.ts +14 -2
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +7 -0
- package/dist/overlay.d.ts +4 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +181 -140
- package/dist/query.d.ts +26 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +34 -6
- package/dist/reach.d.ts +4 -1
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +90 -21
- package/dist/react.d.ts +3 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/read-side.d.ts +3 -1
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +271 -82
- package/dist/reads.d.ts +11 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +98 -0
- package/dist/refresh.d.ts +4 -4
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +35 -16
- package/dist/replica.d.ts +11 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +69 -40
- package/dist/room-draft.d.ts +12 -0
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +11 -12
- package/dist/rows.d.ts +1 -0
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +5 -0
- package/dist/save.d.ts +4 -2
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +195 -111
- package/dist/search.d.ts +17 -2
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +81 -7
- package/dist/seed.d.ts +16 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +75 -0
- package/dist/shared.d.ts +4 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +63 -33
- package/dist/store.d.ts +18 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +398 -316
- package/dist/turn.d.ts +5 -3
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +26 -7
- package/dist/windows.d.ts +26 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +93 -0
- package/dist/wire.d.ts +16 -0
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +4 -0
- package/package.json +2 -2
- package/dist/acquisition.d.ts +0 -14
- package/dist/acquisition.d.ts.map +0 -1
- package/dist/acquisition.js +0 -24
package/dist/read-side.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, inArray, or, toArray, } from '@tanstack/db';
|
|
1
|
+
import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, inArray, not, or, toArray, } from '@tanstack/db';
|
|
2
2
|
import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
|
|
3
3
|
import { documentKey } from './rows.js';
|
|
4
4
|
import { after, inUnits, ROW_WEIGHT } from './turn.js';
|
|
5
|
+
import { beyond, cursorOf, MAXIMUM_WINDOW, windowed } from './windows.js';
|
|
5
6
|
function every(expressions) {
|
|
6
7
|
const [first, second, ...rest] = expressions;
|
|
7
8
|
return second === undefined ? first : and(first, second, ...rest);
|
|
@@ -54,16 +55,21 @@ function conditioned(condition, row) {
|
|
|
54
55
|
function narrowed(query, where, alias) {
|
|
55
56
|
return where === null ? query : query.fn.where((row) => conditioned(where, row[alias]));
|
|
56
57
|
}
|
|
57
|
-
function reached(reach, room, document) {
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
function reached(reach, locked, room, document) {
|
|
59
|
+
return [
|
|
60
|
+
...(reach === null
|
|
61
|
+
? []
|
|
62
|
+
: [or(inArray(room, [...reach.rooms]), inArray(document, [...reach.documents]))]),
|
|
63
|
+
...(locked.length === 0 ? [] : [not(inArray(room, [...locked]))]),
|
|
64
|
+
];
|
|
61
65
|
}
|
|
62
|
-
function scoped(scope, row, rooms, reach) {
|
|
66
|
+
function scoped(scope, row, rooms, reach, locked, paths = null, folder = null) {
|
|
63
67
|
return every([
|
|
64
68
|
eq(row.kind, 'document'),
|
|
65
69
|
...(rooms === null ? [] : [inArray(row.room, [...rooms])]),
|
|
66
|
-
...
|
|
70
|
+
...(paths === null ? [] : [inArray(row.path, [...paths])]),
|
|
71
|
+
...(folder === null ? [] : [eq(row.folder, folder)]),
|
|
72
|
+
...reached(reach, locked, row.room, row.key),
|
|
67
73
|
...(scope.types === null ? [] : [inArray(row.type, [...scope.types])]),
|
|
68
74
|
...(scope.pathPrefix === null ? [] : [inArray(scope.pathPrefix, row.directories)]),
|
|
69
75
|
]);
|
|
@@ -77,26 +83,26 @@ function live(query) {
|
|
|
77
83
|
startSync: true,
|
|
78
84
|
});
|
|
79
85
|
}
|
|
80
|
-
function related(q, rows, scope, field, kept, reach) {
|
|
86
|
+
function related(q, rows, scope, field, kept, reach, locked) {
|
|
81
87
|
const matched = kept === 'source' ? 'target' : 'source';
|
|
82
88
|
const from = q.from({ r: rows });
|
|
83
89
|
const joined = from
|
|
84
90
|
.where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [inArray(field, r.forms)])]))
|
|
85
91
|
.innerJoin({ m: rows }, ({ r, m }) => eq(r[matched], m.key))
|
|
86
|
-
.where(({ m }) => scoped(scope, m, null, reach));
|
|
92
|
+
.where(({ m }) => scoped(scope, m, null, reach, locked));
|
|
87
93
|
return narrowed(joined, scope.where, 'm')
|
|
88
94
|
.groupBy(({ r }) => r[kept])
|
|
89
95
|
.select(({ r }) => ({ key: r[kept] }));
|
|
90
96
|
}
|
|
91
97
|
function filtered(q, rows, plan) {
|
|
92
98
|
const from = q.from({ d: rows });
|
|
93
|
-
let query = narrowed(from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach)), plan.root.where, 'd');
|
|
99
|
+
let query = narrowed(from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach, plan.locked, plan.paths, plan.folder)), plan.root.where, 'd');
|
|
94
100
|
if (plan.hop !== null) {
|
|
95
|
-
const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach);
|
|
101
|
+
const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach, plan.locked);
|
|
96
102
|
query = query.innerJoin({ h: hop }, ({ d, h }) => eq(d.key, h.key));
|
|
97
103
|
}
|
|
98
104
|
if (plan.backlinks !== null) {
|
|
99
|
-
const backlinks = related(q, rows, plan.backlinks, plan.backlinks.field, 'target', plan.reach);
|
|
105
|
+
const backlinks = related(q, rows, plan.backlinks, plan.backlinks.field, 'target', plan.reach, plan.locked);
|
|
100
106
|
query = query.innerJoin({ k: backlinks }, ({ d, k }) => eq(d.key, k.key));
|
|
101
107
|
}
|
|
102
108
|
return query;
|
|
@@ -104,45 +110,75 @@ function filtered(q, rows, plan) {
|
|
|
104
110
|
function bodied(shape) {
|
|
105
111
|
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
106
112
|
}
|
|
113
|
+
function orderingOf(options) {
|
|
114
|
+
const collated = options.stringSort === 'locale';
|
|
115
|
+
return {
|
|
116
|
+
direction: options.direction,
|
|
117
|
+
nulls: options.nulls ?? 'first',
|
|
118
|
+
stringSort: options.stringSort ?? 'locale',
|
|
119
|
+
locale: collated ? options.locale : undefined,
|
|
120
|
+
localeOptions: collated
|
|
121
|
+
? options.localeOptions
|
|
122
|
+
: undefined,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function storedValue(key, row) {
|
|
126
|
+
return key.of === 'field' ? row.values[key.field] : row[key.of];
|
|
127
|
+
}
|
|
128
|
+
function documentOrder(shape) {
|
|
129
|
+
return shape.orderBy.map(({ options }) => orderingOf(options));
|
|
130
|
+
}
|
|
131
|
+
function documentCursor(shape, row) {
|
|
132
|
+
return cursorOf([
|
|
133
|
+
...shape.orderBy.map((_, index) => row[`order${index}`]),
|
|
134
|
+
documentKey(row.room, row.path),
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
107
137
|
function documents(q, rows, plan, shape) {
|
|
108
138
|
let query = filtered(q, rows, plan);
|
|
139
|
+
const cursor = plan.after?.values ?? null;
|
|
140
|
+
if (cursor !== null) {
|
|
141
|
+
const orderings = documentOrder(shape);
|
|
142
|
+
query = query.fn.where(({ d }) => beyond([...shape.orderBy.map(({ key }) => storedValue(key, d)), d.key], cursor, orderings));
|
|
143
|
+
}
|
|
109
144
|
for (const { key, options } of shape.orderBy) {
|
|
110
145
|
query = query.orderBy(({ d }) => keyed(key, d), options);
|
|
111
146
|
}
|
|
112
|
-
if (shape.orderBy.length
|
|
147
|
+
if (shape.orderBy.length > 0 || plan.limit !== null || plan.after !== null) {
|
|
113
148
|
query = query.orderBy(({ d }) => d.key, { stringSort: 'lexical' });
|
|
114
149
|
}
|
|
115
150
|
if (plan.limit !== null)
|
|
116
|
-
query = query.limit(plan.limit);
|
|
151
|
+
query = query.limit(plan.limit + 1);
|
|
117
152
|
return query.select(({ d }) => ({
|
|
118
153
|
room: d.room,
|
|
119
154
|
path: d.path,
|
|
120
155
|
type: d.type,
|
|
121
156
|
binary: d.binary,
|
|
157
|
+
...Object.fromEntries(shape.orderBy.map(({ key }, index) => [`order${index}`, keyed(key, d)])),
|
|
122
158
|
...(shape.fields === null ? {} : { fields: d.fields }),
|
|
123
159
|
...(bodied(shape) ? { text: d.text } : {}),
|
|
124
|
-
...(shape.backlinks
|
|
160
|
+
...(shape.backlinks
|
|
161
|
+
? { backlinks: toArray(backlinked(q, rows, d, plan.reach, plan.locked)) }
|
|
162
|
+
: {}),
|
|
125
163
|
...(shape.change ? { change: d.change } : {}),
|
|
126
164
|
...(shape.fileId ? { fileId: d.fileId } : {}),
|
|
127
165
|
}));
|
|
128
166
|
}
|
|
129
|
-
function backlinked(q, rows, document, reach) {
|
|
167
|
+
function backlinked(q, rows, document, reach, locked) {
|
|
130
168
|
const from = q.from({ r: rows });
|
|
131
169
|
return from
|
|
132
|
-
.where(({ r }) => every([eq(r.target, document.key), ...reached(reach, r.room, r.source)]))
|
|
170
|
+
.where(({ r }) => every([eq(r.target, document.key), ...reached(reach, locked, r.room, r.source)]))
|
|
133
171
|
.orderBy(({ r }) => r.path, { stringSort: 'lexical' })
|
|
134
172
|
.orderBy(({ r }) => r.field, { stringSort: 'lexical' })
|
|
135
173
|
.select(({ r }) => ({ path: r.path, type: r.type, field: r.field }));
|
|
136
174
|
}
|
|
137
|
-
function groups(q, stage, shape
|
|
175
|
+
function groups(q, stage, shape) {
|
|
138
176
|
const keys = shape.groupBy.map((_, index) => `key${index}`);
|
|
139
177
|
const from = q.from({ m: stage });
|
|
140
178
|
let query = from.groupBy(({ m }) => keys.map((key) => m[key]));
|
|
141
179
|
for (const { by, direction } of shape.orderBy) {
|
|
142
180
|
query = query.orderBy(({ m }) => (by === 'count' ? count(m.key) : m[keys[by]]), direction);
|
|
143
181
|
}
|
|
144
|
-
if (limit !== null && shape.orderBy.length > 0)
|
|
145
|
-
query = query.limit(limit);
|
|
146
182
|
return query.select(({ m }) => ({
|
|
147
183
|
...Object.fromEntries(keys.map((key) => [key, m[key]])),
|
|
148
184
|
count: count(m.key),
|
|
@@ -152,15 +188,27 @@ function built(rows, plan) {
|
|
|
152
188
|
const shape = plan.shape;
|
|
153
189
|
if (shape.kind === 'documents') {
|
|
154
190
|
const collection = live((q) => documents(q, rows, plan, shape));
|
|
155
|
-
|
|
191
|
+
const count = plan.total
|
|
192
|
+
? live((q) => filtered(q, rows, plan).select(({ d }) => ({ key: d.key })))
|
|
193
|
+
: null;
|
|
194
|
+
return {
|
|
195
|
+
live: collection,
|
|
196
|
+
count,
|
|
197
|
+
dispose: () => {
|
|
198
|
+
void collection.cleanup();
|
|
199
|
+
if (count !== null)
|
|
200
|
+
void count.cleanup();
|
|
201
|
+
},
|
|
202
|
+
};
|
|
156
203
|
}
|
|
157
204
|
const stage = live((q) => filtered(q, rows, plan).select(({ d }) => ({
|
|
158
205
|
key: d.key,
|
|
159
206
|
...Object.fromEntries(shape.groupBy.map((key, index) => [`key${index}`, keyed(key, d)])),
|
|
160
207
|
})));
|
|
161
|
-
const collection = live((q) => groups(q, stage, shape
|
|
208
|
+
const collection = live((q) => groups(q, stage, shape));
|
|
162
209
|
return {
|
|
163
210
|
live: collection,
|
|
211
|
+
count: null,
|
|
164
212
|
dispose: () => {
|
|
165
213
|
void collection.cleanup();
|
|
166
214
|
void stage.cleanup();
|
|
@@ -262,7 +310,9 @@ function documentOf(key, row, shape, bodies) {
|
|
|
262
310
|
...(shape.markdown && body !== null ? { markdown: body.content } : {}),
|
|
263
311
|
...(shape.backlinks
|
|
264
312
|
? {
|
|
265
|
-
backlinks: row.backlinks
|
|
313
|
+
backlinks: row.backlinks
|
|
314
|
+
.slice(0, MAXIMUM_WINDOW)
|
|
315
|
+
.map(({ path, type, field }) => ({ path, type, field })),
|
|
266
316
|
}
|
|
267
317
|
: {}),
|
|
268
318
|
...(shape.change ? { change: row.change } : {}),
|
|
@@ -272,24 +322,63 @@ function documentOf(key, row, shape, bodies) {
|
|
|
272
322
|
function weight(row) {
|
|
273
323
|
return ROW_WEIGHT + (typeof row?.text === 'string' ? row.text.length : 0);
|
|
274
324
|
}
|
|
275
|
-
|
|
325
|
+
const HELD_PER_WEIGHT = 16;
|
|
326
|
+
function groupValues(shape, [key, row]) {
|
|
327
|
+
return [...shape.orderBy.map(({ by }) => (by === 'count' ? row.count : row[`key${by}`])), key];
|
|
328
|
+
}
|
|
329
|
+
function groupOrder(shape) {
|
|
330
|
+
return shape.orderBy.map(({ direction }) => ({
|
|
331
|
+
direction,
|
|
332
|
+
nulls: 'first',
|
|
333
|
+
stringSort: 'locale',
|
|
334
|
+
locale: undefined,
|
|
335
|
+
localeOptions: undefined,
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
function windowOf(plan, entries, total) {
|
|
339
|
+
const shape = plan.shape;
|
|
340
|
+
if (shape.kind === 'documents') {
|
|
341
|
+
return {
|
|
342
|
+
...windowed(entries, plan.limit, plan.after?.text, ([, row]) => documentCursor(shape, row)),
|
|
343
|
+
total,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
const cursor = plan.after?.values ?? null;
|
|
347
|
+
const orderings = groupOrder(shape);
|
|
348
|
+
const following = cursor === null
|
|
349
|
+
? entries
|
|
350
|
+
: entries.filter((entry) => beyond(groupValues(shape, entry), cursor, orderings));
|
|
351
|
+
return {
|
|
352
|
+
...windowed(following, plan.limit, plan.after?.text, (entry) => cursorOf(groupValues(shape, entry))),
|
|
353
|
+
total: plan.total ? entries.length : undefined,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function framed(rows, { next, total }) {
|
|
357
|
+
return {
|
|
358
|
+
...rows,
|
|
359
|
+
...(next === undefined ? {} : { next }),
|
|
360
|
+
...(total === undefined ? {} : { total }),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function rowsOfPlan(plan, entries, total, bodies, each = (row) => row) {
|
|
276
364
|
const shape = plan.shape;
|
|
365
|
+
const { kept, ...window } = windowOf(plan, entries, total);
|
|
366
|
+
const keys = kept.map(([key]) => key);
|
|
277
367
|
if (shape.kind === 'documents') {
|
|
278
368
|
const documents = [];
|
|
279
|
-
return after(inUnits(
|
|
369
|
+
return after(inUnits(kept, ([key, row]) => {
|
|
280
370
|
documents.push(each(documentOf(key, row, shape, bodies)));
|
|
281
371
|
return weight(row);
|
|
282
|
-
}), () => ({ rows: { documents },
|
|
372
|
+
}), () => ({ rows: framed({ documents }, window), keys, ...window }));
|
|
283
373
|
}
|
|
284
|
-
const bounded = shape.orderBy.length === 0 && plan.limit !== null ? entries.slice(0, plan.limit) : entries;
|
|
285
374
|
const groups = [];
|
|
286
|
-
return after(inUnits(
|
|
375
|
+
return after(inUnits(kept, ([, row]) => {
|
|
287
376
|
groups.push(each({
|
|
288
377
|
key: shape.groupBy.map((_, index) => row[`key${index}`]),
|
|
289
378
|
count: row.count,
|
|
290
379
|
}));
|
|
291
380
|
return ROW_WEIGHT;
|
|
292
|
-
}), () => ({ rows: { groups },
|
|
381
|
+
}), () => ({ rows: framed({ groups }, window), keys, ...window }));
|
|
293
382
|
}
|
|
294
383
|
export function reconciled(previous, next) {
|
|
295
384
|
const before = previous.result;
|
|
@@ -314,13 +403,16 @@ export function reconciled(previous, next) {
|
|
|
314
403
|
const removed = previous.keys.filter((key) => !present.has(key));
|
|
315
404
|
const moved = reordered(previous.keys, next.keys, present);
|
|
316
405
|
const covered = !deepEquals(before.coverage, after.coverage);
|
|
317
|
-
|
|
406
|
+
const framing = reframed(before, after);
|
|
407
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered && framing === null) {
|
|
318
408
|
return null;
|
|
409
|
+
}
|
|
410
|
+
const window = { next: after.next, total: after.total };
|
|
319
411
|
return {
|
|
320
412
|
keyed: {
|
|
321
413
|
result: 'groups' in after
|
|
322
|
-
? { groups: rows, coverage: after.coverage }
|
|
323
|
-
: { documents: rows, coverage: after.coverage },
|
|
414
|
+
? framed({ groups: rows, coverage: after.coverage }, window)
|
|
415
|
+
: framed({ documents: rows, coverage: after.coverage }, window),
|
|
324
416
|
keys: next.keys,
|
|
325
417
|
},
|
|
326
418
|
message: {
|
|
@@ -328,9 +420,17 @@ export function reconciled(previous, next) {
|
|
|
328
420
|
removed,
|
|
329
421
|
order: moved ? next.keys : null,
|
|
330
422
|
...(covered ? { coverage: after.coverage } : {}),
|
|
423
|
+
...framing,
|
|
331
424
|
},
|
|
332
425
|
};
|
|
333
426
|
}
|
|
427
|
+
function reframed(before, after) {
|
|
428
|
+
const next = before.next === after.next ? {} : { next: after.next ?? null };
|
|
429
|
+
const total = before.total === after.total || after.total === undefined ? {} : { total: after.total };
|
|
430
|
+
return Object.keys(next).length === 0 && Object.keys(total).length === 0
|
|
431
|
+
? null
|
|
432
|
+
: { ...next, ...total };
|
|
433
|
+
}
|
|
334
434
|
function reordered(previous, next, present) {
|
|
335
435
|
let at = 0;
|
|
336
436
|
for (const key of previous) {
|
|
@@ -360,6 +460,9 @@ export function createReadSide() {
|
|
|
360
460
|
},
|
|
361
461
|
});
|
|
362
462
|
const shared = new Map();
|
|
463
|
+
let writing = false;
|
|
464
|
+
let publishing = null;
|
|
465
|
+
let closed = false;
|
|
363
466
|
function searched(row) {
|
|
364
467
|
const read = row.text === null ? null : parse(row.text, row.path);
|
|
365
468
|
if (read === null || !read.ok)
|
|
@@ -379,8 +482,11 @@ export function createReadSide() {
|
|
|
379
482
|
return;
|
|
380
483
|
entry.rows.set(key, row);
|
|
381
484
|
}
|
|
485
|
+
function counted(entry) {
|
|
486
|
+
return entry.built.count === null ? undefined : entry.built.count.size;
|
|
487
|
+
}
|
|
382
488
|
function rebuilt(entry) {
|
|
383
|
-
return after(rowsOfPlan(entry.plan, [...entry.built.live.entries()], entry.bodies), ({ rows: found, keys }) => {
|
|
489
|
+
return after(rowsOfPlan(entry.plan, [...entry.built.live.entries()], counted(entry), entry.bodies), ({ rows: found, keys, next, total }) => {
|
|
384
490
|
const present = new Set(keys);
|
|
385
491
|
for (const key of [...entry.rows.keys()])
|
|
386
492
|
if (!present.has(key))
|
|
@@ -390,32 +496,34 @@ export function createReadSide() {
|
|
|
390
496
|
entry.bodies.delete(key);
|
|
391
497
|
}
|
|
392
498
|
keys.forEach((key, index) => kept(entry, key, rowAt(found, index)));
|
|
393
|
-
return keys;
|
|
499
|
+
return { keys, next, total };
|
|
394
500
|
});
|
|
395
501
|
}
|
|
396
502
|
function refreshed(entry, dirty) {
|
|
397
503
|
const shape = entry.plan.shape;
|
|
398
|
-
const
|
|
399
|
-
const
|
|
400
|
-
const
|
|
504
|
+
const { kept: inWindow, next, total, } = windowOf(entry.plan, [...entry.built.live.entries()], counted(entry));
|
|
505
|
+
const keys = inWindow.map(([key]) => key);
|
|
506
|
+
const present = new Set(keys);
|
|
507
|
+
for (const key of [...entry.rows.keys()]) {
|
|
508
|
+
if (present.has(key))
|
|
509
|
+
continue;
|
|
510
|
+
entry.rows.delete(key);
|
|
511
|
+
entry.bodies.delete(key);
|
|
512
|
+
}
|
|
513
|
+
const values = inWindow.filter(([key]) => dirty.has(key) || !entry.rows.has(key));
|
|
401
514
|
const done = inUnits(values, ([key, value]) => {
|
|
402
|
-
|
|
403
|
-
kept(entry, key, documentOf(key, value, shape, entry.bodies));
|
|
404
|
-
else {
|
|
405
|
-
entry.rows.delete(key);
|
|
406
|
-
entry.bodies.delete(key);
|
|
407
|
-
}
|
|
515
|
+
kept(entry, key, documentOf(key, value, shape, entry.bodies));
|
|
408
516
|
return weight(value);
|
|
409
517
|
});
|
|
410
|
-
return after(done, () => keys);
|
|
518
|
+
return after(done, () => ({ keys, next, total }));
|
|
411
519
|
}
|
|
412
|
-
function current(entry,
|
|
413
|
-
const
|
|
520
|
+
function current(entry, found, coverage) {
|
|
521
|
+
const rows = found.keys.map((key) => entry.rows.get(key));
|
|
414
522
|
return {
|
|
415
523
|
result: entry.plan.shape.kind === 'groups'
|
|
416
|
-
? { groups:
|
|
417
|
-
: { documents:
|
|
418
|
-
keys,
|
|
524
|
+
? framed({ groups: rows, coverage }, found)
|
|
525
|
+
: framed({ documents: rows, coverage }, found),
|
|
526
|
+
keys: found.keys,
|
|
419
527
|
};
|
|
420
528
|
}
|
|
421
529
|
function delivered(entry, next) {
|
|
@@ -451,25 +559,26 @@ export function createReadSide() {
|
|
|
451
559
|
const dirty = entry.dirty;
|
|
452
560
|
entry.dirty = new Set();
|
|
453
561
|
entry.whole = false;
|
|
454
|
-
const
|
|
455
|
-
if (
|
|
456
|
-
void
|
|
457
|
-
.then((
|
|
562
|
+
const found = whole ? rebuilt(entry) : refreshed(entry, dirty);
|
|
563
|
+
if (found instanceof Promise) {
|
|
564
|
+
void found
|
|
565
|
+
.then((window) => {
|
|
458
566
|
if (entry.listeners.size > 0)
|
|
459
|
-
delivered(entry, current(entry,
|
|
460
|
-
|
|
567
|
+
delivered(entry, current(entry, window, cover.coverage));
|
|
568
|
+
entry.running = false;
|
|
569
|
+
wake(entry);
|
|
461
570
|
})
|
|
462
571
|
.catch(() => {
|
|
463
572
|
entry.running = false;
|
|
464
573
|
});
|
|
465
574
|
return;
|
|
466
575
|
}
|
|
467
|
-
delivered(entry, current(entry,
|
|
576
|
+
delivered(entry, current(entry, found, cover.coverage));
|
|
468
577
|
}
|
|
469
578
|
entry.running = false;
|
|
470
579
|
}
|
|
471
580
|
function wake(entry) {
|
|
472
|
-
if (entry.subscription === null || entry.running)
|
|
581
|
+
if (entry.subscription === null || entry.running || writing || publishing !== null)
|
|
473
582
|
return;
|
|
474
583
|
entry.running = true;
|
|
475
584
|
try {
|
|
@@ -495,37 +604,92 @@ export function createReadSide() {
|
|
|
495
604
|
wake(entry);
|
|
496
605
|
}
|
|
497
606
|
}
|
|
607
|
+
function committed(next) {
|
|
608
|
+
if (closed || next.size === 0)
|
|
609
|
+
return;
|
|
610
|
+
sync.begin();
|
|
611
|
+
for (const [key, row] of next) {
|
|
612
|
+
if (row === null) {
|
|
613
|
+
if (rows.has(key))
|
|
614
|
+
sync.write({ type: 'delete', key });
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
sync.write({ type: rows.has(key) ? 'update' : 'insert', value: row });
|
|
618
|
+
}
|
|
619
|
+
sync.commit();
|
|
620
|
+
}
|
|
621
|
+
function published(set) {
|
|
622
|
+
let unit = new Map();
|
|
623
|
+
return inUnits(set.documents, (change) => {
|
|
624
|
+
if (closed)
|
|
625
|
+
return 0;
|
|
626
|
+
const shifted = rows.size / HELD_PER_WEIGHT;
|
|
627
|
+
let spent = 0;
|
|
628
|
+
for (const row of change.before) {
|
|
629
|
+
unit.set(row.key, null);
|
|
630
|
+
spent += ROW_WEIGHT + shifted;
|
|
631
|
+
}
|
|
632
|
+
for (const row of change.after) {
|
|
633
|
+
unit.set(row.key, row);
|
|
634
|
+
spent += weight(row) + shifted;
|
|
635
|
+
}
|
|
636
|
+
return spent;
|
|
637
|
+
}, () => {
|
|
638
|
+
committed(unit);
|
|
639
|
+
unit = new Map();
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function released() {
|
|
643
|
+
recover();
|
|
644
|
+
for (const entry of shared.values())
|
|
645
|
+
wake(entry);
|
|
646
|
+
}
|
|
647
|
+
function publishedLater(applied) {
|
|
648
|
+
const fence = applied.then(() => { }, () => { });
|
|
649
|
+
publishing = fence;
|
|
650
|
+
void fence.then(() => {
|
|
651
|
+
if (publishing !== fence)
|
|
652
|
+
return;
|
|
653
|
+
publishing = null;
|
|
654
|
+
released();
|
|
655
|
+
});
|
|
656
|
+
return applied;
|
|
657
|
+
}
|
|
658
|
+
async function settled() {
|
|
659
|
+
while (publishing !== null)
|
|
660
|
+
await publishing;
|
|
661
|
+
}
|
|
498
662
|
return {
|
|
499
|
-
apply(set) {
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
next.set(row.key, null);
|
|
504
|
-
for (const row of change.after)
|
|
505
|
-
next.set(row.key, row);
|
|
663
|
+
apply(set, next = () => { }) {
|
|
664
|
+
const pending = publishing;
|
|
665
|
+
if (pending !== null) {
|
|
666
|
+
return publishedLater(pending.then(() => after(published(set), next)));
|
|
506
667
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
sync.write({ type: rows.has(key) ? 'update' : 'insert', value: row });
|
|
516
|
-
}
|
|
517
|
-
sync.commit();
|
|
668
|
+
writing = true;
|
|
669
|
+
let applied;
|
|
670
|
+
try {
|
|
671
|
+
applied = published(set);
|
|
672
|
+
}
|
|
673
|
+
finally {
|
|
674
|
+
writing = false;
|
|
518
675
|
}
|
|
519
|
-
|
|
676
|
+
if (applied instanceof Promise)
|
|
677
|
+
return publishedLater(applied.then(next));
|
|
678
|
+
released();
|
|
679
|
+
next();
|
|
520
680
|
},
|
|
521
681
|
recover,
|
|
682
|
+
settled,
|
|
522
683
|
async query(plan, at) {
|
|
523
|
-
const { live: collection, dispose } = built(rows, plan);
|
|
684
|
+
const { live: collection, count, dispose } = built(rows, plan);
|
|
524
685
|
try {
|
|
525
686
|
await collection.toArrayWhenReady();
|
|
687
|
+
if (count !== null)
|
|
688
|
+
await count.toArrayWhenReady();
|
|
689
|
+
await settled();
|
|
526
690
|
const entries = [...collection.entries()];
|
|
527
691
|
const held = at();
|
|
528
|
-
const found = await rowsOfPlan(plan, entries, null, structuredClone);
|
|
692
|
+
const found = await rowsOfPlan(plan, entries, count?.size, null, structuredClone);
|
|
529
693
|
return { rows: found.rows, at: held };
|
|
530
694
|
}
|
|
531
695
|
finally {
|
|
@@ -546,13 +710,16 @@ export function createReadSide() {
|
|
|
546
710
|
fresh: new Set(),
|
|
547
711
|
ready: new Set(),
|
|
548
712
|
subscription: null,
|
|
713
|
+
counting: null,
|
|
549
714
|
whole: true,
|
|
550
715
|
dirty: new Set(),
|
|
551
716
|
stale: false,
|
|
552
717
|
running: false,
|
|
553
718
|
};
|
|
554
719
|
created.built.live.on('status:change', ({ status }) => {
|
|
555
|
-
if (status
|
|
720
|
+
if (status !== 'cleaned-up' || created.listeners.size > 0)
|
|
721
|
+
return;
|
|
722
|
+
if (shared.get(identity) === created)
|
|
556
723
|
shared.delete(identity);
|
|
557
724
|
});
|
|
558
725
|
shared.set(identity, created);
|
|
@@ -560,7 +727,14 @@ export function createReadSide() {
|
|
|
560
727
|
}
|
|
561
728
|
const observed = entry;
|
|
562
729
|
observed.listeners.add(deliverTo);
|
|
563
|
-
observed.built.live.onFirstReady(() =>
|
|
730
|
+
const ready = (then) => observed.built.live.onFirstReady(() => observed.built.count === null ? then() : observed.built.count.onFirstReady(then));
|
|
731
|
+
const settled = () => {
|
|
732
|
+
if (observed.built.count === null)
|
|
733
|
+
wake(observed);
|
|
734
|
+
else
|
|
735
|
+
queueMicrotask(() => wake(observed));
|
|
736
|
+
};
|
|
737
|
+
ready(() => {
|
|
564
738
|
if (!observed.listeners.has(deliverTo))
|
|
565
739
|
return;
|
|
566
740
|
observed.fresh.add(deliverTo);
|
|
@@ -572,8 +746,13 @@ export function createReadSide() {
|
|
|
572
746
|
else
|
|
573
747
|
for (const { key } of changes)
|
|
574
748
|
observed.dirty.add(key);
|
|
575
|
-
|
|
749
|
+
settled();
|
|
576
750
|
}, { includeInitialState: false });
|
|
751
|
+
observed.counting =
|
|
752
|
+
observed.built.count?.subscribeChanges(() => {
|
|
753
|
+
observed.stale = true;
|
|
754
|
+
settled();
|
|
755
|
+
}, { includeInitialState: false }) ?? null;
|
|
577
756
|
}
|
|
578
757
|
wake(observed);
|
|
579
758
|
});
|
|
@@ -584,11 +763,16 @@ export function createReadSide() {
|
|
|
584
763
|
return;
|
|
585
764
|
observed.subscription?.unsubscribe();
|
|
586
765
|
observed.subscription = null;
|
|
766
|
+
observed.counting?.unsubscribe();
|
|
767
|
+
observed.counting = null;
|
|
587
768
|
observed.result = undefined;
|
|
588
769
|
observed.rows.clear();
|
|
589
770
|
observed.bodies.clear();
|
|
590
771
|
observed.dirty.clear();
|
|
591
772
|
observed.whole = true;
|
|
773
|
+
if (shared.get(identity) === observed)
|
|
774
|
+
shared.delete(identity);
|
|
775
|
+
observed.built.dispose();
|
|
592
776
|
};
|
|
593
777
|
},
|
|
594
778
|
room(room) {
|
|
@@ -599,11 +783,13 @@ export function createReadSide() {
|
|
|
599
783
|
return row === undefined || row.type === null ? null : searched(row);
|
|
600
784
|
},
|
|
601
785
|
close() {
|
|
786
|
+
closed = true;
|
|
602
787
|
for (const entry of shared.values()) {
|
|
603
788
|
entry.listeners.clear();
|
|
604
789
|
entry.fresh.clear();
|
|
605
790
|
entry.ready.clear();
|
|
606
791
|
entry.subscription?.unsubscribe();
|
|
792
|
+
entry.counting?.unsubscribe();
|
|
607
793
|
entry.built.dispose();
|
|
608
794
|
}
|
|
609
795
|
shared.clear();
|
|
@@ -628,13 +814,16 @@ function changedFrom(previous, next) {
|
|
|
628
814
|
const removed = previous.keys.filter((key) => !present.has(key));
|
|
629
815
|
const moved = reordered(previous.keys, next.keys, present);
|
|
630
816
|
const covered = !deepEquals(before.coverage, after.coverage);
|
|
631
|
-
|
|
817
|
+
const framing = reframed(before, after);
|
|
818
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered && framing === null) {
|
|
632
819
|
return null;
|
|
820
|
+
}
|
|
633
821
|
return {
|
|
634
822
|
changed,
|
|
635
823
|
removed,
|
|
636
824
|
order: moved ? next.keys : null,
|
|
637
825
|
...(covered ? { coverage: after.coverage } : {}),
|
|
826
|
+
...framing,
|
|
638
827
|
};
|
|
639
828
|
}
|
|
640
829
|
function rowAt(rows, index) {
|
package/dist/reads.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Adapter } from './adapter.js';
|
|
2
|
+
import type { Reads } from './bootstrap.js';
|
|
3
|
+
export type DoorReads = Reads & {
|
|
4
|
+
carried(locator: string): number;
|
|
5
|
+
finished(locator: string, turn: number): void;
|
|
6
|
+
opened(): void;
|
|
7
|
+
applied(): void;
|
|
8
|
+
lost(): void;
|
|
9
|
+
};
|
|
10
|
+
export declare function doorReads(adapter: Adapter, declaration: (locator: string) => unknown, generation: () => number): DoorReads;
|
|
11
|
+
//# sourceMappingURL=reads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reads.d.ts","sourceRoot":"","sources":["../src/reads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAOR,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG;IAC9B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB,IAAI,IAAI,IAAI,CAAC;CACd,CAAC;AAiCF,wBAAgB,SAAS,CACvB,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,EACzC,UAAU,EAAE,MAAM,MAAM,GACvB,SAAS,CA0FX"}
|