@agentshouse/mdruntime 0.1.0-cf1e7fdb9c600 → 0.1.0-cf281e8224a5d

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 (264) hide show
  1. package/README.md +9 -9
  2. package/dist/acceptance.d.ts +6 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +385 -0
  5. package/dist/actions.d.ts +20 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +251 -0
  8. package/dist/adapter.d.ts +24 -25
  9. package/dist/adapter.d.ts.map +1 -1
  10. package/dist/attached-model.d.ts +3 -10
  11. package/dist/attached-model.d.ts.map +1 -1
  12. package/dist/attached-model.js +21 -56
  13. package/dist/bootstrap.d.ts +8 -4
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +207 -76
  16. package/dist/cell.d.ts +6 -0
  17. package/dist/cell.d.ts.map +1 -0
  18. package/dist/cell.js +9 -0
  19. package/dist/change.d.ts +1 -0
  20. package/dist/change.d.ts.map +1 -1
  21. package/dist/change.js +4 -2
  22. package/dist/chat-types.d.ts +5 -0
  23. package/dist/chat-types.d.ts.map +1 -0
  24. package/dist/chat-types.js +13 -0
  25. package/dist/client.d.ts +18 -2
  26. package/dist/client.d.ts.map +1 -1
  27. package/dist/client.js +7 -2
  28. package/dist/compile.d.ts +5 -0
  29. package/dist/compile.d.ts.map +1 -1
  30. package/dist/compile.js +202 -80
  31. package/dist/connection.d.ts +5 -141
  32. package/dist/connection.d.ts.map +1 -1
  33. package/dist/connection.js +101 -5280
  34. package/dist/conversation-live.d.ts +136 -0
  35. package/dist/conversation-live.d.ts.map +1 -0
  36. package/dist/conversation-live.js +248 -0
  37. package/dist/conversation-row.d.ts +33 -0
  38. package/dist/conversation-row.d.ts.map +1 -0
  39. package/dist/conversation-row.js +53 -0
  40. package/dist/conversation.d.ts +29 -35
  41. package/dist/conversation.d.ts.map +1 -1
  42. package/dist/conversation.js +838 -840
  43. package/dist/discovery.d.ts +10 -0
  44. package/dist/discovery.d.ts.map +1 -0
  45. package/dist/discovery.js +175 -0
  46. package/dist/durability.d.ts +15 -0
  47. package/dist/durability.d.ts.map +1 -1
  48. package/dist/entry.d.ts.map +1 -1
  49. package/dist/entry.js +4 -3
  50. package/dist/expectation.d.ts +4 -2
  51. package/dist/expectation.d.ts.map +1 -1
  52. package/dist/expectation.js +33 -11
  53. package/dist/figures.d.ts +49 -0
  54. package/dist/figures.d.ts.map +1 -0
  55. package/dist/figures.js +197 -0
  56. package/dist/holdings.d.ts +29 -0
  57. package/dist/holdings.d.ts.map +1 -0
  58. package/dist/holdings.js +95 -0
  59. package/dist/host.d.ts +1 -0
  60. package/dist/host.d.ts.map +1 -1
  61. package/dist/host.js +11 -4
  62. package/dist/identity.d.ts +0 -1
  63. package/dist/identity.d.ts.map +1 -1
  64. package/dist/identity.js +0 -3
  65. package/dist/index.d.ts +20 -6
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +5 -1
  68. package/dist/install.d.ts +56 -0
  69. package/dist/install.d.ts.map +1 -0
  70. package/dist/install.js +554 -0
  71. package/dist/job.d.ts +20 -1
  72. package/dist/job.d.ts.map +1 -1
  73. package/dist/job.js +26 -0
  74. package/dist/lifecycle.d.ts +7 -0
  75. package/dist/lifecycle.d.ts.map +1 -0
  76. package/dist/lifecycle.js +268 -0
  77. package/dist/listing.d.ts +53 -0
  78. package/dist/listing.d.ts.map +1 -0
  79. package/dist/listing.js +176 -0
  80. package/dist/live.d.ts +4 -4
  81. package/dist/live.d.ts.map +1 -1
  82. package/dist/live.js +52 -16
  83. package/dist/locks.d.ts +9 -0
  84. package/dist/locks.d.ts.map +1 -0
  85. package/dist/locks.js +61 -0
  86. package/dist/log.d.ts +1 -0
  87. package/dist/log.d.ts.map +1 -1
  88. package/dist/lost.d.ts +5 -0
  89. package/dist/lost.d.ts.map +1 -0
  90. package/dist/lost.js +61 -0
  91. package/dist/media.d.ts +1 -1
  92. package/dist/media.d.ts.map +1 -1
  93. package/dist/media.js +3 -3
  94. package/dist/memory.d.ts +3 -1
  95. package/dist/memory.d.ts.map +1 -1
  96. package/dist/memory.js +45 -6
  97. package/dist/messages.d.ts +25 -1
  98. package/dist/messages.d.ts.map +1 -1
  99. package/dist/messages.js +89 -16
  100. package/dist/model.d.ts +1 -7
  101. package/dist/model.d.ts.map +1 -1
  102. package/dist/model.js +0 -6
  103. package/dist/models.d.ts +14 -0
  104. package/dist/models.d.ts.map +1 -0
  105. package/dist/models.js +195 -0
  106. package/dist/observation.d.ts.map +1 -1
  107. package/dist/observation.js +12 -17
  108. package/dist/opened.js +3 -3
  109. package/dist/opening.d.ts +4 -0
  110. package/dist/opening.d.ts.map +1 -0
  111. package/dist/opening.js +57 -0
  112. package/dist/outcome.d.ts +4 -1
  113. package/dist/outcome.d.ts.map +1 -1
  114. package/dist/outcome.js +3 -0
  115. package/dist/overlay.d.ts +1 -0
  116. package/dist/overlay.d.ts.map +1 -1
  117. package/dist/overlay.js +11 -0
  118. package/dist/parts.d.ts +60 -0
  119. package/dist/parts.d.ts.map +1 -0
  120. package/dist/parts.js +126 -0
  121. package/dist/paths.d.ts +1 -0
  122. package/dist/paths.d.ts.map +1 -1
  123. package/dist/paths.js +22 -0
  124. package/dist/preparation.d.ts +20 -0
  125. package/dist/preparation.d.ts.map +1 -0
  126. package/dist/preparation.js +226 -0
  127. package/dist/queries.d.ts +16 -0
  128. package/dist/queries.d.ts.map +1 -0
  129. package/dist/queries.js +272 -0
  130. package/dist/query.d.ts +10 -2
  131. package/dist/query.d.ts.map +1 -1
  132. package/dist/questions.d.ts +34 -0
  133. package/dist/questions.d.ts.map +1 -0
  134. package/dist/questions.js +122 -0
  135. package/dist/reach.d.ts +8 -6
  136. package/dist/reach.d.ts.map +1 -1
  137. package/dist/reach.js +159 -100
  138. package/dist/react.d.ts +11 -1
  139. package/dist/react.d.ts.map +1 -1
  140. package/dist/react.js +10 -1
  141. package/dist/read.d.ts +7 -0
  142. package/dist/read.d.ts.map +1 -0
  143. package/dist/read.js +17 -0
  144. package/dist/readiness.d.ts +0 -1
  145. package/dist/readiness.d.ts.map +1 -1
  146. package/dist/reads.d.ts +37 -0
  147. package/dist/reads.d.ts.map +1 -0
  148. package/dist/reads.js +171 -0
  149. package/dist/recovery.d.ts.map +1 -1
  150. package/dist/recovery.js +9 -1
  151. package/dist/refresh.d.ts +1 -1
  152. package/dist/refresh.d.ts.map +1 -1
  153. package/dist/refresh.js +8 -20
  154. package/dist/refusal.d.ts +5 -0
  155. package/dist/refusal.d.ts.map +1 -0
  156. package/dist/refusal.js +55 -0
  157. package/dist/replica.d.ts +7 -7
  158. package/dist/replica.d.ts.map +1 -1
  159. package/dist/replica.js +51 -19
  160. package/dist/room-draft.d.ts +2 -4
  161. package/dist/room-draft.d.ts.map +1 -1
  162. package/dist/room-draft.js +3 -3
  163. package/dist/room-drafts.d.ts +24 -0
  164. package/dist/room-drafts.d.ts.map +1 -0
  165. package/dist/room-drafts.js +217 -0
  166. package/dist/rows.d.ts +14 -5
  167. package/dist/rows.d.ts.map +1 -1
  168. package/dist/rows.js +50 -58
  169. package/dist/runtime.d.ts +43 -0
  170. package/dist/runtime.d.ts.map +1 -0
  171. package/dist/runtime.js +1 -0
  172. package/dist/save.d.ts +5 -1
  173. package/dist/save.d.ts.map +1 -1
  174. package/dist/save.js +21 -20
  175. package/dist/saves.d.ts +70 -0
  176. package/dist/saves.d.ts.map +1 -0
  177. package/dist/saves.js +1005 -0
  178. package/dist/seed.d.ts +11 -2
  179. package/dist/seed.d.ts.map +1 -1
  180. package/dist/seed.js +71 -1
  181. package/dist/shared.d.ts +1 -6
  182. package/dist/shared.d.ts.map +1 -1
  183. package/dist/shared.js +13 -95
  184. package/dist/shown.d.ts +31 -0
  185. package/dist/shown.d.ts.map +1 -0
  186. package/dist/shown.js +292 -0
  187. package/dist/socket/admission.d.ts +7 -0
  188. package/dist/socket/admission.d.ts.map +1 -0
  189. package/dist/socket/admission.js +39 -0
  190. package/dist/socket/answers.d.ts +6 -0
  191. package/dist/socket/answers.d.ts.map +1 -0
  192. package/dist/socket/answers.js +20 -0
  193. package/dist/socket/lanes.d.ts +20 -0
  194. package/dist/socket/lanes.d.ts.map +1 -0
  195. package/dist/socket/lanes.js +77 -0
  196. package/dist/socket/reads.d.ts +10 -0
  197. package/dist/socket/reads.d.ts.map +1 -0
  198. package/dist/socket/reads.js +31 -0
  199. package/dist/socket/rooms.d.ts +44 -0
  200. package/dist/socket/rooms.d.ts.map +1 -0
  201. package/dist/socket/rooms.js +90 -0
  202. package/dist/socket/session.d.ts +38 -0
  203. package/dist/socket/session.d.ts.map +1 -0
  204. package/dist/socket/session.js +23 -0
  205. package/dist/socket.d.ts +70 -0
  206. package/dist/socket.d.ts.map +1 -0
  207. package/dist/socket.js +175 -0
  208. package/dist/source.d.ts +61 -0
  209. package/dist/source.d.ts.map +1 -0
  210. package/dist/source.js +82 -0
  211. package/dist/status.d.ts +0 -2
  212. package/dist/status.d.ts.map +1 -1
  213. package/dist/status.js +0 -6
  214. package/dist/store/fences.d.ts +16 -0
  215. package/dist/store/fences.d.ts.map +1 -0
  216. package/dist/store/fences.js +26 -0
  217. package/dist/store/installs.d.ts +30 -0
  218. package/dist/store/installs.d.ts.map +1 -0
  219. package/dist/store/installs.js +34 -0
  220. package/dist/store/readiness.d.ts +10 -0
  221. package/dist/store/readiness.d.ts.map +1 -0
  222. package/dist/store/readiness.js +52 -0
  223. package/dist/store/records.d.ts +212 -0
  224. package/dist/store/records.d.ts.map +1 -0
  225. package/dist/store/records.js +431 -0
  226. package/dist/store/room-held.d.ts +8 -0
  227. package/dist/store/room-held.d.ts.map +1 -0
  228. package/dist/store/room-held.js +17 -0
  229. package/dist/store/space.d.ts +11 -0
  230. package/dist/store/space.d.ts.map +1 -0
  231. package/dist/store/space.js +41 -0
  232. package/dist/store/watchers.d.ts +17 -0
  233. package/dist/store/watchers.d.ts.map +1 -0
  234. package/dist/store/watchers.js +56 -0
  235. package/dist/store/writes.d.ts +24 -0
  236. package/dist/store/writes.d.ts.map +1 -0
  237. package/dist/store/writes.js +125 -0
  238. package/dist/store.d.ts +107 -153
  239. package/dist/store.d.ts.map +1 -1
  240. package/dist/store.js +143 -396
  241. package/dist/stored.d.ts +59 -65
  242. package/dist/stored.d.ts.map +1 -1
  243. package/dist/stored.js +255 -149
  244. package/dist/surface.d.ts +88 -0
  245. package/dist/surface.d.ts.map +1 -0
  246. package/dist/surface.js +223 -0
  247. package/dist/sync.d.ts +19 -0
  248. package/dist/sync.d.ts.map +1 -0
  249. package/dist/sync.js +532 -0
  250. package/dist/tables.d.ts +114 -51
  251. package/dist/tables.d.ts.map +1 -1
  252. package/dist/tables.js +209 -57
  253. package/dist/tallies.d.ts +18 -0
  254. package/dist/tallies.d.ts.map +1 -0
  255. package/dist/tallies.js +129 -0
  256. package/dist/validation.d.ts.map +1 -1
  257. package/dist/validation.js +4 -4
  258. package/dist/wire.d.ts +16 -1
  259. package/dist/wire.d.ts.map +1 -1
  260. package/dist/wire.js +6 -0
  261. package/package.json +2 -2
  262. package/dist/fence.d.ts +0 -6
  263. package/dist/fence.d.ts.map +0 -1
  264. package/dist/fence.js +0 -10
