@agentshouse/mdruntime 0.1.0-cfbf80d8d4644 → 0.1.0-cfcbb238a5fa0
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 +270 -203
- package/README.md +20 -10
- 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 +83 -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/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 +63 -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 +629 -0
- package/dist/chat/question.d.ts +9 -0
- package/dist/chat/question.d.ts.map +1 -0
- package/dist/chat/question.js +80 -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 +25 -12
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +75 -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 +44 -15
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1788 -400
- package/dist/conversation.d.ts +26 -10
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +414 -148
- 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 +97 -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 +87 -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 +5 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +22 -11
- 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 +32 -3
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +42 -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 +40 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +60 -21
- package/dist/reach.d.ts +8 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +60 -24
- package/dist/react.d.ts +31 -18
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +15 -11
- package/dist/read-side.d.ts +13 -6
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +394 -129
- 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 +9 -6
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +125 -68
- 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 +11 -14
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +89 -49
- package/dist/save.d.ts +31 -1
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +376 -47
- package/dist/search.d.ts +39 -8
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +132 -46
- package/dist/seed.d.ts +16 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +61 -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 +75 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +539 -200
- package/dist/turn.d.ts +7 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +45 -0
- package/dist/wire.d.ts +141 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +61 -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,54 +1,58 @@
|
|
|
1
|
-
import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq,
|
|
2
|
-
import {
|
|
1
|
+
import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, inArray, or, toArray, } from '@tanstack/db';
|
|
2
|
+
import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
|
|
3
3
|
import { documentKey } from './rows.js';
|
|
4
|
+
import { after, inUnits, ROW_WEIGHT } from './turn.js';
|
|
4
5
|
function every(expressions) {
|
|
5
6
|
const [first, second, ...rest] = expressions;
|
|
6
7
|
return second === undefined ? first : and(first, second, ...rest);
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
return second === undefined ? first : or(first, second, ...rest);
|
|
9
|
+
function same(held, value) {
|
|
10
|
+
return Array.isArray(held) ? held.includes(value) : held === value;
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
12
|
+
function ordered(held, [operator, bound]) {
|
|
13
|
+
if (typeof held !== typeof bound || held === null || typeof held === 'object')
|
|
14
|
+
return false;
|
|
15
|
+
const left = held;
|
|
16
|
+
const right = bound;
|
|
17
|
+
if (operator === 'gt')
|
|
18
|
+
return left > right;
|
|
19
|
+
if (operator === 'gte')
|
|
20
|
+
return left >= right;
|
|
21
|
+
if (operator === 'lt')
|
|
22
|
+
return left < right;
|
|
23
|
+
return left <= right;
|
|
14
24
|
}
|
|
15
|
-
function
|
|
25
|
+
function satisfied(test, held) {
|
|
16
26
|
if (test.op === 'eq')
|
|
17
|
-
return
|
|
18
|
-
if (test.op === 'ne')
|
|
19
|
-
return test.value
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const compare = { gt, gte, lt, lte };
|
|
37
|
-
return every(test.bounds.map(([operator, bound]) => compare[operator](field, bound)));
|
|
38
|
-
}
|
|
39
|
-
if (test.op === 'all') {
|
|
40
|
-
return every([not(isUndefined(size)), ...test.values.map((value) => inArray(value, field))]);
|
|
41
|
-
}
|
|
42
|
-
if (test.op === 'size')
|
|
43
|
-
return eq(size, test.size);
|
|
44
|
-
return test.exists ? not(isUndefined(written)) : isUndefined(written);
|
|
27
|
+
return same(held, test.value);
|
|
28
|
+
if (test.op === 'ne')
|
|
29
|
+
return !same(held, test.value);
|
|
30
|
+
if (test.op === 'in')
|
|
31
|
+
return test.values.some((value) => same(held, value));
|
|
32
|
+
if (test.op === 'nin')
|
|
33
|
+
return !test.values.some((value) => same(held, value));
|
|
34
|
+
if (test.op === 'range')
|
|
35
|
+
return test.bounds.every((bound) => ordered(held, bound));
|
|
36
|
+
if (test.op === 'all')
|
|
37
|
+
return Array.isArray(held) && test.values.every((value) => same(held, value));
|
|
38
|
+
return Array.isArray(held) && held.length === test.size;
|
|
39
|
+
}
|
|
40
|
+
function tested(test, reached) {
|
|
41
|
+
if (test.op === 'exists')
|
|
42
|
+
return test.exists === (reached !== undefined);
|
|
43
|
+
if (reached === undefined)
|
|
44
|
+
return test.op === 'ne' || test.op === 'nin';
|
|
45
|
+
return reached.some((held) => satisfied(test, held));
|
|
45
46
|
}
|
|
46
47
|
function conditioned(condition, row) {
|
|
47
48
|
if ('every' in condition)
|
|
48
|
-
return
|
|
49
|
+
return condition.every.every((item) => conditioned(item, row));
|
|
49
50
|
if ('some' in condition)
|
|
50
|
-
return
|
|
51
|
-
return tested(condition.test, row.
|
|
51
|
+
return condition.some.some((item) => conditioned(item, row));
|
|
52
|
+
return tested(condition.test, row.reached[condition.field]);
|
|
53
|
+
}
|
|
54
|
+
function narrowed(query, where, alias) {
|
|
55
|
+
return where === null ? query : query.fn.where((row) => conditioned(where, row[alias]));
|
|
52
56
|
}
|
|
53
57
|
function reached(reach, room, document) {
|
|
54
58
|
return reach === null
|
|
@@ -62,7 +66,6 @@ function scoped(scope, row, rooms, reach) {
|
|
|
62
66
|
...reached(reach, row.room, row.key),
|
|
63
67
|
...(scope.types === null ? [] : [inArray(row.type, [...scope.types])]),
|
|
64
68
|
...(scope.pathPrefix === null ? [] : [inArray(scope.pathPrefix, row.directories)]),
|
|
65
|
-
...(scope.where === null ? [] : [conditioned(scope.where, row)]),
|
|
66
69
|
]);
|
|
67
70
|
}
|
|
68
71
|
function keyed(key, row) {
|
|
@@ -77,16 +80,17 @@ function live(query) {
|
|
|
77
80
|
function related(q, rows, scope, field, kept, reach) {
|
|
78
81
|
const matched = kept === 'source' ? 'target' : 'source';
|
|
79
82
|
const from = q.from({ r: rows });
|
|
80
|
-
|
|
81
|
-
.where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [
|
|
83
|
+
const joined = from
|
|
84
|
+
.where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [inArray(field, r.forms)])]))
|
|
82
85
|
.innerJoin({ m: rows }, ({ r, m }) => eq(r[matched], m.key))
|
|
83
|
-
.where(({ m }) => scoped(scope, m, null, reach))
|
|
86
|
+
.where(({ m }) => scoped(scope, m, null, reach));
|
|
87
|
+
return narrowed(joined, scope.where, 'm')
|
|
84
88
|
.groupBy(({ r }) => r[kept])
|
|
85
89
|
.select(({ r }) => ({ key: r[kept] }));
|
|
86
90
|
}
|
|
87
91
|
function filtered(q, rows, plan) {
|
|
88
92
|
const from = q.from({ d: rows });
|
|
89
|
-
let query = from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach));
|
|
93
|
+
let query = narrowed(from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach)), plan.root.where, 'd');
|
|
90
94
|
if (plan.hop !== null) {
|
|
91
95
|
const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach);
|
|
92
96
|
query = query.innerJoin({ h: hop }, ({ d, h }) => eq(d.key, h.key));
|
|
@@ -97,14 +101,11 @@ function filtered(q, rows, plan) {
|
|
|
97
101
|
}
|
|
98
102
|
return query;
|
|
99
103
|
}
|
|
100
|
-
function
|
|
101
|
-
return
|
|
104
|
+
function bodied(shape) {
|
|
105
|
+
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
102
106
|
}
|
|
103
107
|
function documents(q, rows, plan, shape) {
|
|
104
108
|
let query = filtered(q, rows, plan);
|
|
105
|
-
if (shape.sections !== null || shape.preamble || shape.markdown) {
|
|
106
|
-
query = query.leftJoin({ b: rows }, ({ d, b }) => eq(d.body, b.key));
|
|
107
|
-
}
|
|
108
109
|
for (const { key, options } of shape.orderBy) {
|
|
109
110
|
query = query.orderBy(({ d }) => keyed(key, d), options);
|
|
110
111
|
}
|
|
@@ -113,18 +114,16 @@ function documents(q, rows, plan, shape) {
|
|
|
113
114
|
}
|
|
114
115
|
if (plan.limit !== null)
|
|
115
116
|
query = query.limit(plan.limit);
|
|
116
|
-
return query.select(({ d
|
|
117
|
+
return query.select(({ d }) => ({
|
|
117
118
|
room: d.room,
|
|
118
119
|
path: d.path,
|
|
119
120
|
type: d.type,
|
|
120
|
-
|
|
121
|
-
...(
|
|
122
|
-
...(shape
|
|
123
|
-
...(Array.isArray(shape.sections) ? named('section', shape.sections, b.sections) : {}),
|
|
124
|
-
...(shape.preamble ? { preamble: b.preamble } : {}),
|
|
125
|
-
...(shape.markdown ? { markdown: b.markdown } : {}),
|
|
121
|
+
binary: d.binary,
|
|
122
|
+
...(shape.fields === null ? {} : { fields: d.fields }),
|
|
123
|
+
...(bodied(shape) ? { text: d.text } : {}),
|
|
126
124
|
...(shape.backlinks ? { backlinks: toArray(backlinked(q, rows, d, plan.reach)) } : {}),
|
|
127
125
|
...(shape.change ? { change: d.change } : {}),
|
|
126
|
+
...(shape.fileId ? { fileId: d.fileId } : {}),
|
|
128
127
|
}));
|
|
129
128
|
}
|
|
130
129
|
function backlinked(q, rows, document, reach) {
|
|
@@ -168,63 +167,142 @@ function built(rows, plan) {
|
|
|
168
167
|
},
|
|
169
168
|
};
|
|
170
169
|
}
|
|
171
|
-
function
|
|
172
|
-
return Object.fromEntries(entries.filter(([, value]) => value !== undefined));
|
|
173
|
-
}
|
|
174
|
-
function part(row, prefix, names, whole) {
|
|
170
|
+
function sectionsOf(sections, names) {
|
|
175
171
|
if (names === 'all')
|
|
176
|
-
return
|
|
177
|
-
return
|
|
172
|
+
return sections;
|
|
173
|
+
return Object.fromEntries(names.flatMap((name) => (Object.hasOwn(sections, name) ? [[name, sections[name]]] : [])));
|
|
174
|
+
}
|
|
175
|
+
function bodyOf(row, key, bodies) {
|
|
176
|
+
const text = row.text;
|
|
177
|
+
if (text === null)
|
|
178
|
+
return null;
|
|
179
|
+
const held = bodies?.get(key);
|
|
180
|
+
if (held !== undefined && held.text === text)
|
|
181
|
+
return held.document;
|
|
182
|
+
const read = parse(text, row.path);
|
|
183
|
+
const document = read.ok ? read.document : null;
|
|
184
|
+
bodies?.set(key, { text, document });
|
|
185
|
+
return document;
|
|
178
186
|
}
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
187
|
+
function masked(pointers) {
|
|
188
|
+
const root = { whole: false, members: new Map() };
|
|
189
|
+
for (const pointer of pointers) {
|
|
190
|
+
let mask = root;
|
|
191
|
+
for (const token of pointerSegments(pointer)) {
|
|
192
|
+
const next = mask.members.get(token) ?? { whole: false, members: new Map() };
|
|
193
|
+
mask.members.set(token, next);
|
|
194
|
+
mask = next;
|
|
195
|
+
}
|
|
196
|
+
mask.whole = true;
|
|
197
|
+
}
|
|
198
|
+
return root;
|
|
199
|
+
}
|
|
200
|
+
function merged(left, right) {
|
|
201
|
+
if (left === undefined)
|
|
202
|
+
return right;
|
|
203
|
+
const members = new Map(left.members);
|
|
204
|
+
for (const [token, mask] of right.members)
|
|
205
|
+
members.set(token, merged(members.get(token), mask));
|
|
206
|
+
return { whole: left.whole || right.whole, members };
|
|
207
|
+
}
|
|
208
|
+
function cut(value, mask) {
|
|
209
|
+
if (mask.whole)
|
|
210
|
+
return value;
|
|
211
|
+
if (Array.isArray(value)) {
|
|
212
|
+
const each = {
|
|
213
|
+
whole: false,
|
|
214
|
+
members: new Map([...mask.members].filter(([token]) => !ARRAY_INDEX.test(token))),
|
|
215
|
+
};
|
|
216
|
+
const kept = [];
|
|
217
|
+
value.forEach((item, index) => {
|
|
218
|
+
const part = cut(item, merged(mask.members.get(String(index)), each));
|
|
219
|
+
if (part !== undefined)
|
|
220
|
+
kept[index] = part;
|
|
221
|
+
});
|
|
222
|
+
return kept.length === 0 ? undefined : kept;
|
|
223
|
+
}
|
|
224
|
+
if (typeof value !== 'object' || value === null)
|
|
225
|
+
return undefined;
|
|
226
|
+
const kept = Object.entries(value).flatMap(([key, held]) => {
|
|
227
|
+
const member = mask.members.get(key);
|
|
228
|
+
const part = member === undefined ? undefined : cut(held, member);
|
|
229
|
+
return part === undefined ? [] : [[key, part]];
|
|
230
|
+
});
|
|
231
|
+
return kept.length === 0 ? undefined : Object.fromEntries(kept);
|
|
232
|
+
}
|
|
233
|
+
function selected(fields, pointers) {
|
|
234
|
+
return (cut(fields, masked(pointers)) ?? {});
|
|
235
|
+
}
|
|
236
|
+
function documentOf(key, row, shape, bodies) {
|
|
237
|
+
if (row.type === null) {
|
|
238
|
+
const binary = row.binary;
|
|
239
|
+
return {
|
|
240
|
+
room: row.room,
|
|
241
|
+
path: row.path,
|
|
242
|
+
...(binary === null ? {} : { binaryFile: binary }),
|
|
243
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const body = bodied(shape) ? bodyOf(row, key, bodies) : null;
|
|
182
247
|
return {
|
|
183
248
|
room: row.room,
|
|
184
249
|
path: row.path,
|
|
185
250
|
type: row.type,
|
|
186
251
|
...(shape.fields === null
|
|
187
|
-
? {}
|
|
188
|
-
: { fields: part(row, 'field', shape.fields, row.fields) }),
|
|
189
|
-
...(shape.sections === null
|
|
190
252
|
? {}
|
|
191
253
|
: {
|
|
192
|
-
|
|
254
|
+
fields: shape.fields === 'all'
|
|
255
|
+
? row.fields
|
|
256
|
+
: selected(row.fields, shape.fields),
|
|
193
257
|
}),
|
|
194
|
-
...(shape.
|
|
195
|
-
|
|
258
|
+
...(shape.sections === null || body === null
|
|
259
|
+
? {}
|
|
260
|
+
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
261
|
+
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
262
|
+
...(shape.markdown && body !== null ? { markdown: body.content } : {}),
|
|
196
263
|
...(shape.backlinks
|
|
197
264
|
? {
|
|
198
265
|
backlinks: row.backlinks.map(({ path, type, field }) => ({ path, type, field })),
|
|
199
266
|
}
|
|
200
267
|
: {}),
|
|
201
268
|
...(shape.change ? { change: row.change } : {}),
|
|
269
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
202
270
|
};
|
|
203
271
|
}
|
|
204
|
-
function
|
|
272
|
+
function weight(row) {
|
|
273
|
+
return ROW_WEIGHT + (typeof row?.text === 'string' ? row.text.length : 0);
|
|
274
|
+
}
|
|
275
|
+
function rowsOfPlan(plan, entries, bodies, each = (row) => row) {
|
|
205
276
|
const shape = plan.shape;
|
|
206
277
|
if (shape.kind === 'documents') {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
278
|
+
const documents = [];
|
|
279
|
+
return after(inUnits(entries, ([key, row]) => {
|
|
280
|
+
documents.push(each(documentOf(key, row, shape, bodies)));
|
|
281
|
+
return weight(row);
|
|
282
|
+
}), () => ({ rows: { documents }, keys: entries.map(([key]) => key) }));
|
|
211
283
|
}
|
|
212
284
|
const bounded = shape.orderBy.length === 0 && plan.limit !== null ? entries.slice(0, plan.limit) : entries;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
};
|
|
285
|
+
const groups = [];
|
|
286
|
+
return after(inUnits(bounded, ([, row]) => {
|
|
287
|
+
groups.push(each({
|
|
288
|
+
key: shape.groupBy.map((_, index) => row[`key${index}`]),
|
|
289
|
+
count: row.count,
|
|
290
|
+
}));
|
|
291
|
+
return ROW_WEIGHT;
|
|
292
|
+
}), () => ({ rows: { groups }, keys: bounded.map(([key]) => key) }));
|
|
222
293
|
}
|
|
223
294
|
export function reconciled(previous, next) {
|
|
224
|
-
const before =
|
|
225
|
-
const
|
|
295
|
+
const before = previous.result;
|
|
296
|
+
const after = next.result;
|
|
297
|
+
if ('pending' in before || 'pending' in after) {
|
|
298
|
+
if (deepEquals(before, after))
|
|
299
|
+
return null;
|
|
300
|
+
return { keyed: next, message: next };
|
|
301
|
+
}
|
|
302
|
+
const earlier = 'groups' in before ? before.groups : before.documents;
|
|
303
|
+
const held = new Map(previous.keys.map((key, index) => [key, earlier[index]]));
|
|
226
304
|
const changed = [];
|
|
227
|
-
const rows =
|
|
305
|
+
const rows = ('groups' in after ? after.groups : after.documents).map((row, index) => {
|
|
228
306
|
const key = next.keys[index];
|
|
229
307
|
const kept = held.get(key);
|
|
230
308
|
if (kept !== undefined && deepEquals(kept, row))
|
|
@@ -233,22 +311,37 @@ export function reconciled(previous, next) {
|
|
|
233
311
|
return row;
|
|
234
312
|
});
|
|
235
313
|
const present = new Set(next.keys);
|
|
236
|
-
const survivors = previous.keys.filter((key) => present.has(key));
|
|
237
314
|
const removed = previous.keys.filter((key) => !present.has(key));
|
|
238
|
-
const moved =
|
|
239
|
-
|
|
240
|
-
if (changed.length === 0 && removed.length === 0 && !moved)
|
|
315
|
+
const moved = reordered(previous.keys, next.keys, present);
|
|
316
|
+
const covered = !deepEquals(before.coverage, after.coverage);
|
|
317
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered)
|
|
241
318
|
return null;
|
|
242
319
|
return {
|
|
243
320
|
keyed: {
|
|
244
|
-
result: 'groups' in
|
|
245
|
-
? { groups: rows }
|
|
246
|
-
: { documents: rows },
|
|
321
|
+
result: 'groups' in after
|
|
322
|
+
? { groups: rows, coverage: after.coverage }
|
|
323
|
+
: { documents: rows, coverage: after.coverage },
|
|
247
324
|
keys: next.keys,
|
|
248
325
|
},
|
|
249
|
-
|
|
326
|
+
message: {
|
|
327
|
+
changed,
|
|
328
|
+
removed,
|
|
329
|
+
order: moved ? next.keys : null,
|
|
330
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
331
|
+
},
|
|
250
332
|
};
|
|
251
333
|
}
|
|
334
|
+
function reordered(previous, next, present) {
|
|
335
|
+
let at = 0;
|
|
336
|
+
for (const key of previous) {
|
|
337
|
+
if (!present.has(key))
|
|
338
|
+
continue;
|
|
339
|
+
if (next[at] !== key)
|
|
340
|
+
return true;
|
|
341
|
+
at += 1;
|
|
342
|
+
}
|
|
343
|
+
return at !== next.length;
|
|
344
|
+
}
|
|
252
345
|
export function createReadSide() {
|
|
253
346
|
configureIndexDevMode({ enabled: false });
|
|
254
347
|
let sync;
|
|
@@ -268,23 +361,139 @@ export function createReadSide() {
|
|
|
268
361
|
});
|
|
269
362
|
const shared = new Map();
|
|
270
363
|
function searched(row) {
|
|
271
|
-
const
|
|
364
|
+
const read = row.text === null ? null : parse(row.text, row.path);
|
|
365
|
+
if (read === null || !read.ok)
|
|
366
|
+
return null;
|
|
272
367
|
return {
|
|
273
368
|
room: row.room,
|
|
274
369
|
path: row.path,
|
|
275
370
|
type: row.type,
|
|
276
371
|
fields: row.fields,
|
|
277
|
-
preamble:
|
|
278
|
-
sections:
|
|
372
|
+
preamble: read.document.preamble,
|
|
373
|
+
sections: read.document.sections,
|
|
279
374
|
};
|
|
280
375
|
}
|
|
281
|
-
function
|
|
282
|
-
const
|
|
283
|
-
if (
|
|
376
|
+
function kept(entry, key, row) {
|
|
377
|
+
const held = entry.rows.get(key);
|
|
378
|
+
if (held !== undefined && deepEquals(held, row))
|
|
284
379
|
return;
|
|
285
|
-
entry.
|
|
286
|
-
|
|
287
|
-
|
|
380
|
+
entry.rows.set(key, row);
|
|
381
|
+
}
|
|
382
|
+
function rebuilt(entry) {
|
|
383
|
+
return after(rowsOfPlan(entry.plan, [...entry.built.live.entries()], entry.bodies), ({ rows: found, keys }) => {
|
|
384
|
+
const present = new Set(keys);
|
|
385
|
+
for (const key of [...entry.rows.keys()])
|
|
386
|
+
if (!present.has(key))
|
|
387
|
+
entry.rows.delete(key);
|
|
388
|
+
for (const key of [...entry.bodies.keys()]) {
|
|
389
|
+
if (!present.has(key))
|
|
390
|
+
entry.bodies.delete(key);
|
|
391
|
+
}
|
|
392
|
+
keys.forEach((key, index) => kept(entry, key, rowAt(found, index)));
|
|
393
|
+
return keys;
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
function refreshed(entry, dirty) {
|
|
397
|
+
const shape = entry.plan.shape;
|
|
398
|
+
const keys = [...entry.built.live.keys()];
|
|
399
|
+
const named = new Set([...dirty, ...keys.filter((key) => !entry.rows.has(key))]);
|
|
400
|
+
const values = [...named].map((key) => [key, entry.built.live.get(key)]);
|
|
401
|
+
const done = inUnits(values, ([key, value]) => {
|
|
402
|
+
if (value !== undefined)
|
|
403
|
+
kept(entry, key, documentOf(key, value, shape, entry.bodies));
|
|
404
|
+
else {
|
|
405
|
+
entry.rows.delete(key);
|
|
406
|
+
entry.bodies.delete(key);
|
|
407
|
+
}
|
|
408
|
+
return weight(value);
|
|
409
|
+
});
|
|
410
|
+
return after(done, () => keys);
|
|
411
|
+
}
|
|
412
|
+
function current(entry, keys, coverage) {
|
|
413
|
+
const found = keys.map((key) => entry.rows.get(key));
|
|
414
|
+
return {
|
|
415
|
+
result: entry.plan.shape.kind === 'groups'
|
|
416
|
+
? { groups: found, coverage }
|
|
417
|
+
: { documents: found, coverage },
|
|
418
|
+
keys,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function delivered(entry, next) {
|
|
422
|
+
const previous = entry.result;
|
|
423
|
+
entry.result = next;
|
|
424
|
+
const message = previous === undefined ? null : changedFrom(previous, next);
|
|
425
|
+
if (message !== null)
|
|
426
|
+
for (const listener of entry.ready)
|
|
427
|
+
listener(message);
|
|
428
|
+
for (const listener of entry.fresh) {
|
|
429
|
+
entry.ready.add(listener);
|
|
430
|
+
listener(next);
|
|
431
|
+
}
|
|
432
|
+
entry.fresh.clear();
|
|
433
|
+
}
|
|
434
|
+
function run(entry) {
|
|
435
|
+
while (entry.listeners.size > 0 &&
|
|
436
|
+
(entry.whole || entry.dirty.size > 0 || entry.stale || entry.fresh.size > 0)) {
|
|
437
|
+
entry.stale = false;
|
|
438
|
+
const cover = entry.cover();
|
|
439
|
+
if ('pending' in cover) {
|
|
440
|
+
entry.whole = true;
|
|
441
|
+
entry.dirty.clear();
|
|
442
|
+
entry.rows.clear();
|
|
443
|
+
entry.bodies.clear();
|
|
444
|
+
delivered(entry, { result: cover, keys: [] });
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
const whole = entry.whole ||
|
|
448
|
+
entry.plan.shape.kind === 'groups' ||
|
|
449
|
+
entry.result === undefined ||
|
|
450
|
+
'pending' in entry.result.result;
|
|
451
|
+
const dirty = entry.dirty;
|
|
452
|
+
entry.dirty = new Set();
|
|
453
|
+
entry.whole = false;
|
|
454
|
+
const keys = whole ? rebuilt(entry) : refreshed(entry, dirty);
|
|
455
|
+
if (keys instanceof Promise) {
|
|
456
|
+
void keys
|
|
457
|
+
.then((found) => {
|
|
458
|
+
if (entry.listeners.size > 0)
|
|
459
|
+
delivered(entry, current(entry, found, cover.coverage));
|
|
460
|
+
run(entry);
|
|
461
|
+
})
|
|
462
|
+
.catch(() => {
|
|
463
|
+
entry.running = false;
|
|
464
|
+
});
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
delivered(entry, current(entry, keys, cover.coverage));
|
|
468
|
+
}
|
|
469
|
+
entry.running = false;
|
|
470
|
+
}
|
|
471
|
+
function wake(entry) {
|
|
472
|
+
if (entry.subscription === null || entry.running)
|
|
473
|
+
return;
|
|
474
|
+
entry.running = true;
|
|
475
|
+
try {
|
|
476
|
+
run(entry);
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
entry.running = false;
|
|
480
|
+
throw error;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function recover() {
|
|
484
|
+
for (const entry of shared.values()) {
|
|
485
|
+
if (entry.result === undefined || entry.subscription === null)
|
|
486
|
+
continue;
|
|
487
|
+
const cover = entry.cover();
|
|
488
|
+
const before = entry.result.result;
|
|
489
|
+
const held = 'pending' in cover
|
|
490
|
+
? deepEquals(before, cover)
|
|
491
|
+
: 'coverage' in before && deepEquals(before.coverage, cover.coverage);
|
|
492
|
+
if (held)
|
|
493
|
+
continue;
|
|
494
|
+
entry.stale = true;
|
|
495
|
+
wake(entry);
|
|
496
|
+
}
|
|
288
497
|
}
|
|
289
498
|
return {
|
|
290
499
|
apply(set) {
|
|
@@ -295,38 +504,52 @@ export function createReadSide() {
|
|
|
295
504
|
for (const row of change.after)
|
|
296
505
|
next.set(row.key, row);
|
|
297
506
|
}
|
|
298
|
-
if (next.size
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
507
|
+
if (next.size > 0) {
|
|
508
|
+
sync.begin();
|
|
509
|
+
for (const [key, row] of next) {
|
|
510
|
+
if (row === null) {
|
|
511
|
+
if (rows.has(key))
|
|
512
|
+
sync.write({ type: 'delete', key });
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
sync.write({ type: rows.has(key) ? 'update' : 'insert', value: row });
|
|
306
516
|
}
|
|
307
|
-
sync.
|
|
517
|
+
sync.commit();
|
|
308
518
|
}
|
|
309
|
-
|
|
519
|
+
recover();
|
|
310
520
|
},
|
|
311
|
-
|
|
521
|
+
recover,
|
|
522
|
+
async query(plan, at) {
|
|
312
523
|
const { live: collection, dispose } = built(rows, plan);
|
|
313
524
|
try {
|
|
314
525
|
await collection.toArrayWhenReady();
|
|
315
|
-
|
|
526
|
+
const entries = [...collection.entries()];
|
|
527
|
+
const held = at();
|
|
528
|
+
const found = await rowsOfPlan(plan, entries, null, structuredClone);
|
|
529
|
+
return { rows: found.rows, at: held };
|
|
316
530
|
}
|
|
317
531
|
finally {
|
|
318
532
|
dispose();
|
|
319
533
|
}
|
|
320
534
|
},
|
|
321
|
-
watch(plan, identity,
|
|
535
|
+
watch(plan, identity, cover, deliverTo) {
|
|
322
536
|
let entry = shared.get(identity);
|
|
323
537
|
if (entry === undefined) {
|
|
324
538
|
const created = {
|
|
325
539
|
plan,
|
|
326
540
|
built: built(rows, plan),
|
|
541
|
+
cover,
|
|
327
542
|
result: undefined,
|
|
543
|
+
rows: new Map(),
|
|
544
|
+
bodies: new Map(),
|
|
328
545
|
listeners: new Set(),
|
|
546
|
+
fresh: new Set(),
|
|
547
|
+
ready: new Set(),
|
|
329
548
|
subscription: null,
|
|
549
|
+
whole: true,
|
|
550
|
+
dirty: new Set(),
|
|
551
|
+
stale: false,
|
|
552
|
+
running: false,
|
|
330
553
|
};
|
|
331
554
|
created.built.live.on('status:change', ({ status }) => {
|
|
332
555
|
if (status === 'cleaned-up' && created.listeners.size === 0)
|
|
@@ -336,29 +559,40 @@ export function createReadSide() {
|
|
|
336
559
|
entry = created;
|
|
337
560
|
}
|
|
338
561
|
const observed = entry;
|
|
339
|
-
observed.listeners.add(
|
|
562
|
+
observed.listeners.add(deliverTo);
|
|
340
563
|
observed.built.live.onFirstReady(() => {
|
|
341
|
-
if (!observed.listeners.has(
|
|
564
|
+
if (!observed.listeners.has(deliverTo))
|
|
342
565
|
return;
|
|
566
|
+
observed.fresh.add(deliverTo);
|
|
343
567
|
if (observed.subscription === null) {
|
|
344
|
-
observed.
|
|
345
|
-
observed.subscription = observed.built.live.subscribeChanges(() => {
|
|
346
|
-
|
|
568
|
+
observed.whole = true;
|
|
569
|
+
observed.subscription = observed.built.live.subscribeChanges((changes) => {
|
|
570
|
+
if (observed.plan.shape.kind === 'groups')
|
|
571
|
+
observed.whole = true;
|
|
572
|
+
else
|
|
573
|
+
for (const { key } of changes)
|
|
574
|
+
observed.dirty.add(key);
|
|
575
|
+
wake(observed);
|
|
347
576
|
}, { includeInitialState: false });
|
|
348
577
|
}
|
|
349
|
-
|
|
578
|
+
wake(observed);
|
|
350
579
|
});
|
|
351
580
|
return () => {
|
|
352
|
-
|
|
581
|
+
observed.fresh.delete(deliverTo);
|
|
582
|
+
observed.ready.delete(deliverTo);
|
|
583
|
+
if (!observed.listeners.delete(deliverTo) || observed.listeners.size > 0)
|
|
353
584
|
return;
|
|
354
585
|
observed.subscription?.unsubscribe();
|
|
355
586
|
observed.subscription = null;
|
|
587
|
+
observed.result = undefined;
|
|
588
|
+
observed.rows.clear();
|
|
589
|
+
observed.bodies.clear();
|
|
590
|
+
observed.dirty.clear();
|
|
591
|
+
observed.whole = true;
|
|
356
592
|
};
|
|
357
593
|
},
|
|
358
|
-
|
|
359
|
-
return rows.toArray
|
|
360
|
-
.filter((row) => row.kind === 'document' && row.type !== null)
|
|
361
|
-
.map(searched);
|
|
594
|
+
room(room) {
|
|
595
|
+
return rows.toArray.flatMap((row) => row.kind !== 'document' || row.type === null || row.room !== room ? [] : [row.path]);
|
|
362
596
|
},
|
|
363
597
|
document(room, path) {
|
|
364
598
|
const row = rows.get(documentKey(room, path));
|
|
@@ -367,6 +601,8 @@ export function createReadSide() {
|
|
|
367
601
|
close() {
|
|
368
602
|
for (const entry of shared.values()) {
|
|
369
603
|
entry.listeners.clear();
|
|
604
|
+
entry.fresh.clear();
|
|
605
|
+
entry.ready.clear();
|
|
370
606
|
entry.subscription?.unsubscribe();
|
|
371
607
|
entry.built.dispose();
|
|
372
608
|
}
|
|
@@ -375,3 +611,32 @@ export function createReadSide() {
|
|
|
375
611
|
},
|
|
376
612
|
};
|
|
377
613
|
}
|
|
614
|
+
function changedFrom(previous, next) {
|
|
615
|
+
const before = previous.result;
|
|
616
|
+
const after = next.result;
|
|
617
|
+
if ('pending' in before || 'pending' in after)
|
|
618
|
+
return deepEquals(before, after) ? null : next;
|
|
619
|
+
const earlier = 'groups' in before ? before.groups : before.documents;
|
|
620
|
+
const held = new Map(previous.keys.map((key, index) => [key, earlier[index]]));
|
|
621
|
+
const later = 'groups' in after ? after.groups : after.documents;
|
|
622
|
+
const changed = [];
|
|
623
|
+
next.keys.forEach((key, index) => {
|
|
624
|
+
if (held.get(key) !== later[index])
|
|
625
|
+
changed.push([key, later[index]]);
|
|
626
|
+
});
|
|
627
|
+
const present = new Set(next.keys);
|
|
628
|
+
const removed = previous.keys.filter((key) => !present.has(key));
|
|
629
|
+
const moved = reordered(previous.keys, next.keys, present);
|
|
630
|
+
const covered = !deepEquals(before.coverage, after.coverage);
|
|
631
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered)
|
|
632
|
+
return null;
|
|
633
|
+
return {
|
|
634
|
+
changed,
|
|
635
|
+
removed,
|
|
636
|
+
order: moved ? next.keys : null,
|
|
637
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function rowAt(rows, index) {
|
|
641
|
+
return 'groups' in rows ? rows.groups[index] : rows.documents[index];
|
|
642
|
+
}
|