@agentshouse/mdruntime 0.1.0-ca9afacd5aebd → 0.1.0-caee7332d2235
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 +271 -290
- package/README.md +8 -8
- package/dist/adapter.d.ts +52 -10
- 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 +3 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +33 -7
- package/dist/change-set.d.ts +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +41 -10
- 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/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.d.ts +2 -1
- package/dist/chat/kit.d.ts.map +1 -1
- package/dist/chat/kit.js +3 -3
- package/dist/chat/list.d.ts +8 -1
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +48 -8
- package/dist/chat/markdown.d.ts +3 -1
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +85 -26
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +3 -3
- package/dist/chat/messages.d.ts +1 -0
- package/dist/chat/messages.d.ts.map +1 -1
- package/dist/chat/messages.js +35 -15
- 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 -1
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +65 -80
- 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 -0
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +4 -4
- 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 +4 -4
- package/dist/chat.d.ts +1 -1
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +1 -1
- package/dist/client.d.ts +17 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +19 -6
- package/dist/collection.d.ts +20 -5
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +309 -33
- package/dist/connection.d.ts +31 -9
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1602 -386
- package/dist/conversation.d.ts +20 -7
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +474 -163
- package/dist/durability.d.ts +7 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +17 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +157 -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 +15 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +94 -12
- 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 +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/memory.d.ts +8 -2
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +33 -6
- package/dist/model.d.ts +4 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -1
- package/dist/observation.d.ts +7 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +29 -12
- package/dist/outcome.d.ts +12 -2
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +4 -0
- package/dist/overlay.d.ts +5 -3
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +203 -159
- package/dist/query.d.ts +56 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +36 -7
- package/dist/reach.d.ts +6 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +30 -5
- package/dist/react.d.ts +8 -6
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/read-side.d.ts +16 -7
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +486 -100
- package/dist/readiness.d.ts +53 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +33 -0
- package/dist/reads.d.ts +11 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +98 -0
- 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 +4 -4
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +44 -18
- package/dist/replica.d.ts +11 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +111 -56
- package/dist/room-draft.d.ts +13 -1
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +11 -12
- package/dist/rows.d.ts +6 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +10 -13
- package/dist/save.d.ts +10 -4
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +216 -108
- package/dist/search.d.ts +49 -6
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +192 -47
- 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 +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +398 -0
- package/dist/status.d.ts +4 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/store.d.ts +61 -12
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +580 -268
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +64 -0
- 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 +152 -32
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +52 -31
- package/package.json +12 -8
package/dist/read-side.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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
|
+
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);
|
|
@@ -59,10 +60,12 @@ function reached(reach, room, document) {
|
|
|
59
60
|
? []
|
|
60
61
|
: [or(inArray(room, [...reach.rooms]), inArray(document, [...reach.documents]))];
|
|
61
62
|
}
|
|
62
|
-
function scoped(scope, row, rooms, reach) {
|
|
63
|
+
function scoped(scope, row, rooms, reach, paths = null, folder = null) {
|
|
63
64
|
return every([
|
|
64
65
|
eq(row.kind, 'document'),
|
|
65
66
|
...(rooms === null ? [] : [inArray(row.room, [...rooms])]),
|
|
67
|
+
...(paths === null ? [] : [inArray(row.path, [...paths])]),
|
|
68
|
+
...(folder === null ? [] : [eq(row.folder, folder)]),
|
|
66
69
|
...reached(reach, row.room, row.key),
|
|
67
70
|
...(scope.types === null ? [] : [inArray(row.type, [...scope.types])]),
|
|
68
71
|
...(scope.pathPrefix === null ? [] : [inArray(scope.pathPrefix, row.directories)]),
|
|
@@ -90,7 +93,7 @@ function related(q, rows, scope, field, kept, reach) {
|
|
|
90
93
|
}
|
|
91
94
|
function filtered(q, rows, plan) {
|
|
92
95
|
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');
|
|
96
|
+
let query = narrowed(from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach, plan.paths, plan.folder)), plan.root.where, 'd');
|
|
94
97
|
if (plan.hop !== null) {
|
|
95
98
|
const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach);
|
|
96
99
|
query = query.innerJoin({ h: hop }, ({ d, h }) => eq(d.key, h.key));
|
|
@@ -101,34 +104,59 @@ function filtered(q, rows, plan) {
|
|
|
101
104
|
}
|
|
102
105
|
return query;
|
|
103
106
|
}
|
|
104
|
-
function
|
|
105
|
-
return
|
|
107
|
+
function bodied(shape) {
|
|
108
|
+
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
109
|
+
}
|
|
110
|
+
function orderingOf(options) {
|
|
111
|
+
const collated = options.stringSort === 'locale';
|
|
112
|
+
return {
|
|
113
|
+
direction: options.direction,
|
|
114
|
+
nulls: options.nulls ?? 'first',
|
|
115
|
+
stringSort: options.stringSort ?? 'locale',
|
|
116
|
+
locale: collated ? options.locale : undefined,
|
|
117
|
+
localeOptions: collated
|
|
118
|
+
? options.localeOptions
|
|
119
|
+
: undefined,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function storedValue(key, row) {
|
|
123
|
+
return key.of === 'field' ? row.values[key.field] : row[key.of];
|
|
124
|
+
}
|
|
125
|
+
function documentOrder(shape) {
|
|
126
|
+
return shape.orderBy.map(({ options }) => orderingOf(options));
|
|
127
|
+
}
|
|
128
|
+
function documentCursor(shape, row) {
|
|
129
|
+
return cursorOf([
|
|
130
|
+
...shape.orderBy.map((_, index) => row[`order${index}`]),
|
|
131
|
+
documentKey(row.room, row.path),
|
|
132
|
+
]);
|
|
106
133
|
}
|
|
107
134
|
function documents(q, rows, plan, shape) {
|
|
108
135
|
let query = filtered(q, rows, plan);
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
const cursor = plan.after?.values ?? null;
|
|
137
|
+
if (cursor !== null) {
|
|
138
|
+
const orderings = documentOrder(shape);
|
|
139
|
+
query = query.fn.where(({ d }) => beyond([...shape.orderBy.map(({ key }) => storedValue(key, d)), d.key], cursor, orderings));
|
|
111
140
|
}
|
|
112
141
|
for (const { key, options } of shape.orderBy) {
|
|
113
142
|
query = query.orderBy(({ d }) => keyed(key, d), options);
|
|
114
143
|
}
|
|
115
|
-
if (shape.orderBy.length
|
|
144
|
+
if (shape.orderBy.length > 0 || plan.limit !== null || plan.after !== null) {
|
|
116
145
|
query = query.orderBy(({ d }) => d.key, { stringSort: 'lexical' });
|
|
117
146
|
}
|
|
118
147
|
if (plan.limit !== null)
|
|
119
|
-
query = query.limit(plan.limit);
|
|
120
|
-
return query.select(({ d
|
|
148
|
+
query = query.limit(plan.limit + 1);
|
|
149
|
+
return query.select(({ d }) => ({
|
|
121
150
|
room: d.room,
|
|
122
151
|
path: d.path,
|
|
123
152
|
type: d.type,
|
|
124
153
|
binary: d.binary,
|
|
154
|
+
...Object.fromEntries(shape.orderBy.map(({ key }, index) => [`order${index}`, keyed(key, d)])),
|
|
125
155
|
...(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 } : {}),
|
|
156
|
+
...(bodied(shape) ? { text: d.text } : {}),
|
|
130
157
|
...(shape.backlinks ? { backlinks: toArray(backlinked(q, rows, d, plan.reach)) } : {}),
|
|
131
158
|
...(shape.change ? { change: d.change } : {}),
|
|
159
|
+
...(shape.fileId ? { fileId: d.fileId } : {}),
|
|
132
160
|
}));
|
|
133
161
|
}
|
|
134
162
|
function backlinked(q, rows, document, reach) {
|
|
@@ -139,15 +167,13 @@ function backlinked(q, rows, document, reach) {
|
|
|
139
167
|
.orderBy(({ r }) => r.field, { stringSort: 'lexical' })
|
|
140
168
|
.select(({ r }) => ({ path: r.path, type: r.type, field: r.field }));
|
|
141
169
|
}
|
|
142
|
-
function groups(q, stage, shape
|
|
170
|
+
function groups(q, stage, shape) {
|
|
143
171
|
const keys = shape.groupBy.map((_, index) => `key${index}`);
|
|
144
172
|
const from = q.from({ m: stage });
|
|
145
173
|
let query = from.groupBy(({ m }) => keys.map((key) => m[key]));
|
|
146
174
|
for (const { by, direction } of shape.orderBy) {
|
|
147
175
|
query = query.orderBy(({ m }) => (by === 'count' ? count(m.key) : m[keys[by]]), direction);
|
|
148
176
|
}
|
|
149
|
-
if (limit !== null && shape.orderBy.length > 0)
|
|
150
|
-
query = query.limit(limit);
|
|
151
177
|
return query.select(({ m }) => ({
|
|
152
178
|
...Object.fromEntries(keys.map((key) => [key, m[key]])),
|
|
153
179
|
count: count(m.key),
|
|
@@ -157,28 +183,49 @@ function built(rows, plan) {
|
|
|
157
183
|
const shape = plan.shape;
|
|
158
184
|
if (shape.kind === 'documents') {
|
|
159
185
|
const collection = live((q) => documents(q, rows, plan, shape));
|
|
160
|
-
|
|
186
|
+
const count = plan.total
|
|
187
|
+
? live((q) => filtered(q, rows, plan).select(({ d }) => ({ key: d.key })))
|
|
188
|
+
: null;
|
|
189
|
+
return {
|
|
190
|
+
live: collection,
|
|
191
|
+
count,
|
|
192
|
+
dispose: () => {
|
|
193
|
+
void collection.cleanup();
|
|
194
|
+
if (count !== null)
|
|
195
|
+
void count.cleanup();
|
|
196
|
+
},
|
|
197
|
+
};
|
|
161
198
|
}
|
|
162
199
|
const stage = live((q) => filtered(q, rows, plan).select(({ d }) => ({
|
|
163
200
|
key: d.key,
|
|
164
201
|
...Object.fromEntries(shape.groupBy.map((key, index) => [`key${index}`, keyed(key, d)])),
|
|
165
202
|
})));
|
|
166
|
-
const collection = live((q) => groups(q, stage, shape
|
|
203
|
+
const collection = live((q) => groups(q, stage, shape));
|
|
167
204
|
return {
|
|
168
205
|
live: collection,
|
|
206
|
+
count: null,
|
|
169
207
|
dispose: () => {
|
|
170
208
|
void collection.cleanup();
|
|
171
209
|
void stage.cleanup();
|
|
172
210
|
},
|
|
173
211
|
};
|
|
174
212
|
}
|
|
175
|
-
function
|
|
176
|
-
return Object.fromEntries(entries.filter(([, value]) => value !== undefined));
|
|
177
|
-
}
|
|
178
|
-
function part(row, prefix, names, whole) {
|
|
213
|
+
function sectionsOf(sections, names) {
|
|
179
214
|
if (names === 'all')
|
|
180
|
-
return
|
|
181
|
-
return
|
|
215
|
+
return sections;
|
|
216
|
+
return Object.fromEntries(names.flatMap((name) => (Object.hasOwn(sections, name) ? [[name, sections[name]]] : [])));
|
|
217
|
+
}
|
|
218
|
+
function bodyOf(row, key, bodies) {
|
|
219
|
+
const text = row.text;
|
|
220
|
+
if (text === null)
|
|
221
|
+
return null;
|
|
222
|
+
const held = bodies?.get(key);
|
|
223
|
+
if (held !== undefined && held.text === text)
|
|
224
|
+
return held.document;
|
|
225
|
+
const read = parse(text, row.path);
|
|
226
|
+
const document = read.ok ? read.document : null;
|
|
227
|
+
bodies?.set(key, { text, document });
|
|
228
|
+
return document;
|
|
182
229
|
}
|
|
183
230
|
function masked(pointers) {
|
|
184
231
|
const root = { whole: false, members: new Map() };
|
|
@@ -229,15 +276,17 @@ function cut(value, mask) {
|
|
|
229
276
|
function selected(fields, pointers) {
|
|
230
277
|
return (cut(fields, masked(pointers)) ?? {});
|
|
231
278
|
}
|
|
232
|
-
function documentOf(row, shape) {
|
|
279
|
+
function documentOf(key, row, shape, bodies) {
|
|
233
280
|
if (row.type === null) {
|
|
234
281
|
const binary = row.binary;
|
|
235
282
|
return {
|
|
236
283
|
room: row.room,
|
|
237
284
|
path: row.path,
|
|
238
285
|
...(binary === null ? {} : { binaryFile: binary }),
|
|
286
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
239
287
|
};
|
|
240
288
|
}
|
|
289
|
+
const body = bodied(shape) ? bodyOf(row, key, bodies) : null;
|
|
241
290
|
return {
|
|
242
291
|
room: row.room,
|
|
243
292
|
path: row.path,
|
|
@@ -249,45 +298,95 @@ function documentOf(row, shape) {
|
|
|
249
298
|
? row.fields
|
|
250
299
|
: selected(row.fields, shape.fields),
|
|
251
300
|
}),
|
|
252
|
-
...(shape.sections === null
|
|
301
|
+
...(shape.sections === null || body === null
|
|
253
302
|
? {}
|
|
254
|
-
: {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
...(shape.preamble ? { preamble: row.preamble } : {}),
|
|
258
|
-
...(shape.markdown ? { markdown: row.markdown } : {}),
|
|
303
|
+
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
304
|
+
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
305
|
+
...(shape.markdown && body !== null ? { markdown: body.content } : {}),
|
|
259
306
|
...(shape.backlinks
|
|
260
307
|
? {
|
|
261
|
-
backlinks: row.backlinks
|
|
308
|
+
backlinks: row.backlinks
|
|
309
|
+
.slice(0, MAXIMUM_WINDOW)
|
|
310
|
+
.map(({ path, type, field }) => ({ path, type, field })),
|
|
262
311
|
}
|
|
263
312
|
: {}),
|
|
264
313
|
...(shape.change ? { change: row.change } : {}),
|
|
314
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
265
315
|
};
|
|
266
316
|
}
|
|
267
|
-
function
|
|
317
|
+
function weight(row) {
|
|
318
|
+
return ROW_WEIGHT + (typeof row?.text === 'string' ? row.text.length : 0);
|
|
319
|
+
}
|
|
320
|
+
const HELD_PER_WEIGHT = 16;
|
|
321
|
+
function groupValues(shape, [key, row]) {
|
|
322
|
+
return [...shape.orderBy.map(({ by }) => (by === 'count' ? row.count : row[`key${by}`])), key];
|
|
323
|
+
}
|
|
324
|
+
function groupOrder(shape) {
|
|
325
|
+
return shape.orderBy.map(({ direction }) => ({
|
|
326
|
+
direction,
|
|
327
|
+
nulls: 'first',
|
|
328
|
+
stringSort: 'locale',
|
|
329
|
+
locale: undefined,
|
|
330
|
+
localeOptions: undefined,
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
function windowOf(plan, entries, total) {
|
|
268
334
|
const shape = plan.shape;
|
|
269
335
|
if (shape.kind === 'documents') {
|
|
270
336
|
return {
|
|
271
|
-
|
|
272
|
-
|
|
337
|
+
...windowed(entries, plan.limit, plan.after?.text, ([, row]) => documentCursor(shape, row)),
|
|
338
|
+
total,
|
|
273
339
|
};
|
|
274
340
|
}
|
|
275
|
-
const
|
|
341
|
+
const cursor = plan.after?.values ?? null;
|
|
342
|
+
const orderings = groupOrder(shape);
|
|
343
|
+
const following = cursor === null
|
|
344
|
+
? entries
|
|
345
|
+
: entries.filter((entry) => beyond(groupValues(shape, entry), cursor, orderings));
|
|
276
346
|
return {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
key: shape.groupBy.map((_, index) => row[`key${index}`]),
|
|
280
|
-
count: row.count,
|
|
281
|
-
})),
|
|
282
|
-
}),
|
|
283
|
-
keys: bounded.map(([key]) => key),
|
|
347
|
+
...windowed(following, plan.limit, plan.after?.text, (entry) => cursorOf(groupValues(shape, entry))),
|
|
348
|
+
total: plan.total ? entries.length : undefined,
|
|
284
349
|
};
|
|
285
350
|
}
|
|
351
|
+
function framed(rows, { next, total }) {
|
|
352
|
+
return {
|
|
353
|
+
...rows,
|
|
354
|
+
...(next === undefined ? {} : { next }),
|
|
355
|
+
...(total === undefined ? {} : { total }),
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function rowsOfPlan(plan, entries, total, bodies, each = (row) => row) {
|
|
359
|
+
const shape = plan.shape;
|
|
360
|
+
const { kept, ...window } = windowOf(plan, entries, total);
|
|
361
|
+
const keys = kept.map(([key]) => key);
|
|
362
|
+
if (shape.kind === 'documents') {
|
|
363
|
+
const documents = [];
|
|
364
|
+
return after(inUnits(kept, ([key, row]) => {
|
|
365
|
+
documents.push(each(documentOf(key, row, shape, bodies)));
|
|
366
|
+
return weight(row);
|
|
367
|
+
}), () => ({ rows: framed({ documents }, window), keys, ...window }));
|
|
368
|
+
}
|
|
369
|
+
const groups = [];
|
|
370
|
+
return after(inUnits(kept, ([, row]) => {
|
|
371
|
+
groups.push(each({
|
|
372
|
+
key: shape.groupBy.map((_, index) => row[`key${index}`]),
|
|
373
|
+
count: row.count,
|
|
374
|
+
}));
|
|
375
|
+
return ROW_WEIGHT;
|
|
376
|
+
}), () => ({ rows: framed({ groups }, window), keys, ...window }));
|
|
377
|
+
}
|
|
286
378
|
export function reconciled(previous, next) {
|
|
287
|
-
const before =
|
|
288
|
-
const
|
|
379
|
+
const before = previous.result;
|
|
380
|
+
const after = next.result;
|
|
381
|
+
if ('pending' in before || 'pending' in after) {
|
|
382
|
+
if (deepEquals(before, after))
|
|
383
|
+
return null;
|
|
384
|
+
return { keyed: next, message: next };
|
|
385
|
+
}
|
|
386
|
+
const earlier = 'groups' in before ? before.groups : before.documents;
|
|
387
|
+
const held = new Map(previous.keys.map((key, index) => [key, earlier[index]]));
|
|
289
388
|
const changed = [];
|
|
290
|
-
const rows =
|
|
389
|
+
const rows = ('groups' in after ? after.groups : after.documents).map((row, index) => {
|
|
291
390
|
const key = next.keys[index];
|
|
292
391
|
const kept = held.get(key);
|
|
293
392
|
if (kept !== undefined && deepEquals(kept, row))
|
|
@@ -296,22 +395,48 @@ export function reconciled(previous, next) {
|
|
|
296
395
|
return row;
|
|
297
396
|
});
|
|
298
397
|
const present = new Set(next.keys);
|
|
299
|
-
const survivors = previous.keys.filter((key) => present.has(key));
|
|
300
398
|
const removed = previous.keys.filter((key) => !present.has(key));
|
|
301
|
-
const moved =
|
|
302
|
-
|
|
303
|
-
|
|
399
|
+
const moved = reordered(previous.keys, next.keys, present);
|
|
400
|
+
const covered = !deepEquals(before.coverage, after.coverage);
|
|
401
|
+
const framing = reframed(before, after);
|
|
402
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered && framing === null) {
|
|
304
403
|
return null;
|
|
404
|
+
}
|
|
405
|
+
const window = { next: after.next, total: after.total };
|
|
305
406
|
return {
|
|
306
407
|
keyed: {
|
|
307
|
-
result: 'groups' in
|
|
308
|
-
? { groups: rows }
|
|
309
|
-
: { documents: rows },
|
|
408
|
+
result: 'groups' in after
|
|
409
|
+
? framed({ groups: rows, coverage: after.coverage }, window)
|
|
410
|
+
: framed({ documents: rows, coverage: after.coverage }, window),
|
|
310
411
|
keys: next.keys,
|
|
311
412
|
},
|
|
312
|
-
|
|
413
|
+
message: {
|
|
414
|
+
changed,
|
|
415
|
+
removed,
|
|
416
|
+
order: moved ? next.keys : null,
|
|
417
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
418
|
+
...framing,
|
|
419
|
+
},
|
|
313
420
|
};
|
|
314
421
|
}
|
|
422
|
+
function reframed(before, after) {
|
|
423
|
+
const next = before.next === after.next ? {} : { next: after.next ?? null };
|
|
424
|
+
const total = before.total === after.total || after.total === undefined ? {} : { total: after.total };
|
|
425
|
+
return Object.keys(next).length === 0 && Object.keys(total).length === 0
|
|
426
|
+
? null
|
|
427
|
+
: { ...next, ...total };
|
|
428
|
+
}
|
|
429
|
+
function reordered(previous, next, present) {
|
|
430
|
+
let at = 0;
|
|
431
|
+
for (const key of previous) {
|
|
432
|
+
if (!present.has(key))
|
|
433
|
+
continue;
|
|
434
|
+
if (next[at] !== key)
|
|
435
|
+
return true;
|
|
436
|
+
at += 1;
|
|
437
|
+
}
|
|
438
|
+
return at !== next.length;
|
|
439
|
+
}
|
|
315
440
|
export function createReadSide() {
|
|
316
441
|
configureIndexDevMode({ enabled: false });
|
|
317
442
|
let sync;
|
|
@@ -330,107 +455,336 @@ export function createReadSide() {
|
|
|
330
455
|
},
|
|
331
456
|
});
|
|
332
457
|
const shared = new Map();
|
|
458
|
+
let writing = false;
|
|
459
|
+
let publishing = null;
|
|
460
|
+
let closed = false;
|
|
333
461
|
function searched(row) {
|
|
334
|
-
const
|
|
462
|
+
const read = row.text === null ? null : parse(row.text, row.path);
|
|
463
|
+
if (read === null || !read.ok)
|
|
464
|
+
return null;
|
|
335
465
|
return {
|
|
336
466
|
room: row.room,
|
|
337
467
|
path: row.path,
|
|
338
468
|
type: row.type,
|
|
339
469
|
fields: row.fields,
|
|
340
|
-
preamble:
|
|
341
|
-
sections:
|
|
470
|
+
preamble: read.document.preamble,
|
|
471
|
+
sections: read.document.sections,
|
|
342
472
|
};
|
|
343
473
|
}
|
|
344
|
-
function
|
|
345
|
-
const
|
|
346
|
-
if (
|
|
474
|
+
function kept(entry, key, row) {
|
|
475
|
+
const held = entry.rows.get(key);
|
|
476
|
+
if (held !== undefined && deepEquals(held, row))
|
|
347
477
|
return;
|
|
348
|
-
entry.
|
|
349
|
-
for (const listener of entry.listeners)
|
|
350
|
-
listener(next.change);
|
|
478
|
+
entry.rows.set(key, row);
|
|
351
479
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
480
|
+
function counted(entry) {
|
|
481
|
+
return entry.built.count === null ? undefined : entry.built.count.size;
|
|
482
|
+
}
|
|
483
|
+
function rebuilt(entry) {
|
|
484
|
+
return after(rowsOfPlan(entry.plan, [...entry.built.live.entries()], counted(entry), entry.bodies), ({ rows: found, keys, next, total }) => {
|
|
485
|
+
const present = new Set(keys);
|
|
486
|
+
for (const key of [...entry.rows.keys()])
|
|
487
|
+
if (!present.has(key))
|
|
488
|
+
entry.rows.delete(key);
|
|
489
|
+
for (const key of [...entry.bodies.keys()]) {
|
|
490
|
+
if (!present.has(key))
|
|
491
|
+
entry.bodies.delete(key);
|
|
492
|
+
}
|
|
493
|
+
keys.forEach((key, index) => kept(entry, key, rowAt(found, index)));
|
|
494
|
+
return { keys, next, total };
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
function refreshed(entry, dirty) {
|
|
498
|
+
const shape = entry.plan.shape;
|
|
499
|
+
const { kept: inWindow, next, total, } = windowOf(entry.plan, [...entry.built.live.entries()], counted(entry));
|
|
500
|
+
const keys = inWindow.map(([key]) => key);
|
|
501
|
+
const present = new Set(keys);
|
|
502
|
+
for (const key of [...entry.rows.keys()]) {
|
|
503
|
+
if (present.has(key))
|
|
504
|
+
continue;
|
|
505
|
+
entry.rows.delete(key);
|
|
506
|
+
entry.bodies.delete(key);
|
|
507
|
+
}
|
|
508
|
+
const values = inWindow.filter(([key]) => dirty.has(key) || !entry.rows.has(key));
|
|
509
|
+
const done = inUnits(values, ([key, value]) => {
|
|
510
|
+
kept(entry, key, documentOf(key, value, shape, entry.bodies));
|
|
511
|
+
return weight(value);
|
|
512
|
+
});
|
|
513
|
+
return after(done, () => ({ keys, next, total }));
|
|
514
|
+
}
|
|
515
|
+
function current(entry, found, coverage) {
|
|
516
|
+
const rows = found.keys.map((key) => entry.rows.get(key));
|
|
517
|
+
return {
|
|
518
|
+
result: entry.plan.shape.kind === 'groups'
|
|
519
|
+
? framed({ groups: rows, coverage }, found)
|
|
520
|
+
: framed({ documents: rows, coverage }, found),
|
|
521
|
+
keys: found.keys,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function delivered(entry, next) {
|
|
525
|
+
const previous = entry.result;
|
|
526
|
+
entry.result = next;
|
|
527
|
+
const message = previous === undefined ? null : changedFrom(previous, next);
|
|
528
|
+
if (message !== null)
|
|
529
|
+
for (const listener of entry.ready)
|
|
530
|
+
listener(message);
|
|
531
|
+
for (const listener of entry.fresh) {
|
|
532
|
+
entry.ready.add(listener);
|
|
533
|
+
listener(next);
|
|
534
|
+
}
|
|
535
|
+
entry.fresh.clear();
|
|
536
|
+
}
|
|
537
|
+
function run(entry) {
|
|
538
|
+
while (entry.listeners.size > 0 &&
|
|
539
|
+
(entry.whole || entry.dirty.size > 0 || entry.stale || entry.fresh.size > 0)) {
|
|
540
|
+
entry.stale = false;
|
|
541
|
+
const cover = entry.cover();
|
|
542
|
+
if ('pending' in cover) {
|
|
543
|
+
entry.whole = true;
|
|
544
|
+
entry.dirty.clear();
|
|
545
|
+
entry.rows.clear();
|
|
546
|
+
entry.bodies.clear();
|
|
547
|
+
delivered(entry, { result: cover, keys: [] });
|
|
548
|
+
break;
|
|
360
549
|
}
|
|
361
|
-
|
|
550
|
+
const whole = entry.whole ||
|
|
551
|
+
entry.plan.shape.kind === 'groups' ||
|
|
552
|
+
entry.result === undefined ||
|
|
553
|
+
'pending' in entry.result.result;
|
|
554
|
+
const dirty = entry.dirty;
|
|
555
|
+
entry.dirty = new Set();
|
|
556
|
+
entry.whole = false;
|
|
557
|
+
const found = whole ? rebuilt(entry) : refreshed(entry, dirty);
|
|
558
|
+
if (found instanceof Promise) {
|
|
559
|
+
void found
|
|
560
|
+
.then((window) => {
|
|
561
|
+
if (entry.listeners.size > 0)
|
|
562
|
+
delivered(entry, current(entry, window, cover.coverage));
|
|
563
|
+
entry.running = false;
|
|
564
|
+
wake(entry);
|
|
565
|
+
})
|
|
566
|
+
.catch(() => {
|
|
567
|
+
entry.running = false;
|
|
568
|
+
});
|
|
362
569
|
return;
|
|
363
|
-
sync.begin();
|
|
364
|
-
for (const [key, row] of next) {
|
|
365
|
-
if (row === null) {
|
|
366
|
-
if (rows.has(key))
|
|
367
|
-
sync.write({ type: 'delete', key });
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
sync.write({ type: rows.has(key) ? 'update' : 'insert', value: row });
|
|
371
570
|
}
|
|
372
|
-
|
|
571
|
+
delivered(entry, current(entry, found, cover.coverage));
|
|
572
|
+
}
|
|
573
|
+
entry.running = false;
|
|
574
|
+
}
|
|
575
|
+
function wake(entry) {
|
|
576
|
+
if (entry.subscription === null || entry.running || writing || publishing !== null)
|
|
577
|
+
return;
|
|
578
|
+
entry.running = true;
|
|
579
|
+
try {
|
|
580
|
+
run(entry);
|
|
581
|
+
}
|
|
582
|
+
catch (error) {
|
|
583
|
+
entry.running = false;
|
|
584
|
+
throw error;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function recover() {
|
|
588
|
+
for (const entry of shared.values()) {
|
|
589
|
+
if (entry.result === undefined || entry.subscription === null)
|
|
590
|
+
continue;
|
|
591
|
+
const cover = entry.cover();
|
|
592
|
+
const before = entry.result.result;
|
|
593
|
+
const held = 'pending' in cover
|
|
594
|
+
? deepEquals(before, cover)
|
|
595
|
+
: 'coverage' in before && deepEquals(before.coverage, cover.coverage);
|
|
596
|
+
if (held)
|
|
597
|
+
continue;
|
|
598
|
+
entry.stale = true;
|
|
599
|
+
wake(entry);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function committed(next) {
|
|
603
|
+
if (closed || next.size === 0)
|
|
604
|
+
return;
|
|
605
|
+
sync.begin();
|
|
606
|
+
for (const [key, row] of next) {
|
|
607
|
+
if (row === null) {
|
|
608
|
+
if (rows.has(key))
|
|
609
|
+
sync.write({ type: 'delete', key });
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
sync.write({ type: rows.has(key) ? 'update' : 'insert', value: row });
|
|
613
|
+
}
|
|
614
|
+
sync.commit();
|
|
615
|
+
}
|
|
616
|
+
function published(set) {
|
|
617
|
+
let unit = new Map();
|
|
618
|
+
return inUnits(set.documents, (change) => {
|
|
619
|
+
if (closed)
|
|
620
|
+
return 0;
|
|
621
|
+
const shifted = rows.size / HELD_PER_WEIGHT;
|
|
622
|
+
let spent = 0;
|
|
623
|
+
for (const row of change.before) {
|
|
624
|
+
unit.set(row.key, null);
|
|
625
|
+
spent += ROW_WEIGHT + shifted;
|
|
626
|
+
}
|
|
627
|
+
for (const row of change.after) {
|
|
628
|
+
unit.set(row.key, row);
|
|
629
|
+
spent += weight(row) + shifted;
|
|
630
|
+
}
|
|
631
|
+
return spent;
|
|
632
|
+
}, () => {
|
|
633
|
+
committed(unit);
|
|
634
|
+
unit = new Map();
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
function released() {
|
|
638
|
+
recover();
|
|
639
|
+
for (const entry of shared.values())
|
|
640
|
+
wake(entry);
|
|
641
|
+
}
|
|
642
|
+
function publishedLater(applied) {
|
|
643
|
+
const fence = applied.then(() => { }, () => { });
|
|
644
|
+
publishing = fence;
|
|
645
|
+
void fence.then(() => {
|
|
646
|
+
if (publishing !== fence)
|
|
647
|
+
return;
|
|
648
|
+
publishing = null;
|
|
649
|
+
released();
|
|
650
|
+
});
|
|
651
|
+
return applied;
|
|
652
|
+
}
|
|
653
|
+
async function settled() {
|
|
654
|
+
while (publishing !== null)
|
|
655
|
+
await publishing;
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
apply(set, next = () => { }) {
|
|
659
|
+
const pending = publishing;
|
|
660
|
+
if (pending !== null) {
|
|
661
|
+
return publishedLater(pending.then(() => after(published(set), next)));
|
|
662
|
+
}
|
|
663
|
+
writing = true;
|
|
664
|
+
let applied;
|
|
665
|
+
try {
|
|
666
|
+
applied = published(set);
|
|
667
|
+
}
|
|
668
|
+
finally {
|
|
669
|
+
writing = false;
|
|
670
|
+
}
|
|
671
|
+
if (applied instanceof Promise)
|
|
672
|
+
return publishedLater(applied.then(next));
|
|
673
|
+
released();
|
|
674
|
+
next();
|
|
373
675
|
},
|
|
374
|
-
|
|
375
|
-
|
|
676
|
+
recover,
|
|
677
|
+
settled,
|
|
678
|
+
async query(plan, at) {
|
|
679
|
+
const { live: collection, count, dispose } = built(rows, plan);
|
|
376
680
|
try {
|
|
377
681
|
await collection.toArrayWhenReady();
|
|
378
|
-
|
|
682
|
+
if (count !== null)
|
|
683
|
+
await count.toArrayWhenReady();
|
|
684
|
+
await settled();
|
|
685
|
+
const entries = [...collection.entries()];
|
|
686
|
+
const held = at();
|
|
687
|
+
const found = await rowsOfPlan(plan, entries, count?.size, null, structuredClone);
|
|
688
|
+
return { rows: found.rows, at: held };
|
|
379
689
|
}
|
|
380
690
|
finally {
|
|
381
691
|
dispose();
|
|
382
692
|
}
|
|
383
693
|
},
|
|
384
|
-
watch(plan, identity,
|
|
694
|
+
watch(plan, identity, cover, deliverTo) {
|
|
385
695
|
let entry = shared.get(identity);
|
|
386
696
|
if (entry === undefined) {
|
|
387
697
|
const created = {
|
|
388
698
|
plan,
|
|
389
699
|
built: built(rows, plan),
|
|
700
|
+
cover,
|
|
390
701
|
result: undefined,
|
|
702
|
+
rows: new Map(),
|
|
703
|
+
bodies: new Map(),
|
|
391
704
|
listeners: new Set(),
|
|
705
|
+
fresh: new Set(),
|
|
706
|
+
ready: new Set(),
|
|
392
707
|
subscription: null,
|
|
708
|
+
counting: null,
|
|
709
|
+
whole: true,
|
|
710
|
+
dirty: new Set(),
|
|
711
|
+
stale: false,
|
|
712
|
+
running: false,
|
|
393
713
|
};
|
|
394
714
|
created.built.live.on('status:change', ({ status }) => {
|
|
395
|
-
if (status
|
|
715
|
+
if (status !== 'cleaned-up' || created.listeners.size > 0)
|
|
716
|
+
return;
|
|
717
|
+
if (shared.get(identity) === created)
|
|
396
718
|
shared.delete(identity);
|
|
397
719
|
});
|
|
398
720
|
shared.set(identity, created);
|
|
399
721
|
entry = created;
|
|
400
722
|
}
|
|
401
723
|
const observed = entry;
|
|
402
|
-
observed.listeners.add(
|
|
403
|
-
observed.built.live.onFirstReady(() =>
|
|
404
|
-
|
|
724
|
+
observed.listeners.add(deliverTo);
|
|
725
|
+
const ready = (then) => observed.built.live.onFirstReady(() => observed.built.count === null ? then() : observed.built.count.onFirstReady(then));
|
|
726
|
+
const settled = () => {
|
|
727
|
+
if (observed.built.count === null)
|
|
728
|
+
wake(observed);
|
|
729
|
+
else
|
|
730
|
+
queueMicrotask(() => wake(observed));
|
|
731
|
+
};
|
|
732
|
+
ready(() => {
|
|
733
|
+
if (!observed.listeners.has(deliverTo))
|
|
405
734
|
return;
|
|
735
|
+
observed.fresh.add(deliverTo);
|
|
406
736
|
if (observed.subscription === null) {
|
|
407
|
-
observed.
|
|
408
|
-
observed.subscription = observed.built.live.subscribeChanges(() => {
|
|
409
|
-
|
|
737
|
+
observed.whole = true;
|
|
738
|
+
observed.subscription = observed.built.live.subscribeChanges((changes) => {
|
|
739
|
+
if (observed.plan.shape.kind === 'groups')
|
|
740
|
+
observed.whole = true;
|
|
741
|
+
else
|
|
742
|
+
for (const { key } of changes)
|
|
743
|
+
observed.dirty.add(key);
|
|
744
|
+
settled();
|
|
410
745
|
}, { includeInitialState: false });
|
|
746
|
+
observed.counting =
|
|
747
|
+
observed.built.count?.subscribeChanges(() => {
|
|
748
|
+
observed.stale = true;
|
|
749
|
+
settled();
|
|
750
|
+
}, { includeInitialState: false }) ?? null;
|
|
411
751
|
}
|
|
412
|
-
|
|
752
|
+
wake(observed);
|
|
413
753
|
});
|
|
414
754
|
return () => {
|
|
415
|
-
|
|
755
|
+
observed.fresh.delete(deliverTo);
|
|
756
|
+
observed.ready.delete(deliverTo);
|
|
757
|
+
if (!observed.listeners.delete(deliverTo) || observed.listeners.size > 0)
|
|
416
758
|
return;
|
|
417
759
|
observed.subscription?.unsubscribe();
|
|
418
760
|
observed.subscription = null;
|
|
761
|
+
observed.counting?.unsubscribe();
|
|
762
|
+
observed.counting = null;
|
|
763
|
+
observed.result = undefined;
|
|
764
|
+
observed.rows.clear();
|
|
765
|
+
observed.bodies.clear();
|
|
766
|
+
observed.dirty.clear();
|
|
767
|
+
observed.whole = true;
|
|
768
|
+
if (shared.get(identity) === observed)
|
|
769
|
+
shared.delete(identity);
|
|
770
|
+
observed.built.dispose();
|
|
419
771
|
};
|
|
420
772
|
},
|
|
421
|
-
|
|
422
|
-
return rows.toArray
|
|
423
|
-
.filter((row) => row.kind === 'document' && row.type !== null)
|
|
424
|
-
.map(searched);
|
|
773
|
+
room(room) {
|
|
774
|
+
return rows.toArray.flatMap((row) => row.kind !== 'document' || row.type === null || row.room !== room ? [] : [row.path]);
|
|
425
775
|
},
|
|
426
776
|
document(room, path) {
|
|
427
777
|
const row = rows.get(documentKey(room, path));
|
|
428
778
|
return row === undefined || row.type === null ? null : searched(row);
|
|
429
779
|
},
|
|
430
780
|
close() {
|
|
781
|
+
closed = true;
|
|
431
782
|
for (const entry of shared.values()) {
|
|
432
783
|
entry.listeners.clear();
|
|
784
|
+
entry.fresh.clear();
|
|
785
|
+
entry.ready.clear();
|
|
433
786
|
entry.subscription?.unsubscribe();
|
|
787
|
+
entry.counting?.unsubscribe();
|
|
434
788
|
entry.built.dispose();
|
|
435
789
|
}
|
|
436
790
|
shared.clear();
|
|
@@ -438,3 +792,35 @@ export function createReadSide() {
|
|
|
438
792
|
},
|
|
439
793
|
};
|
|
440
794
|
}
|
|
795
|
+
function changedFrom(previous, next) {
|
|
796
|
+
const before = previous.result;
|
|
797
|
+
const after = next.result;
|
|
798
|
+
if ('pending' in before || 'pending' in after)
|
|
799
|
+
return deepEquals(before, after) ? null : next;
|
|
800
|
+
const earlier = 'groups' in before ? before.groups : before.documents;
|
|
801
|
+
const held = new Map(previous.keys.map((key, index) => [key, earlier[index]]));
|
|
802
|
+
const later = 'groups' in after ? after.groups : after.documents;
|
|
803
|
+
const changed = [];
|
|
804
|
+
next.keys.forEach((key, index) => {
|
|
805
|
+
if (held.get(key) !== later[index])
|
|
806
|
+
changed.push([key, later[index]]);
|
|
807
|
+
});
|
|
808
|
+
const present = new Set(next.keys);
|
|
809
|
+
const removed = previous.keys.filter((key) => !present.has(key));
|
|
810
|
+
const moved = reordered(previous.keys, next.keys, present);
|
|
811
|
+
const covered = !deepEquals(before.coverage, after.coverage);
|
|
812
|
+
const framing = reframed(before, after);
|
|
813
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered && framing === null) {
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
return {
|
|
817
|
+
changed,
|
|
818
|
+
removed,
|
|
819
|
+
order: moved ? next.keys : null,
|
|
820
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
821
|
+
...framing,
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
function rowAt(rows, index) {
|
|
825
|
+
return 'groups' in rows ? rows.groups[index] : rows.documents[index];
|
|
826
|
+
}
|