@agentshouse/mdruntime 0.1.0-c967d1ca66fee → 0.1.0-c96e80d38e5f0

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.
Files changed (175) hide show
  1. package/README.md +22 -9
  2. package/dist/adapter.d.ts +172 -49
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +5 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +38 -4
  7. package/dist/attempt.d.ts +16 -0
  8. package/dist/attempt.d.ts.map +1 -0
  9. package/dist/attempt.js +70 -0
  10. package/dist/binary.d.ts +18 -0
  11. package/dist/binary.d.ts.map +1 -0
  12. package/dist/binary.js +8 -0
  13. package/dist/bootstrap.d.ts +27 -11
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +184 -84
  16. package/dist/chain.d.ts +11 -0
  17. package/dist/chain.d.ts.map +1 -0
  18. package/dist/chain.js +64 -0
  19. package/dist/change-set.d.ts +9 -14
  20. package/dist/change-set.d.ts.map +1 -1
  21. package/dist/change-set.js +11 -62
  22. package/dist/change.d.ts +41 -0
  23. package/dist/change.d.ts.map +1 -1
  24. package/dist/change.js +34 -0
  25. package/dist/client.d.ts +34 -15
  26. package/dist/client.d.ts.map +1 -1
  27. package/dist/client.js +104 -8
  28. package/dist/collection.d.ts +10 -7
  29. package/dist/collection.d.ts.map +1 -1
  30. package/dist/collection.js +38 -79
  31. package/dist/compile.d.ts +16 -0
  32. package/dist/compile.d.ts.map +1 -0
  33. package/dist/compile.js +789 -0
  34. package/dist/connection.d.ts +51 -17
  35. package/dist/connection.d.ts.map +1 -1
  36. package/dist/connection.js +3951 -907
  37. package/dist/conversation.d.ts +48 -19
  38. package/dist/conversation.d.ts.map +1 -1
  39. package/dist/conversation.js +726 -240
  40. package/dist/digest.d.ts +7 -0
  41. package/dist/digest.d.ts.map +1 -0
  42. package/dist/digest.js +113 -0
  43. package/dist/durability.d.ts +4 -1
  44. package/dist/durability.d.ts.map +1 -1
  45. package/dist/durability.js +5 -0
  46. package/dist/entry.d.ts +20 -0
  47. package/dist/entry.d.ts.map +1 -0
  48. package/dist/entry.js +152 -0
  49. package/dist/expectation.d.ts +21 -0
  50. package/dist/expectation.d.ts.map +1 -0
  51. package/dist/expectation.js +70 -0
  52. package/dist/fence.d.ts +6 -0
  53. package/dist/fence.d.ts.map +1 -0
  54. package/dist/fence.js +10 -0
  55. package/dist/file.d.ts +2 -19
  56. package/dist/file.d.ts.map +1 -1
  57. package/dist/file.js +7 -122
  58. package/dist/host.d.ts +12 -3
  59. package/dist/host.d.ts.map +1 -1
  60. package/dist/host.js +125 -18
  61. package/dist/identity.d.ts +3 -0
  62. package/dist/identity.d.ts.map +1 -1
  63. package/dist/identity.js +9 -0
  64. package/dist/index.d.ts +19 -9
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +9 -0
  67. package/dist/live.d.ts +22 -0
  68. package/dist/live.d.ts.map +1 -0
  69. package/dist/live.js +123 -0
  70. package/dist/media.d.ts +8 -0
  71. package/dist/media.d.ts.map +1 -0
  72. package/dist/media.js +218 -0
  73. package/dist/memory.d.ts +18 -7
  74. package/dist/memory.d.ts.map +1 -1
  75. package/dist/memory.js +231 -91
  76. package/dist/messages.d.ts +28 -0
  77. package/dist/messages.d.ts.map +1 -0
  78. package/dist/messages.js +89 -0
  79. package/dist/model.d.ts +9 -2
  80. package/dist/model.d.ts.map +1 -1
  81. package/dist/model.js +8 -2
  82. package/dist/name.d.ts +4 -0
  83. package/dist/name.d.ts.map +1 -0
  84. package/dist/name.js +122 -0
  85. package/dist/observation.d.ts +20 -2
  86. package/dist/observation.d.ts.map +1 -1
  87. package/dist/observation.js +113 -12
  88. package/dist/opened.d.ts +22 -0
  89. package/dist/opened.d.ts.map +1 -0
  90. package/dist/opened.js +107 -0
  91. package/dist/outcome.d.ts +52 -4
  92. package/dist/outcome.d.ts.map +1 -1
  93. package/dist/outcome.js +46 -0
  94. package/dist/overlay.d.ts +15 -5
  95. package/dist/overlay.d.ts.map +1 -1
  96. package/dist/overlay.js +129 -177
  97. package/dist/paths.d.ts +1 -0
  98. package/dist/paths.d.ts.map +1 -1
  99. package/dist/paths.js +4 -0
  100. package/dist/phrase.d.ts +3 -0
  101. package/dist/phrase.d.ts.map +1 -0
  102. package/dist/phrase.js +8 -0
  103. package/dist/query.d.ts +72 -4
  104. package/dist/query.d.ts.map +1 -1
  105. package/dist/query.js +67 -7
  106. package/dist/reach.d.ts +16 -2
  107. package/dist/reach.d.ts.map +1 -1
  108. package/dist/reach.js +222 -56
  109. package/dist/react.d.ts +11 -7
  110. package/dist/react.d.ts.map +1 -1
  111. package/dist/react.js +7 -17
  112. package/dist/readiness.d.ts +50 -0
  113. package/dist/readiness.d.ts.map +1 -0
  114. package/dist/readiness.js +27 -0
  115. package/dist/recovery.d.ts +26 -0
  116. package/dist/recovery.d.ts.map +1 -0
  117. package/dist/recovery.js +224 -0
  118. package/dist/refresh.d.ts +17 -14
  119. package/dist/refresh.d.ts.map +1 -1
  120. package/dist/refresh.js +155 -56
  121. package/dist/replica.d.ts +58 -33
  122. package/dist/replica.d.ts.map +1 -1
  123. package/dist/replica.js +310 -159
  124. package/dist/room-draft.d.ts +52 -5
  125. package/dist/room-draft.d.ts.map +1 -1
  126. package/dist/room-draft.js +261 -56
  127. package/dist/rows.d.ts +38 -35
  128. package/dist/rows.d.ts.map +1 -1
  129. package/dist/rows.js +178 -102
  130. package/dist/save.d.ts +70 -3
  131. package/dist/save.d.ts.map +1 -1
  132. package/dist/save.js +805 -147
  133. package/dist/search.d.ts +15 -55
  134. package/dist/search.d.ts.map +1 -1
  135. package/dist/search.js +10 -121
  136. package/dist/seed.d.ts +16 -0
  137. package/dist/seed.d.ts.map +1 -0
  138. package/dist/seed.js +75 -0
  139. package/dist/shared.d.ts +15 -0
  140. package/dist/shared.d.ts.map +1 -0
  141. package/dist/shared.js +421 -0
  142. package/dist/status.d.ts +5 -0
  143. package/dist/status.d.ts.map +1 -1
  144. package/dist/status.js +3 -1
  145. package/dist/store.d.ts +116 -80
  146. package/dist/store.d.ts.map +1 -1
  147. package/dist/store.js +355 -461
  148. package/dist/stored.d.ts +162 -0
  149. package/dist/stored.d.ts.map +1 -0
  150. package/dist/stored.js +549 -0
  151. package/dist/tables.d.ts +353 -0
  152. package/dist/tables.d.ts.map +1 -0
  153. package/dist/tables.js +585 -0
  154. package/dist/transfer.d.ts +0 -5
  155. package/dist/transfer.d.ts.map +1 -1
  156. package/dist/turn.d.ts +9 -0
  157. package/dist/turn.d.ts.map +1 -0
  158. package/dist/turn.js +75 -0
  159. package/dist/validation.d.ts +29 -0
  160. package/dist/validation.d.ts.map +1 -0
  161. package/dist/validation.js +101 -0
  162. package/dist/windows.d.ts +22 -0
  163. package/dist/windows.d.ts.map +1 -0
  164. package/dist/windows.js +89 -0
  165. package/dist/wire.d.ts +166 -30
  166. package/dist/wire.d.ts.map +1 -1
  167. package/dist/wire.js +57 -29
  168. package/package.json +26 -15
  169. package/AGENTS.md +0 -269
  170. package/dist/attachment.d.ts +0 -3
  171. package/dist/attachment.d.ts.map +0 -1
  172. package/dist/attachment.js +0 -8
  173. package/dist/read-side.d.ts +0 -18
  174. package/dist/read-side.d.ts.map +0 -1
  175. package/dist/read-side.js +0 -433