package/dist/stored.js CHANGED
@@ -1,10 +1,12 @@
1
- import { sql } from '@agentshouse/mdmodel';
1
+ import { instantKey, sql } from '@agentshouse/mdmodel';
2
2
  import { composedOf } from './collection.js';
3
- import { described, derived } from './rows.js';
3
+ import { described, derived, heldOf } from './rows.js';
4
4
  export const REPLICA_TABLES = [
5
5
  'CREATE TABLE replica_keys (key INTEGER PRIMARY KEY AUTOINCREMENT, locator TEXT NOT NULL, transfer TEXT NOT NULL, fingerprint TEXT NOT NULL, rederiving INTEGER)',
6
- 'CREATE TABLE replica_rooms (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, outline TEXT)',
6
+ 'CREATE TABLE replica_rooms (locator TEXT PRIMARY KEY, key INTEGER NOT NULL, record TEXT NOT NULL, documents INTEGER NOT NULL DEFAULT 0) WITHOUT ROWID',
7
+ "CREATE INDEX replica_rooms_model ON replica_rooms (json_extract(record, '$.model.snapshot.identity'), json_extract(record, '$.model.snapshot.version'))",
8
+ 'CREATE TABLE replica_models (identity TEXT NOT NULL, version TEXT NOT NULL, schema TEXT NOT NULL, PRIMARY KEY (identity, version)) WITHOUT ROWID',
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, links TEXT, part TEXT, created TEXT, created_instant TEXT, changed_instant TEXT, bytes INTEGER GENERATED ALWAYS AS (CASE WHEN binary IS NULL THEN length(CAST(content AS BLOB)) ELSE binary ->> '$.byteLength' END) STORED, content TEXT)",
8
10
  'CREATE UNIQUE INDEX replica_entries_path ON replica_entries (key, layer, path)',
