@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
@@ -1,10 +1,22 @@
1
1
  import { EventEmitter } from "node:events";
2
+ import { randomUUID } from "node:crypto";
3
+ import { channel } from "node:diagnostics_channel";
2
4
  import { lstatSync, readdirSync, realpathSync, watch as openNodeWatch } from "node:fs";
3
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
+ import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
4
6
  import { createSuspicionScope, watchOwnerOf, } from "@amalgm/live";
5
7
  import { openFsEvents } from "./fsevents.js";
6
8
  const DEFAULT_RETRY_MINIMUM_MS = 250;
9
+ /** At most this many native notices are admitted per event-loop turn, and a
10
+ * turn's admissions stop once they have held the main thread for the budget.
11
+ * A burst of thousands (a Git checkout) is admitted in arrival order across
12
+ * turns, so no burst holds the main thread for more than one slice, while a
13
+ * quiet notice is admitted at once. Waiting notices stay owed (hasPending). */
14
+ const WATCH_ADMISSION_SLICE = 256;
15
+ const WATCH_ADMISSION_BUDGET_MS = 1.5;
7
16
  const DEFAULT_RETRY_MAXIMUM_MS = 30_000;
17
+ /** Examiners hear each root's oldest unproved ring whenever it moves: a ring
18
+ * starts it, a proof advances or clears it, unbinding clears it. */
19
+ const obligations = channel("amalgm:watch-obligation");
8
20
  /** macOS gets one FSEvents stream per handle. Node's own watcher serves every
9
21
  * handle in the process from one stream that it recreates, resuming from the
10
22
  * present, whenever a handle opens or closes, losing the events in between. */
@@ -16,13 +28,20 @@ function nativeOpenFor(platform) {
16
28
  return (directory, options, listener) => openNodeWatch(realpathSync.native(directory), options, listener);
17
29
  }
