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