@agentshouse/mdruntime 0.1.0-cbb8d8acc9d19 → 0.1.0-cc56db96f86ae
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 +285 -225
- package/README.md +21 -10
- package/dist/adapter.d.ts +114 -26
- package/dist/adapter.d.ts.map +1 -1
- 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 +147 -40
- package/dist/change-set.d.ts +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +50 -16
- package/dist/change.d.ts +45 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +34 -0
- 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/house.d.ts +25 -0
- package/dist/chat/house.d.ts.map +1 -0
- package/dist/chat/house.js +38 -0
- package/dist/chat/input.d.ts +11 -0
- package/dist/chat/input.d.ts.map +1 -0
- package/dist/chat/input.js +92 -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 +28 -0
- package/dist/chat/list.d.ts.map +1 -0
- package/dist/chat/list.js +103 -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 +7 -0
- package/dist/chat/message.d.ts.map +1 -0
- package/dist/chat/message.js +237 -0
- package/dist/chat/messages.d.ts +29 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +190 -0
- package/dist/chat/name.d.ts +7 -0
- package/dist/chat/name.d.ts.map +1 -0
- package/dist/chat/name.js +37 -0
- package/dist/chat/opened.d.ts +12 -0
- package/dist/chat/opened.d.ts.map +1 -0
- package/dist/chat/opened.js +19 -0
- package/dist/chat/overlay.d.ts +32 -0
- package/dist/chat/overlay.d.ts.map +1 -0
- package/dist/chat/overlay.js +103 -0
- package/dist/chat/parts.d.ts +58 -0
- package/dist/chat/parts.d.ts.map +1 -0
- package/dist/chat/parts.js +120 -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 +68 -0
- package/dist/chat/provider.d.ts.map +1 -0
- package/dist/chat/provider.js +619 -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 +37 -0
- package/dist/chat/ready.d.ts.map +1 -0
- package/dist/chat/ready.js +93 -0
- package/dist/chat/room-file.d.ts +8 -0
- package/dist/chat/room-file.d.ts.map +1 -0
- package/dist/chat/room-file.js +67 -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 +306 -0
- package/dist/chat.d.ts +15 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +8 -0
- package/dist/client.d.ts +33 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +81 -16
- package/dist/collection.d.ts +23 -13
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +367 -38
- package/dist/connection.d.ts +54 -16
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2582 -486
- package/dist/conversation.d.ts +39 -12
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +546 -171
- package/dist/digest.d.ts +2 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +100 -0
- 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/file.d.ts +2 -10
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +3 -120
- package/dist/host.d.ts +15 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +107 -21
- 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 +20 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/memory.d.ts +11 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +92 -27
- package/dist/model.d.ts +7 -4
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -1
- package/dist/observation.d.ts +7 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +29 -12
- package/dist/open.d.ts +2 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +1 -0
- package/dist/outcome.d.ts +57 -3
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +67 -1
- package/dist/overlay.d.ts +18 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +271 -165
- package/dist/query.d.ts +61 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +40 -7
- package/dist/reach.d.ts +13 -4
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +201 -47
- 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 +18 -9
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +903 -157
- 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 +198 -0
- package/dist/refresh.d.ts +13 -3
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +73 -43
- package/dist/replica.d.ts +16 -8
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +198 -91
- package/dist/room-draft.d.ts +40 -9
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +153 -37
- 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 +72 -4
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +812 -145
- package/dist/search.d.ts +52 -8
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +242 -46
- 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 +19 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +461 -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 +90 -23
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +675 -295
- package/dist/tables.d.ts +181 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +309 -0
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +67 -0
- package/dist/windows.d.ts +21 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +84 -0
- package/dist/wire.d.ts +165 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +67 -31
- package/package.json +30 -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,9 @@
|
|
|
1
|
-
import { and,
|
|
2
|
-
import { ARRAY_INDEX, pointerSegments } from '@agentshouse/mdmodel';
|
|
3
|
-
import { rowsOf } from './observation.js';
|
|
1
|
+
import { and, coalesce, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, inArray, not, or, } from '@tanstack/db';
|
|
2
|
+
import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
|
|
3
|
+
import { rowsOf, } from './observation.js';
|
|
4
4
|
import { documentKey } from './rows.js';
|
|
5
|
+
import { after, inUnits, ROW_WEIGHT, stepped } from './turn.js';
|
|
6
|
+
import { beyond, cursorOf, FROM_START, MAXIMUM_WINDOW } from './windows.js';
|
|
5
7
|
function every(expressions) {
|
|
6
8
|
const [first, second, ...rest] = expressions;
|
|
7
9
|
return second === undefined ? first : and(first, second, ...rest);
|
|
@@ -54,16 +56,21 @@ function conditioned(condition, row) {
|
|
|
54
56
|
function narrowed(query, where, alias) {
|
|
55
57
|
return where === null ? query : query.fn.where((row) => conditioned(where, row[alias]));
|
|
56
58
|
}
|
|
57
|
-
function reached(reach, room, document) {
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
function reached(reach, locked, room, document) {
|
|
60
|
+
return [
|
|
61
|
+
...(reach === null
|
|
62
|
+
? []
|
|
63
|
+
: [or(inArray(room, [...reach.rooms]), inArray(document, [...reach.documents]))]),
|
|
64
|
+
...(locked.length === 0 ? [] : [not(inArray(room, [...locked]))]),
|
|
65
|
+
];
|
|
61
66
|
}
|
|
62
|
-
function scoped(scope, row, rooms, reach) {
|
|
67
|
+
function scoped(scope, row, rooms, reach, locked, paths = null, folder = null) {
|
|
63
68
|
return every([
|
|
64
69
|
eq(row.kind, 'document'),
|
|
65
70
|
...(rooms === null ? [] : [inArray(row.room, [...rooms])]),
|
|
66
|
-
...
|
|
71
|
+
...(paths === null ? [] : [inArray(row.path, [...paths])]),
|
|
72
|
+
...(folder === null ? [] : [eq(row.folder, folder)]),
|
|
73
|
+
...reached(reach, locked, row.room, row.key),
|
|
67
74
|
...(scope.types === null ? [] : [inArray(row.type, [...scope.types])]),
|
|
68
75
|
...(scope.pathPrefix === null ? [] : [inArray(scope.pathPrefix, row.directories)]),
|
|
69
76
|
]);
|
|
@@ -71,82 +78,165 @@ function scoped(scope, row, rooms, reach) {
|
|
|
71
78
|
function keyed(key, row) {
|
|
72
79
|
return key.of === 'field' ? row.values[key.field] : row[key.of];
|
|
73
80
|
}
|
|
81
|
+
function readied(live) {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
live.onFirstReady(resolve);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function streamed(value) {
|
|
87
|
+
return coalesce(value);
|
|
88
|
+
}
|
|
74
89
|
function live(query) {
|
|
75
90
|
return createLiveQueryCollection({
|
|
76
91
|
query: query,
|
|
77
92
|
startSync: true,
|
|
93
|
+
gcTime: 0,
|
|
78
94
|
});
|
|
79
95
|
}
|
|
80
|
-
function related(q, rows, scope, field, kept, reach) {
|
|
96
|
+
function related(q, rows, scope, field, kept, reach, locked) {
|
|
81
97
|
const matched = kept === 'source' ? 'target' : 'source';
|
|
82
98
|
const from = q.from({ r: rows });
|
|
83
99
|
const joined = from
|
|
84
100
|
.where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [inArray(field, r.forms)])]))
|
|
85
|
-
.innerJoin({ m: rows }, ({ r, m }) => eq(r[matched], m.key))
|
|
86
|
-
.where(({ m }) => scoped(scope, m, null, reach));
|
|
101
|
+
.innerJoin({ m: rows }, ({ r, m }) => eq(streamed(r[matched]), streamed(m.key)))
|
|
102
|
+
.where(({ m }) => scoped(scope, m, null, reach, locked));
|
|
87
103
|
return narrowed(joined, scope.where, 'm')
|
|
88
104
|
.groupBy(({ r }) => r[kept])
|
|
89
105
|
.select(({ r }) => ({ key: r[kept] }));
|
|
90
106
|
}
|
|
91
107
|
function filtered(q, rows, plan) {
|
|
92
108
|
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');
|
|
109
|
+
let query = narrowed(from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach, plan.locked, plan.paths, plan.folder)), plan.root.where, 'd');
|
|
94
110
|
if (plan.hop !== null) {
|
|
95
|
-
const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach);
|
|
96
|
-
query = query.innerJoin({ h: hop }, ({ d, h }) => eq(d.key, h.key));
|
|
111
|
+
const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach, plan.locked);
|
|
112
|
+
query = query.innerJoin({ h: hop }, ({ d, h }) => eq(streamed(d.key), streamed(h.key)));
|
|
97
113
|
}
|
|
98
114
|
if (plan.backlinks !== null) {
|
|
99
|
-
const backlinks = related(q, rows, plan.backlinks, plan.backlinks.field, 'target', plan.reach);
|
|
100
|
-
query = query.innerJoin({ k: backlinks }, ({ d, k }) => eq(d.key, k.key));
|
|
115
|
+
const backlinks = related(q, rows, plan.backlinks, plan.backlinks.field, 'target', plan.reach, plan.locked);
|
|
116
|
+
query = query.innerJoin({ k: backlinks }, ({ d, k }) => eq(streamed(d.key), streamed(k.key)));
|
|
101
117
|
}
|
|
102
118
|
return query;
|
|
103
119
|
}
|
|
104
|
-
function
|
|
105
|
-
return
|
|
120
|
+
function bodied(shape) {
|
|
121
|
+
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
122
|
+
}
|
|
123
|
+
function orderingOf(options) {
|
|
124
|
+
const collated = options.stringSort === 'locale';
|
|
125
|
+
return {
|
|
126
|
+
direction: options.direction,
|
|
127
|
+
nulls: options.nulls ?? 'first',
|
|
128
|
+
stringSort: options.stringSort ?? 'locale',
|
|
129
|
+
locale: collated ? options.locale : undefined,
|
|
130
|
+
localeOptions: collated
|
|
131
|
+
? options.localeOptions
|
|
132
|
+
: undefined,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function storedValue(key, row) {
|
|
136
|
+
return key.of === 'field' ? row.values[key.field] : row[key.of];
|
|
137
|
+
}
|
|
138
|
+
function documentOrder(shape) {
|
|
139
|
+
return shape.orderBy.map(({ options }) => orderingOf(options));
|
|
140
|
+
}
|
|
141
|
+
function documentCursor(shape, row) {
|
|
142
|
+
return cursorOf([
|
|
143
|
+
...shape.orderBy.map((_, index) => row[`order${index}`]),
|
|
144
|
+
documentKey(row.room, row.path),
|
|
145
|
+
]);
|
|
106
146
|
}
|
|
107
147
|
function documents(q, rows, plan, shape) {
|
|
108
148
|
let query = filtered(q, rows, plan);
|
|
109
|
-
|
|
110
|
-
|
|
149
|
+
const cursor = plan.after?.values ?? null;
|
|
150
|
+
if (cursor !== null) {
|
|
151
|
+
const orderings = documentOrder(shape);
|
|
152
|
+
query = query.fn.where(({ d }) => beyond([...shape.orderBy.map(({ key }) => storedValue(key, d)), d.key], cursor, orderings));
|
|
111
153
|
}
|
|
112
154
|
for (const { key, options } of shape.orderBy) {
|
|
113
|
-
query = query.orderBy(({ d }) => keyed(key, d), options);
|
|
155
|
+
query = query.orderBy(({ d }) => streamed(keyed(key, d)), options);
|
|
114
156
|
}
|
|
115
|
-
if (shape.orderBy.length
|
|
116
|
-
query = query.orderBy(({ d }) => d.key, { stringSort: 'lexical' });
|
|
157
|
+
if (shape.orderBy.length > 0 || plan.limit !== null || plan.after !== null) {
|
|
158
|
+
query = query.orderBy(({ d }) => streamed(d.key), { stringSort: 'lexical' });
|
|
117
159
|
}
|
|
118
160
|
if (plan.limit !== null)
|
|
119
|
-
query = query.limit(plan.limit);
|
|
120
|
-
return query.select(({ d
|
|
161
|
+
query = query.limit(plan.limit + 1);
|
|
162
|
+
return query.select(({ d }) => ({
|
|
121
163
|
room: d.room,
|
|
122
164
|
path: d.path,
|
|
123
165
|
type: d.type,
|
|
166
|
+
binary: d.binary,
|
|
167
|
+
...Object.fromEntries(shape.orderBy.map(({ key }, index) => [`order${index}`, keyed(key, d)])),
|
|
124
168
|
...(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 } : {}),
|
|
129
|
-
...(shape.backlinks ? { backlinks: toArray(backlinked(q, rows, d, plan.reach)) } : {}),
|
|
169
|
+
...(bodied(shape) ? { text: d.text } : {}),
|
|
130
170
|
...(shape.change ? { change: d.change } : {}),
|
|
171
|
+
...(shape.fileId ? { fileId: d.fileId } : {}),
|
|
131
172
|
}));
|
|
132
173
|
}
|
|
133
|
-
function
|
|
174
|
+
function references(q, rows, reach, locked) {
|
|
134
175
|
const from = q.from({ r: rows });
|
|
135
176
|
return from
|
|
136
|
-
.where(({ r }) => every([eq(r.
|
|
137
|
-
.
|
|
138
|
-
|
|
139
|
-
|
|
177
|
+
.where(({ r }) => every([eq(r.kind, 'reference'), ...reached(reach, locked, r.room, r.source)]))
|
|
178
|
+
.select(({ r }) => ({
|
|
179
|
+
key: r.key,
|
|
180
|
+
target: r.target,
|
|
181
|
+
path: r.path,
|
|
182
|
+
type: r.type,
|
|
183
|
+
field: r.field,
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
function lexical(left, right) {
|
|
187
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
188
|
+
}
|
|
189
|
+
function linkOrder([leftKey, left], [rightKey, right]) {
|
|
190
|
+
return (lexical(left.path, right.path) || lexical(left.field, right.field) || lexical(leftKey, rightKey));
|
|
191
|
+
}
|
|
192
|
+
function linkAt(held, entry) {
|
|
193
|
+
let low = 0;
|
|
194
|
+
for (let high = held.length; low < high;) {
|
|
195
|
+
const middle = (low + high) >>> 1;
|
|
196
|
+
if (linkOrder(held[middle], entry) < 0)
|
|
197
|
+
low = middle + 1;
|
|
198
|
+
else
|
|
199
|
+
high = middle;
|
|
200
|
+
}
|
|
201
|
+
return low;
|
|
202
|
+
}
|
|
203
|
+
function targetOf(change) {
|
|
204
|
+
return change.value.target;
|
|
205
|
+
}
|
|
206
|
+
function relink(linked, { type, key, value }) {
|
|
207
|
+
const row = value;
|
|
208
|
+
const entry = [String(key), { path: row.path, type: row.type, field: row.field }];
|
|
209
|
+
const held = linked.get(row.target) ?? [];
|
|
210
|
+
const at = linkAt(held, entry);
|
|
211
|
+
const present = held[at]?.[0] === entry[0];
|
|
212
|
+
const shifted = (type === 'delete') === present ? held.length - at : 0;
|
|
213
|
+
if (type === 'delete') {
|
|
214
|
+
if (present)
|
|
215
|
+
held.splice(at, 1);
|
|
216
|
+
}
|
|
217
|
+
else if (present)
|
|
218
|
+
held[at] = entry;
|
|
219
|
+
else
|
|
220
|
+
held.splice(at, 0, entry);
|
|
221
|
+
if (held.length === 0)
|
|
222
|
+
linked.delete(row.target);
|
|
223
|
+
else
|
|
224
|
+
linked.set(row.target, held);
|
|
225
|
+
return shifted;
|
|
140
226
|
}
|
|
141
|
-
function
|
|
227
|
+
function relinked(linked, changes) {
|
|
228
|
+
return strides(changes, (change) => ROW_WEIGHT + relink(linked, change) / HELD_PER_WEIGHT);
|
|
229
|
+
}
|
|
230
|
+
function linking(links, received) {
|
|
231
|
+
return links.subscribeChanges(received, { includeInitialState: true });
|
|
232
|
+
}
|
|
233
|
+
function groups(q, stage, shape) {
|
|
142
234
|
const keys = shape.groupBy.map((_, index) => `key${index}`);
|
|
143
235
|
const from = q.from({ m: stage });
|
|
144
236
|
let query = from.groupBy(({ m }) => keys.map((key) => m[key]));
|
|
145
237
|
for (const { by, direction } of shape.orderBy) {
|
|
146
238
|
query = query.orderBy(({ m }) => (by === 'count' ? count(m.key) : m[keys[by]]), direction);
|
|
147
239
|
}
|
|
148
|
-
if (limit !== null && shape.orderBy.length > 0)
|
|
149
|
-
query = query.limit(limit);
|
|
150
240
|
return query.select(({ m }) => ({
|
|
151
241
|
...Object.fromEntries(keys.map((key) => [key, m[key]])),
|
|
152
242
|
count: count(m.key),
|
|
@@ -156,28 +246,56 @@ function built(rows, plan) {
|
|
|
156
246
|
const shape = plan.shape;
|
|
157
247
|
if (shape.kind === 'documents') {
|
|
158
248
|
const collection = live((q) => documents(q, rows, plan, shape));
|
|
159
|
-
|
|
249
|
+
const count = plan.total
|
|
250
|
+
? live((q) => filtered(q, rows, plan).select(({ d }) => ({ key: d.key })))
|
|
251
|
+
: null;
|
|
252
|
+
const links = shape.backlinks
|
|
253
|
+
? live((q) => references(q, rows, plan.reach, plan.locked))
|
|
254
|
+
: null;
|
|
255
|
+
return {
|
|
256
|
+
live: collection,
|
|
257
|
+
count,
|
|
258
|
+
links,
|
|
259
|
+
dispose: () => {
|
|
260
|
+
void collection.cleanup();
|
|
261
|
+
if (count !== null)
|
|
262
|
+
void count.cleanup();
|
|
263
|
+
if (links !== null)
|
|
264
|
+
void links.cleanup();
|
|
265
|
+
},
|
|
266
|
+
};
|
|
160
267
|
}
|
|
161
268
|
const stage = live((q) => filtered(q, rows, plan).select(({ d }) => ({
|
|
162
269
|
key: d.key,
|
|
163
270
|
...Object.fromEntries(shape.groupBy.map((key, index) => [`key${index}`, keyed(key, d)])),
|
|
164
271
|
})));
|
|
165
|
-
const collection = live((q) => groups(q, stage, shape
|
|
272
|
+
const collection = live((q) => groups(q, stage, shape));
|
|
166
273
|
return {
|
|
167
274
|
live: collection,
|
|
275
|
+
count: null,
|
|
276
|
+
links: null,
|
|
168
277
|
dispose: () => {
|
|
169
278
|
void collection.cleanup();
|
|
170
279
|
void stage.cleanup();
|
|
171
280
|
},
|
|
172
281
|
};
|
|
173
282
|
}
|
|
174
|
-
function
|
|
175
|
-
return Object.fromEntries(entries.filter(([, value]) => value !== undefined));
|
|
176
|
-
}
|
|
177
|
-
function part(row, prefix, names, whole) {
|
|
283
|
+
function sectionsOf(sections, names) {
|
|
178
284
|
if (names === 'all')
|
|
179
|
-
return
|
|
180
|
-
return
|
|
285
|
+
return sections;
|
|
286
|
+
return Object.fromEntries(names.flatMap((name) => (Object.hasOwn(sections, name) ? [[name, sections[name]]] : [])));
|
|
287
|
+
}
|
|
288
|
+
function bodyOf(row, key, bodies) {
|
|
289
|
+
const text = row.text;
|
|
290
|
+
if (text === null)
|
|
291
|
+
return null;
|
|
292
|
+
const held = bodies?.get(key);
|
|
293
|
+
if (held !== undefined && held.text === text)
|
|
294
|
+
return held.document;
|
|
295
|
+
const read = parse(text, row.path);
|
|
296
|
+
const document = read.ok ? read.document : null;
|
|
297
|
+
bodies?.set(key, { text, document });
|
|
298
|
+
return document;
|
|
181
299
|
}
|
|
182
300
|
function masked(pointers) {
|
|
183
301
|
const root = { whole: false, members: new Map() };
|
|
@@ -228,9 +346,17 @@ function cut(value, mask) {
|
|
|
228
346
|
function selected(fields, pointers) {
|
|
229
347
|
return (cut(fields, masked(pointers)) ?? {});
|
|
230
348
|
}
|
|
231
|
-
function documentOf(row, shape) {
|
|
232
|
-
if (row.type === null)
|
|
233
|
-
|
|
349
|
+
function documentOf(key, row, shape, bodies, linked) {
|
|
350
|
+
if (row.type === null) {
|
|
351
|
+
const binary = row.binary;
|
|
352
|
+
return {
|
|
353
|
+
room: row.room,
|
|
354
|
+
path: row.path,
|
|
355
|
+
...(binary === null ? {} : { binaryFile: binary }),
|
|
356
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const body = bodied(shape) ? bodyOf(row, key, bodies) : null;
|
|
234
360
|
return {
|
|
235
361
|
room: row.room,
|
|
236
362
|
path: row.path,
|
|
@@ -242,192 +368,812 @@ function documentOf(row, shape) {
|
|
|
242
368
|
? row.fields
|
|
243
369
|
: selected(row.fields, shape.fields),
|
|
244
370
|
}),
|
|
245
|
-
...(shape.sections === null
|
|
371
|
+
...(shape.sections === null || body === null
|
|
246
372
|
? {}
|
|
247
|
-
: {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
...(shape.preamble ? { preamble: row.preamble } : {}),
|
|
251
|
-
...(shape.markdown ? { markdown: row.markdown } : {}),
|
|
373
|
+
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
374
|
+
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
375
|
+
...(shape.markdown && body !== null ? { markdown: body.content } : {}),
|
|
252
376
|
...(shape.backlinks
|
|
253
377
|
? {
|
|
254
|
-
backlinks:
|
|
378
|
+
backlinks: (linked.get(documentKey(row.room, row.path)) ?? [])
|
|
379
|
+
.slice(0, MAXIMUM_WINDOW)
|
|
380
|
+
.map(([, link]) => link),
|
|
255
381
|
}
|
|
256
382
|
: {}),
|
|
257
383
|
...(shape.change ? { change: row.change } : {}),
|
|
384
|
+
...(shape.fileId ? { fileId: row.fileId } : {}),
|
|
258
385
|
};
|
|
259
386
|
}
|
|
260
|
-
function
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
387
|
+
function valueAt(row, path) {
|
|
388
|
+
let value = row;
|
|
389
|
+
for (const part of path)
|
|
390
|
+
value = value?.[part];
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
function weight(row) {
|
|
394
|
+
return ROW_WEIGHT + (typeof row?.text === 'string' ? row.text.length : 0);
|
|
395
|
+
}
|
|
396
|
+
const HELD_PER_WEIGHT = 16;
|
|
397
|
+
const STRIDE = ROW_WEIGHT * 16;
|
|
398
|
+
function* strides(items, each) {
|
|
399
|
+
let spent = 0;
|
|
400
|
+
for (const item of items) {
|
|
401
|
+
spent += each(item);
|
|
402
|
+
if (spent < STRIDE)
|
|
403
|
+
continue;
|
|
404
|
+
yield spent;
|
|
405
|
+
spent = 0;
|
|
267
406
|
}
|
|
268
|
-
|
|
407
|
+
if (spent > 0)
|
|
408
|
+
yield spent;
|
|
409
|
+
}
|
|
410
|
+
function joined(plan) {
|
|
411
|
+
return (plan.hop !== null ||
|
|
412
|
+
plan.backlinks !== null ||
|
|
413
|
+
(plan.shape.kind === 'documents' && plan.shape.backlinks));
|
|
414
|
+
}
|
|
415
|
+
function admission(plan) {
|
|
416
|
+
if (joined(plan))
|
|
417
|
+
return () => true;
|
|
418
|
+
const rooms = plan.rooms === null ? null : new Set(plan.rooms);
|
|
419
|
+
const paths = plan.paths === null ? null : new Set(plan.paths);
|
|
420
|
+
return (row) => row.kind === 'document' &&
|
|
421
|
+
(rooms === null || rooms.has(row.room)) &&
|
|
422
|
+
(paths === null || paths.has(row.path));
|
|
423
|
+
}
|
|
424
|
+
function groupValues(shape, [key, row]) {
|
|
425
|
+
return [...shape.orderBy.map(({ by }) => (by === 'count' ? row.count : row[`key${by}`])), key];
|
|
426
|
+
}
|
|
427
|
+
function groupOrder(shape) {
|
|
428
|
+
return shape.orderBy.map(({ direction }) => ({
|
|
429
|
+
direction,
|
|
430
|
+
nulls: 'first',
|
|
431
|
+
stringSort: 'locale',
|
|
432
|
+
locale: undefined,
|
|
433
|
+
localeOptions: undefined,
|
|
434
|
+
}));
|
|
435
|
+
}
|
|
436
|
+
function* windowOf(plan, keys, valueOf, total, each) {
|
|
437
|
+
const shape = plan.shape;
|
|
438
|
+
const cursor = shape.kind === 'groups' ? (plan.after?.values ?? null) : null;
|
|
439
|
+
const orderings = shape.kind === 'groups' ? groupOrder(shape) : [];
|
|
440
|
+
let all = 0;
|
|
441
|
+
let following = 0;
|
|
442
|
+
let last;
|
|
443
|
+
yield* strides(keys, (key) => {
|
|
444
|
+
all += 1;
|
|
445
|
+
const admitted = shape.kind === 'documents' ||
|
|
446
|
+
cursor === null ||
|
|
447
|
+
beyond(groupValues(shape, [key, valueOf(key)]), cursor, orderings);
|
|
448
|
+
if (admitted)
|
|
449
|
+
following += 1;
|
|
450
|
+
if (!admitted || (plan.limit !== null && following > plan.limit))
|
|
451
|
+
return ROW_WEIGHT;
|
|
452
|
+
last = key;
|
|
453
|
+
return each(key);
|
|
454
|
+
});
|
|
455
|
+
const next = plan.limit === null || following <= plan.limit
|
|
456
|
+
? undefined
|
|
457
|
+
: last === undefined
|
|
458
|
+
? (plan.after?.text ?? FROM_START)
|
|
459
|
+
: shape.kind === 'documents'
|
|
460
|
+
? documentCursor(shape, valueOf(last))
|
|
461
|
+
: cursorOf(groupValues(shape, [last, valueOf(last)]));
|
|
462
|
+
return { next, total: shape.kind === 'documents' ? total : plan.total ? all : undefined };
|
|
463
|
+
}
|
|
464
|
+
function framed(rows, { next, total }) {
|
|
269
465
|
return {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
count: row.count,
|
|
274
|
-
})),
|
|
275
|
-
}),
|
|
276
|
-
keys: bounded.map(([key]) => key),
|
|
466
|
+
...rows,
|
|
467
|
+
...(next === undefined ? {} : { next }),
|
|
468
|
+
...(total === undefined ? {} : { total }),
|
|
277
469
|
};
|
|
278
470
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
471
|
+
function rowOf(shape, [key, row], bodies, linked) {
|
|
472
|
+
if (shape.kind === 'documents')
|
|
473
|
+
return documentOf(key, row, shape, bodies, linked);
|
|
474
|
+
return { key: shape.groupBy.map((_, index) => row[`key${index}`]), count: row.count };
|
|
475
|
+
}
|
|
476
|
+
function resultOf(grouped, rows, coverage, window) {
|
|
477
|
+
return grouped
|
|
478
|
+
? framed({ groups: rows, coverage }, window)
|
|
479
|
+
: framed({ documents: rows, coverage }, window);
|
|
480
|
+
}
|
|
481
|
+
function* rowsOfPlan(plan, keys, valueOf, total, linked) {
|
|
482
|
+
const rows = [];
|
|
483
|
+
const window = yield* windowOf(plan, keys, valueOf, total, (key) => {
|
|
484
|
+
const value = valueOf(key);
|
|
485
|
+
rows.push(structuredClone(rowOf(plan.shape, [key, value], null, linked)));
|
|
486
|
+
return weight(value);
|
|
487
|
+
});
|
|
488
|
+
return plan.shape.kind === 'groups'
|
|
489
|
+
? framed({ groups: rows }, window)
|
|
490
|
+
: framed({ documents: rows }, window);
|
|
491
|
+
}
|
|
492
|
+
function textOf(row) {
|
|
493
|
+
if (!('path' in row))
|
|
494
|
+
return 0;
|
|
495
|
+
let length = (row.markdown?.length ?? 0) + (row.preamble?.length ?? 0);
|
|
496
|
+
for (const section of Object.values(row.sections ?? {}))
|
|
497
|
+
length += section.length;
|
|
498
|
+
return length;
|
|
499
|
+
}
|
|
500
|
+
export function* reconciled(previous, next) {
|
|
501
|
+
const before = previous.result;
|
|
502
|
+
const after = next.result;
|
|
503
|
+
if ('pending' in before || 'pending' in after) {
|
|
504
|
+
if (deepEquals(before, after))
|
|
505
|
+
return null;
|
|
506
|
+
return { keyed: next, message: next };
|
|
507
|
+
}
|
|
508
|
+
const earlier = rowsOf(before);
|
|
509
|
+
const held = new Map();
|
|
510
|
+
yield* strides(previous.keys.entries(), ([index, key]) => {
|
|
511
|
+
held.set(key, earlier[index]);
|
|
512
|
+
return ROW_WEIGHT;
|
|
513
|
+
});
|
|
514
|
+
const later = rowsOf(after);
|
|
515
|
+
const present = new Set();
|
|
282
516
|
const changed = [];
|
|
283
|
-
const rows =
|
|
284
|
-
|
|
517
|
+
const rows = [];
|
|
518
|
+
yield* strides(next.keys.entries(), ([index, key]) => {
|
|
519
|
+
const row = later[index];
|
|
285
520
|
const kept = held.get(key);
|
|
521
|
+
present.add(key);
|
|
286
522
|
if (kept !== undefined && deepEquals(kept, row))
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
523
|
+
rows.push(kept);
|
|
524
|
+
else {
|
|
525
|
+
changed.push([key, row]);
|
|
526
|
+
rows.push(row);
|
|
527
|
+
}
|
|
528
|
+
return ROW_WEIGHT + textOf(row);
|
|
290
529
|
});
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
530
|
+
const keyed = {
|
|
531
|
+
result: resultOf('groups' in after, rows, after.coverage, {
|
|
532
|
+
next: after.next,
|
|
533
|
+
total: after.total,
|
|
534
|
+
}),
|
|
535
|
+
keys: next.keys,
|
|
536
|
+
};
|
|
537
|
+
const message = yield* changeOf(previous, keyed, changed, (key) => present.has(key));
|
|
538
|
+
return message === null ? null : { keyed, message };
|
|
539
|
+
}
|
|
540
|
+
function* changeOf(previous, next, changed, present, dropped = () => { }) {
|
|
541
|
+
const before = previous.result;
|
|
542
|
+
const after = next.result;
|
|
543
|
+
const removed = [];
|
|
544
|
+
let at = 0;
|
|
545
|
+
let moved = false;
|
|
546
|
+
yield* strides(previous.keys.entries(), ([index, key]) => {
|
|
547
|
+
if (present(key, index)) {
|
|
548
|
+
moved ||= next.keys[at] !== key;
|
|
549
|
+
at += 1;
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
removed.push(key);
|
|
553
|
+
dropped(key);
|
|
554
|
+
}
|
|
555
|
+
return ROW_WEIGHT;
|
|
556
|
+
});
|
|
557
|
+
moved ||= at !== next.keys.length;
|
|
558
|
+
const covered = !deepEquals(before.coverage, after.coverage);
|
|
559
|
+
const framing = reframed(before, after);
|
|
560
|
+
if (changed.length === 0 && removed.length === 0 && !moved && !covered && framing === null) {
|
|
297
561
|
return null;
|
|
562
|
+
}
|
|
298
563
|
return {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
},
|
|
305
|
-
change: { changed, removed, order: moved ? next.keys : null },
|
|
564
|
+
changed,
|
|
565
|
+
removed,
|
|
566
|
+
order: moved ? next.keys : null,
|
|
567
|
+
...(covered ? { coverage: after.coverage } : {}),
|
|
568
|
+
...framing,
|
|
306
569
|
};
|
|
307
570
|
}
|
|
571
|
+
function reframed(before, after) {
|
|
572
|
+
const next = before.next === after.next ? {} : { next: after.next ?? null };
|
|
573
|
+
const total = before.total === after.total || after.total === undefined ? {} : { total: after.total };
|
|
574
|
+
return Object.keys(next).length === 0 && Object.keys(total).length === 0
|
|
575
|
+
? null
|
|
576
|
+
: { ...next, ...total };
|
|
577
|
+
}
|
|
308
578
|
export function createReadSide() {
|
|
309
579
|
configureIndexDevMode({ enabled: false });
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
gcTime: 0,
|
|
314
|
-
startSync: true,
|
|
315
|
-
autoIndex: 'eager',
|
|
316
|
-
defaultIndexType: BasicIndex,
|
|
317
|
-
sync: {
|
|
318
|
-
rowUpdateMode: 'full',
|
|
319
|
-
sync: (params) => {
|
|
320
|
-
sync = params;
|
|
321
|
-
params.markReady();
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
});
|
|
580
|
+
const rows = new Map();
|
|
581
|
+
const roomPaths = new Map();
|
|
582
|
+
const views = new Set();
|
|
325
583
|
const shared = new Map();
|
|
584
|
+
let writing = false;
|
|
585
|
+
let publishing = null;
|
|
586
|
+
let closed = false;
|
|
587
|
+
function viewOf(plan) {
|
|
588
|
+
let sync;
|
|
589
|
+
const view = {
|
|
590
|
+
plan,
|
|
591
|
+
rows: createCollection({
|
|
592
|
+
getKey: (row) => row.key,
|
|
593
|
+
gcTime: 0,
|
|
594
|
+
startSync: true,
|
|
595
|
+
autoIndex: 'off',
|
|
596
|
+
sync: {
|
|
597
|
+
rowUpdateMode: 'full',
|
|
598
|
+
sync: (params) => {
|
|
599
|
+
sync = params;
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
}),
|
|
603
|
+
sync,
|
|
604
|
+
admits: admission(plan),
|
|
605
|
+
stopped: false,
|
|
606
|
+
waiting: null,
|
|
607
|
+
};
|
|
608
|
+
views.add(view);
|
|
609
|
+
return view;
|
|
610
|
+
}
|
|
611
|
+
function freeze(view) {
|
|
612
|
+
view.waiting ??= new Map();
|
|
613
|
+
}
|
|
614
|
+
function thaw(view) {
|
|
615
|
+
const waiting = view.waiting;
|
|
616
|
+
if (waiting === null)
|
|
617
|
+
return;
|
|
618
|
+
let unit = new Map();
|
|
619
|
+
return after(inUnits(waiting, ([key, row]) => {
|
|
620
|
+
waiting.delete(key);
|
|
621
|
+
unit.set(key, row);
|
|
622
|
+
const was = view.rows.get(key);
|
|
623
|
+
return weight(row ?? undefined) + charged(view, was, row ?? undefined);
|
|
624
|
+
}, () => {
|
|
625
|
+
synced(view, unit);
|
|
626
|
+
unit = new Map();
|
|
627
|
+
}), () => {
|
|
628
|
+
if (waiting.size > 0)
|
|
629
|
+
return thaw(view);
|
|
630
|
+
view.waiting = null;
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
function dropped(view) {
|
|
634
|
+
view.stopped = true;
|
|
635
|
+
views.delete(view);
|
|
636
|
+
void view.rows.cleanup();
|
|
637
|
+
}
|
|
638
|
+
function* offered(view) {
|
|
639
|
+
const { plan } = view;
|
|
640
|
+
const { rooms, paths } = plan;
|
|
641
|
+
if (joined(plan) || rooms === null || paths === null) {
|
|
642
|
+
for (const key of rows.keys()) {
|
|
643
|
+
if (view.stopped)
|
|
644
|
+
return;
|
|
645
|
+
yield key;
|
|
646
|
+
}
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
for (const room of rooms) {
|
|
650
|
+
for (const path of paths) {
|
|
651
|
+
if (view.stopped)
|
|
652
|
+
return;
|
|
653
|
+
yield documentKey(room, path);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
function written(view, writes) {
|
|
658
|
+
if (view.waiting === null)
|
|
659
|
+
synced(view, writes);
|
|
660
|
+
else
|
|
661
|
+
for (const [key, row] of writes)
|
|
662
|
+
view.waiting.set(key, row);
|
|
663
|
+
}
|
|
664
|
+
function synced(view, writes) {
|
|
665
|
+
if (view.stopped || writes.size === 0)
|
|
666
|
+
return;
|
|
667
|
+
view.sync.begin();
|
|
668
|
+
for (const [key, row] of writes) {
|
|
669
|
+
if (row !== null) {
|
|
670
|
+
view.sync.write({ type: view.rows.has(key) ? 'update' : 'insert', value: row });
|
|
671
|
+
}
|
|
672
|
+
else if (view.rows.has(key))
|
|
673
|
+
view.sync.write({ type: 'delete', key });
|
|
674
|
+
}
|
|
675
|
+
view.sync.commit();
|
|
676
|
+
}
|
|
677
|
+
function filled(view) {
|
|
678
|
+
let unit = new Map();
|
|
679
|
+
return after(inUnits(offered(view), (key) => {
|
|
680
|
+
const row = rows.get(key);
|
|
681
|
+
if (row === undefined || !view.admits(row) || view.rows.has(key))
|
|
682
|
+
return ROW_WEIGHT;
|
|
683
|
+
unit.set(key, row);
|
|
684
|
+
return weight(row) + charged(view, undefined, row);
|
|
685
|
+
}, () => {
|
|
686
|
+
written(view, unit);
|
|
687
|
+
unit = new Map();
|
|
688
|
+
}), () => {
|
|
689
|
+
if (!view.stopped)
|
|
690
|
+
view.sync.markReady();
|
|
691
|
+
});
|
|
692
|
+
}
|
|
326
693
|
function searched(row) {
|
|
327
|
-
const
|
|
694
|
+
const read = row.text === null ? null : parse(row.text, row.path);
|
|
695
|
+
if (read === null || !read.ok)
|
|
696
|
+
return null;
|
|
328
697
|
return {
|
|
329
698
|
room: row.room,
|
|
330
699
|
path: row.path,
|
|
331
700
|
type: row.type,
|
|
332
701
|
fields: row.fields,
|
|
333
|
-
preamble:
|
|
334
|
-
sections:
|
|
702
|
+
preamble: read.document.preamble,
|
|
703
|
+
sections: read.document.sections,
|
|
335
704
|
};
|
|
336
705
|
}
|
|
337
|
-
function
|
|
338
|
-
|
|
339
|
-
if (next === null)
|
|
340
|
-
return;
|
|
341
|
-
entry.result = next.keyed;
|
|
342
|
-
for (const listener of entry.listeners)
|
|
343
|
-
listener(next.change);
|
|
706
|
+
function counted(entry) {
|
|
707
|
+
return entry.built.count === null ? undefined : entry.built.count.size;
|
|
344
708
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
709
|
+
function* delivery(entry, whole, dirty, touched, relinking, coverage) {
|
|
710
|
+
const { plan } = entry;
|
|
711
|
+
const live = entry.built.live;
|
|
712
|
+
const valueOf = (key) => live.get(key);
|
|
713
|
+
const previous = entry.result;
|
|
714
|
+
const answered = previous === undefined || 'pending' in previous.result ? null : previous;
|
|
715
|
+
const answeredKeys = answered?.keys ?? [];
|
|
716
|
+
const answeredRows = answered === null ? [] : rowsOf(answered.result);
|
|
717
|
+
const matched = new Uint8Array(answeredKeys.length);
|
|
718
|
+
const generation = (entry.generation += 1);
|
|
719
|
+
const keys = [];
|
|
720
|
+
const changed = [];
|
|
721
|
+
const rows = [];
|
|
722
|
+
let at = 0;
|
|
723
|
+
const touches = (key) => {
|
|
724
|
+
const { room, path } = valueOf(key);
|
|
725
|
+
return touched.has(documentKey(room, path));
|
|
726
|
+
};
|
|
727
|
+
const behind = (key) => !live.has(key) || entry.rows.get(key)?.at === generation;
|
|
728
|
+
yield* relinked(entry.linked, relinking);
|
|
729
|
+
const window = yield* windowOf(plan, live.keys(), valueOf, counted(entry), (key) => {
|
|
730
|
+
let skipped = 0;
|
|
731
|
+
while (skipped < STRIDE &&
|
|
732
|
+
at < answeredKeys.length &&
|
|
733
|
+
answeredKeys[at] !== key &&
|
|
734
|
+
behind(answeredKeys[at])) {
|
|
735
|
+
at += 1;
|
|
736
|
+
skipped += ROW_WEIGHT;
|
|
737
|
+
}
|
|
738
|
+
let holder;
|
|
739
|
+
let prior;
|
|
740
|
+
if (at < answeredKeys.length && answeredKeys[at] === key) {
|
|
741
|
+
prior = answeredRows[at];
|
|
742
|
+
matched[at] = 1;
|
|
743
|
+
at += 1;
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
holder = entry.rows.get(key);
|
|
747
|
+
prior = holder?.row;
|
|
748
|
+
if (holder !== undefined)
|
|
749
|
+
holder.at = generation;
|
|
750
|
+
}
|
|
751
|
+
let row = prior;
|
|
752
|
+
let spent = ROW_WEIGHT;
|
|
753
|
+
if (row === undefined || whole || dirty.has(key) || (touched.size > 0 && touches(key))) {
|
|
754
|
+
const value = valueOf(key);
|
|
755
|
+
const renewed = rowOf(plan.shape, [key, value], entry.bodies, entry.linked);
|
|
756
|
+
if (row === undefined || !deepEquals(row, renewed))
|
|
757
|
+
row = renewed;
|
|
758
|
+
spent = weight(value);
|
|
759
|
+
}
|
|
760
|
+
if (row !== prior) {
|
|
761
|
+
holder ??= entry.rows.get(key);
|
|
762
|
+
if (holder === undefined)
|
|
763
|
+
entry.rows.set(key, { row, at: generation });
|
|
764
|
+
else
|
|
765
|
+
holder.row = row;
|
|
766
|
+
changed.push([key, row]);
|
|
353
767
|
}
|
|
354
|
-
|
|
768
|
+
keys.push(key);
|
|
769
|
+
rows.push(row);
|
|
770
|
+
return skipped + spent;
|
|
771
|
+
});
|
|
772
|
+
const result = {
|
|
773
|
+
result: resultOf(plan.shape.kind === 'groups', rows, coverage, window),
|
|
774
|
+
keys,
|
|
775
|
+
};
|
|
776
|
+
if (previous === undefined)
|
|
777
|
+
return { result, message: null };
|
|
778
|
+
if (answered === null)
|
|
779
|
+
return { result, message: result };
|
|
780
|
+
const message = yield* changeOf(answered, result, changed, (key, index) => matched[index] === 1 || entry.rows.get(key)?.at === generation, (key) => {
|
|
781
|
+
entry.rows.delete(key);
|
|
782
|
+
entry.bodies.delete(key);
|
|
783
|
+
});
|
|
784
|
+
return { result, message };
|
|
785
|
+
}
|
|
786
|
+
function delivered(entry, { result, message }) {
|
|
787
|
+
entry.result = result;
|
|
788
|
+
if (message !== null)
|
|
789
|
+
for (const listener of entry.ready)
|
|
790
|
+
listener(message);
|
|
791
|
+
for (const listener of entry.fresh) {
|
|
792
|
+
entry.ready.add(listener);
|
|
793
|
+
listener(result);
|
|
794
|
+
}
|
|
795
|
+
entry.fresh.clear();
|
|
796
|
+
}
|
|
797
|
+
function run(entry) {
|
|
798
|
+
while (entry.listeners.size > 0 &&
|
|
799
|
+
(entry.whole ||
|
|
800
|
+
entry.dirty.size > 0 ||
|
|
801
|
+
entry.touched.size > 0 ||
|
|
802
|
+
entry.stale ||
|
|
803
|
+
entry.fresh.size > 0)) {
|
|
804
|
+
entry.stale = false;
|
|
805
|
+
const cover = entry.cover();
|
|
806
|
+
if ('pending' in cover) {
|
|
807
|
+
entry.whole = true;
|
|
808
|
+
entry.dirty.clear();
|
|
809
|
+
entry.touched.clear();
|
|
810
|
+
entry.rows.clear();
|
|
811
|
+
entry.bodies.clear();
|
|
812
|
+
const result = { result: cover, keys: [] };
|
|
813
|
+
const previous = entry.result;
|
|
814
|
+
const same = previous === undefined || deepEquals(previous.result, cover);
|
|
815
|
+
delivered(entry, { result, message: same ? null : result });
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
const whole = entry.whole ||
|
|
819
|
+
entry.plan.shape.kind === 'groups' ||
|
|
820
|
+
entry.result === undefined ||
|
|
821
|
+
'pending' in entry.result.result;
|
|
822
|
+
const dirty = entry.dirty;
|
|
823
|
+
const touched = entry.touched;
|
|
824
|
+
const relinking = entry.relinking;
|
|
825
|
+
entry.dirty = new Set();
|
|
826
|
+
entry.touched = new Set();
|
|
827
|
+
entry.relinking = [];
|
|
828
|
+
entry.whole = false;
|
|
829
|
+
freeze(entry.view);
|
|
830
|
+
let made;
|
|
831
|
+
try {
|
|
832
|
+
made = stepped(delivery(entry, whole, dirty, touched, relinking, cover.coverage));
|
|
833
|
+
}
|
|
834
|
+
catch (error) {
|
|
835
|
+
void thaw(entry.view);
|
|
836
|
+
throw error;
|
|
837
|
+
}
|
|
838
|
+
if (made instanceof Promise) {
|
|
839
|
+
void made
|
|
840
|
+
.then((delivering) => {
|
|
841
|
+
if (entry.listeners.size > 0)
|
|
842
|
+
delivered(entry, delivering);
|
|
843
|
+
})
|
|
844
|
+
.finally(() => thaw(entry.view))
|
|
845
|
+
.then(() => {
|
|
846
|
+
entry.running = false;
|
|
847
|
+
wake(entry);
|
|
848
|
+
}, () => {
|
|
849
|
+
entry.running = false;
|
|
850
|
+
});
|
|
355
851
|
return;
|
|
356
|
-
|
|
852
|
+
}
|
|
853
|
+
void thaw(entry.view);
|
|
854
|
+
delivered(entry, made);
|
|
855
|
+
}
|
|
856
|
+
entry.running = false;
|
|
857
|
+
}
|
|
858
|
+
function wake(entry) {
|
|
859
|
+
if (entry.subscription === null || entry.running || writing || publishing !== null)
|
|
860
|
+
return;
|
|
861
|
+
entry.running = true;
|
|
862
|
+
try {
|
|
863
|
+
run(entry);
|
|
864
|
+
}
|
|
865
|
+
catch (error) {
|
|
866
|
+
entry.running = false;
|
|
867
|
+
throw error;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function recover() {
|
|
871
|
+
for (const entry of shared.values()) {
|
|
872
|
+
if (entry.result === undefined || entry.subscription === null)
|
|
873
|
+
continue;
|
|
874
|
+
const cover = entry.cover();
|
|
875
|
+
const before = entry.result.result;
|
|
876
|
+
const held = 'pending' in cover
|
|
877
|
+
? deepEquals(before, cover)
|
|
878
|
+
: 'coverage' in before && deepEquals(before.coverage, cover.coverage);
|
|
879
|
+
if (held)
|
|
880
|
+
continue;
|
|
881
|
+
entry.stale = true;
|
|
882
|
+
wake(entry);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
function listed(key, row) {
|
|
886
|
+
const held = rows.get(key) ?? row;
|
|
887
|
+
if (held?.kind !== 'document')
|
|
888
|
+
return;
|
|
889
|
+
const paths = roomPaths.get(held.room) ?? new Set();
|
|
890
|
+
if (row?.kind === 'document' && row.type !== null)
|
|
891
|
+
paths.add(key);
|
|
892
|
+
else
|
|
893
|
+
paths.delete(key);
|
|
894
|
+
if (paths.size === 0)
|
|
895
|
+
roomPaths.delete(held.room);
|
|
896
|
+
else
|
|
897
|
+
roomPaths.set(held.room, paths);
|
|
898
|
+
}
|
|
899
|
+
function committed(next) {
|
|
900
|
+
if (closed || next.size === 0)
|
|
901
|
+
return;
|
|
902
|
+
for (const [key, row] of next) {
|
|
903
|
+
listed(key, row);
|
|
904
|
+
if (row === null)
|
|
905
|
+
rows.delete(key);
|
|
906
|
+
else
|
|
907
|
+
rows.set(key, row);
|
|
908
|
+
}
|
|
909
|
+
for (const view of views) {
|
|
910
|
+
const writes = new Map();
|
|
357
911
|
for (const [key, row] of next) {
|
|
358
|
-
if (row
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
912
|
+
if (row !== null && view.admits(row))
|
|
913
|
+
writes.set(key, row);
|
|
914
|
+
else if (view.rows.has(key) || view.waiting?.has(key))
|
|
915
|
+
writes.set(key, null);
|
|
916
|
+
}
|
|
917
|
+
written(view, writes);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function moved({ plan }, before, after) {
|
|
921
|
+
const shape = plan.shape;
|
|
922
|
+
if (shape.kind === 'documents' && plan.limit === null && shape.orderBy.length > 0) {
|
|
923
|
+
return true;
|
|
924
|
+
}
|
|
925
|
+
const keys = shape.kind === 'documents' ? shape.orderBy.map(({ key }) => key) : shape.groupBy;
|
|
926
|
+
for (const key of keys) {
|
|
927
|
+
const path = key.of === 'field' ? ['values', key.field] : [key.of];
|
|
928
|
+
if (!deepEquals(valueAt(before, path), valueAt(after, path)))
|
|
929
|
+
return true;
|
|
930
|
+
}
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
function charged(view, was, row) {
|
|
934
|
+
const kept = was !== undefined && view.admits(was);
|
|
935
|
+
const admitted = row !== undefined && view.admits(row);
|
|
936
|
+
if (!kept && !admitted)
|
|
937
|
+
return 0;
|
|
938
|
+
const shifted = view.rows.size / HELD_PER_WEIGHT;
|
|
939
|
+
return shifted + (kept && admitted && !moved(view, was, row) ? 0 : shifted);
|
|
940
|
+
}
|
|
941
|
+
function charges(was, row) {
|
|
942
|
+
let spent = 0;
|
|
943
|
+
for (const view of views)
|
|
944
|
+
spent += charged(view, was, row);
|
|
945
|
+
return spent;
|
|
946
|
+
}
|
|
947
|
+
function published(set) {
|
|
948
|
+
let unit = new Map();
|
|
949
|
+
return inUnits(set.documents, (change) => {
|
|
950
|
+
if (closed)
|
|
951
|
+
return 0;
|
|
952
|
+
const { before, after } = change;
|
|
953
|
+
const kept = new Set(after.map((row) => row.key));
|
|
954
|
+
const held = new Map(before.map((row) => [row.key, row]));
|
|
955
|
+
let spent = 0;
|
|
956
|
+
for (const row of before) {
|
|
957
|
+
unit.set(row.key, null);
|
|
958
|
+
spent += ROW_WEIGHT + (kept.has(row.key) ? 0 : charges(row, undefined));
|
|
364
959
|
}
|
|
365
|
-
|
|
960
|
+
for (const row of after) {
|
|
961
|
+
unit.set(row.key, row);
|
|
962
|
+
spent += weight(row) + charges(held.get(row.key), row);
|
|
963
|
+
}
|
|
964
|
+
return spent;
|
|
965
|
+
}, () => {
|
|
966
|
+
committed(unit);
|
|
967
|
+
unit = new Map();
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
function released() {
|
|
971
|
+
recover();
|
|
972
|
+
for (const entry of shared.values())
|
|
973
|
+
wake(entry);
|
|
974
|
+
}
|
|
975
|
+
function publishedLater(applied) {
|
|
976
|
+
const fence = applied.then(() => { }, () => { });
|
|
977
|
+
publishing = fence;
|
|
978
|
+
void fence.then(() => {
|
|
979
|
+
if (publishing !== fence)
|
|
980
|
+
return;
|
|
981
|
+
publishing = null;
|
|
982
|
+
released();
|
|
983
|
+
});
|
|
984
|
+
return applied;
|
|
985
|
+
}
|
|
986
|
+
async function settled() {
|
|
987
|
+
while (publishing !== null)
|
|
988
|
+
await publishing;
|
|
989
|
+
}
|
|
990
|
+
return {
|
|
991
|
+
apply(set, next = () => { }) {
|
|
992
|
+
const pending = publishing;
|
|
993
|
+
if (pending !== null || set instanceof Promise) {
|
|
994
|
+
return publishedLater(Promise.all([set, pending]).then(([held]) => after(published(held), () => next(held))));
|
|
995
|
+
}
|
|
996
|
+
writing = true;
|
|
997
|
+
let applied;
|
|
998
|
+
try {
|
|
999
|
+
applied = published(set);
|
|
1000
|
+
}
|
|
1001
|
+
finally {
|
|
1002
|
+
writing = false;
|
|
1003
|
+
}
|
|
1004
|
+
if (applied instanceof Promise)
|
|
1005
|
+
return publishedLater(applied.then(() => next(set)));
|
|
1006
|
+
released();
|
|
1007
|
+
next(set);
|
|
366
1008
|
},
|
|
367
|
-
|
|
368
|
-
|
|
1009
|
+
recover,
|
|
1010
|
+
settled,
|
|
1011
|
+
async query(plan, at) {
|
|
1012
|
+
const view = viewOf(plan);
|
|
1013
|
+
const linked = new Map();
|
|
1014
|
+
let made = null;
|
|
1015
|
+
let following = null;
|
|
369
1016
|
try {
|
|
370
|
-
|
|
371
|
-
|
|
1017
|
+
const filling = filled(view);
|
|
1018
|
+
made = built(view.rows, plan);
|
|
1019
|
+
const { live: collection, count, links } = made;
|
|
1020
|
+
if (links !== null) {
|
|
1021
|
+
following = linking(links, (changes) => {
|
|
1022
|
+
for (const change of changes)
|
|
1023
|
+
relink(linked, change);
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
await filling;
|
|
1027
|
+
for (const one of [collection, count, links])
|
|
1028
|
+
if (one !== null)
|
|
1029
|
+
await readied(one);
|
|
1030
|
+
await settled();
|
|
1031
|
+
freeze(view);
|
|
1032
|
+
following?.unsubscribe();
|
|
1033
|
+
following = null;
|
|
1034
|
+
const held = at();
|
|
1035
|
+
const rows = await stepped(rowsOfPlan(plan, collection.keys(), (key) => collection.get(key), count?.size, linked));
|
|
1036
|
+
return { rows, at: held };
|
|
372
1037
|
}
|
|
373
1038
|
finally {
|
|
374
|
-
|
|
1039
|
+
following?.unsubscribe();
|
|
1040
|
+
made?.dispose();
|
|
1041
|
+
dropped(view);
|
|
375
1042
|
}
|
|
376
1043
|
},
|
|
377
|
-
watch(plan, identity,
|
|
1044
|
+
watch(plan, identity, cover, deliverTo) {
|
|
378
1045
|
let entry = shared.get(identity);
|
|
379
1046
|
if (entry === undefined) {
|
|
1047
|
+
const view = viewOf(plan);
|
|
1048
|
+
void filled(view);
|
|
380
1049
|
const created = {
|
|
381
1050
|
plan,
|
|
382
|
-
|
|
1051
|
+
view,
|
|
1052
|
+
built: built(view.rows, plan),
|
|
1053
|
+
cover,
|
|
383
1054
|
result: undefined,
|
|
1055
|
+
rows: new Map(),
|
|
1056
|
+
generation: 0,
|
|
1057
|
+
bodies: new Map(),
|
|
384
1058
|
listeners: new Set(),
|
|
1059
|
+
fresh: new Set(),
|
|
1060
|
+
ready: new Set(),
|
|
385
1061
|
subscription: null,
|
|
1062
|
+
counting: null,
|
|
1063
|
+
linking: null,
|
|
1064
|
+
linked: new Map(),
|
|
1065
|
+
relinking: [],
|
|
1066
|
+
touched: new Set(),
|
|
1067
|
+
whole: true,
|
|
1068
|
+
dirty: new Set(),
|
|
1069
|
+
stale: false,
|
|
1070
|
+
running: false,
|
|
386
1071
|
};
|
|
1072
|
+
if (created.built.links !== null) {
|
|
1073
|
+
created.linking = linking(created.built.links, (changes) => {
|
|
1074
|
+
for (const change of changes) {
|
|
1075
|
+
created.relinking.push(change);
|
|
1076
|
+
created.touched.add(targetOf(change));
|
|
1077
|
+
}
|
|
1078
|
+
if (created.subscription !== null)
|
|
1079
|
+
queueMicrotask(() => wake(created));
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
387
1082
|
created.built.live.on('status:change', ({ status }) => {
|
|
388
|
-
if (status
|
|
1083
|
+
if (status !== 'cleaned-up' || created.listeners.size > 0)
|
|
1084
|
+
return;
|
|
1085
|
+
if (shared.get(identity) === created)
|
|
389
1086
|
shared.delete(identity);
|
|
390
1087
|
});
|
|
391
1088
|
shared.set(identity, created);
|
|
392
1089
|
entry = created;
|
|
393
1090
|
}
|
|
394
1091
|
const observed = entry;
|
|
395
|
-
observed.listeners.add(
|
|
396
|
-
observed.built.live.onFirstReady(() => {
|
|
397
|
-
|
|
1092
|
+
observed.listeners.add(deliverTo);
|
|
1093
|
+
const ready = (then) => observed.built.live.onFirstReady(() => {
|
|
1094
|
+
const linked = () => observed.built.links === null ? then() : observed.built.links.onFirstReady(then);
|
|
1095
|
+
return observed.built.count === null
|
|
1096
|
+
? linked()
|
|
1097
|
+
: observed.built.count.onFirstReady(linked);
|
|
1098
|
+
});
|
|
1099
|
+
const settled = () => {
|
|
1100
|
+
if (observed.built.count === null)
|
|
1101
|
+
wake(observed);
|
|
1102
|
+
else
|
|
1103
|
+
queueMicrotask(() => wake(observed));
|
|
1104
|
+
};
|
|
1105
|
+
ready(() => {
|
|
1106
|
+
if (!observed.listeners.has(deliverTo))
|
|
398
1107
|
return;
|
|
1108
|
+
observed.fresh.add(deliverTo);
|
|
399
1109
|
if (observed.subscription === null) {
|
|
400
|
-
observed.
|
|
401
|
-
observed.subscription = observed.built.live.subscribeChanges(() => {
|
|
402
|
-
|
|
1110
|
+
observed.whole = true;
|
|
1111
|
+
observed.subscription = observed.built.live.subscribeChanges((changes) => {
|
|
1112
|
+
if (observed.plan.shape.kind === 'groups')
|
|
1113
|
+
observed.whole = true;
|
|
1114
|
+
else
|
|
1115
|
+
for (const { key } of changes)
|
|
1116
|
+
observed.dirty.add(key);
|
|
1117
|
+
settled();
|
|
403
1118
|
}, { includeInitialState: false });
|
|
1119
|
+
observed.counting =
|
|
1120
|
+
observed.built.count?.subscribeChanges(() => {
|
|
1121
|
+
observed.stale = true;
|
|
1122
|
+
settled();
|
|
1123
|
+
}, { includeInitialState: false }) ?? null;
|
|
404
1124
|
}
|
|
405
|
-
|
|
1125
|
+
wake(observed);
|
|
406
1126
|
});
|
|
407
1127
|
return () => {
|
|
408
|
-
|
|
1128
|
+
observed.fresh.delete(deliverTo);
|
|
1129
|
+
observed.ready.delete(deliverTo);
|
|
1130
|
+
if (!observed.listeners.delete(deliverTo) || observed.listeners.size > 0)
|
|
409
1131
|
return;
|
|
410
1132
|
observed.subscription?.unsubscribe();
|
|
411
1133
|
observed.subscription = null;
|
|
1134
|
+
observed.counting?.unsubscribe();
|
|
1135
|
+
observed.counting = null;
|
|
1136
|
+
observed.linking?.unsubscribe();
|
|
1137
|
+
observed.linking = null;
|
|
1138
|
+
observed.result = undefined;
|
|
1139
|
+
observed.rows.clear();
|
|
1140
|
+
observed.bodies.clear();
|
|
1141
|
+
observed.dirty.clear();
|
|
1142
|
+
observed.touched.clear();
|
|
1143
|
+
observed.whole = true;
|
|
1144
|
+
if (shared.get(identity) === observed)
|
|
1145
|
+
shared.delete(identity);
|
|
1146
|
+
observed.built.dispose();
|
|
1147
|
+
dropped(observed.view);
|
|
412
1148
|
};
|
|
413
1149
|
},
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
1150
|
+
*room(room) {
|
|
1151
|
+
for (const key of roomPaths.get(room) ?? []) {
|
|
1152
|
+
const row = rows.get(key);
|
|
1153
|
+
if (row?.kind === 'document')
|
|
1154
|
+
yield row.path;
|
|
1155
|
+
}
|
|
418
1156
|
},
|
|
419
1157
|
document(room, path) {
|
|
420
1158
|
const row = rows.get(documentKey(room, path));
|
|
421
|
-
return row
|
|
1159
|
+
return row?.kind !== 'document' || row.type === null ? null : searched(row);
|
|
422
1160
|
},
|
|
423
1161
|
close() {
|
|
1162
|
+
closed = true;
|
|
424
1163
|
for (const entry of shared.values()) {
|
|
425
1164
|
entry.listeners.clear();
|
|
1165
|
+
entry.fresh.clear();
|
|
1166
|
+
entry.ready.clear();
|
|
426
1167
|
entry.subscription?.unsubscribe();
|
|
1168
|
+
entry.counting?.unsubscribe();
|
|
1169
|
+
entry.linking?.unsubscribe();
|
|
427
1170
|
entry.built.dispose();
|
|
428
1171
|
}
|
|
429
1172
|
shared.clear();
|
|
430
|
-
|
|
1173
|
+
for (const view of [...views])
|
|
1174
|
+
dropped(view);
|
|
1175
|
+
rows.clear();
|
|
1176
|
+
roomPaths.clear();
|
|
431
1177
|
},
|
|
432
1178
|
};
|
|
433
1179
|
}
|