@amalgm/shell 0.1.164-preview.36072132286 → 0.1.164-preview.36809057014

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 (475) hide show
  1. package/PURPOSE.md +642 -128
  2. package/README.md +10 -8
  3. package/dist/accepted-tail.d.ts +26 -0
  4. package/dist/accepted-tail.js +37 -0
  5. package/dist/accepted-tail.js.map +1 -0
  6. package/dist/agent-execution.d.ts +16 -1
  7. package/dist/agent-execution.js +13 -5
  8. package/dist/agent-execution.js.map +1 -1
  9. package/dist/apply-cleanup.d.ts +28 -0
  10. package/dist/apply-cleanup.js +166 -0
  11. package/dist/apply-cleanup.js.map +1 -0
  12. package/dist/apply-recovery.d.ts +47 -0
  13. package/dist/apply-recovery.js +99 -0
  14. package/dist/apply-recovery.js.map +1 -0
  15. package/dist/apply-rows.d.ts +72 -0
  16. package/dist/apply-rows.js +150 -0
  17. package/dist/apply-rows.js.map +1 -0
  18. package/dist/apply-saves.d.ts +85 -0
  19. package/dist/apply-saves.js +238 -0
  20. package/dist/apply-saves.js.map +1 -0
  21. package/dist/boot.js +2 -1
  22. package/dist/boot.js.map +1 -1
  23. package/dist/capture-work.d.ts +8 -0
  24. package/dist/capture-work.js +67 -0
  25. package/dist/capture-work.js.map +1 -0
  26. package/dist/cargo-group.d.ts +45 -0
  27. package/dist/cargo-group.js +88 -0
  28. package/dist/cargo-group.js.map +1 -0
  29. package/dist/channel-session.d.ts +4 -2
  30. package/dist/channel-session.js +4 -3
  31. package/dist/channel-session.js.map +1 -1
  32. package/dist/chat-composition.d.ts +2 -0
  33. package/dist/chat-composition.js +1 -0
  34. package/dist/chat-composition.js.map +1 -1
  35. package/dist/cli.js +20 -1
  36. package/dist/cli.js.map +1 -1
  37. package/dist/construct-apply-host.d.ts +4 -2
  38. package/dist/construct-apply-host.js +8 -3
  39. package/dist/construct-apply-host.js.map +1 -1
  40. package/dist/construct-door.js +1 -1
  41. package/dist/construct-door.js.map +1 -1
  42. package/dist/construct-ledger.d.ts +6 -1
  43. package/dist/construct-ledger.js +21 -33
  44. package/dist/construct-ledger.js.map +1 -1
  45. package/dist/content-cache-collection.d.ts +18 -0
  46. package/dist/content-cache-collection.js +99 -0
  47. package/dist/content-cache-collection.js.map +1 -0
  48. package/dist/content-cache-host.d.ts +83 -17
  49. package/dist/content-cache-host.js +335 -95
  50. package/dist/content-cache-host.js.map +1 -1
  51. package/dist/content-cache-namespace.d.ts +59 -0
  52. package/dist/content-cache-namespace.js +210 -0
  53. package/dist/content-cache-namespace.js.map +1 -0
  54. package/dist/content-cache-ownership.d.ts +13 -0
  55. package/dist/content-cache-ownership.js +75 -0
  56. package/dist/content-cache-ownership.js.map +1 -0
  57. package/dist/content-cache-retention.d.ts +23 -0
  58. package/dist/content-cache-retention.js +195 -0
  59. package/dist/content-cache-retention.js.map +1 -0
  60. package/dist/content-capture-pool.d.ts +287 -0
  61. package/dist/content-capture-pool.js +461 -0
  62. package/dist/content-capture-pool.js.map +1 -0
  63. package/dist/content-capture-worker.d.ts +8 -0
  64. package/dist/content-capture-worker.js +109 -0
  65. package/dist/content-capture-worker.js.map +1 -0
  66. package/dist/content-clock.d.ts +42 -0
  67. package/dist/content-clock.js +166 -0
  68. package/dist/content-clock.js.map +1 -0
  69. package/dist/content-direct.d.ts +2 -0
  70. package/dist/content-direct.js +12 -0
  71. package/dist/content-direct.js.map +1 -1
  72. package/dist/content-failure.d.ts +18 -0
  73. package/dist/content-failure.js +38 -0
  74. package/dist/content-failure.js.map +1 -0
  75. package/dist/content-stream-thread.d.ts +60 -0
  76. package/dist/content-stream-thread.js +95 -0
  77. package/dist/content-stream-thread.js.map +1 -0
  78. package/dist/content-stream-worker.d.ts +7 -0
  79. package/dist/content-stream-worker.js +52 -0
  80. package/dist/content-stream-worker.js.map +1 -0
  81. package/dist/declaration-absorption.d.ts +37 -0
  82. package/dist/declaration-absorption.js +119 -0
  83. package/dist/declaration-absorption.js.map +1 -0
  84. package/dist/declaration-chain.d.ts +36 -0
  85. package/dist/declaration-chain.js +96 -0
  86. package/dist/declaration-chain.js.map +1 -0
  87. package/dist/declaration-commit.d.ts +31 -0
  88. package/dist/declaration-commit.js +49 -0
  89. package/dist/declaration-commit.js.map +1 -0
  90. package/dist/declaration-hold.d.ts +22 -0
  91. package/dist/declaration-hold.js +24 -0
  92. package/dist/declaration-hold.js.map +1 -0
  93. package/dist/declaration-journal.d.ts +62 -0
  94. package/dist/declaration-journal.js +99 -0
  95. package/dist/declaration-journal.js.map +1 -0
  96. package/dist/declaration-save.d.ts +31 -0
  97. package/dist/declaration-save.js +134 -0
  98. package/dist/declaration-save.js.map +1 -0
  99. package/dist/detection/accepted-result.d.ts +19 -1
  100. package/dist/detection/accepted-result.js +36 -5
  101. package/dist/detection/accepted-result.js.map +1 -1
  102. package/dist/detection/admission-save.d.ts +55 -0
  103. package/dist/detection/admission-save.js +48 -0
  104. package/dist/detection/admission-save.js.map +1 -0
  105. package/dist/detection/admission-units.d.ts +66 -0
  106. package/dist/detection/admission-units.js +322 -0
  107. package/dist/detection/admission-units.js.map +1 -0
  108. package/dist/detection/alias-discovery.d.ts +12 -0
  109. package/dist/detection/alias-discovery.js +80 -0
  110. package/dist/detection/alias-discovery.js.map +1 -0
  111. package/dist/detection/alias-examination.d.ts +30 -0
  112. package/dist/detection/alias-examination.js +49 -0
  113. package/dist/detection/alias-examination.js.map +1 -0
  114. package/dist/detection/apply-ownership.d.ts +81 -0
  115. package/dist/detection/apply-ownership.js +232 -0
  116. package/dist/detection/apply-ownership.js.map +1 -0
  117. package/dist/detection/basis-advanced.d.ts +11 -0
  118. package/dist/detection/basis-advanced.js +26 -0
  119. package/dist/detection/basis-advanced.js.map +1 -0
  120. package/dist/detection/capture-outcome.d.ts +16 -0
  121. package/dist/detection/capture-outcome.js +92 -0
  122. package/dist/detection/capture-outcome.js.map +1 -0
  123. package/dist/detection/container-membership.d.ts +32 -0
  124. package/dist/detection/container-membership.js +49 -0
  125. package/dist/detection/container-membership.js.map +1 -0
  126. package/dist/detection/detect-cancelled.d.ts +6 -0
  127. package/dist/detection/detect-cancelled.js +10 -0
  128. package/dist/detection/detect-cancelled.js.map +1 -0
  129. package/dist/detection/directory-proof.d.ts +47 -0
  130. package/dist/detection/directory-proof.js +77 -0
  131. package/dist/detection/directory-proof.js.map +1 -0
  132. package/dist/detection/disposition-rows.d.ts +18 -0
  133. package/dist/detection/disposition-rows.js +29 -0
  134. package/dist/detection/disposition-rows.js.map +1 -0
  135. package/dist/detection/dispositions.d.ts +130 -0
  136. package/dist/detection/dispositions.js +248 -0
  137. package/dist/detection/dispositions.js.map +1 -0
  138. package/dist/detection/git-notice.d.ts +88 -0
  139. package/dist/detection/git-notice.js +444 -0
  140. package/dist/detection/git-notice.js.map +1 -0
  141. package/dist/detection/ground-proof.d.ts +130 -0
  142. package/dist/detection/ground-proof.js +274 -0
  143. package/dist/detection/ground-proof.js.map +1 -0
  144. package/dist/detection/ground-reader.d.ts +29 -0
  145. package/dist/detection/ground-reader.js +102 -0
  146. package/dist/detection/ground-reader.js.map +1 -0
  147. package/dist/detection/held-territory.d.ts +51 -0
  148. package/dist/detection/held-territory.js +205 -0
  149. package/dist/detection/held-territory.js.map +1 -0
  150. package/dist/detection/identity-discovery.d.ts +45 -0
  151. package/dist/detection/identity-discovery.js +287 -0
  152. package/dist/detection/identity-discovery.js.map +1 -0
  153. package/dist/detection/maintenance-sweep.d.ts +108 -0
  154. package/dist/detection/maintenance-sweep.js +325 -0
  155. package/dist/detection/maintenance-sweep.js.map +1 -0
  156. package/dist/detection/name-reach.d.ts +12 -0
  157. package/dist/detection/name-reach.js +28 -0
  158. package/dist/detection/name-reach.js.map +1 -0
  159. package/dist/detection/notebook-basis.d.ts +5 -7
  160. package/dist/detection/notebook-basis.js +6 -7
  161. package/dist/detection/notebook-basis.js.map +1 -1
  162. package/dist/detection/owed-selection.d.ts +51 -0
  163. package/dist/detection/owed-selection.js +169 -0
  164. package/dist/detection/owed-selection.js.map +1 -0
  165. package/dist/detection/portable.d.ts +34 -34
  166. package/dist/detection/portable.js +67 -93
  167. package/dist/detection/portable.js.map +1 -1
  168. package/dist/detection/reconciliation-coverage.d.ts +7 -0
  169. package/dist/detection/reconciliation-coverage.js +16 -0
  170. package/dist/detection/reconciliation-coverage.js.map +1 -0
  171. package/dist/detection/repository-publication.d.ts +20 -0
  172. package/dist/detection/repository-publication.js +79 -0
  173. package/dist/detection/repository-publication.js.map +1 -0
  174. package/dist/detection/repository-routes.d.ts +43 -0
  175. package/dist/detection/repository-routes.js +97 -0
  176. package/dist/detection/repository-routes.js.map +1 -0
  177. package/dist/detection/root-examination.d.ts +94 -0
  178. package/dist/detection/root-examination.js +523 -0
  179. package/dist/detection/root-examination.js.map +1 -0
  180. package/dist/detection/runtime.d.ts +131 -64
  181. package/dist/detection/runtime.js +808 -1103
  182. package/dist/detection/runtime.js.map +1 -1
  183. package/dist/detection/scoped-identity-port.d.ts +34 -0
  184. package/dist/detection/scoped-identity-port.js +81 -0
  185. package/dist/detection/scoped-identity-port.js.map +1 -0
  186. package/dist/detection/seal-law.d.ts +66 -0
  187. package/dist/detection/seal-law.js +116 -0
  188. package/dist/detection/seal-law.js.map +1 -0
  189. package/dist/detection/sharing-policy.d.ts +11 -0
  190. package/dist/detection/sharing-policy.js +32 -0
  191. package/dist/detection/sharing-policy.js.map +1 -0
  192. package/dist/detection/thread-database.d.ts +4 -0
  193. package/dist/detection/thread-database.js +30 -0
  194. package/dist/detection/thread-database.js.map +1 -0
  195. package/dist/detection/unreadable.d.ts +11 -0
  196. package/dist/detection/unreadable.js +9 -0
  197. package/dist/detection/unreadable.js.map +1 -0
  198. package/dist/detection/verified-content.d.ts +6 -0
  199. package/dist/detection/verified-content.js +12 -0
  200. package/dist/detection/verified-content.js.map +1 -0
  201. package/dist/detection/walk-law.d.ts +34 -0
  202. package/dist/detection/walk-law.js +55 -0
  203. package/dist/detection/walk-law.js.map +1 -0
  204. package/dist/development/fly-guest.js +2 -1
  205. package/dist/development/fly-guest.js.map +1 -1
  206. package/dist/entity-apply-host.d.ts +29 -13
  207. package/dist/entity-apply-host.js +1426 -461
  208. package/dist/entity-apply-host.js.map +1 -1
  209. package/dist/entity-apply-store.d.ts +77 -30
  210. package/dist/entity-apply-store.js +221 -516
  211. package/dist/entity-apply-store.js.map +1 -1
  212. package/dist/entity-record-observability.js +2 -0
  213. package/dist/entity-record-observability.js.map +1 -1
  214. package/dist/entity-record-saves.d.ts +20 -0
  215. package/dist/entity-record-saves.js +91 -0
  216. package/dist/entity-record-saves.js.map +1 -0
  217. package/dist/entity-record-store.d.ts +15 -7
  218. package/dist/entity-record-store.js +57 -117
  219. package/dist/entity-record-store.js.map +1 -1
  220. package/dist/files-register-host.d.ts +22 -3
  221. package/dist/files-register-host.js +70 -31
  222. package/dist/files-register-host.js.map +1 -1
  223. package/dist/filesystem.js +3 -2
  224. package/dist/filesystem.js.map +1 -1
  225. package/dist/git-binding.d.ts +65 -0
  226. package/dist/git-binding.js +220 -0
  227. package/dist/git-binding.js.map +1 -0
  228. package/dist/git-capture.d.ts +39 -0
  229. package/dist/git-capture.js +86 -0
  230. package/dist/git-capture.js.map +1 -0
  231. package/dist/git-card.d.ts +12 -0
  232. package/dist/git-card.js +123 -0
  233. package/dist/git-card.js.map +1 -0
  234. package/dist/git-checkpoint-construction.d.ts +41 -0
  235. package/dist/git-checkpoint-construction.js +96 -0
  236. package/dist/git-checkpoint-construction.js.map +1 -0
  237. package/dist/git-checkpoint-preparation.d.ts +37 -0
  238. package/dist/git-checkpoint-preparation.js +145 -0
  239. package/dist/git-checkpoint-preparation.js.map +1 -0
  240. package/dist/git-command.d.ts +61 -8
  241. package/dist/git-command.js +217 -63
  242. package/dist/git-command.js.map +1 -1
  243. package/dist/git-configuration.d.ts +40 -0
  244. package/dist/git-configuration.js +217 -0
  245. package/dist/git-configuration.js.map +1 -0
  246. package/dist/git-evidence.d.ts +61 -0
  247. package/dist/git-evidence.js +181 -0
  248. package/dist/git-evidence.js.map +1 -0
  249. package/dist/git-index-entries.d.ts +15 -0
  250. package/dist/git-index-entries.js +44 -0
  251. package/dist/git-index-entries.js.map +1 -0
  252. package/dist/git-index-format.d.ts +35 -0
  253. package/dist/git-index-format.js +293 -0
  254. package/dist/git-index-format.js.map +1 -0
  255. package/dist/git-index-witness.d.ts +33 -0
  256. package/dist/git-index-witness.js +69 -0
  257. package/dist/git-index-witness.js.map +1 -0
  258. package/dist/git-literal-host.d.ts +82 -0
  259. package/dist/git-literal-host.js +172 -0
  260. package/dist/git-literal-host.js.map +1 -0
  261. package/dist/git-literal-read.d.ts +88 -0
  262. package/dist/git-literal-read.js +170 -0
  263. package/dist/git-literal-read.js.map +1 -0
  264. package/dist/git-literal-replay.d.ts +33 -0
  265. package/dist/git-literal-replay.js +268 -0
  266. package/dist/git-literal-replay.js.map +1 -0
  267. package/dist/git-native-file.d.ts +42 -0
  268. package/dist/git-native-file.js +145 -0
  269. package/dist/git-native-file.js.map +1 -0
  270. package/dist/git-native-index.d.ts +21 -0
  271. package/dist/git-native-index.js +44 -0
  272. package/dist/git-native-index.js.map +1 -0
  273. package/dist/git-operation-host.d.ts +48 -0
  274. package/dist/git-operation-host.js +127 -0
  275. package/dist/git-operation-host.js.map +1 -0
  276. package/dist/git-private-index.d.ts +11 -0
  277. package/dist/git-private-index.js +42 -0
  278. package/dist/git-private-index.js.map +1 -0
  279. package/dist/git-private-repository.d.ts +4 -0
  280. package/dist/git-private-repository.js +69 -0
  281. package/dist/git-private-repository.js.map +1 -0
  282. package/dist/git-private-view.d.ts +39 -0
  283. package/dist/git-private-view.js +162 -0
  284. package/dist/git-private-view.js.map +1 -0
  285. package/dist/git-ref-store.d.ts +12 -0
  286. package/dist/git-ref-store.js +68 -0
  287. package/dist/git-ref-store.js.map +1 -0
  288. package/dist/git-registration-host.d.ts +20 -30
  289. package/dist/git-registration-host.js +86 -249
  290. package/dist/git-registration-host.js.map +1 -1
  291. package/dist/git-repository-host.d.ts +148 -22
  292. package/dist/git-repository-host.js +633 -650
  293. package/dist/git-repository-host.js.map +1 -1
  294. package/dist/git-worktree-host.d.ts +2 -0
  295. package/dist/git-worktree-host.js +16 -0
  296. package/dist/git-worktree-host.js.map +1 -0
  297. package/dist/graph-publication.d.ts +75 -0
  298. package/dist/graph-publication.js +203 -0
  299. package/dist/graph-publication.js.map +1 -0
  300. package/dist/ground-basis.d.ts +242 -0
  301. package/dist/ground-basis.js +1023 -0
  302. package/dist/ground-basis.js.map +1 -0
  303. package/dist/ground-checkpoint-worker.d.ts +1 -0
  304. package/dist/ground-checkpoint-worker.js +84 -0
  305. package/dist/ground-checkpoint-worker.js.map +1 -0
  306. package/dist/ground-checkpoints.d.ts +16 -0
  307. package/dist/ground-checkpoints.js +45 -0
  308. package/dist/ground-checkpoints.js.map +1 -0
  309. package/dist/ground-coordination.d.ts +107 -3
  310. package/dist/ground-coordination.js +249 -24
  311. package/dist/ground-coordination.js.map +1 -1
  312. package/dist/ground-database.d.ts +42 -0
  313. package/dist/ground-database.js +290 -0
  314. package/dist/ground-database.js.map +1 -0
  315. package/dist/ground-differences.d.ts +23 -0
  316. package/dist/ground-differences.js +47 -0
  317. package/dist/ground-differences.js.map +1 -0
  318. package/dist/ground-holders.d.ts +4 -0
  319. package/dist/ground-holders.js +15 -0
  320. package/dist/ground-holders.js.map +1 -0
  321. package/dist/ground-initialization-worker.d.ts +1 -0
  322. package/dist/ground-initialization-worker.js +20 -0
  323. package/dist/ground-initialization-worker.js.map +1 -0
  324. package/dist/ground-initialization.d.ts +35 -0
  325. package/dist/ground-initialization.js +102 -0
  326. package/dist/ground-initialization.js.map +1 -0
  327. package/dist/ground-install-saves.d.ts +63 -0
  328. package/dist/ground-install-saves.js +139 -0
  329. package/dist/ground-install-saves.js.map +1 -0
  330. package/dist/ground-inventory-schema.d.ts +18 -0
  331. package/dist/ground-inventory-schema.js +168 -0
  332. package/dist/ground-inventory-schema.js.map +1 -0
  333. package/dist/ground-profile.d.ts +7 -0
  334. package/dist/ground-profile.js +52 -0
  335. package/dist/ground-profile.js.map +1 -0
  336. package/dist/ground-root-index.d.ts +2 -1
  337. package/dist/ground-root-index.js +16 -14
  338. package/dist/ground-root-index.js.map +1 -1
  339. package/dist/ground-row.d.ts +62 -0
  340. package/dist/ground-row.js +70 -0
  341. package/dist/ground-row.js.map +1 -0
  342. package/dist/ground-save-ops.d.ts +44 -0
  343. package/dist/ground-save-ops.js +41 -0
  344. package/dist/ground-save-ops.js.map +1 -0
  345. package/dist/ground-saver-worker.d.ts +1 -0
  346. package/dist/ground-saver-worker.js +156 -0
  347. package/dist/ground-saver-worker.js.map +1 -0
  348. package/dist/ground-saver.d.ts +75 -0
  349. package/dist/ground-saver.js +216 -0
  350. package/dist/ground-saver.js.map +1 -0
  351. package/dist/ground-store.d.ts +84 -0
  352. package/dist/ground-store.js +473 -0
  353. package/dist/ground-store.js.map +1 -0
  354. package/dist/ground-territory.d.ts +130 -0
  355. package/dist/ground-territory.js +511 -0
  356. package/dist/ground-territory.js.map +1 -0
  357. package/dist/index.d.ts +1 -1
  358. package/dist/index.js.map +1 -1
  359. package/dist/installation/ownership.d.ts +0 -2
  360. package/dist/installation/ownership.js +3 -15
  361. package/dist/installation/ownership.js.map +1 -1
  362. package/dist/machine-host.d.ts +3 -2
  363. package/dist/machine-host.js +28 -16
  364. package/dist/machine-host.js.map +1 -1
  365. package/dist/materialized-graph.d.ts +7 -7
  366. package/dist/materialized-graph.js +11 -23
  367. package/dist/materialized-graph.js.map +1 -1
  368. package/dist/module-thread.d.ts +9 -0
  369. package/dist/module-thread.js +10 -0
  370. package/dist/module-thread.js.map +1 -0
  371. package/dist/native/fsevents-stream.node +0 -0
  372. package/dist/physical-path-witness.d.ts +64 -0
  373. package/dist/physical-path-witness.js +168 -0
  374. package/dist/physical-path-witness.js.map +1 -0
  375. package/dist/register-upload-fusion.d.ts +5 -4
  376. package/dist/register-upload-fusion.js +9 -6
  377. package/dist/register-upload-fusion.js.map +1 -1
  378. package/dist/registry-graph.d.ts +3 -2
  379. package/dist/registry-graph.js +9 -6
  380. package/dist/registry-graph.js.map +1 -1
  381. package/dist/registry-page-cache.d.ts +5 -2
  382. package/dist/registry-page-cache.js.map +1 -1
  383. package/dist/repository-identity-steps.d.ts +5 -0
  384. package/dist/repository-identity-steps.js +33 -0
  385. package/dist/repository-identity-steps.js.map +1 -0
  386. package/dist/runtime-node-flags.d.ts +10 -0
  387. package/dist/runtime-node-flags.js +11 -0
  388. package/dist/runtime-node-flags.js.map +1 -0
  389. package/dist/service/adapter.d.ts +1 -1
  390. package/dist/service/adapter.js +1 -1
  391. package/dist/service/adapter.js.map +1 -1
  392. package/dist/service/cli.js +16 -20
  393. package/dist/service/cli.js.map +1 -1
  394. package/dist/service/control.d.ts +2 -2
  395. package/dist/service/keepalive.d.ts +4 -11
  396. package/dist/service/keepalive.js +65 -118
  397. package/dist/service/keepalive.js.map +1 -1
  398. package/dist/service/policy.d.ts +36 -0
  399. package/dist/service/policy.js +25 -0
  400. package/dist/service/policy.js.map +1 -0
  401. package/dist/service/portable.d.ts +1 -1
  402. package/dist/service/portable.js +1 -1
  403. package/dist/service/portable.js.map +1 -1
  404. package/dist/service/recovery.d.ts +10 -9
  405. package/dist/service/recovery.js +29 -19
  406. package/dist/service/recovery.js.map +1 -1
  407. package/dist/service/windows-action.d.ts +5 -3
  408. package/dist/service/windows-action.js +6 -9
  409. package/dist/service/windows-action.js.map +1 -1
  410. package/dist/service/windows-task.js +5 -5
  411. package/dist/service/windows-task.js.map +1 -1
  412. package/dist/sharing-admission.d.ts +17 -0
  413. package/dist/sharing-admission.js +128 -0
  414. package/dist/sharing-admission.js.map +1 -0
  415. package/dist/snapshot-delivery-baseline.d.ts +12 -3
  416. package/dist/snapshot-delivery-baseline.js +41 -42
  417. package/dist/snapshot-delivery-baseline.js.map +1 -1
  418. package/dist/source-proof.d.ts +36 -0
  419. package/dist/source-proof.js +70 -0
  420. package/dist/source-proof.js.map +1 -0
  421. package/dist/source-signature.d.ts +56 -0
  422. package/dist/source-signature.js +56 -0
  423. package/dist/source-signature.js.map +1 -0
  424. package/dist/source-witness.d.ts +15 -0
  425. package/dist/source-witness.js +29 -0
  426. package/dist/source-witness.js.map +1 -0
  427. package/dist/sqlite-durability.d.ts +31 -0
  428. package/dist/sqlite-durability.js +28 -0
  429. package/dist/sqlite-durability.js.map +1 -0
  430. package/dist/sqlite.d.ts +8 -2
  431. package/dist/sqlite.js +27 -4
  432. package/dist/sqlite.js.map +1 -1
  433. package/dist/sync-address.d.ts +19 -0
  434. package/dist/sync-address.js +52 -0
  435. package/dist/sync-address.js.map +1 -0
  436. package/dist/sync-setting.d.ts +15 -0
  437. package/dist/sync-setting.js +13 -0
  438. package/dist/sync-setting.js.map +1 -1
  439. package/dist/turns.d.ts +42 -0
  440. package/dist/turns.js +112 -0
  441. package/dist/turns.js.map +1 -0
  442. package/dist/updates/bootstrap.d.ts +53 -7
  443. package/dist/updates/bootstrap.js +224 -137
  444. package/dist/updates/bootstrap.js.map +1 -1
  445. package/dist/updates/controller.d.ts +8 -0
  446. package/dist/updates/controller.js +27 -3
  447. package/dist/updates/controller.js.map +1 -1
  448. package/dist/updates/store.d.ts +3 -0
  449. package/dist/updates/store.js +12 -0
  450. package/dist/updates/store.js.map +1 -1
  451. package/dist/user-ground-host.d.ts +364 -69
  452. package/dist/user-ground-host.js +5231 -3366
  453. package/dist/user-ground-host.js.map +1 -1
  454. package/dist/watching/fsevents.d.ts +52 -1
  455. package/dist/watching/fsevents.js +60 -25
  456. package/dist/watching/fsevents.js.map +1 -1
  457. package/dist/watching/host.d.ts +129 -5
  458. package/dist/watching/host.js +433 -80
  459. package/dist/watching/host.js.map +1 -1
  460. package/dist/watching/index.d.ts +1 -1
  461. package/dist/wire-binary.d.ts +3 -2
  462. package/dist/wire-binary.js +10 -8
  463. package/dist/wire-binary.js.map +1 -1
  464. package/dist/wire-client.d.ts +3 -4
  465. package/dist/wire-client.js +18 -44
  466. package/dist/wire-client.js.map +1 -1
  467. package/dist/wire-outbox.d.ts +34 -0
  468. package/dist/wire-outbox.js +127 -0
  469. package/dist/wire-outbox.js.map +1 -0
  470. package/dist/workspace-host.d.ts +4 -0
  471. package/dist/workspace-host.js +12 -1
  472. package/dist/workspace-host.js.map +1 -1
  473. package/examinations/fixtures/private-wire-authority.js +133 -10
  474. package/npm-shrinkwrap.json +43 -56
  475. package/package.json +26 -13