package/dist/stored.js ADDED
@@ -0,0 +1,549 @@
1
+ import { composedOf } from './collection.js';
2
+ import { described, derived } from './rows.js';
3
+ export const REPLICA_TABLES = [
4
+ 'CREATE TABLE replica_keys (key INTEGER PRIMARY KEY AUTOINCREMENT, locator TEXT NOT NULL, transfer TEXT NOT NULL, fingerprint TEXT NOT NULL, rederiving INTEGER)',
5
+ 'CREATE TABLE replica_rooms (locator TEXT PRIMARY KEY, key INTEGER NOT NULL, record TEXT NOT NULL) WITHOUT ROWID',
6
+ 'CREATE TABLE replica_entries (id INTEGER PRIMARY KEY, key INTEGER NOT NULL, layer INTEGER NOT NULL, path TEXT NOT NULL, visible INTEGER NOT NULL, present INTEGER NOT NULL, queried INTEGER NOT NULL, binary TEXT, revision TEXT, file_id TEXT, change TEXT, written TEXT, type TEXT, declared TEXT, folder TEXT NOT NULL, fields TEXT, content TEXT, outline TEXT)',
7
+ 'CREATE UNIQUE INDEX replica_entries_path ON replica_entries (key, layer, path)',
8
+ 'CREATE INDEX replica_entries_visible ON replica_entries (key, visible, present, path, binary)',
9
+ 'CREATE INDEX replica_entries_folder ON replica_entries (key, folder, path)',
10
+ 'CREATE INDEX replica_entries_type ON replica_entries (key, type, visible, present, queried, folder)',
11
+ 'CREATE INDEX replica_entries_key ON replica_entries (key)',
12
+ 'CREATE TABLE replica_values (doc INTEGER NOT NULL, keys TEXT NOT NULL, places TEXT NOT NULL, item INTEGER NOT NULL, kind TEXT NOT NULL, value, size INTEGER, PRIMARY KEY (doc, keys, places, item)) WITHOUT ROWID',
13
+ 'CREATE TABLE replica_links (doc INTEGER NOT NULL, key INTEGER NOT NULL, kind TEXT NOT NULL, keys TEXT NOT NULL, places TEXT NOT NULL, target TEXT NOT NULL, field TEXT NOT NULL, PRIMARY KEY (doc, kind, keys, places, target)) WITHOUT ROWID',
14
+ 'CREATE INDEX replica_links_target ON replica_links (key, target, kind, keys)',
15
+ 'CREATE TABLE replica_folders (key INTEGER NOT NULL, layer INTEGER NOT NULL, path TEXT NOT NULL, present INTEGER NOT NULL, PRIMARY KEY (key, layer, path)) WITHOUT ROWID',
16
+ 'CREATE TABLE replica_erasing (key INTEGER PRIMARY KEY, locator TEXT NOT NULL)',
17
+ 'CREATE TABLE replica_opened (locator TEXT NOT NULL, path TEXT NOT NULL, PRIMARY KEY (locator, path)) WITHOUT ROWID',
18
+ 'CREATE TABLE replica_facts (name TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID',
19
+ 'CREATE TABLE replica_presentation (app TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID',
20
+ ];
21
+ const CONFIRMED = 0;
22
+ const OVERLAY = 1;
23
+ const REFRESHED = 2;
24
+ const DERIVED_ROWS = 64;
25
+ const ENTRY_WEIGHT = 256;
26
+ const ABSENT = {
27
+ type: null,
28
+ declared: null,
29
+ queried: false,
30
+ folder: '',
31
+ fields: null,
32
+ values: [],
33
+ links: [],
34
+ };
35
+ function text(value) {
36
+ return typeof value === 'string' ? value : null;
37
+ }
38
+ function number(value) {
39
+ return typeof value === 'bigint' ? Number(value) : value;
40
+ }
41
+ function json(value) {
42
+ return typeof value === 'string' ? JSON.parse(value) : null;
43
+ }
44
+ function flag(value) {
45
+ return value ? 1 : 0;
46
+ }
47
+ function paths(list) {
48
+ return JSON.stringify(list);
49
+ }
50
+ function dropDerived(connection, doc) {
51
+ connection.run('DELETE FROM replica_values WHERE doc = ?', [doc]);
52
+ connection.run('DELETE FROM replica_links WHERE doc = ?', [doc]);
53
+ }
54
+ function scalarText(value) {
55
+ if (value === Infinity)
56
+ return 'Infinity';
57
+ if (value === -Infinity)
58
+ return '-Infinity';
59
+ return JSON.stringify(value);
60
+ }
61
+ function writeDerived(connection, doc, key, made) {
62
+ if (made.values.length > 0) {
63
+ const rows = made.values.map((value) => `[${JSON.stringify(value.keys)},${JSON.stringify(value.places)},${value.item},${JSON.stringify(value.kind)},${scalarText(value.value)},${JSON.stringify(value.size)}]`);
64
+ connection.run('INSERT INTO replica_values (doc, keys, places, item, kind, value, size) SELECT ?, value ->> 0, value ->> 1, value ->> 2, value ->> 3, value ->> 4, value ->> 5 FROM json_each(?)', [doc, `[${rows.join(',')}]`]);
65
+ }
66
+ if (made.links.length > 0) {
67
+ connection.run('INSERT OR IGNORE INTO replica_links (doc, key, kind, keys, places, target, field) SELECT ?, ?, value ->> 0, value ->> 1, value ->> 2, value ->> 3, value ->> 4 FROM json_each(?)', [
68
+ doc,
69
+ key,
70
+ JSON.stringify(made.links.map((link) => [link.kind, link.keys, link.places, link.target, link.field])),
71
+ ]);
72
+ }
73
+ }
74
+ function entryId(connection, key, layer, path) {
75
+ const [row] = connection.rows('SELECT id FROM replica_entries WHERE key = ? AND layer = ? AND path = ?', [key, layer, path]);
76
+ return row === undefined ? null : number(row.id);
77
+ }
78
+ function removeEntry(connection, key, layer, path) {
79
+ const id = entryId(connection, key, layer, path);
80
+ if (id === null)
81
+ return;
82
+ dropDerived(connection, id);
83
+ connection.run('DELETE FROM replica_entries WHERE id = ?', [id]);
84
+ }
85
+ function insertEntry(connection, key, layer, entry, present, made) {
86
+ connection.run('INSERT INTO replica_entries (key, layer, path, visible, present, queried, content, outline, binary, revision, file_id, change, written, type, declared, folder, fields) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
87
+ key,
88
+ layer,
89
+ entry.path,
90
+ flag(present && layer !== REFRESHED),
91
+ flag(present),
92
+ flag(made.queried),
93
+ entry.binary === null ? entry.content : null,
94
+ entry.binary === null && entry.outline !== null ? JSON.stringify(entry.outline) : null,
95
+ entry.binary === null ? null : JSON.stringify(entry.binary),
96
+ entry.revision,
97
+ entry.fileId,
98
+ entry.change === null ? null : JSON.stringify(entry.change),
99
+ entry.written,
100
+ made.type,
101
+ made.declared,
102
+ made.folder,
103
+ made.fields,
104
+ ]);
105
+ }
106
+ function textOf(entry) {
107
+ return entry.binary === null ? entry.content : null;
108
+ }
109
+ function putEntry(connection, key, layer, entry, present, composition) {
110
+ removeEntry(connection, key, layer, entry.path);
111
+ const content = textOf(entry);
112
+ const made = present ? derived(entry.path, content, entry.outline, composition) : ABSENT;
113
+ insertEntry(connection, key, layer, entry, present, made);
114
+ if (!present)
115
+ return;
116
+ const id = entryId(connection, key, layer, entry.path);
117
+ writeDerived(connection, id, key, made);
118
+ }
119
+ function placeEntry(connection, key, entry) {
120
+ removeEntry(connection, key, CONFIRMED, entry.path);
121
+ insertEntry(connection, key, CONFIRMED, entry, true, described(entry.path, textOf(entry), entry.outline));
122
+ }
123
+ function revealed(connection, key, touched) {
124
+ if (touched.length === 0)
125
+ return;
126
+ connection.run(`UPDATE replica_entries SET visible = CASE WHEN layer = ${OVERLAY} THEN present ELSE NOT EXISTS (SELECT 1 FROM replica_entries o WHERE o.key = replica_entries.key AND o.layer = ${OVERLAY} AND o.path = replica_entries.path) END WHERE key = ? AND layer IN (${CONFIRMED}, ${OVERLAY}) AND path IN (SELECT value FROM json_each(?))`, [key, paths(touched)]);
127
+ }
128
+ function newKey(connection, locator, transfer, fingerprint, built) {
129
+ const owed = built ? null : 0;
130
+ connection.run('INSERT INTO replica_keys (locator, transfer, fingerprint, rederiving) VALUES (?, ?, ?, ?)', [locator, transfer, fingerprint, owed]);
131
+ const [row] = connection.rows('SELECT last_insert_rowid() AS key');
132
+ return number(row.key);
133
+ }
134
+ function recomposed(connection, key, fingerprint) {
135
+ const [row] = connection.rows('SELECT fingerprint FROM replica_keys WHERE key = ?', [key]);
136
+ if (row === undefined || text(row.fingerprint) === fingerprint)
137
+ return;
138
+ const held = connection.rows('SELECT 1 FROM replica_entries WHERE key = ? LIMIT 1', [key]);
139
+ connection.run(`UPDATE replica_keys SET fingerprint = ?${held.length === 0 ? '' : ', rederiving = 0'} WHERE key = ?`, [fingerprint, key]);
140
+ }
141
+ export function entered(connection, locator, composition, rows) {
142
+ const key = newKey(connection, locator, '', composedOf(composition).fingerprint, true);
143
+ for (const entry of rows)
144
+ putEntry(connection, key, CONFIRMED, entry, true, composition);
145
+ return key;
146
+ }
147
+ export function stage(connection, request) {
148
+ const key = request.key ??
149
+ newKey(connection, request.locator, request.transfer, request.fingerprint, false);
150
+ recomposed(connection, key, request.fingerprint);
151
+ connection.run('UPDATE replica_keys SET rederiving = coalesce(rederiving, last) FROM (SELECT coalesce(max(id), 0) AS last FROM replica_entries) WHERE key = ?', [key]);
152
+ for (const entry of request.entries)
153
+ placeEntry(connection, key, entry);
154
+ for (const path of request.folders) {
155
+ connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present) VALUES (?, ?, ?, 1)', [key, CONFIRMED, path]);
156
+ }
157
+ return key;
158
+ }
159
+ export function refreshing(connection, request) {
160
+ const held = connection.rows('SELECT 1 FROM replica_keys k WHERE k.key = ? AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)', [request.key]);
161
+ if (held.length === 0)
162
+ return null;
163
+ recomposed(connection, request.key, request.composed.fingerprint);
164
+ const composition = request.composed.composition;
165
+ for (const entry of request.entries) {
166
+ putEntry(connection, request.key, REFRESHED, entry, true, composition);
167
+ }
168
+ for (const path of request.removals) {
169
+ putEntry(connection, request.key, REFRESHED, {
170
+ path,
171
+ content: null,
172
+ outline: null,
173
+ binary: null,
174
+ revision: null,
175
+ fileId: null,
176
+ change: null,
177
+ written: null,
178
+ }, false, composition);
179
+ }
180
+ for (const [list, present] of [
181
+ [request.folders, 1],
182
+ [request.folderRemovals, 0],
183
+ ]) {
184
+ for (const path of list) {
185
+ connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present) VALUES (?, ?, ?, ?)', [request.key, REFRESHED, path, present]);
186
+ }
187
+ }
188
+ return null;
189
+ }
190
+ function dropLayer(connection, key, layer) {
191
+ for (const table of ['replica_values', 'replica_links']) {
192
+ connection.run(`DELETE FROM ${table} WHERE doc IN (SELECT id FROM replica_entries WHERE key = ? AND layer = ?)`, [key, layer]);
193
+ }
194
+ connection.run('DELETE FROM replica_entries WHERE key = ? AND layer = ?', [key, layer]);
195
+ connection.run('DELETE FROM replica_folders WHERE key = ? AND layer = ?', [key, layer]);
196
+ }
197
+ export function unrefresh(connection, key) {
198
+ dropLayer(connection, key, REFRESHED);
199
+ return null;
200
+ }
201
+ function refreshedPaths(connection, key) {
202
+ return connection
203
+ .rows('SELECT path FROM replica_entries WHERE key = ? AND layer = ?', [key, REFRESHED])
204
+ .map((row) => text(row.path));
205
+ }
206
+ function applyRefresh(connection, key) {
207
+ const touched = refreshedPaths(connection, key);
208
+ const replaced = `SELECT c.id FROM replica_entries c JOIN replica_entries r ON r.key = c.key AND r.layer = ${REFRESHED} AND r.path = c.path WHERE c.key = ? AND c.layer = ${CONFIRMED}`;
209
+ for (const table of ['replica_values', 'replica_links']) {
210
+ connection.run(`DELETE FROM ${table} WHERE doc IN (${replaced})`, [key]);
211
+ }
212
+ connection.run(`DELETE FROM replica_entries WHERE id IN (${replaced})`, [key]);
213
+ connection.run('DELETE FROM replica_entries WHERE key = ? AND layer = ? AND present = 0', [
214
+ key,
215
+ REFRESHED,
216
+ ]);
217
+ connection.run('UPDATE replica_entries SET layer = ? WHERE key = ? AND layer = ?', [
218
+ CONFIRMED,
219
+ key,
220
+ REFRESHED,
221
+ ]);
222
+ connection.run(`DELETE FROM replica_folders WHERE key = ? AND layer = ${CONFIRMED} AND path IN (SELECT path FROM replica_folders WHERE key = ? AND layer = ${REFRESHED})`, [key, key]);
223
+ connection.run(`UPDATE replica_folders SET layer = ${CONFIRMED} WHERE key = ? AND layer = ${REFRESHED} AND present = 1`, [key]);
224
+ connection.run('DELETE FROM replica_folders WHERE key = ? AND layer = ?', [key, REFRESHED]);
225
+ return touched;
226
+ }
227
+ function beneathRoots(path) {
228
+ return `EXISTS (SELECT 1 FROM json_each(?) r WHERE ${path} = r.value OR (${path} >= r.value || '/' AND ${path} < r.value || '0'))`;
229
+ }
230
+ function counted(connection, key, roots) {
231
+ const [row] = connection.rows(`SELECT count(*) AS held FROM replica_entries WHERE key = ? AND visible = 1 AND present = 1 AND (binary IS NOT NULL OR substr(path, -3) = '.md') AND NOT ${beneathRoots('replica_entries.path')}`, [key, JSON.stringify(roots)]);
232
+ return number(row.held);
233
+ }
234
+ export function counts(connection, rooms) {
235
+ return rooms.map(({ key, roots }) => counted(connection, key, roots));
236
+ }
237
+ function erasing(connection, key, locator) {
238
+ connection.run('INSERT OR REPLACE INTO replica_erasing (key, locator) VALUES (?, ?)', [
239
+ key,
240
+ locator,
241
+ ]);
242
+ }
243
+ function commitRoom(connection, room) {
244
+ const touched = new Set();
245
+ recomposed(connection, room.key, room.composed.fingerprint);
246
+ const composition = room.composed.composition;
247
+ if (room.published !== undefined &&
248
+ room.published.from !== null &&
249
+ room.published.from !== room.key) {
250
+ erasing(connection, room.published.from, room.locator);
251
+ }
252
+ if (room.refreshed === true) {
253
+ for (const path of applyRefresh(connection, room.key))
254
+ touched.add(path);
255
+ }
256
+ for (const entry of room.confirmed?.set ?? []) {
257
+ putEntry(connection, room.key, CONFIRMED, entry, true, composition);
258
+ touched.add(entry.path);
259
+ }
260
+ for (const path of room.confirmed?.removed ?? []) {
261
+ removeEntry(connection, room.key, CONFIRMED, path);
262
+ touched.add(path);
263
+ }
264
+ for (const entry of room.overlay?.set ?? []) {
265
+ putEntry(connection, room.key, OVERLAY, entry, entry.present, composition);
266
+ touched.add(entry.path);
267
+ }
268
+ for (const path of room.overlay?.cleared ?? []) {
269
+ removeEntry(connection, room.key, OVERLAY, path);
270
+ touched.add(path);
271
+ }
272
+ const changed = [...touched];
273
+ revealed(connection, room.key, changed);
274
+ if (room.record !== undefined) {
275
+ connection.run('INSERT OR REPLACE INTO replica_rooms (locator, key, record) VALUES (?, ?, ?)', [
276
+ room.locator,
277
+ room.key,
278
+ room.record,
279
+ ]);
280
+ }
281
+ return room.published === undefined ? changed : null;
282
+ }
283
+ export function commit(connection, rooms) {
284
+ const counted = {};
285
+ const changed = {};
286
+ const indexed = {};
287
+ for (const room of rooms) {
288
+ changed[room.locator] = commitRoom(connection, room);
289
+ counted[room.locator] = counts(connection, [
290
+ { key: room.key, roots: room.roots ?? rootsOf(connection, room.locator) },
291
+ ])[0];
292
+ const [held] = connection.rows('SELECT rederiving FROM replica_keys WHERE key = ?', [room.key]);
293
+ indexed[room.locator] = held !== undefined && held.rederiving === null;
294
+ }
295
+ return { counts: counted, changed, indexed };
296
+ }
297
+ function rootsOf(connection, locator) {
298
+ const [row] = connection.rows('SELECT record FROM replica_rooms WHERE locator = ?', [locator]);
299
+ const record = json(row?.record);
300
+ return record?.typedSelection?.roots ?? [];
301
+ }
302
+ export function fenceRooms(connection, locators) {
303
+ for (const locator of locators) {
304
+ for (const row of connection.rows('SELECT key FROM replica_keys WHERE locator = ? UNION SELECT key FROM replica_rooms WHERE locator = ?', [locator, locator])) {
305
+ erasing(connection, number(row.key), locator);
306
+ }
307
+ connection.run('DELETE FROM replica_rooms WHERE locator = ?', [locator]);
308
+ connection.run('DELETE FROM replica_opened WHERE locator = ?', [locator]);
309
+ }
310
+ return null;
311
+ }
312
+ export function abandon(connection, locator) {
313
+ for (const row of connection.rows(`SELECT k.key FROM replica_keys k WHERE k.locator = ?
314
+ AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
315
+ AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)`, [locator])) {
316
+ erasing(connection, number(row.key), locator);
317
+ }
318
+ return null;
319
+ }
320
+ export function release(connection, key) {
321
+ const [held] = connection.rows(`SELECT k.locator FROM replica_keys k WHERE k.key = ?
322
+ AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)`, [key]);
323
+ if (held !== undefined)
324
+ erasing(connection, key, text(held.locator));
325
+ return null;
326
+ }
327
+ export function erase(connection, locator, limit) {
328
+ const [held] = connection.rows(`SELECT key FROM replica_erasing${locator === null ? '' : ' WHERE locator = ?'} ORDER BY key LIMIT 1`, locator === null ? [] : [locator]);
329
+ if (held === undefined)
330
+ return { remaining: false };
331
+ const key = number(held.key);
332
+ const batch = 'SELECT id FROM replica_entries WHERE key = ? ORDER BY id LIMIT ?';
333
+ for (const table of ['replica_values', 'replica_links']) {
334
+ connection.run(`DELETE FROM ${table} WHERE doc IN (${batch})`, [key, limit]);
335
+ }
336
+ connection.run(`DELETE FROM replica_entries WHERE id IN (${batch})`, [key, limit]);
337
+ const left = connection.rows('SELECT 1 FROM replica_entries WHERE key = ? LIMIT 1', [key]).length > 0;
338
+ if (!left) {
339
+ connection.run('DELETE FROM replica_folders WHERE key = ?', [key]);
340
+ connection.run('DELETE FROM replica_erasing WHERE key = ?', [key]);
341
+ connection.run('DELETE FROM replica_keys WHERE key = ?', [key]);
342
+ }
343
+ const [more] = connection.rows(`SELECT 1 FROM replica_erasing${locator === null ? '' : ' WHERE locator = ?'} LIMIT 1`, locator === null ? [] : [locator]);
344
+ return { remaining: more !== undefined };
345
+ }
346
+ function cursor(value) {
347
+ return value === null || value === undefined ? null : number(value);
348
+ }
349
+ export function rederive(connection, key, composed, bytes, ms, floor) {
350
+ const started = performance.now();
351
+ recomposed(connection, key, composed.fingerprint);
352
+ const [held] = connection.rows('SELECT rederiving FROM replica_keys WHERE key = ?', [key]);
353
+ const deriving = cursor(held?.rederiving);
354
+ if (deriving === null)
355
+ return { done: true, text: 0 };
356
+ const composition = composed.composition;
357
+ let last = deriving;
358
+ let spent = 0;
359
+ let built = 0;
360
+ let read = false;
361
+ reading: for (;;) {
362
+ const rows = connection.rows('SELECT id, path, content, outline FROM replica_entries WHERE key = ? AND present = 1 AND id > ? ORDER BY id LIMIT ?', [key, last, DERIVED_ROWS]);
363
+ for (const row of rows) {
364
+ const id = number(row.id);
365
+ const path = text(row.path);
366
+ const content = text(row.content);
367
+ const made = derived(path, content, json(row.outline), composition);
368
+ dropDerived(connection, id);
369
+ writeDerived(connection, id, key, made);
370
+ built += content?.length ?? 0;
371
+ last = id;
372
+ spent += ENTRY_WEIGHT + (content?.length ?? 0);
373
+ if (spent >= bytes || (spent >= floor && performance.now() - started >= ms))
374
+ break reading;
375
+ }
376
+ if (rows.length < DERIVED_ROWS) {
377
+ read = true;
378
+ break;
379
+ }
380
+ }
381
+ connection.run('UPDATE replica_keys SET rederiving = ? WHERE key = ?', [read ? null : last, key]);
382
+ return { done: read, text: built };
383
+ }
384
+ function outlinedAgain(connection, row, key, content, composition) {
385
+ const id = number(row.id);
386
+ const path = text(row.path);
387
+ const made = derived(path, content, json(row.outline), composition);
388
+ connection.run('UPDATE replica_entries SET content = ?, type = ?, declared = ?, queried = ?, folder = ?, fields = ? WHERE id = ?', [content, made.type, made.declared, flag(made.queried), made.folder, made.fields, id]);
389
+ dropDerived(connection, id);
390
+ writeDerived(connection, id, key, made);
391
+ }
392
+ export function holdTexts(connection, request) {
393
+ const held = [];
394
+ for (const { path, revision, content } of request.texts) {
395
+ const [row] = connection.rows(`SELECT id, path, outline FROM replica_entries WHERE key = ? AND layer = ${CONFIRMED} AND path = ? AND revision = ? AND content IS NULL AND outline IS NOT NULL`, [request.key, path, revision]);
396
+ if (row === undefined)
397
+ continue;
398
+ outlinedAgain(connection, row, request.key, content, request.composed.composition);
399
+ connection.run('INSERT OR IGNORE INTO replica_opened (locator, path) VALUES (?, ?)', [
400
+ request.locator,
401
+ path,
402
+ ]);
403
+ held.push(path);
404
+ }
405
+ return held;
406
+ }
407
+ export function unholdTexts(connection, request) {
408
+ connection.run('DELETE FROM replica_opened WHERE locator = ?', [request.locator]);
409
+ const rows = connection.rows(`SELECT id, path, outline, octet_length(content) AS bytes FROM replica_entries WHERE key = ? AND layer = ${CONFIRMED} AND visible = 1 AND content IS NOT NULL AND outline IS NOT NULL`, [request.key]);
410
+ for (const row of rows) {
411
+ outlinedAgain(connection, row, request.key, null, request.composed.composition);
412
+ }
413
+ return {
414
+ paths: rows.map((row) => text(row.path)),
415
+ bytes: rows.reduce((total, row) => total + number(row.bytes), 0),
416
+ };
417
+ }
418
+ export function remembered(connection, request) {
419
+ connection.run(`INSERT OR IGNORE INTO replica_opened (locator, path) SELECT ?, path FROM replica_entries WHERE key = ? AND layer = ${CONFIRMED} AND content IS NOT NULL AND binary IS NULL AND path IN (SELECT value FROM json_each(?))`, [request.locator, request.key, paths(request.paths)]);
420
+ return null;
421
+ }
422
+ export function wanted(connection, request) {
423
+ return connection
424
+ .rows(`SELECT e.path, e.revision FROM replica_opened o JOIN replica_entries e ON e.key = ? AND e.layer = ${CONFIRMED} AND e.path = o.path WHERE o.locator = ? AND e.content IS NULL AND e.outline IS NOT NULL AND e.revision IS NOT NULL`, [request.key, request.locator])
425
+ .map((row) => ({ path: text(row.path), revision: text(row.revision) }));
426
+ }
427
+ function heldEntry(row) {
428
+ return {
429
+ path: text(row.path),
430
+ content: text(row.content),
431
+ outline: json(row.outline),
432
+ binary: json(row.binary),
433
+ revision: text(row.revision),
434
+ fileId: text(row.file_id),
435
+ change: json(row.change),
436
+ written: text(row.written),
437
+ type: text(row.type),
438
+ };
439
+ }
440
+ const ENTRY_COLUMNS = 'e.path, e.content, e.outline, e.binary, e.revision, e.file_id, e.change, e.written, e.type';
441
+ function unrefreshed(alias) {
442
+ return `NOT EXISTS (SELECT 1 FROM replica_entries r WHERE r.key = ${alias}.key AND r.layer = ${REFRESHED} AND r.path = ${alias}.path)`;
443
+ }
444
+ function viewed(view, alias) {
445
+ if (view === 'visible')
446
+ return `${alias}.visible = 1 AND ${alias}.present = 1`;
447
+ if (view === 'confirmed')
448
+ return `${alias}.layer = ${CONFIRMED}`;
449
+ return `${alias}.present = 1 AND (${alias}.layer = ${REFRESHED} OR (${alias}.layer = ${CONFIRMED} AND ${unrefreshed(alias)}))`;
450
+ }
451
+ function layers(view) {
452
+ if (view !== 'refreshed')
453
+ return [viewed(view, 'e')];
454
+ return [
455
+ `e.layer = ${REFRESHED} AND e.present = 1`,
456
+ `e.layer = ${CONFIRMED} AND e.present = 1 AND ${unrefreshed('e')}`,
457
+ ];
458
+ }
459
+ function pathOrdered(connection, columns, key, view, condition, after, limit) {
460
+ const beyond = after === null ? '' : ' AND e.path > ?';
461
+ const parts = layers(view).map((layer) => `SELECT ${columns} FROM replica_entries e WHERE e.key = ? AND ${layer}${condition}${beyond}`);
462
+ return connection.rows(`${parts.join(' UNION ALL ')} ORDER BY path LIMIT ?`, [
463
+ ...parts.flatMap(() => (after === null ? [key] : [key, after])),
464
+ limit,
465
+ ]);
466
+ }
467
+ export function entries(connection, key, view, list) {
468
+ if (list.length === 0)
469
+ return [];
470
+ return connection
471
+ .rows(`SELECT ${ENTRY_COLUMNS} FROM replica_entries e WHERE e.key = ? AND ${viewed(view, 'e')} AND e.path IN (SELECT value FROM json_each(?))`, [key, paths(list)])
472
+ .map(heldEntry);
473
+ }
474
+ export function typed(connection, key, view, list) {
475
+ if (list.length === 0)
476
+ return [];
477
+ return connection
478
+ .rows(`SELECT e.path, e.declared, e.binary IS NOT NULL AS binary FROM replica_entries e WHERE e.key = ? AND ${viewed(view, 'e')} AND e.path IN (SELECT value FROM json_each(?))`, [key, paths(list)])
479
+ .map((row) => ({
480
+ path: text(row.path),
481
+ type: text(row.declared),
482
+ binary: number(row.binary) === 1,
483
+ }));
484
+ }
485
+ export function referrers(connection, key, view, targets, kinds) {
486
+ if (targets.length === 0)
487
+ return [];
488
+ return connection
489
+ .rows(`SELECT DISTINCT e.path, l.target, l.kind FROM replica_links l JOIN replica_entries e ON e.id = l.doc WHERE l.key = ? AND l.target IN (SELECT value FROM json_each(?)) AND l.kind IN (SELECT value FROM json_each(?)) AND ${viewed(view, 'e')}`, [key, paths(targets), JSON.stringify(kinds)])
490
+ .map((row) => ({
491
+ path: text(row.path),
492
+ target: text(row.target),
493
+ kind: text(row.kind),
494
+ }));
495
+ }
496
+ export function files(connection, key, view, after, limit) {
497
+ return pathOrdered(connection, 'e.path, e.revision', key, view, ' AND e.content IS NOT NULL AND e.revision IS NOT NULL', after, limit).map((row) => ({ path: text(row.path), revision: text(row.revision) }));
498
+ }
499
+ export function folders(connection, key, view, list) {
500
+ return list.filter((path) => connection.rows(`SELECT 1 FROM replica_folders f WHERE f.key = ? AND f.layer = ${CONFIRMED} AND f.present = 1 AND f.path = ? UNION ALL SELECT 1 FROM replica_entries e WHERE e.key = ? AND ${viewed(view, 'e')} AND e.path >= ? AND e.path < ? LIMIT 1`, [key, path, key, `${path}/`, `${path}0`]).length > 0);
501
+ }
502
+ export function typedWithin(connection, key, view, roots, within) {
503
+ return (connection.rows(`SELECT 1 FROM replica_entries e WHERE e.key = ? AND ${viewed(view, 'e')} AND e.declared IS NOT NULL AND e.declared <> 'any' AND ${within ? '' : 'NOT '}${beneathRoots('e.path')} LIMIT 1`, [key, JSON.stringify(roots)]).length > 0);
504
+ }
505
+ export function opened(connection) {
506
+ const facts = {};
507
+ for (const row of connection.rows('SELECT name, value FROM replica_facts')) {
508
+ facts[text(row.name)] = text(row.value);
509
+ }
510
+ return {
511
+ rooms: connection.rows('SELECT locator, key, record FROM replica_rooms').map((row) => ({
512
+ locator: text(row.locator),
513
+ key: number(row.key),
514
+ record: text(row.record),
515
+ })),
516
+ keys: connection
517
+ .rows('SELECT key, locator, transfer, rederiving FROM replica_keys')
518
+ .map((row) => ({
519
+ key: number(row.key),
520
+ locator: text(row.locator),
521
+ transfer: text(row.transfer),
522
+ rederiving: cursor(row.rederiving),
523
+ })),
524
+ facts,
525
+ presentation: connection.rows('SELECT app, value FROM replica_presentation').map((row) => ({
526
+ app: text(row.app),
527
+ value: text(row.value),
528
+ })),
529
+ overlaid: connection
530
+ .rows(`SELECT key, path FROM replica_entries WHERE layer = ${OVERLAY}`)
531
+ .map((row) => ({ key: number(row.key), path: text(row.path) })),
532
+ orphans: connection
533
+ .rows(`SELECT k.key FROM replica_keys k
534
+ WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
535
+ AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)`)
536
+ .map((row) => number(row.key)),
537
+ erasing: connection.rows('SELECT key, locator FROM replica_erasing').map((row) => ({
538
+ key: number(row.key),
539
+ locator: text(row.locator),
540
+ })),
541
+ };
542
+ }
543
+ export function ended(connection) {
544
+ for (const statement of REPLICA_TABLES) {
545
+ const table = /^CREATE TABLE (\w+)/u.exec(statement)?.[1];
546
+ if (table !== undefined)
547
+ connection.run(`DELETE FROM ${table}`);
548
+ }
549
+ }