9
11
  'CREATE INDEX replica_entries_visible ON replica_entries (key, visible, present, path, binary)',
10
12
  'CREATE INDEX replica_entries_folder ON replica_entries (key, folder, path)',
@@ -13,9 +15,8 @@ export const REPLICA_TABLES = [
13
15
  sql.VALUE_TABLE,
14
16
  '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
17
  'CREATE INDEX replica_links_target ON replica_links (key, target, kind, keys)',
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',
18
+ 'CREATE TABLE replica_folders (key INTEGER NOT NULL, layer INTEGER NOT NULL, path TEXT NOT NULL, present INTEGER NOT NULL, part TEXT, PRIMARY KEY (key, layer, path)) WITHOUT ROWID',
17
19
  'CREATE TABLE replica_erasing (key INTEGER PRIMARY KEY, locator TEXT NOT NULL)',
18
- 'CREATE TABLE replica_opened (locator TEXT NOT NULL, path TEXT NOT NULL, PRIMARY KEY (locator, path)) WITHOUT ROWID',
19
20
  'CREATE TABLE replica_facts (name TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID',
20
21
  'CREATE TABLE replica_presentation (app TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID',
21
22
  ];
@@ -30,8 +31,7 @@ const ABSENT = {
30
31
  queried: false,
31
32
  folder: '',
32
33
  fields: null,
33
- values: [],
34
- links: [],
34
+ links: null,
35
35
  };