@@ -0,0 +1,1023 @@
1
+ /** Fixed SQLite facts used to authorize a later ground replacement. Source
2
+ * witnesses still prove disk; this read set proves the database basis.
3
+ *
4
+ * One counter orders every committed ground write. The write that changes a
5
+ * fact stamps that fact's last-change sequence in the same transaction, so a
6
+ * basis read at sequence S0 stays valid exactly while none of its facts has a
7
+ * stamp after S0. Within one epoch stamps are never deleted: a removed row, or
8
+ * a query answer that became empty, keeps its tombstone and still invalidates.
9
+ *
10
+ * Every basis and revision string lives in process memory, so none outlives
11
+ * its process. A process's first open of the ground file therefore begins a
12
+ * new epoch: a new incarnation retires every older basis and the stamps start
13
+ * empty. Which identities ever existed is a separate, permanent fact. */
14
+ import { randomUUID } from "node:crypto";
15
+ import { deserialize, serialize } from "node:v8";
16
+ import { GROUND_ROW_PROJECTION, groundRowProjection, groundRowValues } from "./ground-row.js";
17
+ import { activeRepository, readSubtree, subtreePages, TerritoryHandle, TerritoryView } from "./ground-territory.js";
18
+ import { cachedStatement } from "./sqlite.js";
19
+ import { turnBoundary } from "./turns.js";
20
+ const INVENTORIES = ["entities", "detection_notebook"];
21
+ const key = (dependency) => JSON.stringify([dependency.inventory, dependency.aspect, dependency.id]);
22
+ const reading = new WeakSet();
23
+ export class GroundBasisChangedError extends Error {
24
+ }
25
+ /** The same phase owner applies to both the store and direct snapshot entry. */
26
+ export function assertGroundWriteAllowed(database) {
27
+ if (reading.has(database))
28
+ throw new Error("ground writes cannot run inside an observation snapshot");
29
+ }
30
+ function ensureGroundBasisSchema(database) {
31
+ database.exec(`
32
+ CREATE TABLE IF NOT EXISTS ground_incarnation (
33
+ singleton INTEGER PRIMARY KEY CHECK(singleton = 1), identity TEXT NOT NULL
34
+ );
35
+ CREATE TABLE IF NOT EXISTS ground_sequence (
36
+ singleton INTEGER PRIMARY KEY CHECK(singleton = 1), value INTEGER NOT NULL
37
+ );
38
+ CREATE TABLE IF NOT EXISTS ground_revisions (
39
+ inventory TEXT NOT NULL, aspect TEXT NOT NULL, identity TEXT NOT NULL, revision INTEGER NOT NULL,
40
+ PRIMARY KEY(inventory, aspect, identity)
41
+ ) WITHOUT ROWID;
42
+ CREATE INDEX IF NOT EXISTS ground_revisions_by_revision ON ground_revisions(revision);
43
+ `);
44
+ database.prepare("INSERT OR IGNORE INTO ground_incarnation(singleton, identity) VALUES (1, ?)").run(randomUUID());
45
+ database.prepare("INSERT OR IGNORE INTO ground_sequence(singleton, value) VALUES (1, 0)").run();
46
+ // Unordered random revisions cannot be compared with a sequence. Every fact
47
+ // they name keeps its history at sequence 0 (identity history and
48
+ // tombstones survive), and the new incarnation retires every older basis.
49
+ if (database.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'ground_dependency_revisions'").get()) {
50
+ database.transaction(() => {
51
+ database.exec(`INSERT OR IGNORE INTO ground_revisions(inventory, aspect, identity, revision)
52
+ SELECT inventory, aspect, identity, 0 FROM ground_dependency_revisions;
53
+ DROP TABLE ground_dependency_revisions;`);
54
+ database.prepare("UPDATE ground_incarnation SET identity = ? WHERE singleton = 1").run(randomUUID());
55
+ })();
56
+ }
57
+ // Every UUID ever removed from an inventory. Entity stamps were that record
58
+ // before this table: each one names a UUID that existed, so all of them
59
+ // are carried once, a superset that includes UUIDs still present.
60
+ if (!database.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'ground_identity_history'").get()) {
61
+ database.transaction(() => {
62
+ database.exec(`CREATE TABLE ground_identity_history (
63
+ inventory TEXT NOT NULL, uuid TEXT NOT NULL, PRIMARY KEY(inventory, uuid)
64
+ ) WITHOUT ROWID;
65
+ INSERT INTO ground_identity_history(inventory, uuid)
66
+ SELECT inventory, identity FROM ground_revisions WHERE aspect = 'entity';`);
67
+ })();
68
+ }
69
+ }
70
+ /** A process's first open of the ground file. No basis or revision string of
71
+ * an earlier process can reach this one, so the stamps restart empty under a
72
+ * new incarnation; a basis read before this commit, by any connection, fails
73
+ * its incarnation check instead of reading a missing stamp as unchanged. The
74
+ * counter stays monotonic, so no S0 or sequence string repeats. */
75
+ export function beginGroundEpoch(database) {
76
+ ensureGroundBasisSchema(database);
77
+ database.transaction(() => {
78
+ database.prepare("UPDATE ground_incarnation SET identity = ? WHERE singleton = 1").run(randomUUID());
79
+ database.exec("DELETE FROM ground_revisions");
80
+ })();
81
+ }
82
+ /** One row by UUID, outside any basis: a read that proves nothing. */
83
+ export const readGroundRow = (database, inventory, uuid) => cachedStatement(database, `SELECT ${groundRowProjection(inventory)} FROM ${inventory} WHERE uuid = ?`).get(uuid);
84
+ /** The database incarnation that retires every basis read before a reset. */
85
+ export const groundIncarnation = (database) => cachedStatement(database, "SELECT identity FROM ground_incarnation WHERE singleton = 1").get().identity;
86
+ /** The last stamp any committed write issued: S0 for a read made with it. */
87
+ export const currentSequence = (database) => cachedStatement(database, "SELECT value FROM ground_sequence WHERE singleton = 1", true).get().value;
88
+ const revisionOf = (database, dependency) => cachedStatement(database, "SELECT revision FROM ground_revisions WHERE inventory = ? AND aspect = ? AND identity = ?", true)
89
+ .get(dependency.inventory, dependency.aspect, dependency.id)?.revision;
90
+ /** Every fact stamped after a sequence, with its stamp: one indexed range
91
+ * read, empty while nothing has been written since. */
92
+ const stampsAfter = (database, sequence) => cachedStatement(database, "SELECT inventory, aspect, identity, revision FROM ground_revisions WHERE revision > ?", true)
93
+ .all(sequence)
94
+ .map(row => ({ dependency: { inventory: row.inventory, aspect: row.aspect, id: row.identity }, revision: row.revision }));
95
+ /** The facts stamped after an open observation's S0, as of its latest read.
96
+ * Within an epoch a stamp only rises, so a fact once stamped after S0 stays
97
+ * so, and each late read adds only what was stamped since the read before.
98
+ * A late read then costs one range read over the writes since, however many
99
+ * facts it consumes, instead of a lookup per fact. */
100
+ class LaterStamps {
101
+ #keys = new Set();
102
+ #through;
103
+ constructor(sequence) { this.#through = sequence; }
104
+ /** Bring the set to the snapshot of the read transaction in progress. */
105
+ refresh(database) {
106
+ const current = currentSequence(database);
107
+ if (current <= this.#through)
108
+ return;
109
+ for (const { dependency } of stampsAfter(database, this.#through))
110
+ this.#keys.add(key(dependency));
111
+ this.#through = current;
112
+ }
113
+ has(id) { return this.#keys.has(id); }
114
+ }
115
+ /** One root's committed O/B state: equal exactly while no row of that root,
116
+ * in either inventory, has been written. */
117
+ export function rootRevision(database, rootUUID) {
118
+ return JSON.stringify([groundIncarnation(database), ...["entities", "detection_notebook"]
119
+ .map(inventory => revisionOf(database, { inventory, aspect: "inventory", id: rootUUID })?.toString() ?? null)]);
120
+ }
121
+ /** A database incarnation, the sequence S0 it was read at, and the facts it
122
+ * consumed. Only an open observation can accumulate facts; admission receives
123
+ * a closed basis. */
124
+ export class GroundBasis {
125
+ #incarnation;
126
+ #sequence;
127
+ #dependencies = new Map();
128
+ /** Rows read by identity, and whole repository subtrees, per inventory. */
129
+ #entities = { entities: new Set(), detection_notebook: new Set() };
130
+ #territories = { entities: [], detection_notebook: [] };
131
+ #inventories = new Set();
132
+ /** The activation each row read consumed with its answer. */
133
+ #activations = new Map();
134
+ constructor(incarnation, sequence) {
135
+ this.#incarnation = incarnation;
136
+ this.#sequence = sequence;
137
+ }
138
+ /** How many facts admission must check; the cost of this basis. */
139
+ get size() { return this.#dependencies.size; }
140
+ /** Plain evidence of this basis. It lists the facts of a basis small
141
+ * enough to check one by one and counts a larger one. */
142
+ describe() {
143
+ const facts = [...this.#dependencies.values()];
144
+ return Object.freeze({ database: this.#incarnation, sequence: this.#sequence.toString(), facts: facts.length,
145
+ listed: facts.length > POINT_LOOKUPS ? null
146
+ : Object.freeze(facts.map(({ inventory, aspect, id }) => Object.freeze([inventory, aspect, id]))) });
147
+ }
148
+ /** What a saver thread needs to accept this basis: its facts, and every
149
+ * row it may protect. */
150
+ toData() {
151
+ const protectedRows = (inventory) => [...new Set([...this.#entities[inventory], ...this.#territories[inventory].flatMap(uuids => [...uuids])])];
152
+ return { incarnation: this.#incarnation, sequence: this.#sequence, facts: [...this.#dependencies.keys()],
153
+ protected: { entities: protectedRows("entities"), detection_notebook: protectedRows("detection_notebook") },
154
+ inventories: [...this.#inventories] };
155
+ }
156
+ static fromData(data) {
157
+ const basis = new GroundBasis(data.incarnation, data.sequence);
158
+ for (const id of data.facts) {
159
+ const [inventory, aspect, fact] = JSON.parse(id);
160
+ basis.#dependencies.set(id, { inventory, aspect, id: fact });
161
+ }
162
+ for (const inventory of INVENTORIES)
163
+ for (const uuid of data.protected[inventory])
164
+ basis.#entities[inventory].add(uuid);
165
+ for (const inventory of data.inventories)
166
+ basis.#inventories.add(inventory);
167
+ return basis;
168
+ }
169
+ /** One basis consuming every part's facts and protecting every part's
170
+ * rows; all parts were read at one incarnation and S0. */
171
+ static union(parts) {
172
+ const [first] = parts;
173
+ if (!first)
174
+ throw new Error("a ground basis union needs a part");
175
+ const union = new GroundBasis(first.#incarnation, first.#sequence);
176
+ for (const part of parts) {
177
+ if (part.#incarnation !== first.#incarnation || part.#sequence !== first.#sequence) {
178
+ throw new Error("a ground basis union needs one incarnation and sequence");
179
+ }
180
+ for (const [id, dependency] of part.#dependencies)
181
+ union.#dependencies.set(id, dependency);
182
+ for (const inventory of INVENTORIES) {
183
+ for (const uuid of part.#entities[inventory])
184
+ union.#entities[inventory].add(uuid);
185
+ union.#territories[inventory].push(...part.#territories[inventory]);
186
+ }
187
+ for (const inventory of part.#inventories)
188
+ union.#inventories.add(inventory);
189
+ for (const [id, dependency] of part.#activations)
190
+ union.#activations.set(id, dependency);
191
+ }
192
+ return union;
193
+ }
194
+ static observe(database) {
195
+ let late;
196
+ let closed = false;
197
+ let failed = false;
198
+ let readingNow = false;
199
+ const assertOpen = () => {
200
+ if (closed || failed)
201
+ throw new Error("ground observation is closed or failed");
202
+ if (readingNow) {
203
+ failed = true;
204
+ throw new Error("ground observation cannot read or finish inside its callback");
205
+ }
206
+ };
207
+ return Object.freeze({
208
+ read(operation) {
209
+ assertOpen();
210
+ readingNow = true;
211
+ try {
212
+ const captured = GroundBasis.capture(database, operation, late);
213
+ if (failed)
214
+ throw new Error("ground observation failed during its callback");
215
+ late ??= { basis: captured.basis, stamps: new LaterStamps(captured.basis.#sequence) };
216
+ return captured.value;
217
+ }
218
+ catch (error) {
219
+ failed = true;
220
+ throw error;
221
+ }
222
+ finally {
223
+ readingNow = false;
224
+ }
225
+ },
226
+ finish() {
227
+ assertOpen();
228
+ if (!late)
229
+ throw new Error("ground observation has no reads");
230
+ closed = true;
231
+ return late.basis;
232
+ },
233
+ });
234
+ }
235
+ static capture(database, operation, late) {
236
+ if (database.inTransaction)
237
+ throw new Error("ground observation snapshots require a separate read transaction");
238
+ drainReceipts(database);
239
+ const capture = database.transaction(() => {
240
+ if (late && groundIncarnation(database) !== late.basis.#incarnation) {
241
+ throw new GroundBasisChangedError("ground incarnation changed during observation");
242
+ }
243
+ late?.stamps.refresh(database);
244
+ const basis = late?.basis ?? new GroundBasis(groundIncarnation(database), currentSequence(database));
245
+ // Intern within one inventory. O and B have different evidence even
246
+ // when their common row fields happen to match.
247
+ const retained = new Map();
248
+ const retain = (inventory, row) => {
249
+ const key = `${inventory}:${row.uuid}`, previous = retained.get(key);
250
+ const values = groundRowValues(row, inventory);
251
+ if (previous && groundRowValues(previous, inventory).every((value, index) => value === values[index]))
252
+ return previous;
253
+ const frozen = Object.freeze(row);
254
+ retained.set(key, frozen);
255
+ return frozen;
256
+ };
257
+ let active = true;
258
+ // A first read is S0 itself. A late read recovers S0's answer only
259
+ // while each consumed fact is still unchanged since S0.
260
+ // A repeated late read is checked too: its answer is current, and it
261
+ // may stand for the original only while the fact is unchanged.
262
+ const remember = (dependency) => {
263
+ const id = key(dependency);
264
+ if (late?.stamps.has(id))
265
+ throw new GroundBasisChangedError("ground dependency changed after its observation sequence");
266
+ basis.#dependencies.set(id, dependency);
267
+ };
268
+ const assertActive = () => {
269
+ if (!active)
270
+ throw new Error("ground snapshot reader used outside its transaction");
271
+ };
272
+ // Read in the same transaction as the answer it qualifies.
273
+ const activated = (inventory, uuid, row) => {
274
+ const dependency = activation(inventory, row?.rootUUID ?? "*");
275
+ remember(dependency);
276
+ basis.#activations.set(JSON.stringify([inventory, uuid]), dependency);
277
+ };
278
+ const entity = (inventory, uuid) => {
279
+ assertActive();
280
+ remember({ inventory, aspect: "entity", id: uuid });
281
+ basis.#entities[inventory].add(uuid);
282
+ const row = readGroundRow(database, inventory, uuid);
283
+ activated(inventory, uuid, row);
284
+ return row && retain(inventory, row);
285
+ };
286
+ // A returned row's fields can change with its root's visible
287
+ // generation, whatever key found it.
288
+ const shown = (inventory, found) => {
289
+ for (const rootUUID of new Set(found.map(row => row.rootUUID)))
290
+ remember(activation(inventory, rootUUID));
291
+ };
292
+ const rows = (inventory, values) => {
293
+ shown(inventory, values);
294
+ return Object.freeze(values.map(row => {
295
+ remember({ inventory, aspect: "entity", id: row.uuid });
296
+ basis.#entities[inventory].add(row.uuid);
297
+ return retain(inventory, row);
298
+ }));
299
+ };
300
+ const reader = {
301
+ repositoryMembers() {
302
+ assertActive();
303
+ remember({ inventory: "entities", aspect: "repositories", id: "*" });
304
+ return effectiveRepositories(database);
305
+ },
306
+ physical: uuid => entity("entities", uuid),
307
+ sharing: uuid => entity("detection_notebook", uuid),
308
+ atPath(inventory, rootUUID, relativePath) {
309
+ assertActive();
310
+ remember({ inventory, aspect: "path", id: pathKey(rootUUID, relativePath) });
311
+ remember(activation(inventory, rootUUID));
312
+ return rows(inventory, cachedStatement(database, `SELECT ${groundRowProjection(inventory)} FROM ${inventory}
313
+ WHERE root_uuid = ? AND relative_path = ? ORDER BY uuid`).all(rootUUID, relativePath));
314
+ },
315
+ physicalCandidates(inventory, rootUUID, physicalKey) {
316
+ assertActive();
317
+ remember({ inventory, aspect: "physical", id: physicalAnswer(rootUUID, physicalKey) });
318
+ remember(activation(inventory, rootUUID));
319
+ return rows(inventory, cachedStatement(database, `SELECT ${groundRowProjection(inventory)} FROM ${inventory}
320
+ WHERE physical_key = ? AND root_uuid = ? ORDER BY uuid`).all(physicalKey, rootUUID));
321
+ },
322
+ objectRows(inventory, physicalKey) {
323
+ assertActive();
324
+ remember({ inventory, aspect: "object", id: physicalKey });
325
+ remember(activation(inventory, "*"));
326
+ return rows(inventory, cachedStatement(database, `SELECT ${groundRowProjection(inventory)} FROM ${inventory}
327
+ WHERE physical_key = ? ORDER BY uuid`).all(physicalKey));
328
+ },
329
+ subtree(inventory, uuid) {
330
+ assertActive();
331
+ const owned = reader.territory(inventory, uuid);
332
+ if (owned)
333
+ return owned.rows();
334
+ // Any other subtree is its members' complete child relations, each
335
+ // including an empty one; a new descendant invalidates its nearest
336
+ // old parent.
337
+ const values = cachedStatement(database, `WITH RECURSIVE descendants(uuid) AS (
338
+ SELECT uuid FROM ${inventory} WHERE uuid = ?
339
+ UNION SELECT child.uuid FROM ${inventory} AS child JOIN descendants ON child.parent_uuid = descendants.uuid
340
+ ) SELECT ${groundRowProjection(inventory)} FROM ${inventory}
341
+ WHERE uuid IN (SELECT uuid FROM descendants) ORDER BY uuid`).all(uuid);
342
+ for (const row of values)
343
+ remember({ inventory, aspect: "namespace", id: row.uuid });
344
+ return rows(inventory, values);
345
+ },
346
+ territory(inventory, uuid) {
347
+ assertActive();
348
+ if (!activeRepository(entity(inventory, uuid)))
349
+ return undefined; // An absent root is itself evidence.
350
+ // A repository's subtree is exactly its territory and, for each
351
+ // active repository within it, that one's own row and territory.
352
+ const view = warmTerritory(database, inventory, uuid);
353
+ for (const fact of territoryFacts(view))
354
+ remember(fact);
355
+ return new TerritoryHandle(view, {
356
+ read(row) {
357
+ basis.#entities[inventory].add(row.uuid);
358
+ basis.#activations.set(JSON.stringify([inventory, row.uuid]), activation(inventory, row.rootUUID));
359
+ },
360
+ readAll(uuids) { basis.#territories[inventory].push(uuids); },
361
+ moved: () => new GroundBasisChangedError("territory view moved after its observation"),
362
+ });
363
+ },
364
+ rootsAtPath(inventory, absolutePath) {
365
+ assertActive();
366
+ remember({ inventory, aspect: "roots", id: absolutePath });
367
+ const found = cachedStatement(database, `SELECT ${GROUND_ROW_PROJECTION} FROM ${inventory}
368
+ WHERE absolute_path = ? AND relative_path = '' AND status = 'active' ORDER BY uuid`).all(absolutePath);
369
+ for (const row of found)
370
+ remember({ inventory, aspect: "structure", id: row.uuid });
371
+ shown(inventory, found);
372
+ return Object.freeze(found.map(row => Object.freeze(structure(inventory, row))));
373
+ },
374
+ structure(inventory, uuid) {
375
+ assertActive();
376
+ remember({ inventory, aspect: "structure", id: uuid });
377
+ const row = cachedStatement(database, `SELECT ${GROUND_ROW_PROJECTION} FROM ${inventory} WHERE uuid = ?`).get(uuid);
378
+ activated(inventory, uuid, row);
379
+ return row && Object.freeze(structure(inventory, row));
380
+ },
381
+ children(inventory, parentUUID) {
382
+ assertActive();
383
+ reader.structure(inventory, parentUUID);
384
+ remember({ inventory, aspect: "namespace", id: parentUUID });
385
+ const rows = cachedStatement(database, `SELECT ${NAMESPACE_PROJECTION}
386
+ FROM ${inventory} WHERE parent_uuid = ? ORDER BY uuid`)
387
+ .all(parentUUID);
388
+ shown(inventory, rows);
389
+ return Object.freeze(rows.map(row => Object.freeze(row)));
390
+ },
391
+ slot(inventory, parentUUID, name) {
392
+ assertActive();
393
+ reader.structure(inventory, parentUUID);
394
+ remember({ inventory, aspect: "slot", id: slotKey(parentUUID, name) });
395
+ const rows = cachedStatement(database, `SELECT ${NAMESPACE_PROJECTION}
396
+ FROM ${inventory} WHERE parent_uuid = ? AND name = ? ORDER BY uuid`)
397
+ .all(parentUUID, name);
398
+ shown(inventory, rows);
399
+ return Object.freeze(rows.map(row => Object.freeze(row)));
400
+ },
401
+ members(inventory, parentUUID) {
402
+ assertActive();
403
+ entity(inventory, parentUUID); // An absent container is itself evidence.
404
+ remember({ inventory, aspect: "namespace", id: parentUUID });
405
+ return rows(inventory, cachedStatement(database, `SELECT ${groundRowProjection(inventory)} FROM ${inventory}
406
+ WHERE parent_uuid = ? ORDER BY uuid`).all(parentUUID));
407
+ },
408
+ inventory(inventory, rootUUID) {
409
+ assertActive();
410
+ remember({ inventory, aspect: "inventory", id: rootUUID ?? "*" });
411
+ basis.#inventories.add(JSON.stringify([inventory, rootUUID]));
412
+ const sql = `SELECT ${groundRowProjection(inventory)} FROM ${inventory}`;
413
+ const cursor = (rootUUID === null ? cachedStatement(database, sql).iterate()
414
+ : cachedStatement(database, `${sql} WHERE root_uuid = ?`).iterate(rootUUID));
415
+ const rows = [];
416
+ for (const row of cursor)
417
+ rows.push(retain(inventory, row));
418
+ return Object.freeze(rows);
419
+ },
420
+ };
421
+ try {
422
+ const value = operation(reader);
423
+ if (value && typeof value.then === "function")
424
+ throw new Error("ground snapshots cannot contain asynchronous work");
425
+ return { value, basis };
426
+ }
427
+ finally {
428
+ active = false;
429
+ }
430
+ });
431
+ reading.add(database);
432
+ try {
433
+ return capture();
434
+ }
435
+ finally {
436
+ reading.delete(database);
437
+ }
438
+ }
439
+ /** Same incarnation, and no consumed fact stamped after S0. Never compares
440
+ * the global counter with S0: unrelated writes do not invalidate. A small
441
+ * basis looks up each fact; a large one intersects the few facts stamped
442
+ * since S0, so the check is bounded by the smaller side. */
443
+ unchanged(database) {
444
+ if (groundIncarnation(database) !== this.#incarnation)
445
+ return false;
446
+ if (this.#dependencies.size <= POINT_LOOKUPS) {
447
+ for (const dependency of this.#dependencies.values()) {
448
+ const stamp = revisionOf(database, dependency);
449
+ if (stamp !== undefined && stamp > this.#sequence)
450
+ return false;
451
+ }
452
+ return true;
453
+ }
454
+ return !stampsAfter(database, this.#sequence).some(({ dependency }) => this.#dependencies.has(key(dependency)));
455
+ }
456
+ /** Every fact of this basis is a fact of one of these, read against the
457
+ * same incarnation and S0, so their unchanged answers are its answer. No
458
+ * expectation is merged: all of them are S0. */
459
+ within(parts) {
460
+ const same = parts.filter(part => part.#incarnation === this.#incarnation && part.#sequence === this.#sequence);
461
+ for (const id of this.#dependencies.keys())
462
+ if (!same.some(part => part.#dependencies.has(id)))
463
+ return false;
464
+ return true;
465
+ }
466
+ /** Entities of one inventory written by anyone after S0, and roots whose
467
+ * visible generation changed after S0, each with the sequence that last
468
+ * stamped it. Each such root is a responsibility of its own: its rows can
469
+ * change without their own stamps. */
470
+ changedSince(database, inventory) {
471
+ if (groundIncarnation(database) !== this.#incarnation)
472
+ throw new GroundBasisChangedError("ground incarnation changed");
473
+ const stamped = stampsAfter(database, this.#sequence).filter(({ dependency }) => dependency.inventory === inventory);
474
+ return {
475
+ entities: stamped.filter(({ dependency }) => dependency.aspect === "entity").map(({ dependency, revision }) => [dependency.id, revision]),
476
+ roots: stamped.filter(({ dependency: { aspect, id } }) => aspect === "activation" && id !== "*")
477
+ .map(({ dependency, revision }) => [dependency.id, revision]),
478
+ };
479
+ }
480
+ /** One admission unit's basis: the same incarnation and S0, over only the
481
+ * facts that unit consumed. Its answers derive from the S0 snapshot, so a
482
+ * fact unchanged since S0 is exactly the validity condition. An entity is
483
+ * protected only when the original observation could protect it; a
484
+ * scoped basis never widens allocation authority. */
485
+ scope(build) {
486
+ const scoped = new GroundBasis(this.#incarnation, this.#sequence);
487
+ const add = (dependency) => { scoped.#dependencies.set(key(dependency), dependency); };
488
+ let open = true;
489
+ const guard = (operation) => (...values) => {
490
+ if (!open)
491
+ throw new Error("a ground scope cannot grow after it is built");
492
+ operation(...values);
493
+ };
494
+ // A row fact carries the activation its original read consumed; one the
495
+ // original never read by identity consumes the widest.
496
+ const consumed = (inventory, uuid) => this.#activations.get(JSON.stringify([inventory, uuid])) ?? activation(inventory, "*");
497
+ build(Object.freeze({
498
+ entity: guard((inventory, uuid, before) => {
499
+ add({ inventory, aspect: "entity", id: uuid });
500
+ add(activation(inventory, before?.rootUUID ?? "*"));
501
+ if (this.protects(inventory, uuid, before))
502
+ scoped.#entities[inventory].add(uuid);
503
+ }),
504
+ structure: guard((inventory, uuid) => {
505
+ add({ inventory, aspect: "structure", id: uuid });
506
+ add(consumed(inventory, uuid));
507
+ }),
508
+ namespace: guard((inventory, uuid) => {
509
+ add({ inventory, aspect: "namespace", id: uuid });
510
+ add(consumed(inventory, uuid));
511
+ }),
512
+ path: guard((inventory, rootUUID, relativePath) => {
513
+ add({ inventory, aspect: "path", id: pathKey(rootUUID, relativePath) });
514
+ add(activation(inventory, rootUUID));
515
+ }),
516
+ physical: guard((inventory, rootUUID, physicalKey) => {
517
+ add({ inventory, aspect: "physical", id: physicalAnswer(rootUUID, physicalKey) });
518
+ add(activation(inventory, rootUUID));
519
+ }),
520
+ roots: guard((inventory, absolutePath) => add({ inventory, aspect: "roots", id: absolutePath })),
521
+ repositories: guard(() => add({ inventory: "entities", aspect: "repositories", id: "*" })),
522
+ territory: guard((inventory, uuid) => {
523
+ add(territory(inventory, uuid));
524
+ add(consumed(inventory, uuid));
525
+ }),
526
+ }));
527
+ open = false;
528
+ return scoped;
529
+ }
530
+ /** A scoped inventory protects its existing rows. A proposed destination
531
+ * cannot prove that a missing UUID was originally absent everywhere. */
532
+ protects(inventory, uuid, before) {
533
+ return this.#entities[inventory].has(uuid) || this.#territories[inventory].some(uuids => uuids.has(uuid))
534
+ || this.#inventories.has(JSON.stringify([inventory, null]))
535
+ || (before !== undefined && this.includesInventory(inventory, before.rootUUID));
536
+ }
537
+ includesInventory(inventory, rootUUID) {
538
+ return this.#inventories.has(JSON.stringify([inventory, null]))
539
+ || this.#inventories.has(JSON.stringify([inventory, rootUUID]));
540
+ }
541
+ /** May place a row this inventory never held: its root inventory, or its
542
+ * exact destination slot, was observed free. Identity history remains a
543
+ * separate proof; revision <= S0 never authorizes resurrection. */
544
+ allocates(inventory, after) {
545
+ return this.includesInventory(inventory, after.rootUUID)
546
+ || this.#dependencies.has(key({ inventory, aspect: "path", id: pathKey(after.rootUUID, after.relativePath) }));
547
+ }
548
+ }
549
+ const POINT_LOOKUPS = 64;
550
+ const pathKey = (rootUUID, relativePath) => JSON.stringify([rootUUID, relativePath]);
551
+ const physicalAnswer = (rootUUID, physicalKey) => JSON.stringify([rootUUID, physicalKey]);
552
+ const slotKey = (parentUUID, name) => JSON.stringify([parentUUID, name]);
553
+ const NAMESPACE_PROJECTION = `uuid, resource_id AS resourceId, root_uuid AS rootUUID, parent_uuid AS parentUUID,
554
+ name, type, status, relative_path AS relativePath, absolute_path AS absolutePath`;
555
+ const activation = (inventory, rootUUID) => ({ inventory, aspect: "activation", id: rootUUID });
556
+ const territory = (inventory, uuid) => ({ inventory, aspect: "territory", id: uuid });
557
+ /** The write changed an active repository's own head only; its placement in
558
+ * its owner's package, and so that territory, is unchanged. */
559
+ export const repositoryHeadOnly = (before, after) => activeRepository(before) && activeRepository(after) && before.filesystemMode === after.filesystemMode
560
+ && equal(namespace(before), namespace(after));
561
+ /** The nearest active repository strictly above each identity in one graph,
562
+ * memoized and safe on cycles: the owner whose territory the identity is. */
563
+ export function repositoryOwners(lookup) {
564
+ // The nearest active repository at or above each walked identity.
565
+ const memo = new Map();
566
+ return (uuid) => {
567
+ const chain = new Set();
568
+ let owner = null;
569
+ for (let current = lookup(uuid)?.parentUUID ?? null; current !== null;) {
570
+ if (memo.has(current)) {
571
+ owner = memo.get(current);
572
+ break;
573
+ }
574
+ const row = lookup(current);
575
+ if (!row || chain.has(current))
576
+ break;
577
+ if (activeRepository(row)) {
578
+ owner = current;
579
+ break;
580
+ }
581
+ chain.add(current);
582
+ current = row.parentUUID;
583
+ }
584
+ for (const walked of chain)
585
+ memo.set(walked, owner);
586
+ if (owner !== null)
587
+ memo.set(owner, owner);
588
+ return owner;
589
+ };
590
+ }
591
+ const namespace = (row) => row && ({
592
+ uuid: row.uuid, resourceId: row.resourceId, rootUUID: row.rootUUID, parentUUID: row.parentUUID,
593
+ name: row.name, type: row.type, status: row.status, relativePath: row.relativePath, absolutePath: row.absolutePath,
594
+ });
595
+ const structure = (inventory, row) => row && ({
596
+ ...namespace(row),
597
+ association: inventory === "entities" ? Object.freeze({
598
+ physicalKey: row.physicalKey, kind: (row.filesystemMode ?? 0) & 0o170000,
599
+ }) : null,
600
+ });
601
+ const equal = (left, right) => JSON.stringify(left) === JSON.stringify(right);
602
+ /** Each query answer a row belongs to, keyed exactly by the query arguments
603
+ * that would return it. A row entering or leaving an answer stamps it. */
604
+ function answers(inventory, row) {
605
+ if (!row)
606
+ return [];
607
+ const found = [{ inventory, aspect: "path", id: pathKey(row.rootUUID, row.relativePath) }];
608
+ if (row.physicalKey !== null && row.physicalKey !== undefined) {
609
+ found.push({ inventory, aspect: "physical", id: physicalAnswer(row.rootUUID, row.physicalKey) }, { inventory, aspect: "object", id: row.physicalKey });
610
+ }
611
+ if (row.relativePath === "" && row.status === "active")
612
+ found.push({ inventory, aspect: "roots", id: row.absolutePath });
613
+ return found;
614
+ }
615
+ /** A row's place in the repository membership answer: an active repository's
616
+ * identity and address. Its state never enters that answer. */
617
+ const member = (inventory, row) => inventory === "entities" && row?.type === "repo.git" && row.status === "active"
618
+ ? [row.uuid, row.rootUUID, row.absolutePath] : null;
619
+ /** A row's place in the ancestry that decides effective activity. */
620
+ const container = (inventory, row) => inventory === "entities" && row && ["workspace", "folder", "repo.git"].includes(row.type)
621
+ ? [row.type, row.status, row.parentUUID] : null;
622
+ /** Active repositories with no inactive ancestor. The answer is a function of
623
+ * repository identities and container ancestry, so it is derived once per
624
+ * pair of their stamps and reused while neither has advanced. The climb
625
+ * carries each ancestor's status, so it reads only repositories and their
626
+ * ancestors by key, never the whole inventory. */
627
+ const effectiveMembers = new WeakMap();
628
+ function effectiveRepositories(database) {
629
+ const witness = JSON.stringify([groundIncarnation(database), ...["repositories", "containers"]
630
+ .map(aspect => revisionOf(database, { inventory: "entities", aspect, id: "*" })?.toString() ?? null)]);
631
+ const known = effectiveMembers.get(database);
632
+ if (known?.witness === witness)
633
+ return known.members;
634
+ const members = Object.freeze(cachedStatement(database, `WITH RECURSIVE ancestry(repository, ancestor, inactive) AS (
635
+ SELECT uuid, parent_uuid, 0 FROM entities WHERE type = 'repo.git' AND status = 'active'
636
+ UNION SELECT ancestry.repository, entities.parent_uuid, entities.status <> 'active'
637
+ FROM ancestry JOIN entities ON entities.uuid = ancestry.ancestor WHERE NOT ancestry.inactive
638
+ )
639
+ SELECT uuid, root_uuid AS rootUUID, absolute_path AS absolutePath FROM entities
640
+ WHERE type = 'repo.git' AND status = 'active'
641
+ AND uuid NOT IN (SELECT repository FROM ancestry WHERE inactive)
642
+ ORDER BY uuid`).all().map(member => Object.freeze(member)));
643
+ effectiveMembers.set(database, { witness, members });
644
+ return members;
645
+ }
646
+ /** The facts a repository's subtree consumes: the top's row and territory,
647
+ * each active repository's row and territory beneath it, and the visible
648
+ * generation of every root its rows belong to. */
649
+ const territoryFacts = (view) => [
650
+ { inventory: view.inventory, aspect: "entity", id: view.uuid }, territory(view.inventory, view.uuid),
651
+ ...[...view.nested].flatMap((uuid) => [{ inventory: view.inventory, aspect: "entity", id: uuid },
652
+ territory(view.inventory, uuid)]),
653
+ ...[...view.roots].map(rootUUID => activation(view.inventory, rootUUID)),
654
+ ];
655
+ const stampedAfter = (database, dependency, sequence) => {
656
+ const stamp = revisionOf(database, dependency);
657
+ return stamp !== undefined && stamp > sequence;
658
+ };
659
+ /** Warm repository subtrees per ground file, most recently read last, each
660
+ * with the sequence it is exact at. A few cover the repositories a machine
661
+ * edits at once; any other read reloads, which is always exact. Only the
662
+ * main thread keeps them; every write it does not make itself reaches them
663
+ * as a saver receipt, in sequence order. */
664
+ const WARM_TERRITORIES = 16;
665
+ const warmFiles = new Map();
666
+ /** Inside a snapshot: the kept view while none of its facts is stamped after
667
+ * its sequence, since then its answer is this snapshot's; else a reload. */
668
+ function warmTerritory(database, inventory, uuid) {
669
+ const id = JSON.stringify([inventory, uuid]);
670
+ return keptTerritory(database, id) ?? keepTerritory(database, id, { incarnation: groundIncarnation(database),
671
+ sequence: currentSequence(database), view: TerritoryView.load(database, inventory, uuid) });
672
+ }
673
+ /** Inside a read: the kept view, now the most recently read and exact at this
674
+ * read's sequence, while none of its facts was stamped since; else none. */
675
+ function keptTerritory(database, id) {
676
+ if (!database.inTransaction)
677
+ throw new Error("a kept territory is judged inside one read");
678
+ const warm = warmFiles.get(database.name), known = warm?.get(id);
679
+ if (!known)
680
+ return undefined;
681
+ warm.delete(id);
682
+ if (known.incarnation !== groundIncarnation(database)
683
+ || territoryFacts(known.view).some(fact => stampedAfter(database, fact, known.sequence))) {
684
+ known.view.retire();
685
+ return undefined;
686
+ }
687
+ warm.set(id, known);
688
+ known.sequence = currentSequence(database);
689
+ return known.view;
690
+ }
691
+ function keepTerritory(database, id, kept) {
692
+ let warm = warmFiles.get(database.name);
693
+ if (!warm)
694
+ warmFiles.set(database.name, warm = new Map());
695
+ warm.get(id)?.view.retire();
696
+ warm.delete(id);
697
+ warm.set(id, kept);
698
+ if (warm.size > WARM_TERRITORIES) {
699
+ const [oldest, evicted] = warm.entries().next().value;
700
+ warm.delete(oldest);
701
+ evicted.view.retire();
702
+ }
703
+ return kept.view;
704
+ }
705
+ /** Every warm view of one file stops: its receipts may be missing, or the
706
+ * file closed. */
707
+ export function retireGroundViews(file) {
708
+ for (const { view } of warmFiles.get(file)?.values() ?? [])
709
+ view.retire();
710
+ warmFiles.delete(file);
711
+ }
712
+ /** On the saver's own connection: every stamping write's receipt. */
713
+ const receiptSinks = new WeakMap();
714
+ export function saveGroundReceipts(database, sink) {
715
+ receiptSinks.set(database, sink);
716
+ }
717
+ /** On the main thread: each file's receipts already sent, taken now. A read
718
+ * drains before its snapshot, never inside it, so no view runs ahead of the
719
+ * snapshot that uses it; a write drains inside its own transaction, which
720
+ * already sees every committed receipt. */
721
+ const receiptSources = new Map();
722
+ export function registerReceiptSource(file, drain) {
723
+ if (drain)
724
+ receiptSources.set(file, drain);
725
+ else
726
+ receiptSources.delete(file);
727
+ }
728
+ const drainReceipts = (database) => receiptSources.get(database.name)?.();
729
+ /** Receipts arrive in sequence order, and the main thread's own writes carry
730
+ * its views synchronously, so a view exact at an earlier sequence is exact
731
+ * just before this one. A view this write touches is carried from the rows
732
+ * it stored; one it cannot carry is dropped. */
733
+ export function carryGroundReceipt(file, receipt) {
734
+ const warm = warmFiles.get(file);
735
+ if (!warm || receipt.stamped.length === 0)
736
+ return;
737
+ const stamped = new Set(receipt.stamped);
738
+ const decoded = new Map();
739
+ const writtenIn = (inventory) => {
740
+ let rows = decoded.get(inventory);
741
+ if (!rows) {
742
+ const bytes = receipt.written[inventory];
743
+ const entries = bytes ? deserialize(bytes) : [];
744
+ decoded.set(inventory, rows = new Map(entries.map(([uuid, row, children]) => [uuid, { row, children }])));
745
+ }
746
+ return rows;
747
+ };
748
+ for (const [id, known] of warm) {
749
+ if (receipt.sequence <= known.sequence)
750
+ continue;
751
+ const facts = territoryFacts(known.view);
752
+ if (!facts.some(fact => stamped.has(key(fact))))
753
+ continue;
754
+ const written = writtenIn(known.view.inventory);
755
+ const plan = known.incarnation === receipt.incarnation
756
+ && !facts.some(fact => fact.aspect === "activation" && stamped.has(key(fact)))
757
+ ? known.view.plan(uuid => written.get(uuid)?.row ?? undefined, uuid => written.get(uuid)?.children ?? undefined, written.keys())
758
+ : null;
759
+ if (plan) {
760
+ known.view.apply(plan);
761
+ known.sequence = receipt.sequence;
762
+ }
763
+ else {
764
+ warm.delete(id);
765
+ known.view.retire();
766
+ }
767
+ }
768
+ }
769
+ /** On a content thread's read-only connection. */
770
+ export function readTerritories(database, uuid, inventories) {
771
+ const read = database.transaction(() => ({ incarnation: groundIncarnation(database), sequence: currentSequence(database),
772
+ subtrees: inventories.map(inventory => readSubtree(database, inventory, uuid)) }))();
773
+ return { ...read, subtrees: read.subtrees.map(rows => rows && subtreePages(rows)) };
774
+ }
775
+ /** Outside every snapshot, before one reads these repositories: the views no
776
+ * snapshot would find exact are read off this thread (`read`), built a page
777
+ * per step, and kept once a short read finds them exact now; each pair's
778
+ * agreement is compared a few rows per step. A view some write moved after
779
+ * its read is read again, a few times at most; one still moving is left to
780
+ * the snapshot, which reads it inside itself. */
781
+ export async function prepareTerritories(database, repositories, read) {
782
+ for (const uuid of repositories) {
783
+ const views = new Map();
784
+ for (let attempt = 0; attempt < PREPARE_ATTEMPTS; attempt++) {
785
+ drainReceipts(database);
786
+ for (const inventory of INVENTORIES) {
787
+ const view = views.get(inventory) ?? currentTerritory(database, JSON.stringify([inventory, uuid]));
788
+ if (view)
789
+ views.set(inventory, view);
790
+ }
791
+ const missing = INVENTORIES.filter(inventory => !views.has(inventory));
792
+ if (!missing.length)
793
+ break;
794
+ const { incarnation, sequence, subtrees } = await read(uuid, missing);
795
+ let absent = 0;
796
+ for (const [at, inventory] of missing.entries()) {
797
+ // No active repository: the snapshot answers that itself.
798
+ if (!subtrees[at]) {
799
+ absent += 1;
800
+ continue;
801
+ }
802
+ const building = TerritoryView.building(inventory, uuid, subtrees[at]);
803
+ let step = building.next();
804
+ for (; !step.done; step = building.next())
805
+ await turnBoundary();
806
+ const id = JSON.stringify([inventory, uuid]);
807
+ drainReceipts(database);
808
+ if (!currentTerritory(database, id))
809
+ keepTerritory(database, id, { incarnation, sequence, view: step.value });
810
+ }
811
+ if (absent === missing.length)
812
+ break;
813
+ }
814
+ const o = views.get("entities"), b = views.get("detection_notebook");
815
+ if (!o || !b)
816
+ continue;
817
+ const agreeing = TerritoryView.agreeing(o, b);
818
+ while (!agreeing.next().done)
819
+ await turnBoundary();
820
+ }
821
+ }
822
+ const PREPARE_ATTEMPTS = 3;
823
+ /** Outside every snapshot: the kept view when it is exact now, in one short read. */
824
+ const currentTerritory = (database, id) => database.transaction(() => keptTerritory(database, id))();
825
+ /** Inside a write, after its rows and before its stamps. A view whose facts
826
+ * this write stamps is carried to the write's sequence only by an outermost
827
+ * commit, only when it was exact before the write, and only from the rows
828
+ * the write stored; any other such view is dropped. A savepoint cannot
829
+ * certify its commit, and a changed visible generation is no row change.
830
+ * The returned step runs after the commit and never before it. */
831
+ function carryTerritories(database, stamped, written, outermost) {
832
+ drainReceipts(database);
833
+ const warm = warmFiles.get(database.name);
834
+ if (!warm)
835
+ return () => { };
836
+ const incarnation = groundIncarnation(database);
837
+ const stored = new Map();
838
+ const carried = [];
839
+ for (const [id, known] of warm) {
840
+ const facts = territoryFacts(known.view);
841
+ if (!facts.some(fact => stamped.has(key(fact))))
842
+ continue;
843
+ const { inventory } = known.view;
844
+ const plan = outermost && known.incarnation === incarnation
845
+ && !facts.some(fact => fact.aspect === "activation" && stamped.has(key(fact)))
846
+ && !facts.some(fact => stampedAfter(database, fact, known.sequence))
847
+ ? known.view.plan(uuid => {
848
+ const id = JSON.stringify([inventory, uuid]);
849
+ if (!stored.has(id))
850
+ stored.set(id, readGroundRow(database, inventory, uuid));
851
+ return stored.get(id);
852
+ }, uuid => cachedStatement(database, `SELECT uuid FROM ${inventory} WHERE parent_uuid = ?`).pluck().all(uuid), written.filter(change => change.inventory === inventory).map(({ before, after }) => (after ?? before).uuid))
853
+ : null;
854
+ if (plan)
855
+ carried.push({ known, plan });
856
+ else {
857
+ warm.delete(id);
858
+ known.view.retire();
859
+ }
860
+ }
861
+ return sequence => {
862
+ for (const { known, plan } of carried) {
863
+ known.view.apply(plan);
864
+ known.sequence = sequence;
865
+ }
866
+ };
867
+ }
868
+ /** Every fact a change of this root's visible generation can change without
869
+ * a member write, in both inventories, read inside the changing transaction
870
+ * after the change: the root's own activation and aggregates, the roots
871
+ * answers its root rows supply, the namespace and slot their portable
872
+ * parents list them in, and repository membership when it holds a repository. Ancestry
873
+ * is stamped as a whole. It names the answers the root now supplies; a
874
+ * change that also hides stored rows must add the answers they supplied. */
875
+ function activationVector(database, rootUUID) {
876
+ const vector = [{ inventory: "entities", aspect: "containers", id: "*" }];
877
+ for (const inventory of ["entities", "detection_notebook"]) {
878
+ vector.push(activation(inventory, rootUUID), activation(inventory, "*"), { inventory, aspect: "inventory", id: rootUUID }, { inventory, aspect: "inventory", id: "*" });
879
+ for (const { absolutePath, parentUUID, name, status } of cachedStatement(database, `SELECT absolute_path AS absolutePath,
880
+ parent_uuid AS parentUUID, name, status FROM ${inventory} WHERE root_uuid = ? AND relative_path = ''`)
881
+ .all(rootUUID)) {
882
+ if (status === "active")
883
+ vector.push({ inventory, aspect: "roots", id: absolutePath });
884
+ if (parentUUID !== null) {
885
+ vector.push({ inventory, aspect: "namespace", id: parentUUID }, { inventory, aspect: "slot", id: slotKey(parentUUID, name) });
886
+ }
887
+ }
888
+ }
889
+ if (cachedStatement(database, `SELECT EXISTS(SELECT 1 FROM entities
890
+ WHERE root_uuid = ? AND type = 'repo.git' AND status = 'active')`).pluck().get(rootUUID)) {
891
+ vector.push({ inventory: "entities", aspect: "repositories", id: "*" });
892
+ }
893
+ return vector;
894
+ }
895
+ /** Stamps every fact these committed transitions change, and the activation
896
+ * vector of each root whose visible generation this transaction changed,
897
+ * with one new sequence, inside the caller's write transaction. Returns the
898
+ * stamp count and the step the caller runs after its outermost commit.
899
+ *
900
+ * A row placed where none stood, in a root whose first generation this
901
+ * transaction installs, beneath a parent placed the same way or none, is
902
+ * stamped by that vector alone. Every earlier answer it could join was
903
+ * empty of it and consumed that root's activation or `*`: an answer keyed
904
+ * inside the root names the root, one keyed by an absent identity or an
905
+ * object names `*`, and the aggregates and the root rows' namespaces
906
+ * are the vector's. A child list of a parent that stood consumed its own
907
+ * root's activation, so a row placed beneath one is stamped as usual. An
908
+ * installation therefore stamps a few facts however many rows it places,
909
+ * and no territory can hold its rows unless its activation is stamped. */
910
+ export function advanceGroundBasis(database, transitions, activated, outermost) {
911
+ const changed = new Map();
912
+ const add = (dependency) => { changed.set(key(dependency), dependency); };
913
+ const installed = new Set(activated);
914
+ for (const rootUUID of installed)
915
+ for (const dependency of activationVector(database, rootUUID))
916
+ add(dependency);
917
+ const written = [...transitions].filter(({ inventory, before, after }) => !equal(before && groundRowValues(before, inventory), after && groundRowValues(after, inventory)));
918
+ const placed = new Set(written.filter(({ before, after }) => !before && installed.has(after.rootUUID))
919
+ .map(({ inventory, after }) => JSON.stringify([inventory, after.uuid])));
920
+ const stamping = placed.size === 0 ? written : written.filter(({ inventory, before, after }) => before || !placed.has(JSON.stringify([inventory, after.uuid]))
921
+ || (after.parentUUID !== null && !placed.has(JSON.stringify([inventory, after.parentUUID]))));
922
+ // Owners resolve in the graph before this transaction, from each written
923
+ // row's original, and in the committed graph after it.
924
+ const owners = Object.fromEntries(["entities", "detection_notebook"].map(inventory => {
925
+ const originals = new Map(written.filter(change => change.inventory === inventory)
926
+ .map(change => [(change.after ?? change.before).uuid, change.before]));
927
+ const current = (uuid) => cachedStatement(database, `SELECT parent_uuid AS parentUUID, type, status FROM ${inventory} WHERE uuid = ?`).get(uuid);
928
+ return [inventory, {
929
+ before: repositoryOwners(uuid => originals.has(uuid) ? originals.get(uuid) : current(uuid)),
930
+ after: repositoryOwners(current),
931
+ }];
932
+ }));
933
+ // What a territory can hold, for the saver's receipt.
934
+ const held = { entities: [], detection_notebook: [] };
935
+ const repositories = new Set();
936
+ for (const transition of stamping) {
937
+ const { inventory, before, after } = transition;
938
+ const uuid = (after ?? before).uuid;
939
+ const repository = activeRepository(before) || activeRepository(after);
940
+ let owned = false;
941
+ if (!repositoryHeadOnly(before, after)) {
942
+ for (const owner of [before && owners[inventory].before(uuid), after && owners[inventory].after(uuid)]) {
943
+ if (owner) {
944
+ add(territory(inventory, owner));
945
+ owned = true;
946
+ }
947
+ }
948
+ }
949
+ if (repository || owned)
950
+ held[inventory].push(transition);
951
+ if (repository)
952
+ repositories.add(key({ inventory, aspect: "entity", id: uuid }));
953
+ if (activeRepository(before) !== activeRepository(after))
954
+ add(territory(inventory, uuid));
955
+ add({ inventory, aspect: "entity", id: uuid });
956
+ add({ inventory, aspect: "inventory", id: "*" });
957
+ for (const row of [before, after])
958
+ if (row)
959
+ add({ inventory, aspect: "inventory", id: row.rootUUID });
960
+ if (!equal(structure(inventory, before), structure(inventory, after)))
961
+ add({ inventory, aspect: "structure", id: uuid });
962
+ if (!equal(namespace(before), namespace(after)))
963
+ for (const row of [before, after]) {
964
+ if (!row?.parentUUID)
965
+ continue;
966
+ add({ inventory, aspect: "namespace", id: row.parentUUID });
967
+ add({ inventory, aspect: "slot", id: slotKey(row.parentUUID, row.name) });
968
+ }
969
+ // Both the old and the new answer change when membership moves; an
970
+ // answer that stays with the row changes only through its entity key.
971
+ const left = answers(inventory, before), right = answers(inventory, after);
972
+ const kept = new Set(left.map(key).filter(id => right.some(dependency => key(dependency) === id)));
973
+ for (const dependency of [...left, ...right])
974
+ if (!kept.has(key(dependency)))
975
+ add(dependency);
976
+ if (!equal(member(inventory, before), member(inventory, after)))
977
+ add({ inventory, aspect: "repositories", id: "*" });
978
+ if (!equal(container(inventory, before), container(inventory, after)))
979
+ add({ inventory, aspect: "containers", id: "*" });
980
+ }
981
+ if (changed.size === 0)
982
+ return { writes: 0, committed: () => { } };
983
+ const carry = carryTerritories(database, changed, written, outermost);
984
+ const sequence = cachedStatement(database, "UPDATE ground_sequence SET value = value + 1 WHERE singleton = 1 RETURNING value", true).get().value;
985
+ const write = cachedStatement(database, `INSERT INTO ground_revisions(inventory, aspect, identity, revision)
986
+ VALUES (?, ?, ?, ?) ON CONFLICT(inventory, aspect, identity) DO UPDATE SET revision = excluded.revision`);
987
+ for (const dependency of changed.values())
988
+ write.run(dependency.inventory, dependency.aspect, dependency.id, sequence);
989
+ receiptSinks.get(database)?.(receipt(database, sequence, changed, held, repositories));
990
+ return { writes: changed.size, committed: () => carry(sequence) };
991
+ }
992
+ /** On the saver, inside the write: its receipt, with the stamps a warm view
993
+ * consumes and the rows it held read back as stored. */
994
+ function receipt(database, sequence, changed, held, repositories) {
995
+ const stamped = [...changed].filter(([id, { aspect }]) => aspect === "territory" || aspect === "activation" || repositories.has(id)).map(([id]) => id);
996
+ const written = {};
997
+ for (const inventory of stamped.length ? INVENTORIES : []) {
998
+ const changes = held[inventory];
999
+ if (!changes.length)
1000
+ continue;
1001
+ const uuids = [...new Set(changes.map(({ before, after }) => (after ?? before).uuid))];
1002
+ const rows = new Map(cachedStatement(database, `SELECT ${groundRowProjection(inventory)} FROM ${inventory}
1003
+ WHERE uuid IN (SELECT value FROM json_each(?))`).all(JSON.stringify(uuids)).map(row => [row.uuid, row]));
1004
+ const moved = changes.some(({ before, after }) => !before || (after && after.parentUUID !== before.parentUUID));
1005
+ const children = new Map();
1006
+ if (moved) {
1007
+ for (const { parent, uuid } of cachedStatement(database, `SELECT parent_uuid AS parent, uuid FROM ${inventory}
1008
+ WHERE parent_uuid IN (SELECT value FROM json_each(?))`).all(JSON.stringify([...rows.keys()]))) {
1009
+ const listed = children.get(parent);
1010
+ if (listed)
1011
+ listed.push(uuid);
1012
+ else
1013
+ children.set(parent, [uuid]);
1014
+ }
1015
+ }
1016
+ written[inventory] = serialize(uuids.map(uuid => {
1017
+ const row = rows.get(uuid) ?? null;
1018
+ return [uuid, row, row && moved ? children.get(uuid) ?? [] : null];
1019
+ }));
1020
+ }
1021
+ return { incarnation: groundIncarnation(database), sequence, stamped, written };
1022
+ }
1023
+ //# sourceMappingURL=ground-basis.js.map