18
30
  /** Linux has no recursive inotify primitive. This one logical handle expands
19
- * to exactly one non-recursive fs.watch handle per directory, never per file. */
31
+ * to exactly one non-recursive fs.watch handle per directory, never per file.
32
+ *
33
+ * A descendant the tree cannot enter is its parent's to hear, never a lost
34
+ * tree: one that vanished needs no handle, and one this process may not read
35
+ * is withheld, keeping any handle it already had, until an event on its name
36
+ * asks again. Detect names withheld ground unavailable meanwhile. Entering it
37
+ * later ends an interval no handle heard, so it suspects everything. */
20
38
  class LinuxDirectoryTreeWatch extends EventEmitter {
21
39
  root;
22
40
  open;
23
41
  listener;
24
42
  excluded;
25
43
  handles = new Map();
44
+ withheld = new Set();
26
45
  closed = false;
27
46
  constructor(root, open, listener, excluded) {
28
47
  super();
@@ -50,30 +69,64 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
50
69
  this.closeAll();
51
70
  this.emit("close");
52
71
  }
53
- openTree(directory) {
72
+ /** Opens every directory the tree can enter at or below this one. `reached`
73
+ * collects each directory the walk stood on, entered or withheld. */
74
+ openTree(directory, reached) {
54
75
  if (this.closed || this.isExcluded(directory) || !nativeDirectory(directory))
55
76
  return;
56
- this.openDirectory(directory);
57
- for (const entry of readdirSync(directory, { withFileTypes: true })) {
77
+ let entries;
78
+ try {
79
+ this.openDirectory(directory);
80
+ entries = readdirSync(directory, { withFileTypes: true });
81
+ }
82
+ catch (error) {
83
+ if (!this.cannotEnter(directory, error))
84
+ throw error;
85
+ if (this.withheld.has(directory))
86
+ reached?.add(directory);
87
+ return;
88
+ }
89
+ reached?.add(directory);
90
+ for (const entry of entries) {
58
91
  if (!entry.isDirectory() || entry.isSymbolicLink())
59
92
  continue;
60
- this.openTree(join(directory, entry.name));
93
+ this.openTree(join(directory, entry.name), reached);
61
94
  }
95
+ if (this.withheld.delete(directory))
96
+ this.listener("rename", null);
97
+ }
98
+ /** Whether a failure to enter this directory is ground its parent hears,
99
+ * rather than coverage the tree lost. The root is never its parent's. */
100
+ cannotEnter(directory, error) {
101
+ if (directory === this.root)
102
+ return false;
103
+ const code = error?.code;
104
+ if (code === "ENOENT" || code === "ENOTDIR") {
105
+ this.closeSubtree(directory);
106
+ return true;
107
+ }
108
+ if (code === "EACCES" || code === "EPERM") {
109
+ this.withheld.add(directory);
110
+ return true;
111
+ }
112
+ return false;
62
113
  }
63
114
  openDirectory(directory) {
64
115
  if (this.handles.has(directory))
65
116
  return;
66
- const handle = this.open(directory, { recursive: false }, (event, filename) => {
117
+ const localDirectory = localPath(this.root, directory);
118
+ const handle = this.open(directory, { recursive: false }, (event, filename, evidence) => {
67
119
  if (this.closed)
68
120
  return;
69
- const localDirectory = slash(relative(this.root, directory));
70
121
  const hint = normalizedHint(filename);
71
- const rootedHint = hint === null
122
+ const rootedHint = hint === null || localDirectory === null
72
123
  ? null
73
- : [localDirectory, hint].filter(Boolean).join("/");
74
- this.listener(event, rootedHint);
75
- if (event === "rename")
124
+ : localDirectory ? `${localDirectory}/${hint}` : hint;
125
+ this.listener(event, rootedHint, evidence);
126
+ // Access returning to withheld ground changes only its attributes.
127
+ if (event === "rename" || (hint !== null && this.withheld.has(below(directory, hint)))) {
76
128
  this.refresh(directory, hint);
129
+ }
77
130
  });
78
131
  const record = { handle, intentional: false };
79
132
  this.handles.set(directory, record);
@@ -89,7 +142,7 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
89
142
  this.reconcileTree();
90
143
  return;
91
144
  }
92
- const candidate = resolve(directory, ...hint.split("/"));
145
+ const candidate = below(directory, hint);
93
146
  if (!within(this.root, candidate) || this.isExcluded(candidate))
94
147
  return;
95
148
  if (nativeDirectory(candidate))
@@ -102,23 +155,19 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
102
155
  }
103
156
  });
104
157
  }
158
+ /** Handles below withheld ground stay: no walk can prove them gone. */
105
159
  reconcileTree() {
106
- const wanted = new Set();
107
- const collect = (directory) => {
108
- if (this.isExcluded(directory) || !nativeDirectory(directory))
109
- return;
110
- wanted.add(directory);
111
- for (const entry of readdirSync(directory, { withFileTypes: true })) {
112
- if (entry.isDirectory() && !entry.isSymbolicLink())
113
- collect(join(directory, entry.name));
114
- }
115
- };
116
- collect(this.root);
117
- for (const directory of wanted)
118
- this.openDirectory(directory);
119
- for (const directory of this.handles.keys()) {
120
- if (!wanted.has(directory))
121
- this.closeOne(directory);
160
+ const reached = new Set();
161
+ this.openTree(this.root, reached);
162
+ for (const directory of [...this.withheld]) {
163
+ if (!reached.has(directory))
164
+ this.withheld.delete(directory);
165
+ }
166
+ const withheld = [...this.withheld];
167
+ for (const directory of [...this.handles.keys()]) {
168
+ if (reached.has(directory) || withheld.some((held) => within(held, directory)))
169
+ continue;
170
+ this.closeOne(directory);
122
171
  }
123
172
  }
124
173
  closeSubtree(directory) {
@@ -126,6 +175,10 @@ class LinuxDirectoryTreeWatch extends EventEmitter {
126
175
  if (within(directory, watched))
127
176
  this.closeOne(watched);
128
177
  }
178
+ for (const held of [...this.withheld]) {
179
+ if (within(directory, held))
180
+ this.withheld.delete(held);
181
+ }
129
182
  }
130
183
  closeOne(directory) {
131
184
  const record = this.handles.get(directory);
@@ -174,9 +227,22 @@ export class NodeWatchHost {
174
227
  stopErrors = [];
175
228
  closeTask = null;
176
229
  plan = emptyPlan();
230
+ domainOwner = randomUUID();
231
+ bindingGeneration = 0;
232
+ domainGeneration = 0;
233
+ resolvedDomainGeneration = 0;
234
+ coverageGap = false;
177
235
  retryTimer = null;
178
236
  retryAttempt = 0;
179
237
  closed = false;
238
+ finalCutoff = false;
239
+ /** Native notices beyond this turn's slice, in arrival order. */
240
+ backlog = [];
241
+ backlogHead = 0;
242
+ sliceLeft = WATCH_ADMISSION_SLICE;
243
+ /** When this turn's budget ends; set by the turn's first admission. */
244
+ sliceEnds = Infinity;
245
+ turnScheduled = false;
180
246
  currentHealth = {
181
247
  state: "degraded",
182
248
  logicalRoots: 0,
@@ -226,6 +292,8 @@ export class NodeWatchHost {
226
292
  const next = buildPlan(grounds, previous.roots);
227
293
  if (samePlan(previous, next))
228
294
  return this.currentHealth;
295
+ this.bindingGeneration += 1;
296
+ const domainPending = this.domainGeneration > this.resolvedDomainGeneration;
229
297
  this.cancelRetry();
230
298
  const changed = changedRoots(previous, next);
231
299
  const authoritativeBaselines = new Set(options.authoritativeBaselineRootIds ?? []);
@@ -239,17 +307,26 @@ export class NodeWatchHost {
239
307
  if (!root.materialized) {
240
308
  throw new Error(`authoritative Watch baseline is not materialized: ${rootId}`);
241
309
  }
242
- const baseline = root.suspicion.snapshot();
310
+ const baseline = root.suspicion.freeze(null);
243
311
  root.suspicion.settle(baseline);
244
- root.settledGeneration = baseline.generation;
312
+ root.suspicion.release(baseline);
245
313
  }
246
314
  this.plan = next;
315
+ for (const root of previous.roots.values())
316
+ if (!next.roots.has(root.rootId))
317
+ this.observeObligation(root, false);
318
+ for (const root of next.roots.values())
319
+ this.observeObligation(root);
247
320
  const health = this.establish(next, authoritativeBaselines);
248
321
  const unknownChanges = changed.filter((rootId) => !authoritativeBaselines.has(rootId));
249
322
  if (health.state === "healthy" && unknownChanges.length > 0) {
250
323
  this.suspectRoots(unknownChanges);
251
324
  this.signal();
252
325
  }
326
+ // This is the caller's declared binding set, including temporarily missing
327
+ // ground. A changed set cannot inherit a proof against its predecessor.
328
+ if (domainPending)
329
+ this.suspectDomain();
253
330
  return health;
254
331
  }
255
332
  /** Retry the retained desired topology immediately. Useful to supervisors and deterministic tests. */
@@ -272,7 +349,7 @@ export class NodeWatchHost {
272
349
  rootId: root.rootId,
273
350
  directory: root.directory,
274
351
  materialized: root.materialized,
275
- currency: root.suspicion.size > 0 ? "catching-up" : "current",
352
+ currency: root.suspicion.owes ? "catching-up" : "current",
276
353
  contentOwner: (() => {
277
354
  const ownerId = this.plan.ownerByRoot.get(root.rootId);
278
355
  return ownerId ? this.plan.roots.get(ownerId)?.directory ?? null : null;
@@ -287,38 +364,211 @@ export class NodeWatchHost {
287
364
  .sort((left, right) => left.directory.localeCompare(right.directory)),
288
365
  };
289
366
  }
290
- observations() {
367
+ /** What every root still owes. Scheduling reads this; only a cut proves. */
368
+ pending() {
291
369
  return [...this.plan.roots.values()]
292
- .map((root) => ({
293
- rootId: root.rootId,
294
- directory: root.directory,
295
- suspicion: root.suspicion.snapshot(),
296
- }))
370
+ .map(root => ({ rootId: root.rootId, directory: root.directory, pending: root.suspicion.pending() }))
297
371
  .sort((left, right) => left.rootId.localeCompare(right.rootId));
298
372
  }
373
+ /** Cut every root at what it owes now; an owed domain cuts all roots
374
+ * together. Native cutoffs are never widened by the passage of time. */
375
+ freeze() {
376
+ return this.domainGeneration === this.resolvedDomainGeneration
377
+ ? this.freezeRoots(() => true, "owed")
378
+ : this.domainObservations(this.freezeRoots(() => true, "complete"));
379
+ }
380
+ /** Cut the selected roots alone. A partial cut never carries the domain.
381
+ * "escalated" cuts the rings a named proof escalated while no ring owes
382
+ * the complete root; otherwise the complete root. */
383
+ freezeRoots(select, scope) {
384
+ return [...this.plan.roots.values()]
385
+ .filter(root => select(root))
386
+ .sort((left, right) => left.rootId.localeCompare(right.rootId))
387
+ .map(root => {
388
+ const owed = root.suspicion.pending();
389
+ return this.cut(root, scope === "escalated" ? owed.escalated
390
+ : scope === "complete" || owed.complete ? null : owed.paths);
391
+ });
392
+ }
393
+ /** Cut one root at exactly these named rings. A lane that proves part of a
394
+ * root's names cuts only its own; the rest stay owed to their lane. */
395
+ freezePaths(rootId, paths) {
396
+ const root = this.plan.roots.get(rootId);
397
+ return root ? this.cut(root, paths) : null;
398
+ }
399
+ /** A ticket for exact addresses: each root waits only for the named
400
+ * addresses inside it. A root inside an address, or an address outside
401
+ * every root, needs that complete proof — the scope `suspect` rang. */
402
+ freezeAddresses(absolutePaths) {
403
+ const roots = [...this.plan.roots.values()].sort((left, right) => left.rootId.localeCompare(right.rootId));
404
+ const addresses = absolutePaths.map(path => resolve(path));
405
+ const unbound = addresses.length === 0 || addresses.some(address => !roots.some(root => localPath(root.directory, address) !== null));
406
+ return roots.flatMap(root => {
407
+ if (unbound || addresses.some(address => localPath(address, root.directory) !== null)) {
408
+ return [this.cut(root, null)];
409
+ }
410
+ const named = addresses.map(address => localPath(root.directory, address))
411
+ .filter((local) => local !== null);
412
+ return named.length ? [this.cut(root, named)] : [];
413
+ });
414
+ }
415
+ cut(root, paths) {
416
+ return Object.freeze({ rootId: root.rootId, directory: root.directory, suspicion: root.suspicion.freeze(paths) });
417
+ }
418
+ /** Every participant is one root's complete cut. */
419
+ domainObservations(complete) {
420
+ const domain = Object.freeze({
421
+ owner: this.domainOwner, generation: this.domainGeneration, bindings: this.bindingGeneration,
422
+ participants: Object.freeze(complete.map(observation => Object.freeze({
423
+ rootId: observation.rootId, directory: observation.directory, generation: observation.suspicion.generation,
424
+ }))),
425
+ });
426
+ return complete.map(observation => Object.freeze({ ...observation, domain }));
427
+ }
428
+ /** One finite complete-lane batch: an owed domain, else the roots owing a
429
+ * complete-root or escalated proof. Named rings are not complete work;
430
+ * their own lanes serve them. A root whose ground is `held` is not cut: it
431
+ * is `withheld`. A `parked` root is not cut either; its park's own cause
432
+ * wakes it. A domain, which cuts every root together, waits whole. */
433
+ selectComplete(held, parked) {
434
+ const roots = [...this.plan.roots.values()];
435
+ if (this.domainGeneration > this.resolvedDomainGeneration) {
436
+ const withheld = roots.map(root => root.directory).filter(held);
437
+ const waits = withheld.length > 0 || roots.some(root => parked(root.rootId));
438
+ return { cuts: waits ? [] : this.domainObservations(this.freezeRoots(() => true, "complete")), withheld };
439
+ }
440
+ const owing = new Set(roots.filter(root => root.suspicion.pending().complete && !parked(root.rootId))
441
+ .map(root => root.directory));
442
+ const withheld = [...owing].filter(held);
443
+ return { cuts: this.freezeRoots(root => owing.has(root.directory) && !withheld.includes(root.directory), "escalated"),
444
+ withheld };
445
+ }
446
+ /** The binding world a turn begins in; a later rebinding invalidates it. */
447
+ boundary() {
448
+ return Object.freeze({ owner: this.domainOwner, bindings: this.bindingGeneration });
449
+ }
450
+ boundaryCurrent(boundary) {
451
+ return boundary.owner === this.domainOwner && boundary.bindings === this.bindingGeneration;
452
+ }
453
+ /** A named cut widened: its rings keep their generations and are now owed
454
+ * a complete-root proof. The cut itself stays live until released. */
455
+ escalate(observation) {
456
+ const root = this.plan.roots.get(observation.rootId);
457
+ if (root?.suspicion.holds(observation.suspicion))
458
+ root.suspicion.escalate(observation.suspicion);
459
+ }
460
+ /** Names another examiner found (the sweep) that a named proof could not
461
+ * settle. Watch never rang them, so they are rung here and escalated at
462
+ * once: owed the same scoped wider proof as a widened native ring, never
463
+ * the complete root merely because Watch did not hear them. */
464
+ escalatePaths(rootId, paths) {
465
+ const root = this.plan.roots.get(rootId);
466
+ if (this.closed || !root || paths.length === 0)
467
+ return;
468
+ // The root itself is only ever owed its complete proof.
469
+ if (paths.includes("")) {
470
+ this.ring(root, null);
471
+ return;
472
+ }
473
+ for (const path of paths)
474
+ this.ring(root, path);
475
+ const cut = root.suspicion.freeze(paths);
476
+ root.suspicion.escalate(cut);
477
+ root.suspicion.release(cut);
478
+ }
479
+ /** Every cut must be released by its owner, proved or not. */
480
+ release(observations) {
481
+ for (const observation of observations) {
482
+ const root = this.plan.roots.get(observation.rootId);
483
+ if (root?.suspicion.holds(observation.suspicion))
484
+ root.suspicion.release(observation.suspicion);
485
+ }
486
+ }
487
+ completeDomain(observations) {
488
+ const domain = observations[0]?.domain;
489
+ if (!domain || observations.length !== this.plan.roots.size || observations.length === 0
490
+ || domain.participants.length !== observations.length
491
+ || new Set(domain.participants.map(item => item.rootId)).size !== observations.length
492
+ || new Set(observations.map(item => item.rootId)).size !== observations.length
493
+ || domain.owner !== this.domainOwner || domain.bindings !== this.bindingGeneration
494
+ || !this.continuous())
495
+ return false;
496
+ return domain.participants.every(({ rootId, directory, generation }) => {
497
+ const root = this.plan.roots.get(rootId);
498
+ return root?.directory === directory && root.materialized
499
+ && observations.some(item => item.rootId === rootId && item.directory === directory
500
+ && item.domain === domain && item.suspicion.paths === null
501
+ && item.suspicion.generation === generation
502
+ && root.suspicion.holds(item.suspicion));
503
+ });
504
+ }
299
505
  settle(observations) {
506
+ const domain = observations.find(observation => observation.domain)?.domain;
507
+ if (domain && !this.completeDomain(observations))
508
+ return;
300
509
  for (const observation of observations) {
301
510
  const root = this.plan.roots.get(observation.rootId);
302
- if (root?.directory === observation.directory) {
511
+ if (root?.directory === observation.directory && root.suspicion.holds(observation.suspicion)) {
303
512
  root.suspicion.settle(observation.suspicion);
304
- root.settledGeneration = Math.max(root.settledGeneration, observation.suspicion.generation);
513
+ this.observeObligation(root);
305
514
  }
306
515
  }
516
+ if (domain)
517
+ this.resolvedDomainGeneration = Math.max(this.resolvedDomainGeneration, domain.generation);
307
518
  }
308
- /** True only when every generation frozen by this observation has been
309
- * proved. Newer rings do not change the answer and retain their own pass. */
519
+ /** True only when everything each cut scoped at or before its generation
520
+ * has been proved, by any proof. Newer rings keep their own pass. A root
521
+ * no longer bound owes this caller nothing. */
310
522
  settled(observations) {
311
523
  return observations.every((observation) => {
312
524
  const root = this.plan.roots.get(observation.rootId);
313
- return root?.directory === observation.directory
314
- && root.settledGeneration >= observation.suspicion.generation;
525
+ if (!root?.suspicion.holds(observation.suspicion))
526
+ return true;
527
+ return (!observation.domain || (observation.domain.owner === this.domainOwner
528
+ && observation.domain.bindings === this.bindingGeneration
529
+ && this.resolvedDomainGeneration >= observation.domain.generation))
530
+ && root.suspicion.settled(observation.suspicion);
315
531
  });
316
532
  }
533
+ /** A capture may depend on another root's unfinished observation. Return
534
+ * scopes only; Apply ownership and recovery decisions remain with the host. */
535
+ dependencies(observations) {
536
+ const directories = new Set(observations.flatMap(observation => observation.domain?.participants.map(participant => participant.directory) ?? [observation.directory]));
537
+ if (this.domainGeneration > this.resolvedDomainGeneration) {
538
+ for (const root of this.plan.roots.values())
539
+ directories.add(root.directory);
540
+ }
541
+ return [...directories];
542
+ }
543
+ /** Coverage is continuous while healthy. A catch-up close is a final cutoff,
544
+ * not a gap: it suspects every root before retiring its handles, and every
545
+ * later change belongs to the next startup's blind interval. */
546
+ continuous() {
547
+ return !this.coverageGap && (this.currentHealth.state === "healthy" || this.finalCutoff);
548
+ }
549
+ bindingsCurrent(observations) {
550
+ return observations.every(observation => !observation.domain
551
+ || (this.continuous()
552
+ && observation.domain.owner === this.domainOwner
553
+ && observation.domain.bindings === this.bindingGeneration));
554
+ }
317
555
  suspectAll() {
318
556
  if (this.closed)
319
557
  return;
320
558
  for (const root of this.plan.roots.values())
321
- root.suspicion.suspectAll();
559
+ this.ring(root, null);
560
+ }
561
+ /** One ambiguous effect can reach another root through a historical alias.
562
+ * Keep one bounded obligation, not a history of native notifications. */
563
+ suspectDomain() {
564
+ if (this.closed)
565
+ return;
566
+ this.suspectAll();
567
+ this.domainGeneration += 1;
568
+ if (this.plan.roots.size === 0) {
569
+ this.resolvedDomainGeneration = this.domainGeneration;
570
+ }
571
+ this.signal();
322
572
  }
323
573
  /** A host effect that touched exact ground rings the same named suspicion a
324
574
  * native callback would: each root containing the address suspects that
@@ -333,26 +583,27 @@ export class NodeWatchHost {
333
583
  const local = localPath(root.directory, absolute);
334
584
  if (local === null)
335
585
  continue;
336
- if (local === "")
337
- root.suspicion.suspectAll();
338
- else
339
- root.suspicion.ring(local);
586
+ this.ring(root, local === "" ? null : local);
340
587
  named = true;
341
588
  }
342
589
  if (!named)
343
590
  this.suspectRoots(this.plan.roots.keys());
344
591
  }
345
592
  get hasPending() {
346
- return [...this.plan.roots.values()].some((root) => root.suspicion.size > 0);
593
+ return this.backlogHead < this.backlog.length
594
+ || [...this.plan.roots.values()].some((root) => root.suspicion.owes);
347
595
  }
348
596
  close(options = {}) {
349
597
  if (this.closeTask)
350
598
  return this.closeTask;
351
599
  if (options.catchUp !== false) {
352
600
  for (const root of this.plan.roots.values())
353
- root.suspicion.suspectAll();
601
+ this.ring(root, null);
602
+ this.finalCutoff = true;
354
603
  }
355
604
  this.closed = true;
605
+ this.backlog = [];
606
+ this.backlogHead = 0;
356
607
  this.cancelRetry();
357
608
  closeRecords(this.content);
358
609
  closeRecords(this.addresses);
@@ -377,7 +628,7 @@ export class NodeWatchHost {
377
628
  for (const directory of wantedContent) {
378
629
  if (this.content.has(directory))
379
630
  continue;
380
- this.content.set(directory, this.openHandle("content", directory, true, (filename) => this.routeContent(directory, filename)));
631
+ this.content.set(directory, this.openHandle("content", directory, true, (filename, evidence) => this.routeContent(directory, filename, evidence)));
381
632
  openedContent.push(directory);
382
633
  }
383
634
  for (const directory of wantedAddresses) {
@@ -390,9 +641,7 @@ export class NodeWatchHost {
390
641
  catch (error) {
391
642
  closeSelected(this.content, openedContent);
392
643
  closeSelected(this.addresses, openedAddresses);
393
- this.suspectRoots(plan.roots.keys());
394
- const health = this.degrade(errorMessage(error));
395
- this.signal();
644
+ const health = this.loseCoverage(errorMessage(error));
396
645
  this.scheduleRetry();
397
646
  return health;
398
647
  }
@@ -420,15 +669,38 @@ export class NodeWatchHost {
420
669
  contentHandles: this.content.size,
421
670
  addressHandles: this.addresses.size,
422
671
  };
672
+ const recovered = this.coverageGap;
673
+ if (recovered) {
674
+ this.coverageGap = false;
675
+ // An examination acquired inside the blind interval cannot prove
676
+ // recovery. The still-pending cohort must be read under new coverage.
677
+ this.bindingGeneration += 1;
678
+ }
423
679
  this.publishHealth(health);
424
- if (introducedSuspicion)
680
+ if (introducedSuspicion || recovered)
425
681
  this.signal();
426
682
  return health;
427
683
  }
428
684
  openHandle(kind, directory, recursive, route) {
685
+ const deliver = (event, filename, native) => {
686
+ if (this.closed)
687
+ return;
688
+ const at = Date.now();
689
+ this.admit(() => {
690
+ // Evidence is built within the admitted slice, dated at arrival.
691
+ this.onEvent?.({ kind, directory, path: normalizedHint(filename), event, ...(native ? { native } : {}), at });
692
+ // Preserve scope before ignored/control paths, repository routing, or
693
+ // current path lookup can discard the reason for wider examination.
694
+ if (native?.scope === "domain")
695
+ this.suspectDomain();
696
+ else
697
+ route(filename, native?.alias
698
+ ? { alias: true, ...(native.file === null ? {} : { object: native.file }) } : undefined);
699
+ });
700
+ };
429
701
  const handle = recursive && this.platform === "linux"
430
- ? new LinuxDirectoryTreeWatch(directory, this.open, (_event, filename) => route(filename), this.plan.exclusions.filter((excluded) => within(directory, excluded)))
431
- : this.open(directory, { recursive }, (_event, filename) => route(filename));
702
+ ? new LinuxDirectoryTreeWatch(directory, this.open, (event, filename, evidence) => deliver(event, filename, evidence), this.plan.exclusions.filter((excluded) => within(directory, excluded)))
703
+ : this.open(directory, { recursive }, (event, filename, evidence) => deliver(event, filename, evidence));
432
704
  const record = {
433
705
  handle,
434
706
  identity: directoryIdentity(directory),
@@ -452,15 +724,12 @@ export class NodeWatchHost {
452
724
  catch {
453
725
  // The handle is already unusable; coverage loss is recorded below.
454
726
  }
455
- const roots = kind === "content"
456
- ? this.plan.membersByContent.get(directory)
457
- : this.plan.targetsByAddress.get(directory);
458
- this.suspectRoots(roots ?? []);
459
- this.degrade(`${kind} watcher ${directory}: ${reason}`);
460
- this.signal();
727
+ // The lost interval could contain an edit through an alias in another
728
+ // root. Reopening this handle repairs coverage, not that attribution.
729
+ this.loseCoverage(`${kind} watcher ${directory}: ${reason}`);
461
730
  this.scheduleRetry();
462
731
  }
463
- routeContent(directory, filename) {
732
+ routeContent(directory, filename, evidence) {
464
733
  if (this.closed)
465
734
  return;
466
735
  const members = this.plan.membersByContent.get(directory);
@@ -470,9 +739,8 @@ export class NodeWatchHost {
470
739
  return;
471
740
  }
472
741
  const hint = normalizedHint(filename);
473
- this.onEvent?.({ kind: "content", directory, path: hint, at: Date.now() });
474
742
  if (hint !== null) {
475
- const absolute = resolve(directory, ...hint.split("/"));
743
+ const absolute = below(directory, hint);
476
744
  if (this.plan.exclusions.some((excluded) => within(excluded, absolute)))
477
745
  return;
478
746
  let signaled = false;
@@ -482,11 +750,11 @@ export class NodeWatchHost {
482
750
  continue;
483
751
  const local = localPath(root.directory, absolute);
484
752
  if (local !== null && local !== "") {
485
- root.suspicion.ring(local);
753
+ this.ring(root, local, evidence);
486
754
  signaled = true;
487
755
  }
488
756
  else if (local === "" || within(absolute, root.directory)) {
489
- root.suspicion.suspectAll();
757
+ this.ring(root, null);
490
758
  signaled = true;
491
759
  }
492
760
  }
@@ -507,7 +775,6 @@ export class NodeWatchHost {
507
775
  return;
508
776
  }
509
777
  const hint = normalizedHint(filename);
510
- this.onEvent?.({ kind: "address", directory, path: hint, at: Date.now() });
511
778
  if (hint === null) {
512
779
  this.suspectRoots(targets);
513
780
  this.rearmAddressedContent(targets);
@@ -553,13 +820,46 @@ export class NodeWatchHost {
553
820
  closeRecord(record);
554
821
  this.content.delete(directory);
555
822
  }
556
- if (addressed)
823
+ if (addressed) {
824
+ this.loseCoverage("content directory identity changed");
557
825
  this.establish(this.plan);
826
+ }
558
827
  return suspects;
559
828
  }
560
829
  suspectRoots(rootIds) {
561
- for (const rootId of rootIds)
562
- this.plan.roots.get(rootId)?.suspicion.suspectAll();
830
+ for (const rootId of rootIds) {
831
+ const root = this.plan.roots.get(rootId);
832
+ if (root)
833
+ this.ring(root, null);
834
+ }
835
+ }
836
+ /** Every doorbell enters here; null suspects the complete root. A ring can
837
+ * only start an obligation, so examiners hear it only when none was owed. */
838
+ ring(root, local, evidence) {
839
+ if (local === null)
840
+ root.suspicion.suspectAll();
841
+ else
842
+ root.suspicion.ring(local, evidence);
843
+ if (root.oldest === null)
844
+ this.observeObligation(root);
845
+ }
846
+ observeObligation(root, bound = true) {
847
+ if (!obligations.hasSubscribers)
848
+ return;
849
+ const oldest = bound ? root.suspicion.pending().oldest : null;
850
+ if (oldest === root.oldest)
851
+ return;
852
+ root.oldest = oldest;
853
+ obligations.publish({ rootId: root.rootId, oldest, at: performance.now() });
854
+ }
855
+ loseCoverage(reason) {
856
+ const health = this.degrade(reason);
857
+ if (!this.coverageGap) {
858
+ this.coverageGap = true;
859
+ this.bindingGeneration += 1;
860
+ this.suspectDomain();
861
+ }
862
+ return health;
563
863
  }
564
864
  degrade(reason) {
565
865
  const health = {
@@ -581,6 +881,47 @@ export class NodeWatchHost {
581
881
  signal() {
582
882
  this.onSignal();
583
883
  }
884
+ /** Admit a notice now while this turn's slice lasts and nothing earlier
885
+ * waits; otherwise after every earlier notice, in a later turn. */
886
+ admit(notice) {
887
+ this.nextTurn();
888
+ if (this.backlogHead === this.backlog.length && this.sliceLeft > 0) {
889
+ this.admitNow(notice);
890
+ return;
891
+ }
892
+ this.backlog.push(notice);
893
+ }
894
+ admitNow(notice) {
895
+ if (this.sliceEnds === Infinity)
896
+ this.sliceEnds = performance.now() + WATCH_ADMISSION_BUDGET_MS;
897
+ this.sliceLeft -= 1;
898
+ notice();
899
+ if (performance.now() >= this.sliceEnds)
900
+ this.sliceLeft = 0;
901
+ }
902
+ /** Each turn renews the slice and admits the oldest waiting notices. */
903
+ nextTurn() {
904
+ if (this.turnScheduled || this.closed)
905
+ return;
906
+ this.turnScheduled = true;
907
+ setImmediate(() => {
908
+ this.turnScheduled = false;
909
+ this.sliceLeft = WATCH_ADMISSION_SLICE;
910
+ this.sliceEnds = Infinity;
911
+ while (this.sliceLeft > 0 && this.backlogHead < this.backlog.length && !this.closed) {
912
+ const notice = this.backlog[this.backlogHead];
913
+ this.backlogHead += 1;
914
+ this.admitNow(notice);
915
+ }
916
+ if (this.backlogHead < this.backlog.length) {
917
+ this.nextTurn();
918
+ }
919
+ else {
920
+ this.backlog = [];
921
+ this.backlogHead = 0;
922
+ }
923
+ });
924
+ }
584
925
  scheduleRetry() {
585
926
  if (this.closed || this.retryTimer)
586
927
  return;
@@ -614,7 +955,7 @@ function buildPlan(grounds, previous) {
614
955
  paths.add(directory);
615
956
  const excludedSubtrees = [...new Set((ground.excludedSubtrees ?? []).map((path) => resolve(path)))].sort();
616
957
  const prior = previous.get(rootId);
617
- const suspicion = prior?.suspicion ?? createSuspicionScope();
958
+ const suspicion = prior?.suspicion ?? createSuspicionScope(() => performance.now());
618
959
  if (!prior || prior.directory !== directory)
619
960
  suspicion.suspectAll();
620
961
  roots.set(rootId, {
@@ -623,7 +964,7 @@ function buildPlan(grounds, previous) {
623
964
  materialized: ground.materialized !== false,
624
965
  excludedSubtrees,
625
966
  suspicion,
626
- settledGeneration: prior?.settledGeneration ?? 0,
967
+ oldest: prior?.oldest ?? null,
627
968
  });
628
969
  }
629
970
  const exclusions = [...new Set([...roots.values()].flatMap((root) => root.excludedSubtrees))].sort();
@@ -735,13 +1076,25 @@ function normalizedHint(filename) {
735
1076
  return null;
736
1077
  return segments.join("/");
737
1078
  }
1079
+ /** A canonical path's address below a canonical root: "" for the root itself,
1080
+ * null outside it. Plan paths are resolved once when the plan is built and
1081
+ * event paths are built from normalized hints, so routing an event costs one
1082
+ * prefix comparison, never a normalization. A path that is not canonical can
1083
+ * only fail to match, which widens its ring. Windows compares names without
1084
+ * case, as its path relation does. */
738
1085
  function localPath(root, path) {
739
- const result = relative(root, path);
740
- if (result === "")
1086
+ const folded = sep === "\\";
1087
+ const base = folded ? root.toLowerCase() : root;
1088
+ const candidate = folded ? path.toLowerCase() : path;
1089
+ if (candidate === base)
741
1090
  return "";
742
- if (result === ".." || result.startsWith(`..${sep}`) || isAbsolute(result))
743
- return null;
744
- return slash(result);
1091
+ const prefix = base.endsWith(sep) ? base : base + sep;
1092
+ return candidate.startsWith(prefix) ? slash(path.slice(prefix.length)) : null;
1093
+ }
1094
+ /** One canonical address from a canonical directory and a normalized hint. */
1095
+ function below(directory, hint) {
1096
+ const native = sep === "/" ? hint : hint.replaceAll("/", sep);
1097
+ return directory.endsWith(sep) ? directory + native : directory + sep + native;
745
1098
  }
746
1099
  function within(parent, child) {
747
1100
  return localPath(parent, child) !== null;