36
36
  function text(value) {
37
37
  return typeof value === 'string' ? value : null;
@@ -52,7 +52,8 @@ function dropDerived(connection, doc) {
52
52
  connection.run('DELETE FROM replica_values WHERE doc = ?', [doc]);
53
53
  connection.run('DELETE FROM replica_links WHERE doc = ?', [doc]);
54
54
  }
55
- function writeDerived(connection, doc, key, made) {
55
+ function writeDerived(connection, doc, key, path, held, composition) {
56
+ const made = derived(path, held, composition);
56
57
  if (made.values.length > 0) {
57
58
  const inserted = sql.insertedValues(doc, made.values);
58
59
  connection.run(inserted.sql, inserted.bind);
@@ -76,8 +77,8 @@ function removeEntry(connection, key, layer, path) {
76
77
  dropDerived(connection, id);
77
78
  connection.run('DELETE FROM replica_entries WHERE id = ?', [id]);
78
79
  }
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
80
+ function insertEntry(connection, key, layer, entry, present, made, part = null) {
81
+ connection.run('INSERT INTO replica_entries (key, layer, path, visible, present, queried, content, binary, revision, file_id, created, created_instant, change, changed_instant, written, type, declared, folder, fields, links, part) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
81
82
  key,
82
83
  layer,
83
84
  entry.path,
@@ -85,16 +86,20 @@ function insertEntry(connection, key, layer, entry, present, made) {
85
86
  flag(present),
86
87
  flag(made.queried),
87
88
  entry.binary === null ? entry.content : null,
88
- entry.binary === null && entry.outline !== null ? JSON.stringify(entry.outline) : null,
89
89
  entry.binary === null ? null : JSON.stringify(entry.binary),
90
90
  entry.revision,
91
91
  entry.fileId,
92
+ entry.created,
93
+ instantKey(entry.created),
92
94
  entry.change === null ? null : JSON.stringify(entry.change),
95
+ instantKey(entry.change?.time),
93
96
  entry.written,
94
97
  made.type,
95
98
  made.declared,
96
99
  made.folder,
97
100
  made.fields,
101
+ made.links,
102
+ part,
98
103
  ]);
99
104
  }
100
105
  function textOf(entry) {
@@ -103,16 +108,15 @@ function textOf(entry) {
103
108
  function putEntry(connection, key, layer, entry, present, composition) {
104
109
  removeEntry(connection, key, layer, entry.path);
105
110
  const content = textOf(entry);
106
- const made = present ? derived(entry.path, content, entry.outline, composition) : ABSENT;
107
- insertEntry(connection, key, layer, entry, present, made);
111
+ insertEntry(connection, key, layer, entry, present, present ? described(entry.path, content, entry.facts) : ABSENT);
108
112
  if (!present)
109
113
  return;
110
114
  const id = entryId(connection, key, layer, entry.path);
111
- writeDerived(connection, id, key, made);
115
+ writeDerived(connection, id, key, entry.path, heldOf(entry.path, content, entry.facts), composition);
112
116
  }
113
- function placeEntry(connection, key, entry) {
117
+ function placeEntry(connection, key, entry, part) {
114
118
  removeEntry(connection, key, CONFIRMED, entry.path);
115
- insertEntry(connection, key, CONFIRMED, entry, true, described(entry.path, textOf(entry), entry.outline));
119
+ insertEntry(connection, key, CONFIRMED, entry, true, described(entry.path, textOf(entry), entry.facts), part);
116
120
  }
117
121
  function revealed(connection, key, touched) {
118
122
  if (touched.length === 0)
@@ -139,17 +143,83 @@ export function entered(connection, locator, composition, rows) {
139
143
  return key;
140
144
  }
141
145
  export function stage(connection, request) {
146
+ if (request.key === null) {
147
+ for (const row of connection.rows(STAGED_KEYS, [request.locator])) {
148
+ erasing(connection, number(row.key), request.locator);
149
+ }
150
+ }
142
151
  const key = request.key ??
143
152
  newKey(connection, request.locator, request.transfer, request.fingerprint, false);
144
153
  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]);
154
+ owing(connection, key);
146
155
  for (const entry of request.entries)
147
- placeEntry(connection, key, entry);
156
+ placeEntry(connection, key, entry, request.part);
148
157
  for (const path of request.folders) {
149
- connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present) VALUES (?, ?, ?, 1)', [key, CONFIRMED, path]);
158
+ connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present, part) VALUES (?, ?, ?, 1, ?)', [key, CONFIRMED, path, request.part]);
150
159
  }
151
160
  return key;
152
161
  }
162
+ function owing(connection, key) {
163
+ connection.run('UPDATE replica_keys SET rederiving = coalesce(rederiving, last) FROM (SELECT coalesce(max(id), 0) AS last FROM replica_entries) WHERE key = ?', [key]);
164
+ }
165
+ const STAGED_KEYS = `SELECT k.key, k.transfer FROM replica_keys k WHERE k.locator = ?
166
+ AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
167
+ AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)
168
+ ORDER BY k.key DESC`;
169
+ const UNPARTED = `EXISTS (SELECT 1 FROM replica_entries e WHERE e.key = k.key AND e.part IS NULL)
170
+ OR EXISTS (SELECT 1 FROM replica_folders f WHERE f.key = k.key AND f.part IS NULL)`;
171
+ function partsOf(connection, key, layer) {
172
+ const scoped = layer === null ? '' : ` AND layer = ${layer}`;
173
+ return new Set(connection
174
+ .rows(`SELECT part FROM replica_entries WHERE key = ?${scoped} AND part IS NOT NULL UNION SELECT part FROM replica_folders WHERE key = ?${scoped} AND part IS NOT NULL`, [key, key])
175
+ .map((row) => text(row.part)));
176
+ }
177
+ function resumable(connection, locator) {
178
+ let kept = null;
179
+ for (const row of connection.rows(STAGED_KEYS, [locator])) {
180
+ const key = number(row.key);
181
+ const parted = kept === null &&
182
+ connection.rows(`SELECT 1 FROM replica_keys k WHERE k.key = ? AND NOT (${UNPARTED})`, [key])
183
+ .length > 0;
184
+ if (parted)
185
+ kept = { key, transfer: text(row.transfer) };
186
+ else
187
+ erasing(connection, key, locator);
188
+ }
189
+ return kept;
190
+ }
191
+ export function resume(connection, request) {
192
+ const wanted = new Set(request.parts);
193
+ const held = resumable(connection, request.locator);
194
+ const staged = held === null ? new Set() : partsOf(connection, held.key, null);
195
+ const dropped = [...staged].filter((part) => !wanted.has(part));
196
+ if (held !== null && dropped.length > 0) {
197
+ const named = JSON.stringify(dropped);
198
+ const rows = `SELECT id FROM replica_entries WHERE key = ? AND part IN (SELECT value FROM json_each(?))`;
199
+ for (const table of ['replica_values', 'replica_links']) {
200
+ connection.run(`DELETE FROM ${table} WHERE doc IN (${rows})`, [held.key, named]);
201
+ }
202
+ connection.run(`DELETE FROM replica_entries WHERE id IN (${rows})`, [held.key, named]);
203
+ connection.run('DELETE FROM replica_folders WHERE key = ? AND part IN (SELECT value FROM json_each(?))', [held.key, named]);
204
+ }
205
+ const kept = [...staged].filter((part) => wanted.has(part));
206
+ const publishedParts = request.published === null
207
+ ? new Set()
208
+ : partsOf(connection, request.published, CONFIRMED);
209
+ const copied = request.parts.filter((part) => !staged.has(part) && publishedParts.has(part));
210
+ if (held === null && copied.length === 0) {
211
+ return { key: null, transfer: request.transfer, staged: [] };
212
+ }
213
+ const key = held?.key ?? newKey(connection, request.locator, request.transfer, request.fingerprint, false);
214
+ recomposed(connection, key, request.fingerprint);
215
+ if (copied.length > 0) {
216
+ owing(connection, key);
217
+ const named = JSON.stringify(copied);
218
+ connection.run(`INSERT INTO replica_entries (key, layer, path, visible, present, queried, content, binary, revision, file_id, created, created_instant, change, changed_instant, written, type, declared, folder, fields, links, part) SELECT ?, ${CONFIRMED}, path, 1, 1, queried, content, binary, revision, file_id, created, created_instant, change, changed_instant, written, type, declared, folder, fields, links, part FROM replica_entries WHERE key = ? AND layer = ${CONFIRMED} AND present = 1 AND part IN (SELECT value FROM json_each(?)) ORDER BY path`, [key, request.published, named]);
219
+ connection.run(`INSERT OR REPLACE INTO replica_folders (key, layer, path, present, part) SELECT ?, ${CONFIRMED}, path, 1, part FROM replica_folders WHERE key = ? AND layer = ${CONFIRMED} AND present = 1 AND part IN (SELECT value FROM json_each(?))`, [key, request.published, named]);
220
+ }
221
+ return { key, transfer: held?.transfer ?? request.transfer, staged: [...kept, ...copied] };
222
+ }
153
223
  export function refreshing(connection, request) {
154
224
  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]);
155
225
  if (held.length === 0)
@@ -163,12 +233,13 @@ export function refreshing(connection, request) {
163
233
  putEntry(connection, request.key, REFRESHED, {
164
234
  path,
165
235
  content: null,
166
- outline: null,
167
236
  binary: null,
168
237
  revision: null,
169
238
  fileId: null,
239
+ created: null,
170
240
  change: null,
171
241
  written: null,
242
+ facts: null,
172
243
  }, false, composition);
173
244
  }
174
245
  for (const [list, present] of [
@@ -225,15 +296,18 @@ function counted(connection, key, roots) {
225
296
  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)]);
226
297
  return number(row.held);
227
298
  }
228
- export function counts(connection, rooms) {
229
- return rooms.map(({ key, roots }) => counted(connection, key, roots));
230
- }
231
299
  function erasing(connection, key, locator) {
232
300
  connection.run('INSERT OR REPLACE INTO replica_erasing (key, locator) VALUES (?, ?)', [
233
301
  key,
234
302
  locator,
235
303
  ]);
236
304
  }
305
+ function saved(connection, key, entry, at) {
306
+ if (!entry.present || entry.created !== null)
307
+ return entry;
308
+ const [held] = connection.rows(`SELECT created FROM replica_entries WHERE key = ? AND layer = ${OVERLAY} AND path = ? AND present = 1`, [key, entry.path]);
309
+ return { ...entry, created: text(held?.created) ?? at };
310
+ }
237
311
  function commitRoom(connection, room) {
238
312
  const touched = new Set();
239
313
  recomposed(connection, room.key, room.composed.fingerprint);
@@ -255,8 +329,9 @@ function commitRoom(connection, room) {
255
329
  removeEntry(connection, room.key, CONFIRMED, path);
256
330
  touched.add(path);
257
331
  }
332
+ const savedAt = new Date().toISOString();
258
333
  for (const entry of room.overlay?.set ?? []) {
259
- putEntry(connection, room.key, OVERLAY, entry, entry.present, composition);
334
+ putEntry(connection, room.key, OVERLAY, saved(connection, room.key, entry, savedAt), entry.present, composition);
260
335
  touched.add(entry.path);
261
336
  }
262
337
  for (const path of room.overlay?.cleared ?? []) {
@@ -266,27 +341,54 @@ function commitRoom(connection, room) {
266
341
  const changed = [...touched];
267
342
  revealed(connection, room.key, changed);
268
343
  if (room.record !== undefined) {
269
- connection.run('INSERT OR REPLACE INTO replica_rooms (locator, key, record) VALUES (?, ?, ?)', [
270
- room.locator,
271
- room.key,
272
- room.record,
273
- ]);
344
+ const replaced = heldModel(connection, room.locator);
345
+ connection.run(KEEP_MODEL, [room.record, room.record, room.record, room.record]);
346
+ connection.run(`INSERT OR REPLACE INTO replica_rooms (locator, key, record) VALUES (?, ?, ${MODEL_LEFT})`, [room.locator, room.key, room.record, room.record, room.record]);
347
+ letGo(connection, replaced);
274
348
  }
275
349
  return room.published === undefined ? changed : null;
276
350
  }
351
+ const SCHEMA = '$.model.snapshot.schema';
352
+ const KEEP_MODEL = `INSERT OR IGNORE INTO replica_models (identity, version, schema)
353
+ SELECT json_extract(?, '$.model.snapshot.identity'), json_extract(?, '$.model.snapshot.version'),
354
+ json_extract(?, '${SCHEMA}')
355
+ WHERE json_type(?, '${SCHEMA}') = 'text'`;
356
+ const MODEL_LEFT = `CASE WHEN json_type(?, '${SCHEMA}') = 'text' THEN json_set(?, '${SCHEMA}', json('null')) ELSE ? END`;
357
+ const HELD_MODEL = `SELECT json_extract(record, '$.model.snapshot.identity') AS identity,
358
+ json_extract(record, '$.model.snapshot.version') AS version
359
+ FROM replica_rooms WHERE locator = ?`;
360
+ const UNHELD_MODEL = `DELETE FROM replica_models WHERE identity = ? AND version = ? AND NOT EXISTS (
361
+ SELECT 1 FROM replica_rooms r
362
+ WHERE json_extract(r.record, '$.model.snapshot.identity') = replica_models.identity
363
+ AND json_extract(r.record, '$.model.snapshot.version') = replica_models.version
364
+ )`;
365
+ function heldModel(connection, locator) {
366
+ return connection.rows(HELD_MODEL, [locator])[0];
367
+ }
368
+ function letGo(connection, model) {
369
+ if (model !== undefined)
370
+ connection.run(UNHELD_MODEL, [model.identity, model.version]);
371
+ }
372
+ const HELD_RECORDS = `SELECT r.locator, r.key, CASE WHEN m.schema IS NULL THEN r.record
373
+ ELSE json_set(r.record, '${SCHEMA}', m.schema) END AS record
374
+ FROM replica_rooms r
375
+ LEFT JOIN replica_models m
376
+ ON m.identity = json_extract(r.record, '$.model.snapshot.identity')
377
+ AND m.version = json_extract(r.record, '$.model.snapshot.version')`;
277
378
  export function commit(connection, rooms) {
278
- const counted = {};
279
379
  const changed = {};
280
380
  const indexed = {};
281
381
  for (const room of rooms) {
282
382
  changed[room.locator] = commitRoom(connection, room);
283
- counted[room.locator] = counts(connection, [
284
- { key: room.key, roots: room.roots ?? rootsOf(connection, room.locator) },
285
- ])[0];
383
+ connection.run('UPDATE replica_rooms SET documents = ? WHERE locator = ? AND key = ?', [
384
+ counted(connection, room.key, room.roots ?? rootsOf(connection, room.locator)),
385
+ room.locator,
386
+ room.key,
387
+ ]);
286
388
  const [held] = connection.rows('SELECT rederiving FROM replica_keys WHERE key = ?', [room.key]);
287
389
  indexed[room.locator] = held !== undefined && held.rederiving === null;
288
390
  }
289
- return { counts: counted, changed, indexed };
391
+ return { changed, indexed };
290
392
  }
291
393
  function rootsOf(connection, locator) {
292
394
  const [row] = connection.rows('SELECT record FROM replica_rooms WHERE locator = ?', [locator]);
@@ -298,8 +400,9 @@ export function fenceRooms(connection, locators) {
298
400
  for (const row of connection.rows('SELECT key FROM replica_keys WHERE locator = ? UNION SELECT key FROM replica_rooms WHERE locator = ?', [locator, locator])) {
299
401
  erasing(connection, number(row.key), locator);
300
402
  }
403
+ const replaced = heldModel(connection, locator);
301
404
  connection.run('DELETE FROM replica_rooms WHERE locator = ?', [locator]);
302
- connection.run('DELETE FROM replica_opened WHERE locator = ?', [locator]);
405
+ letGo(connection, replaced);
303
406
  }
304
407
  return null;
305
408
  }
@@ -353,17 +456,20 @@ export function rederive(connection, key, composed, bytes, ms, floor) {
353
456
  let built = 0;
354
457
  let read = false;
355
458
  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]);
459
+ const rows = connection.rows('SELECT id, path, type, fields, links, octet_length(content) AS size FROM replica_entries WHERE key = ? AND present = 1 AND id > ? ORDER BY id LIMIT ?', [key, last, DERIVED_ROWS]);
357
460
  for (const row of rows) {
358
461
  const id = number(row.id);
359
462
  const path = text(row.path);
360
- const content = text(row.content);
361
- const made = derived(path, content, json(row.outline), composition);
463
+ const size = row.size === null ? 0 : number(row.size);
362
464
  dropDerived(connection, id);
363
- writeDerived(connection, id, key, made);
364
- built += content?.length ?? 0;
465
+ writeDerived(connection, id, key, path, {
466
+ type: text(row.type),
467
+ fields: json(row.fields),
468
+ links: json(row.links) ?? [],
469
+ }, composition);
470
+ built += size;
365
471
  last = id;
366
- spent += ENTRY_WEIGHT + (content?.length ?? 0);
472
+ spent += ENTRY_WEIGHT + size;
367
473
  if (spent >= bytes || (spent >= floor && performance.now() - started >= ms))
368
474
  break reading;
369
475
  }
@@ -375,63 +481,31 @@ export function rederive(connection, key, composed, bytes, ms, floor) {
375
481
  connection.run('UPDATE replica_keys SET rederiving = ? WHERE key = ?', [read ? null : last, key]);
376
482
  return { done: read, text: built };
377
483
  }
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]);
404
- for (const row of rows) {
405
- outlinedAgain(connection, row, request.key, null, request.composed.composition);
406
- }
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) }));
420
- }
421
484
  function heldEntry(row) {
422
485
  return {
423
486
  path: text(row.path),
424
487
  content: text(row.content),
425
- outline: json(row.outline),
426
488
  binary: json(row.binary),
427
489
  revision: text(row.revision),
428
490
  fileId: text(row.file_id),
491
+ created: text(row.created),
429
492
  change: json(row.change),
430
493
  written: text(row.written),
431
494
  type: text(row.type),
495
+ facts: factsHeld(row),
432
496
  };
433
497
  }
