@agentshouse/mdruntime 0.1.0-cacb4c3f6734e → 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 +277 -222
- package/README.md +20 -10
- package/dist/adapter.d.ts +95 -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 +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +54 -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 +21 -0
- package/dist/chat/markdown.d.ts.map +1 -0
- package/dist/chat/markdown.js +246 -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 +171 -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/pieces.d.ts +62 -0
- package/dist/chat/pieces.d.ts.map +1 -0
- package/dist/chat/pieces.js +1110 -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 +630 -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/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.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 +30 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +79 -16
- package/dist/collection.d.ts +23 -13
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +349 -36
- package/dist/connection.d.ts +50 -16
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1968 -424
- package/dist/conversation.d.ts +31 -10
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +521 -170
- 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/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 +15 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +102 -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 +11 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +91 -24
- 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 +18 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +247 -165
- package/dist/query.d.ts +60 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +41 -38
- package/dist/reach.d.ts +8 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +73 -24
- package/dist/react.d.ts +31 -20
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +13 -21
- package/dist/read-side.d.ts +16 -7
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +495 -102
- 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 +72 -42
- package/dist/replica.d.ts +17 -8
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +165 -79
- package/dist/room-draft.d.ts +38 -9
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +141 -33
- package/dist/rows.d.ts +10 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +19 -14
- package/dist/save.d.ts +35 -3
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +522 -107
- 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 +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +398 -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 +91 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +704 -283
- 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 +157 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +65 -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,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,33 +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,
|
|
153
|
+
binary: d.binary,
|
|
154
|
+
...Object.fromEntries(shape.orderBy.map(({ key }, index) => [`order${index}`, keyed(key, d)])),
|
|
124
155
|
...(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 } : {}),
|
|
156
|
+
...(bodied(shape) ? { text: d.text } : {}),
|
|
129
157
|
...(shape.backlinks ? { backlinks: toArray(backlinked(q, rows, d, plan.reach)) } : {}),
|
|
130
158
|
...(shape.change ? { change: d.change } : {}),
|
|
159
|
+
...(shape.fileId ? { fileId: d.fileId } : {}),
|
|
131
160
|
}));
|
|
132
161
|
}
|
|
133
162
|
function backlinked(q, rows, document, reach) {
|
|
@@ -138,15 +167,13 @@ function backlinked(q, rows, document, reach) {
|
|
|
138
167
|
.orderBy(({ r }) => r.field, { stringSort: 'lexical' })
|
|
139
168
|
.select(({ r }) => ({ path: r.path, type: r.type, field: r.field }));
|
|
140
169
|
}
|
|
141
|
-
function groups(q, stage, shape
|
|
170
|
+
function groups(q, stage, shape) {
|
|
142
171
|
const keys = shape.groupBy.map((_, index) => `key${index}`);
|
|
143
172
|
const from = q.from({ m: stage });
|
|
144
173
|
let query = from.groupBy(({ m }) => keys.map((key) => m[key]));
|
|
145
174
|
for (const { by, direction } of shape.orderBy) {
|
|
146
175
|
query = query.orderBy(({ m }) => (by === 'count' ? count(m.key) : m[keys[by]]), direction);
|
|
147
176
|
}
|
|
148
|
-
if (limit !== null && shape.orderBy.length > 0)
|
|
149
|
-
query = query.limit(limit);
|
|
150
177
|
return query.select(({ m }) => ({
|
|
151
178
|
...Object.fromEntries(keys.map((key) => [key, m[key]])),
|
|
152
179
|
count: count(m.key),
|
|
@@ -156,28 +183,49 @@ function built(rows, plan) {
|
|
|
156
183
|
const shape = plan.shape;
|
|
157
184
|
if (shape.kind === 'documents') {
|
|
158
185
|
const collection = live((q) => documents(q, rows, plan, shape));
|
|
159
|
-
|
|
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
|
+
};
|
|
160
198
|
}
|
|
161
199
|
const stage = live((q) => filtered(q, rows, plan).select(({ d }) => ({
|
|
162
200
|
key: d.key,
|
|
163
201
|
...Object.fromEntries(shape.groupBy.map((key, index) => [`key${index}`, keyed(key, d)])),
|
|
164
202
|
})));
|
|
165
|
-
const collection = live((q) => groups(q, stage, shape
|
|
203
|
+
const collection = live((q) => groups(q, stage, shape));
|
|
166
204
|
return {
|
|
167
205
|
live: collection,
|
|
206
|
+
count: null,
|
|
168
207
|
dispose: () => {
|
|
169
208
|
void collection.cleanup();
|
|
170
209
|
void stage.cleanup();
|
|
171
210
|
},
|
|
172
211
|
};
|
|
173
212
|
}
|
|
174
|
-
function
|
|
175
|
-
return Object.fromEntries(entries.filter(([, value]) => value !== undefined));
|
|
176
|
-
}
|
|
177
|
-
function part(row, prefix, names, whole) {
|
|
213
|
+
function sectionsOf(sections, names) {
|
|
178
214
|
if (names === 'all')
|
|
179
|
-
return
|
|
180
|
-
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;
|
|
181
229
|
}
|
|
182
230
|
function masked(pointers) {
|
|
183
231
|
const root = { whole: false, members: new Map() };
|
|
@@ -228,9 +276,17 @@ function cut(value, mask) {
|
|
|
228
276
|
function selected(fields, pointers) {
|
|
229
277
|
return (cut(fields, masked(pointers)) ?? {});
|
|
230
278
|
}
|
|
231
|
-
function documentOf(row, shape) {
|
|
232
|
-
if (row.type === null)
|
|
233
|
-
|
|
279
|
+
function documentOf(key, row, shape, bodies) {
|
|
280
|
+
if (row.type === null) {
|
|
281
|
+
const binary = row.binary;
|
|
282
|
+
return {
|
|
283
|
+
room: row.room,
|
|
284
|
+
path: row.path,
|
|
285
|
+
...(binary === null ? {} : { binaryFile: binary }),
|
|
286
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const body = bodied(shape) ? bodyOf(row, key, bodies) : null;
|
|
234
290
|
return {
|
|
235
291
|
room: row.room,
|
|
236
292
|
path: row.path,
|
|
@@ -242,45 +298,95 @@ function documentOf(row, shape) {
|
|
|
242
298
|
? row.fields
|
|
243
299
|
: selected(row.fields, shape.fields),
|
|
244
300
|
}),
|
|
245
|
-
...(shape.sections === null
|
|
301
|
+
...(shape.sections === null || body === null
|
|
246
302
|
? {}
|
|
247
|
-
: {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
...(shape.preamble ? { preamble: row.preamble } : {}),
|
|
251
|
-
...(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 } : {}),
|
|
252
306
|
...(shape.backlinks
|
|
253
307
|
? {
|
|
254
|
-
backlinks: row.backlinks
|
|
308
|
+
backlinks: row.backlinks
|
|
309
|
+
.slice(0, MAXIMUM_WINDOW)
|
|
310
|
+
.map(({ path, type, field }) => ({ path, type, field })),
|
|
255
311
|
}
|
|
256
312
|
: {}),
|
|
257
313
|
...(shape.change ? { change: row.change } : {}),
|
|
314
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
258
315
|
};
|
|
259
316
|
}
|
|
260
|
-
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) {
|
|
261
334
|
const shape = plan.shape;
|
|
262
335
|
if (shape.kind === 'documents') {
|
|
263
336
|
return {
|
|
264
|
-
|
|
265
|
-
|
|
337
|
+
...windowed(entries, plan.limit, plan.after?.text, ([, row]) => documentCursor(shape, row)),
|
|
338
|
+
total,
|
|
266
339
|
};
|
|
267
340
|
}
|
|
268
|
-
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));
|
|
346
|
+
return {
|
|
347
|
+
...windowed(following, plan.limit, plan.after?.text, (entry) => cursorOf(groupValues(shape, entry))),
|
|
348
|
+
total: plan.total ? entries.length : undefined,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function framed(rows, { next, total }) {
|
|
269
352
|
return {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
count: row.count,
|
|
274
|
-
})),
|
|
275
|
-
}),
|
|
276
|
-
keys: bounded.map(([key]) => key),
|
|
353
|
+
...rows,
|
|
354
|
+
...(next === undefined ? {} : { next }),
|
|
355
|
+
...(total === undefined ? {} : { total }),
|
|
277
356
|
};
|
|
278
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
|
+
}
|
|
279
378
|
export function reconciled(previous, next) {
|
|
280
|
-
const before =
|
|
281
|
-
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]]));
|
|
282
388
|
const changed = [];
|
|
283
|
-
const rows =
|
|
389
|
+
const rows = ('groups' in after ? after.groups : after.documents).map((row, index) => {
|
|
284
390
|
const key = next.keys[index];
|
|
285
391
|
const kept = held.get(key);
|
|
286
392
|
if (kept !== undefined && deepEquals(kept, row))
|
|
@@ -289,22 +395,48 @@ export function reconciled(previous, next) {
|
|
|
289
395
|
return row;
|
|
290
396
|
});
|
|
291
397
|
const present = new Set(next.keys);
|
|
292
|
-
const survivors = previous.keys.filter((key) => present.has(key));
|
|
293
398
|
const removed = previous.keys.filter((key) => !present.has(key));
|
|
294
|
-
const moved =
|
|
295
|
-
|
|
296
|
-
|
|
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) {
|
|
297
403
|
return null;
|
|
404
|
+
}
|
|
405
|
+
const window = { next: after.next, total: after.total };
|
|
298
406
|
return {
|
|
299
407
|
keyed: {
|
|
300
|
-
result: 'groups' in
|
|
301
|
-
? { groups: rows }
|
|
302
|
-
: { documents: rows },
|
|
408
|
+
result: 'groups' in after
|
|
409
|
+
? framed({ groups: rows, coverage: after.coverage }, window)
|
|
410
|
+
: framed({ documents: rows, coverage: after.coverage }, window),
|
|
303
411
|
keys: next.keys,
|
|
304
412
|
},
|
|
305
|
-
|
|
413
|
+
message: {
|
|
414
|
+
changed,
|
|
415
|
+
removed,
|
|
416
|
+
order: moved ? next.keys : null,
|
|
417
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
418
|
+
...framing,
|
|
419
|
+
},
|
|
306
420
|
};
|
|
307
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
|
+
}
|
|
308
440
|
export function createReadSide() {
|
|
309
441
|
configureIndexDevMode({ enabled: false });
|
|
310
442
|
let sync;
|
|
@@ -323,107 +455,336 @@ export function createReadSide() {
|
|
|
323
455
|
},
|
|
324
456
|
});
|
|
325
457
|
const shared = new Map();
|
|
458
|
+
let writing = false;
|
|
459
|
+
let publishing = null;
|
|
460
|
+
let closed = false;
|
|
326
461
|
function searched(row) {
|
|
327
|
-
const
|
|
462
|
+
const read = row.text === null ? null : parse(row.text, row.path);
|
|
463
|
+
if (read === null || !read.ok)
|
|
464
|
+
return null;
|
|
328
465
|
return {
|
|
329
466
|
room: row.room,
|
|
330
467
|
path: row.path,
|
|
331
468
|
type: row.type,
|
|
332
469
|
fields: row.fields,
|
|
333
|
-
preamble:
|
|
334
|
-
sections:
|
|
470
|
+
preamble: read.document.preamble,
|
|
471
|
+
sections: read.document.sections,
|
|
335
472
|
};
|
|
336
473
|
}
|
|
337
|
-
function
|
|
338
|
-
const
|
|
339
|
-
if (
|
|
474
|
+
function kept(entry, key, row) {
|
|
475
|
+
const held = entry.rows.get(key);
|
|
476
|
+
if (held !== undefined && deepEquals(held, row))
|
|
340
477
|
return;
|
|
341
|
-
entry.
|
|
342
|
-
for (const listener of entry.listeners)
|
|
343
|
-
listener(next.change);
|
|
478
|
+
entry.rows.set(key, row);
|
|
344
479
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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);
|
|
353
492
|
}
|
|
354
|
-
|
|
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;
|
|
549
|
+
}
|
|
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
|
+
});
|
|
355
569
|
return;
|
|
356
|
-
sync.begin();
|
|
357
|
-
for (const [key, row] of next) {
|
|
358
|
-
if (row === null) {
|
|
359
|
-
if (rows.has(key))
|
|
360
|
-
sync.write({ type: 'delete', key });
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
sync.write({ type: rows.has(key) ? 'update' : 'insert', value: row });
|
|
364
570
|
}
|
|
365
|
-
|
|
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();
|
|
366
675
|
},
|
|
367
|
-
|
|
368
|
-
|
|
676
|
+
recover,
|
|
677
|
+
settled,
|
|
678
|
+
async query(plan, at) {
|
|
679
|
+
const { live: collection, count, dispose } = built(rows, plan);
|
|
369
680
|
try {
|
|
370
681
|
await collection.toArrayWhenReady();
|
|
371
|
-
|
|
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 };
|
|
372
689
|
}
|
|
373
690
|
finally {
|
|
374
691
|
dispose();
|
|
375
692
|
}
|
|
376
693
|
},
|
|
377
|
-
watch(plan, identity,
|
|
694
|
+
watch(plan, identity, cover, deliverTo) {
|
|
378
695
|
let entry = shared.get(identity);
|
|
379
696
|
if (entry === undefined) {
|
|
380
697
|
const created = {
|
|
381
698
|
plan,
|
|
382
699
|
built: built(rows, plan),
|
|
700
|
+
cover,
|
|
383
701
|
result: undefined,
|
|
702
|
+
rows: new Map(),
|
|
703
|
+
bodies: new Map(),
|
|
384
704
|
listeners: new Set(),
|
|
705
|
+
fresh: new Set(),
|
|
706
|
+
ready: new Set(),
|
|
385
707
|
subscription: null,
|
|
708
|
+
counting: null,
|
|
709
|
+
whole: true,
|
|
710
|
+
dirty: new Set(),
|
|
711
|
+
stale: false,
|
|
712
|
+
running: false,
|
|
386
713
|
};
|
|
387
714
|
created.built.live.on('status:change', ({ status }) => {
|
|
388
|
-
if (status
|
|
715
|
+
if (status !== 'cleaned-up' || created.listeners.size > 0)
|
|
716
|
+
return;
|
|
717
|
+
if (shared.get(identity) === created)
|
|
389
718
|
shared.delete(identity);
|
|
390
719
|
});
|
|
391
720
|
shared.set(identity, created);
|
|
392
721
|
entry = created;
|
|
393
722
|
}
|
|
394
723
|
const observed = entry;
|
|
395
|
-
observed.listeners.add(
|
|
396
|
-
observed.built.live.onFirstReady(() =>
|
|
397
|
-
|
|
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))
|
|
398
734
|
return;
|
|
735
|
+
observed.fresh.add(deliverTo);
|
|
399
736
|
if (observed.subscription === null) {
|
|
400
|
-
observed.
|
|
401
|
-
observed.subscription = observed.built.live.subscribeChanges(() => {
|
|
402
|
-
|
|
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();
|
|
403
745
|
}, { includeInitialState: false });
|
|
746
|
+
observed.counting =
|
|
747
|
+
observed.built.count?.subscribeChanges(() => {
|
|
748
|
+
observed.stale = true;
|
|
749
|
+
settled();
|
|
750
|
+
}, { includeInitialState: false }) ?? null;
|
|
404
751
|
}
|
|
405
|
-
|
|
752
|
+
wake(observed);
|
|
406
753
|
});
|
|
407
754
|
return () => {
|
|
408
|
-
|
|
755
|
+
observed.fresh.delete(deliverTo);
|
|
756
|
+
observed.ready.delete(deliverTo);
|
|
757
|
+
if (!observed.listeners.delete(deliverTo) || observed.listeners.size > 0)
|
|
409
758
|
return;
|
|
410
759
|
observed.subscription?.unsubscribe();
|
|
411
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();
|
|
412
771
|
};
|
|
413
772
|
},
|
|
414
|
-
|
|
415
|
-
return rows.toArray
|
|
416
|
-
.filter((row) => row.kind === 'document' && row.type !== null)
|
|
417
|
-
.map(searched);
|
|
773
|
+
room(room) {
|
|
774
|
+
return rows.toArray.flatMap((row) => row.kind !== 'document' || row.type === null || row.room !== room ? [] : [row.path]);
|
|
418
775
|
},
|
|
419
776
|
document(room, path) {
|
|
420
777
|
const row = rows.get(documentKey(room, path));
|
|
421
778
|
return row === undefined || row.type === null ? null : searched(row);
|
|
422
779
|
},
|
|
423
780
|
close() {
|
|
781
|
+
closed = true;
|
|
424
782
|
for (const entry of shared.values()) {
|
|
425
783
|
entry.listeners.clear();
|
|
784
|
+
entry.fresh.clear();
|
|
785
|
+
entry.ready.clear();
|
|
426
786
|
entry.subscription?.unsubscribe();
|
|
787
|
+
entry.counting?.unsubscribe();
|
|
427
788
|
entry.built.dispose();
|
|
428
789
|
}
|
|
429
790
|
shared.clear();
|
|
@@ -431,3 +792,35 @@ export function createReadSide() {
|
|
|
431
792
|
},
|
|
432
793
|
};
|
|
433
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
|
+
}
|