@agentshouse/mdruntime 0.1.0-c3c7796382e28 → 0.1.0-c3faae279d4d2

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