@superbee/core 0.2.0-pre.3 → 0.2.0-pre.30

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 (534) hide show
  1. package/README.md +1082 -0
  2. package/dist/artifact-contract.d.ts +90 -0
  3. package/dist/artifact-contract.d.ts.map +1 -0
  4. package/dist/artifact-contract.js +205 -0
  5. package/dist/artifact-contract.js.map +1 -0
  6. package/dist/backend.d.ts +19 -1
  7. package/dist/backend.d.ts.map +1 -1
  8. package/dist/backend.js +83 -36
  9. package/dist/backend.js.map +1 -1
  10. package/dist/blob-contract.d.ts +80 -0
  11. package/dist/blob-contract.d.ts.map +1 -0
  12. package/dist/blob-contract.js +131 -0
  13. package/dist/blob-contract.js.map +1 -0
  14. package/dist/blob-journal.d.ts +302 -0
  15. package/dist/blob-journal.d.ts.map +1 -0
  16. package/dist/blob-journal.js +540 -0
  17. package/dist/blob-journal.js.map +1 -0
  18. package/dist/body-replace-guards.d.ts +52 -0
  19. package/dist/body-replace-guards.d.ts.map +1 -0
  20. package/dist/body-replace-guards.js +176 -0
  21. package/dist/body-replace-guards.js.map +1 -0
  22. package/dist/builtin-recipes/content.d.ts +93 -0
  23. package/dist/builtin-recipes/content.d.ts.map +1 -0
  24. package/dist/builtin-recipes/content.js +288 -0
  25. package/dist/builtin-recipes/content.js.map +1 -0
  26. package/dist/builtin-recipes.d.ts +10 -0
  27. package/dist/builtin-recipes.d.ts.map +1 -0
  28. package/dist/builtin-recipes.js +97 -0
  29. package/dist/builtin-recipes.js.map +1 -0
  30. package/dist/bundle-health/index.d.ts +14 -0
  31. package/dist/bundle-health/index.d.ts.map +1 -0
  32. package/dist/bundle-health/index.js +12 -0
  33. package/dist/bundle-health/index.js.map +1 -0
  34. package/dist/bundle-health/kind-findings.d.ts +44 -0
  35. package/dist/bundle-health/kind-findings.d.ts.map +1 -0
  36. package/dist/bundle-health/kind-findings.js +68 -0
  37. package/dist/bundle-health/kind-findings.js.map +1 -0
  38. package/dist/bundle-health/legacy-page.d.ts +39 -0
  39. package/dist/bundle-health/legacy-page.d.ts.map +1 -0
  40. package/dist/bundle-health/legacy-page.js +50 -0
  41. package/dist/bundle-health/legacy-page.js.map +1 -0
  42. package/dist/bundle-health/report.d.ts +202 -0
  43. package/dist/bundle-health/report.d.ts.map +1 -0
  44. package/dist/bundle-health/report.js +438 -0
  45. package/dist/bundle-health/report.js.map +1 -0
  46. package/dist/bundle-ops.d.ts +1 -1
  47. package/dist/bundle-ops.d.ts.map +1 -1
  48. package/dist/bundle-ops.js +2 -2
  49. package/dist/bundle-ops.js.map +1 -1
  50. package/dist/bundle.d.ts +2 -1
  51. package/dist/bundle.d.ts.map +1 -1
  52. package/dist/bundle.js +3 -3
  53. package/dist/bundle.js.map +1 -1
  54. package/dist/code-point-order.d.ts +8 -0
  55. package/dist/code-point-order.d.ts.map +1 -0
  56. package/dist/code-point-order.js +20 -0
  57. package/dist/code-point-order.js.map +1 -0
  58. package/dist/connector-request.d.ts +87 -0
  59. package/dist/connector-request.d.ts.map +1 -0
  60. package/dist/connector-request.js +370 -0
  61. package/dist/connector-request.js.map +1 -0
  62. package/dist/connectors.d.ts +262 -0
  63. package/dist/connectors.d.ts.map +1 -0
  64. package/dist/connectors.js +1179 -0
  65. package/dist/connectors.js.map +1 -0
  66. package/dist/cross-bundle.d.ts +135 -0
  67. package/dist/cross-bundle.d.ts.map +1 -0
  68. package/dist/cross-bundle.js +186 -0
  69. package/dist/cross-bundle.js.map +1 -0
  70. package/dist/dependencies.d.ts +94 -0
  71. package/dist/dependencies.d.ts.map +1 -0
  72. package/dist/dependencies.js +180 -0
  73. package/dist/dependencies.js.map +1 -0
  74. package/dist/document-codec.d.ts +5 -1
  75. package/dist/document-codec.d.ts.map +1 -1
  76. package/dist/document-codec.js +7 -4
  77. package/dist/document-codec.js.map +1 -1
  78. package/dist/document-field-actions.d.ts +31 -1
  79. package/dist/document-field-actions.d.ts.map +1 -1
  80. package/dist/document-field-actions.js +137 -34
  81. package/dist/document-field-actions.js.map +1 -1
  82. package/dist/document-mutation.d.ts +22 -10
  83. package/dist/document-mutation.d.ts.map +1 -1
  84. package/dist/document-mutation.js +61 -27
  85. package/dist/document-mutation.js.map +1 -1
  86. package/dist/document-write-policy.d.ts.map +1 -1
  87. package/dist/document-write-policy.js +4 -2
  88. package/dist/document-write-policy.js.map +1 -1
  89. package/dist/domain-model.d.ts +73 -0
  90. package/dist/domain-model.d.ts.map +1 -0
  91. package/dist/domain-model.js +169 -0
  92. package/dist/domain-model.js.map +1 -0
  93. package/dist/engine.d.ts +16 -8
  94. package/dist/engine.d.ts.map +1 -1
  95. package/dist/engine.js +42 -4
  96. package/dist/engine.js.map +1 -1
  97. package/dist/file-journaled-backend-internal.d.ts +21 -0
  98. package/dist/file-journaled-backend-internal.d.ts.map +1 -0
  99. package/dist/file-journaled-backend-internal.js +12 -0
  100. package/dist/file-journaled-backend-internal.js.map +1 -0
  101. package/dist/file-journaled-backend.d.ts +160 -0
  102. package/dist/file-journaled-backend.d.ts.map +1 -0
  103. package/dist/file-journaled-backend.js +1345 -0
  104. package/dist/file-journaled-backend.js.map +1 -0
  105. package/dist/filesystem-host.d.ts +15 -0
  106. package/dist/filesystem-host.d.ts.map +1 -0
  107. package/dist/filesystem-host.js +20 -0
  108. package/dist/filesystem-host.js.map +1 -0
  109. package/dist/filesystem-identity.d.ts +86 -4
  110. package/dist/filesystem-identity.d.ts.map +1 -1
  111. package/dist/filesystem-identity.js +338 -102
  112. package/dist/filesystem-identity.js.map +1 -1
  113. package/dist/filesystem-lock.d.ts +16 -3
  114. package/dist/filesystem-lock.d.ts.map +1 -1
  115. package/dist/filesystem-lock.js +323 -77
  116. package/dist/filesystem-lock.js.map +1 -1
  117. package/dist/filesystem-push-role.d.ts +77 -0
  118. package/dist/filesystem-push-role.d.ts.map +1 -0
  119. package/dist/filesystem-push-role.js +165 -0
  120. package/dist/filesystem-push-role.js.map +1 -0
  121. package/dist/filesystem.d.ts +17 -0
  122. package/dist/filesystem.d.ts.map +1 -0
  123. package/dist/filesystem.js +18 -0
  124. package/dist/filesystem.js.map +1 -0
  125. package/dist/governed-body-write.d.ts +106 -0
  126. package/dist/governed-body-write.d.ts.map +1 -0
  127. package/dist/governed-body-write.js +251 -0
  128. package/dist/governed-body-write.js.map +1 -0
  129. package/dist/host-policy-snapshot.d.ts +9 -0
  130. package/dist/host-policy-snapshot.d.ts.map +1 -0
  131. package/dist/host-policy-snapshot.js +33 -0
  132. package/dist/host-policy-snapshot.js.map +1 -0
  133. package/dist/host-text.d.ts +31 -0
  134. package/dist/host-text.d.ts.map +1 -0
  135. package/dist/host-text.js +77 -0
  136. package/dist/host-text.js.map +1 -0
  137. package/dist/hosted-client/binding.d.ts +7 -0
  138. package/dist/hosted-client/binding.d.ts.map +1 -0
  139. package/dist/hosted-client/binding.js +10 -0
  140. package/dist/hosted-client/binding.js.map +1 -0
  141. package/dist/hosted-client/blob-delivery.d.ts +67 -0
  142. package/dist/hosted-client/blob-delivery.d.ts.map +1 -0
  143. package/dist/hosted-client/blob-delivery.js +169 -0
  144. package/dist/hosted-client/blob-delivery.js.map +1 -0
  145. package/dist/hosted-client/blob-write.d.ts +386 -0
  146. package/dist/hosted-client/blob-write.d.ts.map +1 -0
  147. package/dist/hosted-client/blob-write.js +770 -0
  148. package/dist/hosted-client/blob-write.js.map +1 -0
  149. package/dist/hosted-client/bundle-listing.d.ts +46 -0
  150. package/dist/hosted-client/bundle-listing.d.ts.map +1 -0
  151. package/dist/hosted-client/bundle-listing.js +36 -0
  152. package/dist/hosted-client/bundle-listing.js.map +1 -0
  153. package/dist/hosted-client/client.d.ts +219 -0
  154. package/dist/hosted-client/client.d.ts.map +1 -0
  155. package/dist/hosted-client/client.js +309 -0
  156. package/dist/hosted-client/client.js.map +1 -0
  157. package/dist/hosted-client/directory.d.ts +46 -0
  158. package/dist/hosted-client/directory.d.ts.map +1 -0
  159. package/dist/hosted-client/directory.js +74 -0
  160. package/dist/hosted-client/directory.js.map +1 -0
  161. package/dist/hosted-client/errors.d.ts +26 -0
  162. package/dist/hosted-client/errors.d.ts.map +1 -0
  163. package/dist/hosted-client/errors.js +35 -0
  164. package/dist/hosted-client/errors.js.map +1 -0
  165. package/dist/hosted-client/inbox.d.ts +320 -0
  166. package/dist/hosted-client/inbox.d.ts.map +1 -0
  167. package/dist/hosted-client/inbox.js +379 -0
  168. package/dist/hosted-client/inbox.js.map +1 -0
  169. package/dist/hosted-client/index.d.ts +30 -0
  170. package/dist/hosted-client/index.d.ts.map +1 -0
  171. package/dist/hosted-client/index.js +32 -0
  172. package/dist/hosted-client/index.js.map +1 -0
  173. package/dist/hosted-client/journal.d.ts +103 -0
  174. package/dist/hosted-client/journal.d.ts.map +1 -0
  175. package/dist/hosted-client/journal.js +464 -0
  176. package/dist/hosted-client/journal.js.map +1 -0
  177. package/dist/hosted-client/node.d.ts +74 -0
  178. package/dist/hosted-client/node.d.ts.map +1 -0
  179. package/dist/hosted-client/node.js +219 -0
  180. package/dist/hosted-client/node.js.map +1 -0
  181. package/dist/hosted-client/reference.d.ts +12 -0
  182. package/dist/hosted-client/reference.d.ts.map +1 -0
  183. package/dist/hosted-client/reference.js +29 -0
  184. package/dist/hosted-client/reference.js.map +1 -0
  185. package/dist/hosted-client/write.d.ts +217 -0
  186. package/dist/hosted-client/write.d.ts.map +1 -0
  187. package/dist/hosted-client/write.js +418 -0
  188. package/dist/hosted-client/write.js.map +1 -0
  189. package/dist/hosted-transport/answer-rows.d.ts +243 -0
  190. package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
  191. package/dist/hosted-transport/answer-rows.js +454 -0
  192. package/dist/hosted-transport/answer-rows.js.map +1 -0
  193. package/dist/hosted-transport/blob-reads.d.ts +35 -0
  194. package/dist/hosted-transport/blob-reads.d.ts.map +1 -0
  195. package/dist/hosted-transport/blob-reads.js +179 -0
  196. package/dist/hosted-transport/blob-reads.js.map +1 -0
  197. package/dist/hosted-transport/blob-write.d.ts +34 -0
  198. package/dist/hosted-transport/blob-write.d.ts.map +1 -0
  199. package/dist/hosted-transport/blob-write.js +115 -0
  200. package/dist/hosted-transport/blob-write.js.map +1 -0
  201. package/dist/hosted-transport/carrier.d.ts +151 -0
  202. package/dist/hosted-transport/carrier.d.ts.map +1 -0
  203. package/dist/hosted-transport/carrier.js +260 -0
  204. package/dist/hosted-transport/carrier.js.map +1 -0
  205. package/dist/hosted-transport/history-pages.d.ts +55 -0
  206. package/dist/hosted-transport/history-pages.d.ts.map +1 -0
  207. package/dist/hosted-transport/history-pages.js +69 -0
  208. package/dist/hosted-transport/history-pages.js.map +1 -0
  209. package/dist/hosted-transport/index.d.ts +18 -0
  210. package/dist/hosted-transport/index.d.ts.map +1 -0
  211. package/dist/hosted-transport/index.js +18 -0
  212. package/dist/hosted-transport/index.js.map +1 -0
  213. package/dist/hosted-transport/operations.d.ts +85 -0
  214. package/dist/hosted-transport/operations.d.ts.map +1 -0
  215. package/dist/hosted-transport/operations.js +128 -0
  216. package/dist/hosted-transport/operations.js.map +1 -0
  217. package/dist/hosted-transport/paged-reads.d.ts +87 -0
  218. package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
  219. package/dist/hosted-transport/paged-reads.js +238 -0
  220. package/dist/hosted-transport/paged-reads.js.map +1 -0
  221. package/dist/hosted-transport/read-adapter.d.ts +256 -0
  222. package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
  223. package/dist/hosted-transport/read-adapter.js +568 -0
  224. package/dist/hosted-transport/read-adapter.js.map +1 -0
  225. package/dist/hosted-transport/root-write.d.ts +114 -0
  226. package/dist/hosted-transport/root-write.d.ts.map +1 -0
  227. package/dist/hosted-transport/root-write.js +171 -0
  228. package/dist/hosted-transport/root-write.js.map +1 -0
  229. package/dist/hosted-transport/whole-document-transport.d.ts +204 -0
  230. package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
  231. package/dist/hosted-transport/whole-document-transport.js +447 -0
  232. package/dist/hosted-transport/whole-document-transport.js.map +1 -0
  233. package/dist/index.d.ts +34 -10
  234. package/dist/index.d.ts.map +1 -1
  235. package/dist/index.js +26 -8
  236. package/dist/index.js.map +1 -1
  237. package/dist/indexeddb-backend.d.ts +28 -37
  238. package/dist/indexeddb-backend.d.ts.map +1 -1
  239. package/dist/indexeddb-backend.js +467 -113
  240. package/dist/indexeddb-backend.js.map +1 -1
  241. package/dist/journaled-backend.d.ts +232 -15
  242. package/dist/journaled-backend.d.ts.map +1 -1
  243. package/dist/journaled-backend.js +254 -55
  244. package/dist/journaled-backend.js.map +1 -1
  245. package/dist/kind-authoring.d.ts +170 -0
  246. package/dist/kind-authoring.d.ts.map +1 -0
  247. package/dist/kind-authoring.js +451 -0
  248. package/dist/kind-authoring.js.map +1 -0
  249. package/dist/kind-conformance-error.d.ts +15 -0
  250. package/dist/kind-conformance-error.d.ts.map +1 -0
  251. package/dist/kind-conformance-error.js +20 -0
  252. package/dist/kind-conformance-error.js.map +1 -0
  253. package/dist/kind-coverage.d.ts +99 -0
  254. package/dist/kind-coverage.d.ts.map +1 -0
  255. package/dist/kind-coverage.js +186 -0
  256. package/dist/kind-coverage.js.map +1 -0
  257. package/dist/kind-field-mutation.d.ts +4 -0
  258. package/dist/kind-field-mutation.d.ts.map +1 -1
  259. package/dist/kind-field-mutation.js +28 -3
  260. package/dist/kind-field-mutation.js.map +1 -1
  261. package/dist/kinds-load.js +4 -4
  262. package/dist/kinds-load.js.map +1 -1
  263. package/dist/kinds.d.ts +65 -2
  264. package/dist/kinds.d.ts.map +1 -1
  265. package/dist/kinds.js +341 -21
  266. package/dist/kinds.js.map +1 -1
  267. package/dist/link-types.d.ts +26 -0
  268. package/dist/link-types.d.ts.map +1 -0
  269. package/dist/link-types.js +57 -0
  270. package/dist/link-types.js.map +1 -0
  271. package/dist/links.d.ts +21 -3
  272. package/dist/links.d.ts.map +1 -1
  273. package/dist/links.js +213 -14
  274. package/dist/links.js.map +1 -1
  275. package/dist/managed-document-metadata.d.ts +2 -0
  276. package/dist/managed-document-metadata.d.ts.map +1 -0
  277. package/dist/managed-document-metadata.js +9 -0
  278. package/dist/managed-document-metadata.js.map +1 -0
  279. package/dist/memory-backend.d.ts.map +1 -1
  280. package/dist/memory-backend.js +6 -2
  281. package/dist/memory-backend.js.map +1 -1
  282. package/dist/paths.d.ts +16 -0
  283. package/dist/paths.d.ts.map +1 -1
  284. package/dist/paths.js +25 -0
  285. package/dist/paths.js.map +1 -1
  286. package/dist/platform.d.ts +13 -2
  287. package/dist/platform.d.ts.map +1 -1
  288. package/dist/platform.js +16 -0
  289. package/dist/platform.js.map +1 -1
  290. package/dist/portable-export.d.ts +119 -0
  291. package/dist/portable-export.d.ts.map +1 -0
  292. package/dist/portable-export.js +501 -0
  293. package/dist/portable-export.js.map +1 -0
  294. package/dist/publication-filesystem.d.ts +1 -1
  295. package/dist/publication-filesystem.d.ts.map +1 -1
  296. package/dist/publication-filesystem.js +1 -1
  297. package/dist/publication-filesystem.js.map +1 -1
  298. package/dist/qualified-links.d.ts +67 -0
  299. package/dist/qualified-links.d.ts.map +1 -0
  300. package/dist/qualified-links.js +117 -0
  301. package/dist/qualified-links.js.map +1 -0
  302. package/dist/query-filter.d.ts +16 -2
  303. package/dist/query-filter.d.ts.map +1 -1
  304. package/dist/query-filter.js +40 -1
  305. package/dist/query-filter.js.map +1 -1
  306. package/dist/query-order.d.ts +22 -0
  307. package/dist/query-order.d.ts.map +1 -0
  308. package/dist/query-order.js +42 -0
  309. package/dist/query-order.js.map +1 -0
  310. package/dist/recipe-lifecycle/errors.d.ts +96 -0
  311. package/dist/recipe-lifecycle/errors.d.ts.map +1 -0
  312. package/dist/recipe-lifecycle/errors.js +122 -0
  313. package/dist/recipe-lifecycle/errors.js.map +1 -0
  314. package/dist/recipe-lifecycle/evolution.d.ts +112 -0
  315. package/dist/recipe-lifecycle/evolution.d.ts.map +1 -0
  316. package/dist/recipe-lifecycle/evolution.js +941 -0
  317. package/dist/recipe-lifecycle/evolution.js.map +1 -0
  318. package/dist/recipe-lifecycle/index.d.ts +30 -0
  319. package/dist/recipe-lifecycle/index.d.ts.map +1 -0
  320. package/dist/recipe-lifecycle/index.js +26 -0
  321. package/dist/recipe-lifecycle/index.js.map +1 -0
  322. package/dist/recipe-lifecycle/install.d.ts +155 -0
  323. package/dist/recipe-lifecycle/install.d.ts.map +1 -0
  324. package/dist/recipe-lifecycle/install.js +594 -0
  325. package/dist/recipe-lifecycle/install.js.map +1 -0
  326. package/dist/recipe-lifecycle/legacy-aliases.d.ts +16 -0
  327. package/dist/recipe-lifecycle/legacy-aliases.d.ts.map +1 -0
  328. package/dist/recipe-lifecycle/legacy-aliases.js +51 -0
  329. package/dist/recipe-lifecycle/legacy-aliases.js.map +1 -0
  330. package/dist/recipe-lifecycle/stamp.d.ts +151 -0
  331. package/dist/recipe-lifecycle/stamp.d.ts.map +1 -0
  332. package/dist/recipe-lifecycle/stamp.js +364 -0
  333. package/dist/recipe-lifecycle/stamp.js.map +1 -0
  334. package/dist/recipes.d.ts +29 -1
  335. package/dist/recipes.d.ts.map +1 -1
  336. package/dist/recipes.js +137 -1
  337. package/dist/recipes.js.map +1 -1
  338. package/dist/record-overlay/apply.d.ts +43 -0
  339. package/dist/record-overlay/apply.d.ts.map +1 -0
  340. package/dist/record-overlay/apply.js +527 -0
  341. package/dist/record-overlay/apply.js.map +1 -0
  342. package/dist/record-overlay/canonical.d.ts +22 -0
  343. package/dist/record-overlay/canonical.d.ts.map +1 -0
  344. package/dist/record-overlay/canonical.js +47 -0
  345. package/dist/record-overlay/canonical.js.map +1 -0
  346. package/dist/record-overlay/digest.d.ts +4 -0
  347. package/dist/record-overlay/digest.d.ts.map +1 -0
  348. package/dist/record-overlay/digest.js +11 -0
  349. package/dist/record-overlay/digest.js.map +1 -0
  350. package/dist/record-overlay/errors.d.ts +44 -0
  351. package/dist/record-overlay/errors.d.ts.map +1 -0
  352. package/dist/record-overlay/errors.js +55 -0
  353. package/dist/record-overlay/errors.js.map +1 -0
  354. package/dist/record-overlay/findings.d.ts +5 -0
  355. package/dist/record-overlay/findings.d.ts.map +1 -0
  356. package/dist/record-overlay/findings.js +83 -0
  357. package/dist/record-overlay/findings.js.map +1 -0
  358. package/dist/record-overlay/index.d.ts +23 -0
  359. package/dist/record-overlay/index.d.ts.map +1 -0
  360. package/dist/record-overlay/index.js +21 -0
  361. package/dist/record-overlay/index.js.map +1 -0
  362. package/dist/record-overlay/plan.d.ts +12 -0
  363. package/dist/record-overlay/plan.d.ts.map +1 -0
  364. package/dist/record-overlay/plan.js +285 -0
  365. package/dist/record-overlay/plan.js.map +1 -0
  366. package/dist/record-overlay/policy.d.ts +69 -0
  367. package/dist/record-overlay/policy.d.ts.map +1 -0
  368. package/dist/record-overlay/policy.js +228 -0
  369. package/dist/record-overlay/policy.js.map +1 -0
  370. package/dist/record-overlay/redact.d.ts +3 -0
  371. package/dist/record-overlay/redact.d.ts.map +1 -0
  372. package/dist/record-overlay/redact.js +27 -0
  373. package/dist/record-overlay/redact.js.map +1 -0
  374. package/dist/record-overlay/signal-kind.d.ts +30 -0
  375. package/dist/record-overlay/signal-kind.d.ts.map +1 -0
  376. package/dist/record-overlay/signal-kind.js +38 -0
  377. package/dist/record-overlay/signal-kind.js.map +1 -0
  378. package/dist/record-overlay/signal-text.d.ts +45 -0
  379. package/dist/record-overlay/signal-text.d.ts.map +1 -0
  380. package/dist/record-overlay/signal-text.js +116 -0
  381. package/dist/record-overlay/signal-text.js.map +1 -0
  382. package/dist/record-overlay/source-index.d.ts +19 -0
  383. package/dist/record-overlay/source-index.d.ts.map +1 -0
  384. package/dist/record-overlay/source-index.js +102 -0
  385. package/dist/record-overlay/source-index.js.map +1 -0
  386. package/dist/record-overlay/types.d.ts +291 -0
  387. package/dist/record-overlay/types.d.ts.map +1 -0
  388. package/dist/record-overlay/types.js +2 -0
  389. package/dist/record-overlay/types.js.map +1 -0
  390. package/dist/remote-backend.d.ts +67 -67
  391. package/dist/remote-backend.d.ts.map +1 -1
  392. package/dist/remote-backend.js +168 -201
  393. package/dist/remote-backend.js.map +1 -1
  394. package/dist/remote-document-codec.d.ts +2 -1
  395. package/dist/remote-document-codec.d.ts.map +1 -1
  396. package/dist/remote-document-codec.js +3 -2
  397. package/dist/remote-document-codec.js.map +1 -1
  398. package/dist/remote-error.d.ts +38 -0
  399. package/dist/remote-error.d.ts.map +1 -0
  400. package/dist/remote-error.js +59 -0
  401. package/dist/remote-error.js.map +1 -0
  402. package/dist/remote-operations.d.ts +0 -40
  403. package/dist/remote-operations.d.ts.map +1 -1
  404. package/dist/remote-operations.js +26 -6
  405. package/dist/remote-operations.js.map +1 -1
  406. package/dist/remote-parsers.d.ts +86 -0
  407. package/dist/remote-parsers.d.ts.map +1 -0
  408. package/dist/remote-parsers.js +181 -0
  409. package/dist/remote-parsers.js.map +1 -0
  410. package/dist/remote.d.ts +5 -2
  411. package/dist/remote.d.ts.map +1 -1
  412. package/dist/remote.js +5 -1
  413. package/dist/remote.js.map +1 -1
  414. package/dist/search/backend-source.d.ts +6 -0
  415. package/dist/search/backend-source.d.ts.map +1 -0
  416. package/dist/search/backend-source.js +33 -0
  417. package/dist/search/backend-source.js.map +1 -0
  418. package/dist/search/contract.d.ts +151 -0
  419. package/dist/search/contract.d.ts.map +1 -0
  420. package/dist/search/contract.js +78 -0
  421. package/dist/search/contract.js.map +1 -0
  422. package/dist/search/errors.d.ts +30 -0
  423. package/dist/search/errors.d.ts.map +1 -0
  424. package/dist/search/errors.js +50 -0
  425. package/dist/search/errors.js.map +1 -0
  426. package/dist/search/identity.d.ts +12 -0
  427. package/dist/search/identity.d.ts.map +1 -0
  428. package/dist/search/identity.js +90 -0
  429. package/dist/search/identity.js.map +1 -0
  430. package/dist/search/index.d.ts +16 -0
  431. package/dist/search/index.d.ts.map +1 -0
  432. package/dist/search/index.js +15 -0
  433. package/dist/search/index.js.map +1 -0
  434. package/dist/search/memory-source.d.ts +36 -0
  435. package/dist/search/memory-source.d.ts.map +1 -0
  436. package/dist/search/memory-source.js +235 -0
  437. package/dist/search/memory-source.js.map +1 -0
  438. package/dist/search/rank.d.ts +34 -0
  439. package/dist/search/rank.d.ts.map +1 -0
  440. package/dist/search/rank.js +129 -0
  441. package/dist/search/rank.js.map +1 -0
  442. package/dist/search/row-text.d.ts +38 -0
  443. package/dist/search/row-text.d.ts.map +1 -0
  444. package/dist/search/row-text.js +88 -0
  445. package/dist/search/row-text.js.map +1 -0
  446. package/dist/search/search.d.ts +28 -0
  447. package/dist/search/search.d.ts.map +1 -0
  448. package/dist/search/search.js +257 -0
  449. package/dist/search/search.js.map +1 -0
  450. package/dist/search/source.d.ts +67 -0
  451. package/dist/search/source.d.ts.map +1 -0
  452. package/dist/search/source.js +2 -0
  453. package/dist/search/source.js.map +1 -0
  454. package/dist/search/text.d.ts +47 -0
  455. package/dist/search/text.d.ts.map +1 -0
  456. package/dist/search/text.js +150 -0
  457. package/dist/search/text.js.map +1 -0
  458. package/dist/site-capabilities.d.ts +345 -0
  459. package/dist/site-capabilities.d.ts.map +1 -0
  460. package/dist/site-capabilities.js +724 -0
  461. package/dist/site-capabilities.js.map +1 -0
  462. package/dist/source-engine/guard.d.ts +78 -0
  463. package/dist/source-engine/guard.d.ts.map +1 -0
  464. package/dist/source-engine/guard.js +423 -0
  465. package/dist/source-engine/guard.js.map +1 -0
  466. package/dist/source-engine/index.d.ts +26 -0
  467. package/dist/source-engine/index.d.ts.map +1 -0
  468. package/dist/source-engine/index.js +21 -0
  469. package/dist/source-engine/index.js.map +1 -0
  470. package/dist/source-engine/machine-state.d.ts +74 -0
  471. package/dist/source-engine/machine-state.d.ts.map +1 -0
  472. package/dist/source-engine/machine-state.js +48 -0
  473. package/dist/source-engine/machine-state.js.map +1 -0
  474. package/dist/source-engine/observe.d.ts +18 -0
  475. package/dist/source-engine/observe.d.ts.map +1 -0
  476. package/dist/source-engine/observe.js +620 -0
  477. package/dist/source-engine/observe.js.map +1 -0
  478. package/dist/source-engine/overlay.d.ts +49 -0
  479. package/dist/source-engine/overlay.d.ts.map +1 -0
  480. package/dist/source-engine/overlay.js +114 -0
  481. package/dist/source-engine/overlay.js.map +1 -0
  482. package/dist/source-engine/robots.d.ts +53 -0
  483. package/dist/source-engine/robots.d.ts.map +1 -0
  484. package/dist/source-engine/robots.js +158 -0
  485. package/dist/source-engine/robots.js.map +1 -0
  486. package/dist/source-engine/types.d.ts +287 -0
  487. package/dist/source-engine/types.d.ts.map +1 -0
  488. package/dist/source-engine/types.js +2 -0
  489. package/dist/source-engine/types.js.map +1 -0
  490. package/dist/source-harness/conformance.d.ts +16 -0
  491. package/dist/source-harness/conformance.d.ts.map +1 -0
  492. package/dist/source-harness/conformance.js +266 -0
  493. package/dist/source-harness/conformance.js.map +1 -0
  494. package/dist/source-harness/fixture.d.ts +95 -0
  495. package/dist/source-harness/fixture.d.ts.map +1 -0
  496. package/dist/source-harness/fixture.js +273 -0
  497. package/dist/source-harness/fixture.js.map +1 -0
  498. package/dist/source-harness/index.d.ts +22 -0
  499. package/dist/source-harness/index.d.ts.map +1 -0
  500. package/dist/source-harness/index.js +19 -0
  501. package/dist/source-harness/index.js.map +1 -0
  502. package/dist/source-harness/peer.d.ts +74 -0
  503. package/dist/source-harness/peer.d.ts.map +1 -0
  504. package/dist/source-harness/peer.js +106 -0
  505. package/dist/source-harness/peer.js.map +1 -0
  506. package/dist/source-profiles/index.d.ts +31 -0
  507. package/dist/source-profiles/index.d.ts.map +1 -0
  508. package/dist/source-profiles/index.js +206 -0
  509. package/dist/source-profiles/index.js.map +1 -0
  510. package/dist/source-profiles/link-header.d.ts +6 -0
  511. package/dist/source-profiles/link-header.d.ts.map +1 -0
  512. package/dist/source-profiles/link-header.js +131 -0
  513. package/dist/source-profiles/link-header.js.map +1 -0
  514. package/dist/storage.d.ts +6 -2
  515. package/dist/storage.d.ts.map +1 -1
  516. package/dist/storage.js +4 -1
  517. package/dist/storage.js.map +1 -1
  518. package/dist/types.d.ts +38 -5
  519. package/dist/types.d.ts.map +1 -1
  520. package/dist/types.js +0 -10
  521. package/dist/types.js.map +1 -1
  522. package/dist/uncertain-write.d.ts +161 -19
  523. package/dist/uncertain-write.d.ts.map +1 -1
  524. package/dist/uncertain-write.js +90 -31
  525. package/dist/uncertain-write.js.map +1 -1
  526. package/dist/versioning.d.ts +9 -4
  527. package/dist/versioning.d.ts.map +1 -1
  528. package/dist/versioning.js +9 -4
  529. package/dist/versioning.js.map +1 -1
  530. package/dist/write-identity.d.ts +5 -0
  531. package/dist/write-identity.d.ts.map +1 -0
  532. package/dist/write-identity.js +9 -0
  533. package/dist/write-identity.js.map +1 -0
  534. package/package.json +103 -2
