@agentshouse/mdruntime 0.1.0-ca4dc698bd0c4 → 0.1.0-ca6edc1abd618

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