434
- const ENTRY_COLUMNS = 'e.path, e.content, e.outline, e.binary, e.revision, e.file_id, e.change, e.written, e.type';
498
+ function factsHeld(row) {
499
+ const type = text(row.declared);
500
+ if (type === null)
501
+ return null;
502
+ return {
503
+ type,
504
+ fields: json(row.fields) ?? {},
505
+ links: json(row.links) ?? [],
506
+ };
507
+ }
508
+ const ENTRY_COLUMNS = 'e.path, e.content, e.binary, e.revision, e.file_id, e.created, e.change, e.written, e.type, e.declared, e.fields, e.links';
435
509
  function unrefreshed(alias) {
436
510
  return `NOT EXISTS (SELECT 1 FROM replica_entries r WHERE r.key = ${alias}.key AND r.layer = ${REFRESHED} AND r.path = ${alias}.path)`;
437
511
  }
@@ -442,22 +516,6 @@ function viewed(view, alias) {
442
516
  return `${alias}.layer = ${CONFIRMED}`;
443
517
  return `${alias}.present = 1 AND (${alias}.layer = ${REFRESHED} OR (${alias}.layer = ${CONFIRMED} AND ${unrefreshed(alias)}))`;
444
518
  }
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
- ]);
460
- }
461
519
  export function entries(connection, key, view, list) {
462
520
  if (list.length === 0)
463
521
  return [];
@@ -476,64 +534,112 @@ export function typed(connection, key, view, list) {
476
534
  binary: number(row.binary) === 1,
477
535
  }));