@@ -1,5 +1,6 @@
1
1
  import { promises as fs, realpathSync } from "node:fs";
2
- import { homedir, hostname, userInfo } from "node:os";
2
+ import { homedir, hostname } from "node:os";
3
+ import { captureFilesystemHostPolicy } from "./filesystem-host.js";
3
4
  import path from "node:path";
4
5
  import { createHash, randomUUID } from "node:crypto";
5
6
  const OWNER_FILE = "owner.json";
@@ -19,19 +20,6 @@ export class FilesystemMutationLockError extends Error {
19
20
  this.malformed = details.malformed;
20
21
  }
21
22
  }
22
- function runtimeOwnerKey() {
23
- const uid = process.getuid?.();
24
- if (uid !== undefined)
25
- return `uid-${uid}`;
26
- let username = "unknown";
27
- try {
28
- username = userInfo().username;
29
- }
30
- catch {
31
- // Windows temp directories are already user-scoped; this is only a stable path segment.
32
- }
33
- return `user-${createHash("sha256").update(username).digest("hex").slice(0, 16)}`;
34
- }
35
23
  function canonicalExistingPath(value) {
36
24
  try {
37
25
  return realpathSync(value);
@@ -45,17 +33,13 @@ function pathContains(root, candidate) {
45
33
  return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
46
34
  }
47
35
  /** Stable per-user runtime namespace outside `portableRoot`; refuses an impossible root bundle. */
48
- export function filesystemMutationLockRoot(portableRoot) {
49
- // POSIX TMPDIR is process/session-scoped (notably shell vs launchd on macOS). Use the
50
- // system-wide sticky directory so every same-user writer derives one lock namespace. Windows
51
- // temp variables are process-scoped too, so use the per-user LocalAppData known folder instead
52
- // of tmpdir(); otherwise two shells can derive different locks for the same portable target.
53
- const runtimeParent = process.platform === "win32"
54
- ? path.join(homedir(), "AppData", "Local")
55
- : "/tmp";
36
+ export function filesystemMutationLockRoot(portableRoot, hostPolicy) {
37
+ const policy = captureFilesystemHostPolicy(hostPolicy);
38
+ // A stable host parent keeps different process/session temp settings in one namespace.
39
+ const runtimeParent = policy.runtimeLockParent();
56
40
  const tempParent = canonicalExistingPath(runtimeParent);
57
41
  const homeParent = canonicalExistingPath(homedir());
58
- const ownerKey = runtimeOwnerKey();
42
+ const ownerKey = policy.runtimeOwnerKey();
59
43
  const candidates = [
60
44
  path.join(tempParent, `agentstate-lite-mutation-locks-${ownerKey}`),
61
45
  path.join(homeParent, ".agentstate", `mutation-locks-${ownerKey}`),
@@ -78,8 +62,8 @@ function explicitFilesystemMutationLockRoot(root, portableRoot) {
78
62
  throw new FilesystemMutationLockError(`cannot place filesystem mutation locks outside portable root '${portable}'`, { lockPath: portable, owner: null, stale: false, malformed: true });
79
63
  }
80
64
  /** Runtime lock directory for one already-canonical physical target. */
81
- export function filesystemMutationLockPath(target, portableRoot) {
82
- return filesystemMutationLockPathInRoot(target, filesystemMutationLockRoot(portableRoot));
65
+ export function filesystemMutationLockPath(target, portableRoot, hostPolicy) {
66
+ return filesystemMutationLockPathInRoot(target, filesystemMutationLockRoot(portableRoot, hostPolicy));
83
67
  }
84
68
  function filesystemMutationLockPathInRoot(target, lockRoot) {
85
69
  const digest = createHash("sha256").update(path.resolve(target)).digest("hex");
@@ -113,14 +97,44 @@ export function parseFilesystemMutationLockOwner(value) {
113
97
  target: value.target,
114
98
  };
115
99
  }
116
- async function readOwner(lockPath) {
100
+ /** A missing path or malformed content is `absent`; every other read failure is `unreadable`. */
101
+ async function readOwnerRecord(lockPath) {
102
+ let raw;
103
+ try {
104
+ raw = await fs.readFile(path.join(lockPath, OWNER_FILE), "utf8");
105
+ }
106
+ catch (err) {
107
+ const code = err.code;
108
+ return code === "ENOENT" || code === "ENOTDIR" ? { state: "absent" } : { state: "unreadable", error: err };
109
+ }
110
+ let owner = null;
117
111
  try {
118
- return parseFilesystemMutationLockOwner(JSON.parse(await fs.readFile(path.join(lockPath, OWNER_FILE), "utf8")));
112
+ owner = parseFilesystemMutationLockOwner(JSON.parse(raw));
119
113
  }
120
114
  catch {
121
- return null;
115
+ owner = null;
116
+ }
117
+ return owner === null ? { state: "absent" } : { state: "record", owner };
118
+ }
119
+ /**
120
+ * Resolve the owner record to a definitive answer, polling out an indeterminate read inside the
121
+ * caller's remaining budget. `EMFILE`/`ENFILE` under concurrency, and the `EBUSY`/`EACCES`/`EPERM`
122
+ * open transients that Windows indexing and antivirus software produce, otherwise read as "no
123
+ * record" and let a caller conclude that a lock it still owns has changed hands. An unreadable
124
+ * record never authorizes the destructive step: the caller fails closed when the budget expires.
125
+ */
126
+ async function resolveOwnerRecord(lockPath, started, waitMs, pollMs) {
127
+ while (true) {
128
+ const record = await readOwnerRecord(lockPath);
129
+ if (record.state !== "unreadable" || Date.now() - started >= waitMs)
130
+ return record;
131
+ await delay(pollMs);
122
132
  }
123
133
  }
134
+ async function readOwner(lockPath) {
135
+ const record = await readOwnerRecord(lockPath);
136
+ return record.state === "record" ? record.owner : null;
137
+ }
124
138
  function processExists(pid) {
125
139
  try {
126
140
  process.kill(pid, 0);
@@ -130,6 +144,27 @@ function processExists(pid) {
130
144
  return err.code !== "ESRCH";
131
145
  }
132
146
  }
147
+ /**
148
+ * Tokens of the claims held in this process, from just before each owner record is written until
149
+ * its release. The set lives on the global object so every copy of this module one process loads
150
+ * (each published bundle carries its own) shares it: the start-time test below cannot protect
151
+ * such a copy's claims, because a suspend or a forward clock step moves the start it computes past
152
+ * them.
153
+ */
154
+ const HELD_TOKENS = Symbol.for("superbee.filesystem-lock.held-tokens");
155
+ const heldTokens = (globalThis[HELD_TOKENS] ??= new Set());
156
+ /**
157
+ * Whether a same-host owner is demonstrably gone. `kill(pid, 0)` answers that for another process,
158
+ * but for this process's own id it always answers "alive", so a record left by an earlier process
159
+ * that had this id (a container entry point that is PID 1 again after a restart) would hold its
160
+ * lock forever. Such a record is gone only when no claim in this process carries its token and it
161
+ * was written before this process started.
162
+ */
163
+ function ownerIsGone(owner) {
164
+ if (owner.pid !== process.pid)
165
+ return !processExists(owner.pid);
166
+ return !heldTokens.has(owner.token) && owner.created_at_ms < Date.now() - process.uptime() * 1000;
167
+ }
133
168
  function staleLockQuarantinePath(lockPath, owner) {
134
169
  const tokenHash = createHash("sha256").update(owner.token).digest("hex");
135
170
  return `${lockPath}.stale-${tokenHash}`;
@@ -148,10 +183,15 @@ async function pathExists(candidate) {
148
183
  /**
149
184
  * Move one demonstrably dead same-host lock aside without deleting its evidence. The destination
150
185
  * is stable for the dead owner's token and remains non-empty, so a delayed competing reclaimer
151
- * cannot rename a replacement live lock over it on either POSIX or Windows.
186
+ * cannot rename a replacement live lock over it across supported filesystems.
152
187
  */
153
- async function quarantineStaleLock(lockPath, owner) {
154
- if (owner.hostname !== hostname() || processExists(owner.pid))
188
+ async function quarantineStaleLock(lockPath, owner, policy) {
189
+ if (owner.hostname !== hostname() || !ownerIsGone(owner))
190
+ return false;
191
+ // Death was established for this owner's snapshot. If the path no longer carries that owner's
192
+ // record, the lock changed hands after the snapshot and the rename would move a live lock.
193
+ const current = await readOwner(lockPath);
194
+ if (current === null || current.token !== owner.token)
155
195
  return false;
156
196
  const quarantinePath = staleLockQuarantinePath(lockPath, owner);
157
197
  try {
@@ -164,7 +204,7 @@ async function quarantineStaleLock(lockPath, owner) {
164
204
  return false;
165
205
  if (await pathExists(quarantinePath))
166
206
  return false;
167
- if (process.platform === "win32" && WINDOWS_DIRECTORY_CONTENTION_CODES.has(code ?? ""))
207
+ if (policy.isDirectoryContentionError(err))
168
208
  return false;
169
209
  throw err;
170
210
  }
@@ -185,7 +225,7 @@ export function isPrivateFilesystemMutationLockRoot(facts) {
185
225
  const unsafeMode = facts.enforcePrivateMode && (facts.mode & 0o777) !== 0o700;
186
226
  return facts.directory && !facts.symbolicLink && !wrongOwner && !unsafeMode;
187
227
  }
188
- async function ensurePrivateLockRoot(root) {
228
+ async function ensurePrivateLockRoot(root, policy) {
189
229
  try {
190
230
  await fs.mkdir(root, { recursive: true, mode: 0o700 });
191
231
  }
@@ -201,7 +241,7 @@ async function ensurePrivateLockRoot(root) {
201
241
  ownerUid: stat.uid,
202
242
  expectedUid: uid,
203
243
  mode: stat.mode,
204
- enforcePrivateMode: process.platform !== "win32",
244
+ enforcePrivateMode: policy.enforcePrivateMode,
205
245
  })) {
206
246
  throw new FilesystemMutationLockError(`refusing unsafe filesystem mutation lock root '${root}'; it must be a private directory owned by this user`, { lockPath: root, owner: null, stale: false, malformed: true });
207
247
  }
@@ -231,7 +271,7 @@ async function canonicalTargetInDirectory(directory, requestedBasename) {
231
271
  candidateStat = await fs.lstat(candidate);
232
272
  }
233
273
  catch {
234
- // A sibling may disappear during the scan, or Windows may deny metadata for a protected
274
+ // A sibling may disappear during the scan, or the host may deny metadata for a protected
235
275
  // system entry in an otherwise-readable directory. Neither sibling can be the successfully
236
276
  // witnessed requested entry, so it contributes no alias evidence and must not block locking.
237
277
  continue;
@@ -250,10 +290,24 @@ async function canonicalTargetInDirectory(directory, requestedBasename) {
250
290
  * a lock reached through its own fold key holds the same identity by construction, but nothing
251
291
  * here re-derives that from an arbitrary `owner.json`, so it must not assert the equivalence.
252
292
  */
253
- function timeoutError(lockPath, owner, guarded) {
293
+ async function timeoutError(lockPath, snapshot, guarded) {
294
+ // A dead-owner answer is only about the snapshot; report it as stale only while the path still
295
+ // carries that owner's record. Otherwise the lock was released and possibly claimed since, so
296
+ // diagnose the record there now, never the dead snapshot as its holder: a caller that checks the
297
+ // named PID (the push role's PID-reuse check, or a person running `ps`) would otherwise judge a
298
+ // live replacement by a process that no longer holds anything.
299
+ let diagnosed = snapshot;
300
+ let stale = false;
301
+ if (snapshot !== null && snapshot.hostname === hostname() && ownerIsGone(snapshot)) {
302
+ const current = await readOwner(lockPath);
303
+ if (current?.token === snapshot.token)
304
+ stale = true;
305
+ else
306
+ diagnosed = current;
307
+ }
308
+ const owner = diagnosed;
254
309
  const malformed = owner === null;
255
310
  const sameHost = owner?.hostname === hostname();
256
- const stale = owner !== null && sameHost && !processExists(owner.pid);
257
311
  let message;
258
312
  if (malformed) {
259
313
  message =
@@ -265,9 +319,21 @@ function timeoutError(lockPath, owner, guarded) {
265
319
  `stale filesystem mutation lock '${lockPath}' belongs to absent PID ${owner.pid} on ${owner.hostname}. ` +
266
320
  `Inspect and remove the lock, then retry.`;
267
321
  }
322
+ else if (!sameHost) {
323
+ // Another host's process ids cannot be probed from here, and a holder that crashed before this
324
+ // host was renamed looks exactly like a live holder elsewhere, so no retry can clear it.
325
+ message =
326
+ `filesystem mutation lock '${lockPath}' is held by PID ${owner.pid} on ${owner.hostname}, which is not this host (${hostname()}); ` +
327
+ `whether its holder is alive cannot be checked from here, so the lock is never reclaimed automatically. ` +
328
+ `A person must check it: once no process on ${owner.hostname} is mutating the target (this host may have been renamed since the lock was taken), remove the lock, then retry.`;
329
+ }
268
330
  else {
331
+ // A live process id is not proof of the holder: the holder may have exited and its id been
332
+ // reused, which this module cannot tell apart from a long mutation.
269
333
  message =
270
- `timed out waiting for filesystem mutation lock '${lockPath}' held by PID ${owner.pid} on ${owner.hostname}; retry the mutation.`;
334
+ `timed out waiting for filesystem mutation lock '${lockPath}' held by PID ${owner.pid} on ${owner.hostname}; retry the mutation. ` +
335
+ `If it stays held, PID ${owner.pid} may no longer be the process that claimed it, because a process id is reused after its process exits: ` +
336
+ `remove the lock only after confirming no process is mutating the target.`;
271
337
  }
272
338
  const heldFor = owner !== null && owner.target !== guarded
273
339
  ? ` Its holder recorded '${owner.target}' for the same lock key.`
@@ -280,22 +346,21 @@ async function resolvedPortableRoot(portableRoot) {
280
346
  return undefined;
281
347
  return fs.realpath(portableRoot).catch(() => path.resolve(portableRoot));
282
348
  }
283
- async function selectLockRoot(options) {
349
+ async function selectLockRoot(options, policy) {
284
350
  const portableRoot = await resolvedPortableRoot(options.portableRoot);
285
351
  const lockRoot = options.lockRoot !== undefined
286
352
  ? explicitFilesystemMutationLockRoot(options.lockRoot, portableRoot)
287
- : filesystemMutationLockRoot(portableRoot);
288
- await ensurePrivateLockRoot(lockRoot);
353
+ : filesystemMutationLockRoot(portableRoot, policy);
354
+ await ensurePrivateLockRoot(lockRoot, policy);
289
355
  return lockRoot;
290
356
  }
291
- const WINDOWS_DIRECTORY_CONTENTION_CODES = new Set(["EACCES", "EBUSY", "EPERM"]);
292
- async function classifyLockClaimFailure(error, lockPath) {
357
+ async function classifyLockClaimFailure(error, lockPath, policy) {
293
358
  const code = error.code;
294
359
  if (code === "EEXIST")
295
360
  return "contention";
296
- if (process.platform !== "win32" || !WINDOWS_DIRECTORY_CONTENTION_CODES.has(code ?? ""))
361
+ if (!policy.isDirectoryContentionError(error))
297
362
  return "terminal";
298
- // Win32 may report a sharing-shaped error while another claimer creates or removes this exact
363
+ // A host may report a contention-shaped error while another claimer creates or removes this exact
299
364
  // directory. A witnessed path is contention. An absent path is ambiguous: permit one bounded
300
365
  // retry in case the competing directory operation just completed, but never turn a durable
301
366
  // create denial into a malformed-lock timeout. A denied probe remains terminal immediately.
@@ -306,7 +371,7 @@ async function classifyLockClaimFailure(error, lockPath) {
306
371
  catch (probeError) {
307
372
  const probeCode = probeError.code;
308
373
  return probeCode === "ENOENT" || probeCode === "ENOTDIR"
309
- ? "unwitnessed-windows-sharing-error"
374
+ ? "unwitnessed-contention-error"
310
375
  : "terminal";
311
376
  }
312
377
  }
@@ -315,31 +380,31 @@ async function classifyLockClaimFailure(error, lockPath) {
315
380
  * owner record makes a crash leftover diagnosable, and release is token-checked so a caller can
316
381
  * never remove a lock it no longer owns. Waits up to `waitMs`, polling every `pollMs`.
317
382
  */
318
- async function claimLockPath(lockPath, owner, waitMs, pollMs) {
383
+ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
319
384
  const started = owner.created_at_ms;
320
- let unwitnessedWindowsRetryUsed = false;
385
+ let unwitnessedRetryUsed = false;
321
386
  while (true) {
322
387
  try {
323
388
  await fs.mkdir(lockPath, { mode: 0o700 });
324
389
  }
325
390
  catch (err) {
326
- const failure = await classifyLockClaimFailure(err, lockPath);
391
+ const failure = await classifyLockClaimFailure(err, lockPath, policy);
327
392
  if (failure === "terminal")
328
393
  throw err;
329
- if (failure === "unwitnessed-windows-sharing-error") {
330
- if (unwitnessedWindowsRetryUsed)
394
+ if (failure === "unwitnessed-contention-error") {
395
+ if (unwitnessedRetryUsed)
331
396
  throw err;
332
- unwitnessedWindowsRetryUsed = true;
397
+ unwitnessedRetryUsed = true;
333
398
  // This is one immediate re-attempt, not a wait. It is permitted even with waitMs: 0 so
334
399
  // the next result can distinguish a one-shot sharing race from a durable create denial.
335
400
  continue;
336
401
  }
337
402
  else {
338
- unwitnessedWindowsRetryUsed = false;
403
+ unwitnessedRetryUsed = false;
339
404
  }
340
405
  let existingOwner = await readOwner(lockPath);
341
406
  if (existingOwner !== null) {
342
- if (await quarantineStaleLock(lockPath, existingOwner))
407
+ if (await quarantineStaleLock(lockPath, existingOwner, policy))
343
408
  continue;
344
409
  // A failed quarantine attempt is non-progress: another reclaimer may have moved the stale
345
410
  // lock and installed a live replacement while this caller was delayed in rename. Diagnose
@@ -347,12 +412,18 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs) {
347
412
  existingOwner = await readOwner(lockPath);
348
413
  }
349
414
  if (Date.now() - started >= waitMs)
350
- throw timeoutError(lockPath, existingOwner, owner.target);
415
+ throw await timeoutError(lockPath, existingOwner, owner.target);
351
416
  await delay(pollMs);
352
417
  continue;
353
418
  }
354
- // The claim is ours now. Owner initialization and rollback failures are not claim
355
- // contention—even when Windows reports a sharing-shaped errno—and must propagate unchanged.
419
+ // The directory this claim made, to recognize it later even if the owner record never lands.
420
+ const claimed = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
421
+ // The claim is ours now, unless this claimer was suspended long enough for the owner-less
422
+ // directory to be removed as orphaned and claimed again. Owner initialization and rollback
423
+ // failures are not claim contention, even when the host reports a contention-shaped error, and
424
+ // must propagate unchanged. A lost claim is contention: a record this claim never wrote
425
+ // (EEXIST), or no directory to write into (ENOENT, ENOTDIR).
426
+ heldTokens.add(owner.token);
356
427
  try {
357
428
  await fs.writeFile(path.join(lockPath, OWNER_FILE), `${JSON.stringify(owner)}\n`, {
358
429
  encoding: "utf8",
@@ -361,22 +432,172 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs) {
361
432
  });
362
433
  }
363
434
  catch (err) {
364
- await fs.rm(lockPath, { recursive: true, force: true }).catch(() => { });
435
+ const code = err.code;
436
+ // `wx` fails with EEXIST only when another claimer's owner.json was already there, possibly
437
+ // opened but not yet written. The directory is that claimer's now: never roll it back.
438
+ // A lost claim re-enters the loop, whose next `mkdir` either claims or reaches the timeout check.
439
+ if (code === "EEXIST") {
440
+ heldTokens.delete(owner.token);
441
+ continue;
442
+ }
443
+ await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => { });
444
+ heldTokens.delete(owner.token);
445
+ if (code === "ENOENT" || code === "ENOTDIR")
446
+ continue;
365
447
  throw err;
366
448
  }
367
- return async () => {
368
- const current = await readOwner(lockPath);
369
- if (current?.token !== owner.token) {
370
- throw new FilesystemMutationLockError(`refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`, { lockPath, owner: current, stale: false, malformed: current === null });
371
- }
372
- try {
373
- await fs.rm(lockPath, { recursive: true, force: false });
449
+ // One release in flight per claim: a concurrent second invocation shares the first outcome
450
+ // instead of racing it, so two removers can never act on one claim. A later invocation
451
+ // re-verifies the record and refuses, as it always did.
452
+ let inFlight;
453
+ return () => {
454
+ if (inFlight)
455
+ return inFlight;
456
+ inFlight = (async () => {
457
+ // One budget spans ownership resolution and removal, so the worst-case hold stays the
458
+ // mutation plus waitMs however the two steps divide it.
459
+ const started = Date.now();
460
+ const current = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
461
+ if (current.state === "unreadable")
462
+ throw unreadableOwnerRefusal(lockPath, current.error);
463
+ if (current.state !== "record" || current.owner.token !== owner.token) {
464
+ throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
465
+ }
466
+ await removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy);
467
+ heldTokens.delete(owner.token);
468
+ })();
469
+ return inFlight.finally(() => {
470
+ inFlight = undefined;
471
+ });
472
+ };
473
+ }
474
+ }
475
+ /**
476
+ * Undo a claim whose owner record could not be written, removing the lock directory only when it
477
+ * is provably this claim's: a compare-and-release. The directory is this claim's when it carries
478
+ * this claim's owner record, or when it carries no record and is still the very directory this
479
+ * claim made (same device, inode and birth time). Any other record is another process's lock, and
480
+ * a directory that is not the one this claim made may be another claim in progress; both are left
481
+ * alone. So is a record this process still cannot read once the wait budget is spent (a transient
482
+ * read failure is polled out first), and a host that reports no birth time (Linux on NFS, say), where the
483
+ * directory cannot be told from a successor that reused its inode: an owner-less lock left behind
484
+ * is reported as orphaned once the claim grace passes, while a deleted live lock breaks exclusion.
485
+ */
486
+ async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy) {
487
+ const started = Date.now();
488
+ const record = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
489
+ if (record.state === "record") {
490
+ if (record.owner.token === owner.token)
491
+ await removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy);
492
+ return;
493
+ }
494
+ if (record.state === "unreadable" || !(await isClaimedDirectory(lockPath, claimed)))
495
+ return;
496
+ // Move exactly the verified directory aside, under a name only this claim can produce, then remove it.
497
+ const remnant = releasedLockRemnantPath(lockPath, owner);
498
+ await fs.rename(lockPath, remnant);
499
+ await fs.rm(remnant, { recursive: true, force: true });
500
+ }
501
+ async function isClaimedDirectory(lockPath, claimed) {
502
+ if (claimed === null || claimed.birthtimeNs <= 0n)
503
+ return false;
504
+ const current = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
505
+ return current !== null && current.isDirectory() && current.dev === claimed.dev && current.ino === claimed.ino && current.birthtimeNs === claimed.birthtimeNs;
506
+ }
507
+ function changedOwnerRefusal(lockPath, current) {
508
+ return new FilesystemMutationLockError(`refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`, { lockPath, owner: current, stale: false, malformed: current === null });
509
+ }
510
+ /**
511
+ * A record this process could never read is not a record that changed; say which one happened.
512
+ * `malformed` stays true because the outcome for a consumer is the same one it already handled,
513
+ * no usable owner record, and the message carries the distinction the flag cannot.
514
+ */
515
+ function unreadableOwnerRefusal(lockPath, error) {
516
+ const message = error instanceof Error ? error.message : String(error);
517
+ return new FilesystemMutationLockError(`refusing to release filesystem mutation lock '${lockPath}' because its owner record could not be read within the wait budget (${message}); the mutation may have completed, inspect the lock before retrying.`, { lockPath, owner: null, stale: false, malformed: true });
518
+ }
519
+ /** Token-derived sibling name that only this claim can produce; a competitor can never claim it. */
520
+ function releasedLockRemnantPath(lockPath, owner) {
521
+ const tokenHash = createHash("sha256").update(owner.token).digest("hex");
522
+ return `${lockPath}.released-${tokenHash}`;
523
+ }
524
+ function removalFailure(inspectPath, owner, err, attempts, detail) {
525
+ const message = err instanceof Error ? err.message : String(err);
526
+ const suffix = attempts > 1 ? ` after ${attempts} bounded attempts` : "";
527
+ return new FilesystemMutationLockError(`mutation completed but ${detail}${suffix} (${message}); inspect the lock before retrying.`, { lockPath: inspectPath, owner, stale: false, malformed: false });
528
+ }
529
+ /**
530
+ * Release a lock directory whose owner record was just verified as this caller's, in two steps
531
+ * that keep the destructive action fenced to this claim.
532
+ *
533
+ * Step one renames the directory to a token-derived sibling. Rename is atomic and moves exactly
534
+ * the directory whose record was verified; the lock key is free the instant it succeeds, and a
535
+ * competitor's later claim lands on a fresh directory that step two never names. Step two removes
536
+ * the renamed remnant, which only this claim can produce. What remains is the check-then-act gap
537
+ * between a record read and the next rename syscall, reachable only through an external actor
538
+ * that removes the verified directory and a completed competitor claim inside that gap: the same
539
+ * class the stale-lock quarantine rename accepts.
540
+ *
541
+ * A host may report a contention-shaped error while another claimer still holds a handle inside
542
+ * the directory: Windows unlink through older libuv-backed Node releases marks a file delete-on-close, so a
543
+ * competitor's poll of `owner.json` briefly blocks both the directory rename and the removal of
544
+ * its unlinked record. Retry only what the host policy classifies as directory contention, inside
545
+ * the budget `started` at release entry and bounded by the claim's `waitMs`, polling every
546
+ * `pollMs`, so the worst-case hold is the mutation plus `waitMs`. The default supported policy
547
+ * classifies nothing, so its release stays single-shot. Before each rename retry the record is
548
+ * re-read: rename never removes the record, so anything but this claim's own record means the
549
+ * directory changed hands and the release refuses, unless the directory itself is gone. A record
550
+ * this process cannot read is neither, and is polled out rather than acted on. A directory gone
551
+ * between attempts is the requested outcome, and so is an absent remnant, whose token-derived
552
+ * name no competitor can produce.
553
+ */
554
+ async function removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy) {
555
+ const remnant = releasedLockRemnantPath(lockPath, owner);
556
+ let attempts = 0;
557
+ while (true) {
558
+ try {
559
+ await fs.rename(lockPath, remnant);
560
+ break;
561
+ }
562
+ catch (err) {
563
+ attempts += 1;
564
+ if (attempts > 1 && err.code === "ENOENT")
565
+ return;
566
+ if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
567
+ throw removalFailure(lockPath, owner, err, attempts, `filesystem lock '${lockPath}' could not be removed`);
374
568
  }
375
- catch (err) {
376
- const message = err instanceof Error ? err.message : String(err);
377
- throw new FilesystemMutationLockError(`mutation completed but filesystem lock '${lockPath}' could not be removed (${message}); inspect the lock before retrying.`, { lockPath, owner: current, stale: false, malformed: false });
569
+ }
570
+ await delay(pollMs);
571
+ // Only a definitive record authorizes another rename, so an indeterminate read delays this
572
+ // claim's own cleanup instead of ever pointing the rename at a directory it did not verify.
573
+ const current = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
574
+ if (current.state === "record" && current.owner.token === owner.token)
575
+ continue;
576
+ if (current.state === "unreadable")
577
+ throw unreadableOwnerRefusal(lockPath, current.error);
578
+ // Rename never removes the record, so a record-less directory here is a competitor's claim in
579
+ // progress, never this caller's leftover; only an absent directory means already released.
580
+ if (current.state === "absent" && !(await pathExists(lockPath)))
581
+ return;
582
+ throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
583
+ }
584
+ attempts = 0;
585
+ while (true) {
586
+ try {
587
+ await fs.rm(remnant, { recursive: true, force: false });
588
+ return;
589
+ }
590
+ catch (err) {
591
+ attempts += 1;
592
+ // No competitor can produce this token-derived name, so an absent remnant on any attempt,
593
+ // the first included, means the release already reached its requested end state.
594
+ if (err.code === "ENOENT")
595
+ return;
596
+ if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
597
+ throw removalFailure(remnant, owner, err, attempts, `filesystem lock '${lockPath}' was released yet its remnant '${remnant}' could not be removed`);
378
598
  }
379
- };
599
+ }
600
+ await delay(pollMs);
380
601
  }
381
602
  }
382
603
  function newOwner(target) {
@@ -398,13 +619,14 @@ function newOwner(target) {
398
619
  * fail-closed.
399
620
  */
400
621
  export async function acquireFilesystemMutationLock(target, options = {}) {
622
+ const policy = captureFilesystemHostPolicy(options.hostPolicy);
401
623
  const waitMs = positiveOption(options.waitMs, DEFAULT_WAIT_MS, "waitMs");
402
624
  const pollMs = positiveOption(options.pollMs, DEFAULT_POLL_MS, "pollMs");
403
625
  const targetResolved = path.resolve(target);
404
626
  const targetDir = path.dirname(targetResolved);
405
627
  const owner = newOwner(targetResolved);
406
- // An existing Windows drive root (for example C:\) can reject even a recursive mkdir with
407
- // EPERM. Resolve first and create only when the parent is genuinely absent. This preserves the
628
+ // Existing filesystem roots can reject even a recursive mkdir. Resolve first and create
629
+ // only when the parent is genuinely absent. This preserves the
408
630
  // create-on-demand behavior without mutating an already-existing filesystem root.
409
631
  let canonicalDir;
410
632
  try {
@@ -419,9 +641,9 @@ export async function acquireFilesystemMutationLock(target, options = {}) {
419
641
  // Two callers may spell the same bundle through real and symlinked parent paths. Canonicalize
420
642
  // the now-existing parent so both claim the same runtime lock for the physical target.
421
643
  const targetCanonical = await canonicalTargetInDirectory(canonicalDir, path.basename(targetResolved));
422
- const lockRoot = await selectLockRoot(options);
644
+ const lockRoot = await selectLockRoot(options, policy);
423
645
  const lockPath = filesystemMutationLockPathInRoot(targetCanonical, lockRoot);
424
- return claimLockPath(lockPath, { ...owner, target: targetCanonical }, waitMs, pollMs);
646
+ return claimLockPath(lockPath, { ...owner, target: targetCanonical }, waitMs, pollMs, policy);
425
647
  }
426
648
  const IDENTITY_KEY_SHAPE = /^[0-9a-f]{64}$/;
427
649
  function assertIdentityKey(key) {
@@ -429,9 +651,9 @@ function assertIdentityKey(key) {
429
651
  throw new TypeError("identity key must be a lowercase hex sha256 digest");
430
652
  }
431
653
  /** @internal Runtime lock directory for one identity key; the key is the whole path component. */
432
- export function filesystemIdentityLockPath(key, portableRoot) {
654
+ export function filesystemIdentityLockPath(key, portableRoot, hostPolicy) {
433
655
  assertIdentityKey(key);
434
- return path.join(filesystemMutationLockRoot(portableRoot), `${key}.lock`);
656
+ return path.join(filesystemMutationLockRoot(portableRoot, hostPolicy), `${key}.lock`);
435
657
  }
436
658
  /**
437
659
  * @internal Claim the same-user cross-process lock for an identity key that the caller derived
@@ -441,11 +663,12 @@ export function filesystemIdentityLockPath(key, portableRoot) {
441
663
  */
442
664
  export async function acquireFilesystemIdentityLock(key, identity, options = {}) {
443
665
  assertIdentityKey(key);
666
+ const policy = captureFilesystemHostPolicy(options.hostPolicy);
444
667
  const waitMs = positiveOption(options.waitMs, DEFAULT_WAIT_MS, "waitMs");
445
668
  const pollMs = positiveOption(options.pollMs, DEFAULT_POLL_MS, "pollMs");
446
669
  const owner = newOwner(identity);
447
- const lockRoot = await selectLockRoot(options);
448
- return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs);
670
+ const lockRoot = await selectLockRoot(options, policy);
671
+ return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs, policy);
449
672
  }
450
673
  /** Run `fn` while holding the same-user cross-process mutation lock for `target`. */
451
674
  export async function withFilesystemMutationLock(target, fn, options = {}) {
@@ -457,4 +680,27 @@ export async function withFilesystemMutationLock(target, fn, options = {}) {
457
680
  await release();
458
681
  }
459
682
  }
683
+ /**
684
+ * How long ago a lock directory last changed (its owner record written or removed), or null when
685
+ * it is gone. Lets a caller tell a claim or release in progress from an orphaned lock.
686
+ */
687
+ export async function filesystemLockAgeMs(lockPath) {
688
+ try {
689
+ const info = await fs.stat(lockPath);
690
+ return Math.max(0, Date.now() - info.mtimeMs);
691
+ }
692
+ catch {
693
+ return null;
694
+ }
695
+ }
696
+ /**
697
+ * Whether the lock at `lockPath` still carries the owner record with `token`: a definite answer
698
+ * when a record or its absence was read, `null` when this process could not read it.
699
+ */
700
+ export async function filesystemLockHeldBy(lockPath, token) {
701
+ const record = await readOwnerRecord(lockPath);
702
+ if (record.state === "unreadable")
703
+ return null;
704
+ return record.state === "record" && record.owner.token === token;
705
+ }
460
706
  //# sourceMappingURL=filesystem-lock.js.map