@agentshouse/mdruntime 0.1.0-cee9c8aa4cd86 → 0.1.0-cef5833f6c689

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