478
536
  }
479
- export function referrers(connection, key, view, targets, kinds) {
537
+ export function referrers(connection, key, view, targets) {
480
538
  if (targets.length === 0)
481
539
  return [];
482
540
  return connection
483
- .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)])
484
- .map((row) => ({
485
- path: text(row.path),
486
- target: text(row.target),
487
- kind: text(row.kind),
488
- }));
541
+ .rows(`SELECT DISTINCT e.path, l.target 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 ${viewed(view, 'e')}`, [key, paths(targets)])
542
+ .map((row) => ({ path: text(row.path), target: text(row.target) }));
489
543
  }
490
- export function files(connection, key, view, after, limit) {
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);
544
+ export function folders(connection, key, list) {
545
+ 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('visible', 'e')} AND e.path >= ? AND e.path < ? LIMIT 1`, [key, path, key, `${path}/`, `${path}0`]).length > 0);
495
546
  }
496
547
  export function typedWithin(connection, key, view, roots, within) {
497
548
  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);
498
549
  }
550
+ const ORPHANS = `SELECT k.key FROM replica_keys k
551
+ WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
552
+ AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)
553
+ AND (${UNPARTED} OR NOT EXISTS (
554
+ SELECT 1 FROM replica_entries e WHERE e.key = k.key
555
+ UNION ALL SELECT 1 FROM replica_folders f WHERE f.key = k.key
556
+ ))`;
557
+ const STAGED = `SELECT DISTINCT k.locator FROM replica_keys k
558
+ WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
559
+ AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)
560
+ AND k.key NOT IN (${ORPHANS})`;
499
561
  export function opened(connection) {
500
- const facts = {};
501
- for (const row of connection.rows('SELECT name, value FROM replica_facts')) {
502
- facts[text(row.name)] = text(row.value);
503
- }
504
562
  return {
505
- rooms: connection.rows('SELECT locator, key, record FROM replica_rooms').map((row) => ({
506
- locator: text(row.locator),
507
- key: number(row.key),
508
- record: text(row.record),
509
- })),
510
- keys: connection
511
- .rows('SELECT key, locator, transfer, rederiving FROM replica_keys')
512
- .map((row) => ({
513
- key: number(row.key),
514
- locator: text(row.locator),
515
- transfer: text(row.transfer),
516
- rederiving: cursor(row.rederiving),
517
- })),
518
- facts,
519
- presentation: connection.rows('SELECT app, value FROM replica_presentation').map((row) => ({
520
- app: text(row.app),
521
- value: text(row.value),
522
- })),
523
- overlaid: connection
524
- .rows(`SELECT key, path FROM replica_entries WHERE layer = ${OVERLAY}`)
525
- .map((row) => ({ key: number(row.key), path: text(row.path) })),
526
- orphans: connection
527
- .rows(`SELECT k.key FROM replica_keys k
528
- WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
529
- AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)`)
530
- .map((row) => number(row.key)),
531
- erasing: connection.rows('SELECT key, locator FROM replica_erasing').map((row) => ({
532
- key: number(row.key),
533
- locator: text(row.locator),
534
- })),
563
+ orphans: connection.rows(ORPHANS).map((row) => number(row.key)),
564
+ erasing: connection.rows('SELECT 1 FROM replica_erasing LIMIT 1').length > 0,
565
+ };
566
+ }
567
+ const ROOM_ROWS = `SELECT h.locator, h.key, h.record, r.documents, k.rederiving IS NULL AS indexed
568
+ FROM (${HELD_RECORDS}) h
569
+ JOIN replica_rooms r ON r.locator = h.locator
570
+ LEFT JOIN replica_keys k ON k.key = h.key`;
571
+ function roomRow(row) {
572
+ return {
573
+ locator: text(row.locator),
574
+ key: number(row.key),
575
+ record: text(row.record),
576
+ indexed: number(row.indexed) === 1,
577
+ documents: number(row.documents),
535
578
  };
536
579
  }
580
+ export function heldRooms(connection, locators) {
581
+ return (locators === null
582
+ ? connection.rows(`${ROOM_ROWS} ORDER BY h.locator`)
583
+ : connection.rows(`${ROOM_ROWS} WHERE h.locator IN (SELECT value FROM json_each(?)) ORDER BY h.locator`, [JSON.stringify(locators)])).map(roomRow);
584
+ }
585
+ export function heldRoom(connection, locator) {
586
+ return heldRooms(connection, [locator])[0] ?? null;
587
+ }
588
+ export function overlaid(connection, key) {
589
+ return connection
590
+ .rows(`SELECT path FROM replica_entries WHERE key = ? AND layer = ${OVERLAY}`, [key])
591
+ .map((row) => text(row.path));
592
+ }
593
+ export function covering(connection) {
594
+ return connection
595
+ .rows(`SELECT r.locator, r.key, r.documents, json_extract(r.record, '$.transfer') AS transfer,
596
+ json_extract(r.record, '$.roomVersion') AS room_version,
597
+ coalesce(json_extract(r.record, '$.inventory.bytes'), 0) AS bytes,
598
+ json_extract(r.record, '$.replica.offline') AS offline,
599
+ k.rederiving IS NULL AS indexed
600
+ FROM replica_rooms r LEFT JOIN replica_keys k ON k.key = r.key ORDER BY r.locator`)
601
+ .map((row) => ({
602
+ locator: text(row.locator),
603
+ key: number(row.key),
604
+ transfer: text(row.transfer),
605
+ roomVersion: text(row.room_version),
606
+ indexed: number(row.indexed) === 1,
607
+ documents: number(row.documents),
608
+ bytes: number(row.bytes),
609
+ offline: text(row.offline),
610
+ }));
611
+ }
612
+ export function offlineMode(connection, locator) {
613
+ const [held] = connection.rows("SELECT json_extract(record, '$.replica.offline') AS offline FROM replica_rooms WHERE locator = ?", [locator]);
614
+ return text(held?.offline);
615
+ }
616
+ export function stagedLocators(connection) {
617
+ return connection.rows(STAGED).map((row) => text(row.locator));
618
+ }
619
+ export function heldLocators(connection) {
620
+ return connection
621
+ .rows(`SELECT locator FROM replica_rooms
622
+ UNION SELECT locator FROM authored_room_drafts
623
+ UNION SELECT locator FROM authored_queues
624
+ UNION ${STAGED}`)
625
+ .map((row) => text(row.locator));
626
+ }
627
+ export function demoted(connection) {
628
+ connection.run(`UPDATE replica_rooms SET record = json_set(record, '$.model.status', 'cached')
629
+ WHERE json_extract(record, '$.model.status') = 'current'`);
630
+ return null;
631
+ }
632
+ export function facts(connection) {
633
+ const held = {};
634
+ for (const row of connection.rows('SELECT name, value FROM replica_facts')) {
635
+ held[text(row.name)] = text(row.value);
636
+ }
637
+ return held;
638
+ }
639
+ export function presentation(connection, app) {
640
+ const [held] = connection.rows('SELECT value FROM replica_presentation WHERE app = ?', [app]);
641
+ return text(held?.value);
642
+ }
537
643
  export function ended(connection) {
538
644
  for (const statement of REPLICA_TABLES) {
539
645
  const table = /^CREATE TABLE (\w+)/u.exec(statement)?.[1];