@aigne/afs-did-space 1.12.0-beta.5

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 (510) hide show
  1. package/LICENSE.md +26 -0
  2. package/README.md +129 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
  5. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  6. package/dist/afs-metadata-context.cjs +20 -0
  7. package/dist/afs-metadata-context.d.cts +21 -0
  8. package/dist/afs-metadata-context.d.cts.map +1 -0
  9. package/dist/afs-metadata-context.d.mts +21 -0
  10. package/dist/afs-metadata-context.d.mts.map +1 -0
  11. package/dist/afs-metadata-context.mjs +18 -0
  12. package/dist/afs-metadata-context.mjs.map +1 -0
  13. package/dist/blocklet-hook.cjs +180 -0
  14. package/dist/blocklet-hook.d.cts +29 -0
  15. package/dist/blocklet-hook.d.cts.map +1 -0
  16. package/dist/blocklet-hook.d.mts +29 -0
  17. package/dist/blocklet-hook.d.mts.map +1 -0
  18. package/dist/blocklet-hook.mjs +178 -0
  19. package/dist/blocklet-hook.mjs.map +1 -0
  20. package/dist/changelog-schema.cjs +603 -0
  21. package/dist/changelog-schema.d.cts +395 -0
  22. package/dist/changelog-schema.d.cts.map +1 -0
  23. package/dist/changelog-schema.d.mts +395 -0
  24. package/dist/changelog-schema.d.mts.map +1 -0
  25. package/dist/changelog-schema.mjs +570 -0
  26. package/dist/changelog-schema.mjs.map +1 -0
  27. package/dist/changelog-write.cjs +80 -0
  28. package/dist/changelog-write.d.cts +70 -0
  29. package/dist/changelog-write.d.cts.map +1 -0
  30. package/dist/changelog-write.d.mts +70 -0
  31. package/dist/changelog-write.d.mts.map +1 -0
  32. package/dist/changelog-write.mjs +77 -0
  33. package/dist/changelog-write.mjs.map +1 -0
  34. package/dist/cid-byte-cache.cjs +126 -0
  35. package/dist/cid-byte-cache.d.cts +46 -0
  36. package/dist/cid-byte-cache.d.cts.map +1 -0
  37. package/dist/cid-byte-cache.d.mts +47 -0
  38. package/dist/cid-byte-cache.d.mts.map +1 -0
  39. package/dist/cid-byte-cache.mjs +125 -0
  40. package/dist/cid-byte-cache.mjs.map +1 -0
  41. package/dist/cid.cjs +68 -0
  42. package/dist/cid.d.cts +22 -0
  43. package/dist/cid.d.cts.map +1 -0
  44. package/dist/cid.d.mts +22 -0
  45. package/dist/cid.d.mts.map +1 -0
  46. package/dist/cid.mjs +65 -0
  47. package/dist/cid.mjs.map +1 -0
  48. package/dist/client.cjs +221 -0
  49. package/dist/client.d.cts +72 -0
  50. package/dist/client.d.cts.map +1 -0
  51. package/dist/client.d.mts +72 -0
  52. package/dist/client.d.mts.map +1 -0
  53. package/dist/client.mjs +220 -0
  54. package/dist/client.mjs.map +1 -0
  55. package/dist/cloudflare.cjs +510 -0
  56. package/dist/cloudflare.d.cts +307 -0
  57. package/dist/cloudflare.d.cts.map +1 -0
  58. package/dist/cloudflare.d.mts +308 -0
  59. package/dist/cloudflare.d.mts.map +1 -0
  60. package/dist/cloudflare.mjs +507 -0
  61. package/dist/cloudflare.mjs.map +1 -0
  62. package/dist/ctime-backfill.cjs +272 -0
  63. package/dist/ctime-backfill.d.cts +169 -0
  64. package/dist/ctime-backfill.d.cts.map +1 -0
  65. package/dist/ctime-backfill.d.mts +169 -0
  66. package/dist/ctime-backfill.d.mts.map +1 -0
  67. package/dist/ctime-backfill.mjs +267 -0
  68. package/dist/ctime-backfill.mjs.map +1 -0
  69. package/dist/d1-gc-queue.cjs +80 -0
  70. package/dist/d1-gc-queue.d.cts +59 -0
  71. package/dist/d1-gc-queue.d.cts.map +1 -0
  72. package/dist/d1-gc-queue.d.mts +59 -0
  73. package/dist/d1-gc-queue.d.mts.map +1 -0
  74. package/dist/d1-gc-queue.mjs +81 -0
  75. package/dist/d1-gc-queue.mjs.map +1 -0
  76. package/dist/d1-resolve-store.cjs +189 -0
  77. package/dist/d1-resolve-store.d.cts +93 -0
  78. package/dist/d1-resolve-store.d.cts.map +1 -0
  79. package/dist/d1-resolve-store.d.mts +93 -0
  80. package/dist/d1-resolve-store.d.mts.map +1 -0
  81. package/dist/d1-resolve-store.mjs +190 -0
  82. package/dist/d1-resolve-store.mjs.map +1 -0
  83. package/dist/d1-schema.cjs +361 -0
  84. package/dist/d1-schema.d.cts +237 -0
  85. package/dist/d1-schema.d.cts.map +1 -0
  86. package/dist/d1-schema.d.mts +237 -0
  87. package/dist/d1-schema.d.mts.map +1 -0
  88. package/dist/d1-schema.mjs +341 -0
  89. package/dist/d1-schema.mjs.map +1 -0
  90. package/dist/d1-share-store.cjs +0 -0
  91. package/dist/d1-share-store.d.cts +53 -0
  92. package/dist/d1-share-store.d.cts.map +1 -0
  93. package/dist/d1-share-store.d.mts +53 -0
  94. package/dist/d1-share-store.d.mts.map +1 -0
  95. package/dist/d1-share-store.mjs +0 -0
  96. package/dist/d1-share-store.mjs.map +1 -0
  97. package/dist/d1-tree-index.cjs +1098 -0
  98. package/dist/d1-tree-index.d.cts +203 -0
  99. package/dist/d1-tree-index.d.cts.map +1 -0
  100. package/dist/d1-tree-index.d.mts +203 -0
  101. package/dist/d1-tree-index.d.mts.map +1 -0
  102. package/dist/d1-tree-index.mjs +1094 -0
  103. package/dist/d1-tree-index.mjs.map +1 -0
  104. package/dist/data-backend.cjs +147 -0
  105. package/dist/data-backend.d.cts +30 -0
  106. package/dist/data-backend.d.cts.map +1 -0
  107. package/dist/data-backend.d.mts +30 -0
  108. package/dist/data-backend.d.mts.map +1 -0
  109. package/dist/data-backend.mjs +148 -0
  110. package/dist/data-backend.mjs.map +1 -0
  111. package/dist/dedup-backfill.cjs +278 -0
  112. package/dist/dedup-backfill.d.cts +165 -0
  113. package/dist/dedup-backfill.d.cts.map +1 -0
  114. package/dist/dedup-backfill.d.mts +165 -0
  115. package/dist/dedup-backfill.d.mts.map +1 -0
  116. package/dist/dedup-backfill.mjs +273 -0
  117. package/dist/dedup-backfill.mjs.map +1 -0
  118. package/dist/did-space-like.d.cts +2 -0
  119. package/dist/did-space-like.d.mts +2 -0
  120. package/dist/did-utils.cjs +44 -0
  121. package/dist/did-utils.d.cts +25 -0
  122. package/dist/did-utils.d.cts.map +1 -0
  123. package/dist/did-utils.d.mts +25 -0
  124. package/dist/did-utils.d.mts.map +1 -0
  125. package/dist/did-utils.mjs +43 -0
  126. package/dist/did-utils.mjs.map +1 -0
  127. package/dist/dirty-path-queue.cjs +31 -0
  128. package/dist/dirty-path-queue.d.cts +104 -0
  129. package/dist/dirty-path-queue.d.cts.map +1 -0
  130. package/dist/dirty-path-queue.d.mts +104 -0
  131. package/dist/dirty-path-queue.d.mts.map +1 -0
  132. package/dist/dirty-path-queue.mjs +31 -0
  133. package/dist/dirty-path-queue.mjs.map +1 -0
  134. package/dist/dirty-path-schema.cjs +142 -0
  135. package/dist/dirty-path-schema.d.cts +90 -0
  136. package/dist/dirty-path-schema.d.cts.map +1 -0
  137. package/dist/dirty-path-schema.d.mts +90 -0
  138. package/dist/dirty-path-schema.d.mts.map +1 -0
  139. package/dist/dirty-path-schema.mjs +136 -0
  140. package/dist/dirty-path-schema.mjs.map +1 -0
  141. package/dist/download-actions.cjs +43 -0
  142. package/dist/download-actions.mjs +40 -0
  143. package/dist/download-actions.mjs.map +1 -0
  144. package/dist/fs-object-store.cjs +153 -0
  145. package/dist/fs-object-store.d.cts +21 -0
  146. package/dist/fs-object-store.d.cts.map +1 -0
  147. package/dist/fs-object-store.d.mts +22 -0
  148. package/dist/fs-object-store.d.mts.map +1 -0
  149. package/dist/fs-object-store.mjs +153 -0
  150. package/dist/fs-object-store.mjs.map +1 -0
  151. package/dist/gc-queue.d.cts +43 -0
  152. package/dist/gc-queue.d.cts.map +1 -0
  153. package/dist/gc-queue.d.mts +43 -0
  154. package/dist/gc-queue.d.mts.map +1 -0
  155. package/dist/index.cjs +2712 -0
  156. package/dist/index.d.cts +504 -0
  157. package/dist/index.d.cts.map +1 -0
  158. package/dist/index.d.mts +505 -0
  159. package/dist/index.d.mts.map +1 -0
  160. package/dist/index.mjs +2537 -0
  161. package/dist/index.mjs.map +1 -0
  162. package/dist/inline-content-migration.cjs +212 -0
  163. package/dist/inline-content-migration.d.cts +110 -0
  164. package/dist/inline-content-migration.d.cts.map +1 -0
  165. package/dist/inline-content-migration.d.mts +111 -0
  166. package/dist/inline-content-migration.d.mts.map +1 -0
  167. package/dist/inline-content-migration.mjs +207 -0
  168. package/dist/inline-content-migration.mjs.map +1 -0
  169. package/dist/inline-content.cjs +145 -0
  170. package/dist/inline-content.d.cts +98 -0
  171. package/dist/inline-content.d.cts.map +1 -0
  172. package/dist/inline-content.d.mts +98 -0
  173. package/dist/inline-content.d.mts.map +1 -0
  174. package/dist/inline-content.mjs +137 -0
  175. package/dist/inline-content.mjs.map +1 -0
  176. package/dist/local-changelog.cjs +241 -0
  177. package/dist/local-changelog.d.cts +119 -0
  178. package/dist/local-changelog.d.cts.map +1 -0
  179. package/dist/local-changelog.d.mts +119 -0
  180. package/dist/local-changelog.d.mts.map +1 -0
  181. package/dist/local-changelog.mjs +240 -0
  182. package/dist/local-changelog.mjs.map +1 -0
  183. package/dist/local.cjs +435 -0
  184. package/dist/local.d.cts +254 -0
  185. package/dist/local.d.cts.map +1 -0
  186. package/dist/local.d.mts +254 -0
  187. package/dist/local.d.mts.map +1 -0
  188. package/dist/local.mjs +427 -0
  189. package/dist/local.mjs.map +1 -0
  190. package/dist/memory-gc-queue.cjs +36 -0
  191. package/dist/memory-gc-queue.d.cts +22 -0
  192. package/dist/memory-gc-queue.d.cts.map +1 -0
  193. package/dist/memory-gc-queue.d.mts +22 -0
  194. package/dist/memory-gc-queue.d.mts.map +1 -0
  195. package/dist/memory-gc-queue.mjs +36 -0
  196. package/dist/memory-gc-queue.mjs.map +1 -0
  197. package/dist/memory-tree-index.cjs +734 -0
  198. package/dist/memory-tree-index.d.cts +159 -0
  199. package/dist/memory-tree-index.d.cts.map +1 -0
  200. package/dist/memory-tree-index.d.mts +159 -0
  201. package/dist/memory-tree-index.d.mts.map +1 -0
  202. package/dist/memory-tree-index.mjs +735 -0
  203. package/dist/memory-tree-index.mjs.map +1 -0
  204. package/dist/metadata-db.cjs +562 -0
  205. package/dist/metadata-db.d.cts +102 -0
  206. package/dist/metadata-db.d.cts.map +1 -0
  207. package/dist/metadata-db.d.mts +102 -0
  208. package/dist/metadata-db.d.mts.map +1 -0
  209. package/dist/metadata-db.mjs +562 -0
  210. package/dist/metadata-db.mjs.map +1 -0
  211. package/dist/mime-utils.cjs +2 -0
  212. package/dist/mime-utils.d.cts +2 -0
  213. package/dist/mime-utils.d.mts +2 -0
  214. package/dist/mime-utils.mjs +3 -0
  215. package/dist/object-key.cjs +41 -0
  216. package/dist/object-key.d.cts +25 -0
  217. package/dist/object-key.d.cts.map +1 -0
  218. package/dist/object-key.d.mts +25 -0
  219. package/dist/object-key.d.mts.map +1 -0
  220. package/dist/object-key.mjs +40 -0
  221. package/dist/object-key.mjs.map +1 -0
  222. package/dist/packages/session/dist/index.cjs +788 -0
  223. package/dist/packages/session/dist/index.mjs +782 -0
  224. package/dist/packages/session/dist/index.mjs.map +1 -0
  225. package/dist/platform/r2/dist/types.d.cts +49 -0
  226. package/dist/platform/r2/dist/types.d.cts.map +1 -0
  227. package/dist/prefix-range.cjs +33 -0
  228. package/dist/prefix-range.d.cts +29 -0
  229. package/dist/prefix-range.d.cts.map +1 -0
  230. package/dist/prefix-range.d.mts +29 -0
  231. package/dist/prefix-range.d.mts.map +1 -0
  232. package/dist/prefix-range.mjs +33 -0
  233. package/dist/prefix-range.mjs.map +1 -0
  234. package/dist/prefixed-tree-index.cjs +238 -0
  235. package/dist/prefixed-tree-index.d.cts +85 -0
  236. package/dist/prefixed-tree-index.d.cts.map +1 -0
  237. package/dist/prefixed-tree-index.d.mts +85 -0
  238. package/dist/prefixed-tree-index.d.mts.map +1 -0
  239. package/dist/prefixed-tree-index.mjs +239 -0
  240. package/dist/prefixed-tree-index.mjs.map +1 -0
  241. package/dist/presign-signer.cjs +7 -0
  242. package/dist/presign-signer.d.cts +99 -0
  243. package/dist/presign-signer.d.cts.map +1 -0
  244. package/dist/presign-signer.d.mts +99 -0
  245. package/dist/presign-signer.d.mts.map +1 -0
  246. package/dist/presign-signer.mjs +7 -0
  247. package/dist/presign-signer.mjs.map +1 -0
  248. package/dist/providers/platform/r2/dist/index.d.mts +3 -0
  249. package/dist/providers/platform/r2/dist/types.d.mts +49 -0
  250. package/dist/providers/platform/r2/dist/types.d.mts.map +1 -0
  251. package/dist/reconcile.cjs +184 -0
  252. package/dist/reconcile.d.cts +26 -0
  253. package/dist/reconcile.d.cts.map +1 -0
  254. package/dist/reconcile.d.mts +26 -0
  255. package/dist/reconcile.d.mts.map +1 -0
  256. package/dist/reconcile.mjs +184 -0
  257. package/dist/reconcile.mjs.map +1 -0
  258. package/dist/reference-check.cjs +49 -0
  259. package/dist/reference-check.d.cts +10 -0
  260. package/dist/reference-check.d.cts.map +1 -0
  261. package/dist/reference-check.d.mts +10 -0
  262. package/dist/reference-check.d.mts.map +1 -0
  263. package/dist/reference-check.mjs +48 -0
  264. package/dist/reference-check.mjs.map +1 -0
  265. package/dist/remote-afs.cjs +681 -0
  266. package/dist/remote-afs.d.cts +236 -0
  267. package/dist/remote-afs.d.cts.map +1 -0
  268. package/dist/remote-afs.d.mts +236 -0
  269. package/dist/remote-afs.d.mts.map +1 -0
  270. package/dist/remote-afs.mjs +676 -0
  271. package/dist/remote-afs.mjs.map +1 -0
  272. package/dist/rename-guard.cjs +18 -0
  273. package/dist/rename-guard.mjs +18 -0
  274. package/dist/rename-guard.mjs.map +1 -0
  275. package/dist/resolve.cjs +260 -0
  276. package/dist/resolve.d.cts +113 -0
  277. package/dist/resolve.d.cts.map +1 -0
  278. package/dist/resolve.d.mts +113 -0
  279. package/dist/resolve.d.mts.map +1 -0
  280. package/dist/resolve.mjs +260 -0
  281. package/dist/resolve.mjs.map +1 -0
  282. package/dist/rsync-content.cjs +33 -0
  283. package/dist/rsync-content.mjs +33 -0
  284. package/dist/rsync-content.mjs.map +1 -0
  285. package/dist/rsync.cjs +400 -0
  286. package/dist/rsync.d.cts +157 -0
  287. package/dist/rsync.d.cts.map +1 -0
  288. package/dist/rsync.d.mts +157 -0
  289. package/dist/rsync.d.mts.map +1 -0
  290. package/dist/rsync.mjs +397 -0
  291. package/dist/rsync.mjs.map +1 -0
  292. package/dist/run-gc.cjs +57 -0
  293. package/dist/run-gc.d.cts +40 -0
  294. package/dist/run-gc.d.cts.map +1 -0
  295. package/dist/run-gc.d.mts +41 -0
  296. package/dist/run-gc.d.mts.map +1 -0
  297. package/dist/run-gc.mjs +57 -0
  298. package/dist/run-gc.mjs.map +1 -0
  299. package/dist/scope-id.cjs +70 -0
  300. package/dist/scope-id.d.cts +35 -0
  301. package/dist/scope-id.d.cts.map +1 -0
  302. package/dist/scope-id.d.mts +35 -0
  303. package/dist/scope-id.d.mts.map +1 -0
  304. package/dist/scope-id.mjs +68 -0
  305. package/dist/scope-id.mjs.map +1 -0
  306. package/dist/scope-migration-node.cjs +172 -0
  307. package/dist/scope-migration-node.d.cts +44 -0
  308. package/dist/scope-migration-node.d.cts.map +1 -0
  309. package/dist/scope-migration-node.d.mts +44 -0
  310. package/dist/scope-migration-node.d.mts.map +1 -0
  311. package/dist/scope-migration-node.mjs +171 -0
  312. package/dist/scope-migration-node.mjs.map +1 -0
  313. package/dist/scope-migration.cjs +155 -0
  314. package/dist/scope-migration.d.cts +65 -0
  315. package/dist/scope-migration.d.cts.map +1 -0
  316. package/dist/scope-migration.d.mts +65 -0
  317. package/dist/scope-migration.d.mts.map +1 -0
  318. package/dist/scope-migration.mjs +154 -0
  319. package/dist/scope-migration.mjs.map +1 -0
  320. package/dist/share-declaration.cjs +225 -0
  321. package/dist/share-declaration.d.cts +30 -0
  322. package/dist/share-declaration.d.cts.map +1 -0
  323. package/dist/share-declaration.d.mts +32 -0
  324. package/dist/share-declaration.d.mts.map +1 -0
  325. package/dist/share-declaration.mjs +221 -0
  326. package/dist/share-declaration.mjs.map +1 -0
  327. package/dist/share-hook.cjs +63 -0
  328. package/dist/share-hook.mjs +62 -0
  329. package/dist/share-hook.mjs.map +1 -0
  330. package/dist/share-resolve.cjs +176 -0
  331. package/dist/share-resolve.d.cts +36 -0
  332. package/dist/share-resolve.d.cts.map +1 -0
  333. package/dist/share-resolve.d.mts +36 -0
  334. package/dist/share-resolve.d.mts.map +1 -0
  335. package/dist/share-resolve.mjs +177 -0
  336. package/dist/share-resolve.mjs.map +1 -0
  337. package/dist/share-serve.cjs +145 -0
  338. package/dist/share-serve.d.cts +30 -0
  339. package/dist/share-serve.d.cts.map +1 -0
  340. package/dist/share-serve.d.mts +30 -0
  341. package/dist/share-serve.d.mts.map +1 -0
  342. package/dist/share-serve.mjs +144 -0
  343. package/dist/share-serve.mjs.map +1 -0
  344. package/dist/share-store.cjs +25 -0
  345. package/dist/share-store.d.cts +65 -0
  346. package/dist/share-store.d.cts.map +1 -0
  347. package/dist/share-store.d.mts +65 -0
  348. package/dist/share-store.d.mts.map +1 -0
  349. package/dist/share-store.mjs +25 -0
  350. package/dist/share-store.mjs.map +1 -0
  351. package/dist/share-virtual-path.cjs +102 -0
  352. package/dist/share-virtual-path.mjs +101 -0
  353. package/dist/share-virtual-path.mjs.map +1 -0
  354. package/dist/space-resolver.cjs +54 -0
  355. package/dist/space-resolver.d.cts +34 -0
  356. package/dist/space-resolver.d.cts.map +1 -0
  357. package/dist/space-resolver.d.mts +34 -0
  358. package/dist/space-resolver.d.mts.map +1 -0
  359. package/dist/space-resolver.mjs +55 -0
  360. package/dist/space-resolver.mjs.map +1 -0
  361. package/dist/sqlite-gc-queue.cjs +51 -0
  362. package/dist/sqlite-gc-queue.d.cts +22 -0
  363. package/dist/sqlite-gc-queue.d.cts.map +1 -0
  364. package/dist/sqlite-gc-queue.d.mts +22 -0
  365. package/dist/sqlite-gc-queue.d.mts.map +1 -0
  366. package/dist/sqlite-gc-queue.mjs +52 -0
  367. package/dist/sqlite-gc-queue.mjs.map +1 -0
  368. package/dist/sqlite-resolve-store.cjs +163 -0
  369. package/dist/sqlite-resolve-store.d.cts +50 -0
  370. package/dist/sqlite-resolve-store.d.cts.map +1 -0
  371. package/dist/sqlite-resolve-store.d.mts +50 -0
  372. package/dist/sqlite-resolve-store.d.mts.map +1 -0
  373. package/dist/sqlite-resolve-store.mjs +164 -0
  374. package/dist/sqlite-resolve-store.mjs.map +1 -0
  375. package/dist/sqlite-share-store.cjs +157 -0
  376. package/dist/sqlite-share-store.d.cts +31 -0
  377. package/dist/sqlite-share-store.d.cts.map +1 -0
  378. package/dist/sqlite-share-store.d.mts +31 -0
  379. package/dist/sqlite-share-store.d.mts.map +1 -0
  380. package/dist/sqlite-share-store.mjs +157 -0
  381. package/dist/sqlite-share-store.mjs.map +1 -0
  382. package/dist/sqlite-tree-index.cjs +901 -0
  383. package/dist/sqlite-tree-index.d.cts +107 -0
  384. package/dist/sqlite-tree-index.d.cts.map +1 -0
  385. package/dist/sqlite-tree-index.d.mts +107 -0
  386. package/dist/sqlite-tree-index.d.mts.map +1 -0
  387. package/dist/sqlite-tree-index.mjs +902 -0
  388. package/dist/sqlite-tree-index.mjs.map +1 -0
  389. package/dist/sync-engine.cjs +1324 -0
  390. package/dist/sync-engine.d.cts +473 -0
  391. package/dist/sync-engine.d.cts.map +1 -0
  392. package/dist/sync-engine.d.mts +473 -0
  393. package/dist/sync-engine.d.mts.map +1 -0
  394. package/dist/sync-engine.mjs +1315 -0
  395. package/dist/sync-engine.mjs.map +1 -0
  396. package/dist/sync-state.cjs +303 -0
  397. package/dist/sync-state.d.cts +224 -0
  398. package/dist/sync-state.d.cts.map +1 -0
  399. package/dist/sync-state.d.mts +224 -0
  400. package/dist/sync-state.d.mts.map +1 -0
  401. package/dist/sync-state.mjs +297 -0
  402. package/dist/sync-state.mjs.map +1 -0
  403. package/dist/timestamp.cjs +92 -0
  404. package/dist/timestamp.mjs +91 -0
  405. package/dist/timestamp.mjs.map +1 -0
  406. package/dist/transports/http.cjs +54 -0
  407. package/dist/transports/http.d.cts +26 -0
  408. package/dist/transports/http.d.cts.map +1 -0
  409. package/dist/transports/http.d.mts +26 -0
  410. package/dist/transports/http.d.mts.map +1 -0
  411. package/dist/transports/http.mjs +54 -0
  412. package/dist/transports/http.mjs.map +1 -0
  413. package/dist/transports/static.cjs +55 -0
  414. package/dist/transports/static.d.cts +28 -0
  415. package/dist/transports/static.d.cts.map +1 -0
  416. package/dist/transports/static.d.mts +28 -0
  417. package/dist/transports/static.d.mts.map +1 -0
  418. package/dist/transports/static.mjs +55 -0
  419. package/dist/transports/static.mjs.map +1 -0
  420. package/dist/transports/types.cjs +13 -0
  421. package/dist/transports/types.d.cts +72 -0
  422. package/dist/transports/types.d.cts.map +1 -0
  423. package/dist/transports/types.d.mts +72 -0
  424. package/dist/transports/types.d.mts.map +1 -0
  425. package/dist/transports/types.mjs +13 -0
  426. package/dist/transports/types.mjs.map +1 -0
  427. package/dist/transports/ws.cjs +207 -0
  428. package/dist/transports/ws.d.cts +47 -0
  429. package/dist/transports/ws.d.cts.map +1 -0
  430. package/dist/transports/ws.d.mts +47 -0
  431. package/dist/transports/ws.d.mts.map +1 -0
  432. package/dist/transports/ws.mjs +206 -0
  433. package/dist/transports/ws.mjs.map +1 -0
  434. package/dist/tree-index.cjs +333 -0
  435. package/dist/tree-index.d.cts +524 -0
  436. package/dist/tree-index.d.cts.map +1 -0
  437. package/dist/tree-index.d.mts +524 -0
  438. package/dist/tree-index.d.mts.map +1 -0
  439. package/dist/tree-index.mjs +322 -0
  440. package/dist/tree-index.mjs.map +1 -0
  441. package/dist/tree-query.cjs +655 -0
  442. package/dist/tree-query.d.cts +70 -0
  443. package/dist/tree-query.d.cts.map +1 -0
  444. package/dist/tree-query.d.mts +70 -0
  445. package/dist/tree-query.d.mts.map +1 -0
  446. package/dist/tree-query.mjs +648 -0
  447. package/dist/tree-query.mjs.map +1 -0
  448. package/dist/upload-actions.cjs +97 -0
  449. package/dist/upload-actions.d.cts +19 -0
  450. package/dist/upload-actions.d.cts.map +1 -0
  451. package/dist/upload-actions.d.mts +21 -0
  452. package/dist/upload-actions.d.mts.map +1 -0
  453. package/dist/upload-actions.mjs +90 -0
  454. package/dist/upload-actions.mjs.map +1 -0
  455. package/dist/upload-session-store.cjs +185 -0
  456. package/dist/upload-session-store.d.cts +99 -0
  457. package/dist/upload-session-store.d.cts.map +1 -0
  458. package/dist/upload-session-store.d.mts +99 -0
  459. package/dist/upload-session-store.d.mts.map +1 -0
  460. package/dist/upload-session-store.mjs +184 -0
  461. package/dist/upload-session-store.mjs.map +1 -0
  462. package/dist/url-normalize.cjs +61 -0
  463. package/dist/url-normalize.d.cts +33 -0
  464. package/dist/url-normalize.d.cts.map +1 -0
  465. package/dist/url-normalize.d.mts +33 -0
  466. package/dist/url-normalize.d.mts.map +1 -0
  467. package/dist/url-normalize.mjs +61 -0
  468. package/dist/url-normalize.mjs.map +1 -0
  469. package/dist/versioned-content-backends.cjs +155 -0
  470. package/dist/versioned-content-backends.d.cts +49 -0
  471. package/dist/versioned-content-backends.d.cts.map +1 -0
  472. package/dist/versioned-content-backends.d.mts +49 -0
  473. package/dist/versioned-content-backends.d.mts.map +1 -0
  474. package/dist/versioned-content-backends.mjs +154 -0
  475. package/dist/versioned-content-backends.mjs.map +1 -0
  476. package/dist/versioned-content-index.cjs +102 -0
  477. package/dist/versioned-content-index.d.cts +46 -0
  478. package/dist/versioned-content-index.d.cts.map +1 -0
  479. package/dist/versioned-content-index.d.mts +46 -0
  480. package/dist/versioned-content-index.d.mts.map +1 -0
  481. package/dist/versioned-content-index.mjs +103 -0
  482. package/dist/versioned-content-index.mjs.map +1 -0
  483. package/dist/versioned-content-store.cjs +121 -0
  484. package/dist/versioned-content-store.d.cts +90 -0
  485. package/dist/versioned-content-store.d.cts.map +1 -0
  486. package/dist/versioned-content-store.d.mts +90 -0
  487. package/dist/versioned-content-store.d.mts.map +1 -0
  488. package/dist/versioned-content-store.mjs +122 -0
  489. package/dist/versioned-content-store.mjs.map +1 -0
  490. package/dist/versioned-scope.cjs +39 -0
  491. package/dist/versioned-scope.d.cts +57 -0
  492. package/dist/versioned-scope.d.cts.map +1 -0
  493. package/dist/versioned-scope.d.mts +57 -0
  494. package/dist/versioned-scope.d.mts.map +1 -0
  495. package/dist/versioned-scope.mjs +37 -0
  496. package/dist/versioned-scope.mjs.map +1 -0
  497. package/migrations/0001_tree_entries.sql +17 -0
  498. package/migrations/0002_resolve_entries.sql +7 -0
  499. package/migrations/0003_entries_v2.sql +38 -0
  500. package/migrations/0004_gc_queue.sql +5 -0
  501. package/migrations/0006_resolve_enhance.sql +14 -0
  502. package/migrations/0007_resolve_created_at.sql +10 -0
  503. package/migrations/0008_changelog.sql +34 -0
  504. package/migrations/0009_device_sync_state.sql +29 -0
  505. package/migrations/0010_scope_meta.sql +33 -0
  506. package/migrations/0011_shares.sql +25 -0
  507. package/migrations/0012_scope_meta_file_count.sql +41 -0
  508. package/migrations/0013_scope_meta_total_bytes.sql +47 -0
  509. package/migrations/0014_rename_tables.sql +62 -0
  510. package/package.json +102 -0
package/dist/local.mjs ADDED
@@ -0,0 +1,427 @@
1
+ import { normalizeId, validateId } from "./did-utils.mjs";
2
+ import { SqliteUploadSessionStore } from "./upload-session-store.mjs";
3
+ import { attachMetadataContext } from "./afs-metadata-context.mjs";
4
+ import { CachingObjectStore, CidByteCache } from "./cid-byte-cache.mjs";
5
+ import { PrefixedTreeIndex } from "./prefixed-tree-index.mjs";
6
+ import { referencedAnywhereLibsql } from "./reference-check.mjs";
7
+ import { DIDSpaceResolver } from "./resolve.mjs";
8
+ import { runGc } from "./run-gc.mjs";
9
+ import { deriveScope } from "./scope-id.mjs";
10
+ import { SqliteGcQueue } from "./sqlite-gc-queue.mjs";
11
+ import { SqliteResolveStore } from "./sqlite-resolve-store.mjs";
12
+ import { SQLiteTreeIndex } from "./sqlite-tree-index.mjs";
13
+ import { DIDSpaceProvider } from "./index.mjs";
14
+ import { FsObjectStore } from "./fs-object-store.mjs";
15
+ import { MetadataDB } from "./metadata-db.mjs";
16
+ import { reconcile } from "./reconcile.mjs";
17
+ import { migrateNodeMetadataDb, migrateNodeSpaces } from "./scope-migration-node.mjs";
18
+ import { AFS } from "@aigne/afs";
19
+ import { mkdirSync } from "node:fs";
20
+ import { join } from "node:path";
21
+ import { AFSFS } from "@aigne/afs-fs";
22
+
23
+ //#region src/local.ts
24
+ /**
25
+ * DIDSpace — local (Node) backend for DID-scoped storage.
26
+ *
27
+ * Phase 3 (mobile-didspace-transport): unified onto the single
28
+ * `DIDSpaceProvider`, exactly like the Cloudflare backend — only the injected
29
+ * storage primitives differ (Node: `FsObjectStore` + `SQLiteTreeIndex`; CF: R2
30
+ * + `D1TreeIndex`). This makes the backend runtime-agnostic per CLAUDE.md and
31
+ * gives Node the content-addressed `.objects/{cid}`, `.changelog`, `maxDepth:-1`
32
+ * manifest, device-aware writes, and ifMatch that the bare `AFSFS` mount lacked.
33
+ *
34
+ * Layout (relative to `userDir`, all under `entries.scope = <storage scope>` —
35
+ * the derived scope when de-id is on, else the plaintext DID; `userDir` itself
36
+ * is `{rootPath}/{storage scope}`):
37
+ * role:"system" → PrefixedTreeIndex `blocklets/{i}/system/` (mounted at `/`)
38
+ * role:"user" → PrefixedTreeIndex `blocklets/{i}/user/` (mounted at `/`)
39
+ * space root → no-prefix SQLiteTreeIndex (whole space; CLI manifest)
40
+ * config → AFSFS `blocklets/{i}/config/` (blob fragment)
41
+ * Content blobs live once at `{userDir}/objects/{cid}` (FsObjectStore); the
42
+ * index (`_metadata.db`) is the source of truth. The old eventual-mirror
43
+ * subscriber + `desktop:default` deviceId hardcode are gone — the provider's
44
+ * `put` writes entries + changelog atomically.
45
+ *
46
+ * Usage:
47
+ * const space = new DIDSpace({ rootPath: "~/.afs/spaces", userDid: "did:abt:z1..." });
48
+ * const afs = await space.getInstanceSpace({ instanceDid: "z1app", role: "system" });
49
+ * await afs.write("/hello.txt", { content: "hello" });
50
+ */
51
+ /** Local desktop device label written into changelog rows for local writes. */
52
+ const LOCAL_DEVICE_ID = "desktop:default";
53
+ var DIDSpace = class DIDSpace {
54
+ rootPath;
55
+ /** Real normalizeId(DID) — identity, returned to callers (NOT a storage key). */
56
+ userDid;
57
+ /**
58
+ * Storage scope — `deriveScope(userDid, secret)` when de-id is on, else the
59
+ * plaintext `userDid`. Drives the directory name (`userDir`), every
60
+ * SQLiteTreeIndex scope, and `_shares.db` owner_did. Named `_scope` to mirror
61
+ * the CF backend (cloudflare.ts).
62
+ */
63
+ _scope;
64
+ userDir;
65
+ instanceSpaces = /* @__PURE__ */ new Map();
66
+ _resolver;
67
+ /** Backing store for {@link resolver}; closed in {@link close}. */
68
+ _resolveStore;
69
+ /** User-level MetadataDB; lazy-opened on first getInstanceSpace/getUserSpace */
70
+ metadataDb = null;
71
+ /** Shared content-addressed object store (one objects/ dir per user space). */
72
+ objectStore = null;
73
+ /**
74
+ * `objectStore` wrapped with a content-addressed byte cache (#674 perf). Keyed
75
+ * by cid (immutable) → no staleness, no TTL; bounded LRU keeps memory flat.
76
+ * Persists for this DID-Space's lifetime (the Node daemon reuses it per user).
77
+ */
78
+ cachingStore = null;
79
+ objectByteCache = new CidByteCache();
80
+ /** GC queue over `_metadata.db` (deferred object cleanup); lazy, ensureTable once. */
81
+ gcQueue = null;
82
+ /**
83
+ * Share-out Phase 3: builds the ShareResolver for getUserSpace providers.
84
+ * Read lazily at provider-construction time, so a late {@link
85
+ * setShareResolverFactory} (installer space) still covers providers built
86
+ * after the call. Undefined → no share-out (back-compat: dark-wired off).
87
+ */
88
+ _shareResolverFactory;
89
+ /** upload-limits Phase 5 per-scope byte quota (0 = off, the opt-in default). */
90
+ maxBytesPerScope;
91
+ constructor(options) {
92
+ validateId(options.userDid);
93
+ this.rootPath = options.rootPath;
94
+ this.maxBytesPerScope = options.maxBytesPerScope ?? 0;
95
+ this.userDid = normalizeId(options.userDid);
96
+ this._scope = options.scopeHmacSecret ? deriveScope(this.userDid, options.scopeHmacSecret) : this.userDid;
97
+ this.userDir = join(this.rootPath, this._scope);
98
+ this._shareResolverFactory = options.shareResolverFactory;
99
+ }
100
+ /**
101
+ * Storage scope — mirrors `CloudflareDIDSpace.scope` (cloudflare.ts:443).
102
+ * De-id on → derived HMAC hash; de-id off → plaintext `userDid`.
103
+ * Used by consumers (e.g. the Node daemon `/user/index` mount) that need
104
+ * the same de-identified partition key used for physical storage.
105
+ */
106
+ get scope() {
107
+ return this._scope;
108
+ }
109
+ /**
110
+ * Construct a SQLiteTreeIndex for this scope with the per-scope byte quota
111
+ * (upload-limits Phase 5) always injected — so every write path enforces it
112
+ * without each call site threading it. 0 = quota off (the opt-in default).
113
+ */
114
+ sqliteTreeIndex(metadataDb, opts) {
115
+ return new SQLiteTreeIndex(metadataDb, this._scope, {
116
+ ...opts,
117
+ maxBytesPerScope: this.maxBytesPerScope
118
+ });
119
+ }
120
+ /**
121
+ * Open a space DIRECTLY by its storage scope, WITHOUT deriving (README §5.6
122
+ * `/spaces` naming contract). For `/spaces/{X}` inspection where `X` is already
123
+ * the storage scope (a base32 hash under de-id, or a plaintext DID otherwise):
124
+ * the directory `{rootPath}/{storageScope}` is opened verbatim, `did` returns
125
+ * the opaque `storageScope`, and NO `deriveScope` runs (re-deriving would point
126
+ * at a non-existent `{rootPath}/{derive(storageScope)}`). Constructing with no
127
+ * secret guarantees `_scope === storageScope`. Callers mount it readonly.
128
+ */
129
+ static openStorageScope(opts) {
130
+ return new DIDSpace({
131
+ rootPath: opts.rootPath,
132
+ userDid: opts.storageScope
133
+ });
134
+ }
135
+ /**
136
+ * Late-inject the share-resolver factory (share-out Phase 3). Used for the
137
+ * installer space, which `resolveLocalDIDSpace()` builds BEFORE the
138
+ * daemon-global share store exists. Lazy provider construction means this
139
+ * covers every getUserSpace provider built after the call.
140
+ */
141
+ setShareResolverFactory(factory) {
142
+ this._shareResolverFactory = factory;
143
+ }
144
+ /** Expose constructor options for daemon integration (rootPath, userDid). */
145
+ get config() {
146
+ return {
147
+ rootPath: this.rootPath,
148
+ userDid: this.userDid
149
+ };
150
+ }
151
+ /**
152
+ * Lazily open (or reuse) the user-level `_metadata.db`.
153
+ * Creates `userDir` if missing. Only one instance per DIDSpace.
154
+ */
155
+ async getMetadataDb() {
156
+ if (this.metadataDb) return this.metadataDb;
157
+ mkdirSync(this.userDir, { recursive: true });
158
+ this.metadataDb = await MetadataDB.open(this.userDir);
159
+ return this.metadataDb;
160
+ }
161
+ /**
162
+ * Upload-plane deps for the user-facing provider (design §7 / §15 decision 8).
163
+ * Node is FALLBACK-ONLY: it implements the actions + SQLite session table
164
+ * (protocol parity) but injects NO signer, so `prepare-upload` returns
165
+ * `grant: null` and the native client falls back to batchWrite.
166
+ */
167
+ uploadDeps(metadataDb) {
168
+ return {
169
+ uploadSessionStore: new SqliteUploadSessionStore(metadataDb.rawClient),
170
+ uploadScope: this._scope
171
+ };
172
+ }
173
+ /**
174
+ * Lazily create the shared FsObjectStore rooted at `{userDir}/objects`, wrapped
175
+ * with the content-addressed byte cache. Returns the caching wrapper so every
176
+ * provider read benefits; the raw {@link objectStore} field is kept only so
177
+ * {@link close} can drop it.
178
+ */
179
+ getObjectStore() {
180
+ if (!this.cachingStore) {
181
+ mkdirSync(this.userDir, { recursive: true });
182
+ this.objectStore = new FsObjectStore(this.userDir);
183
+ this.cachingStore = new CachingObjectStore(this.objectStore, this.objectByteCache);
184
+ }
185
+ return this.cachingStore;
186
+ }
187
+ /**
188
+ * Lazily create the `SqliteGcQueue` over the user-level `_metadata.db`
189
+ * (via the `MetadataDB.rawClient` getter — never reaches into private fields).
190
+ * One queue per DIDSpace; injected into every DIDSpaceProvider so overwrites/
191
+ * deletes enqueue the orphaned oldCid for the deferred sweep.
192
+ */
193
+ async getGcQueue() {
194
+ if (!this.gcQueue) {
195
+ const queue = new SqliteGcQueue((await this.getMetadataDb()).rawClient);
196
+ await queue.ensureTable();
197
+ this.gcQueue = queue;
198
+ }
199
+ return this.gcQueue;
200
+ }
201
+ /**
202
+ * Run one object-GC sweep over this space's single owner scope (design §4.6).
203
+ * Scope-agnostic: `referencedAnywhere` is scope-blind over the one
204
+ * `_metadata.db`, so one sweep covers the whole daemon. Used by both the
205
+ * `arc space gc` CLI and the daemon's scheduled GC task (AFSScheduler, #742 S3).
206
+ */
207
+ async runGc(opts) {
208
+ const metadataDb = await this.getMetadataDb();
209
+ const stats = await runGc({
210
+ queue: await this.getGcQueue(),
211
+ referencedAnywhere: (cid, nowMs) => referencedAnywhereLibsql(metadataDb.rawClient, cid, nowMs),
212
+ objectStore: this.getObjectStore(),
213
+ graceMs: opts?.graceMs,
214
+ limit: opts?.limit,
215
+ log: opts?.log
216
+ });
217
+ await new SqliteUploadSessionStore(metadataDb.rawClient).sweepExpired(Date.now());
218
+ return stats;
219
+ }
220
+ /**
221
+ * Back-compat no-op: the eventual-mirror subscriber is gone (the provider now
222
+ * writes entries + changelog atomically), so there is no in-flight work to
223
+ * drain. Kept so existing test/daemon call sites don't break.
224
+ */
225
+ async drainSubscribers() {}
226
+ /**
227
+ * Get an isolated AFS instance for a blocklet instance under this DID Space,
228
+ * scoped by role (system = installer-side data, user = caller-side data).
229
+ *
230
+ * Mounts a single `DIDSpaceProvider` whose treeIndex is a `PrefixedTreeIndex`
231
+ * over the user-scoped `SQLiteTreeIndex` at `blocklets/{i}/{role}/`. The
232
+ * prefix is MANDATORY — without it the role root would degrade to the whole
233
+ * space root (data越界). Blobs share the user-level `FsObjectStore`.
234
+ *
235
+ * `userDid` is @deprecated for role:"user" — per-user isolation comes from
236
+ * WHICH space backs the call (the space is already the caller's), so it never
237
+ * enters the path; validated only as defense if supplied.
238
+ */
239
+ async getInstanceSpace(opts) {
240
+ validateId(opts.instanceDid);
241
+ if (opts.role !== "system" && opts.role !== "user") throw new Error(`getInstanceSpace: invalid role "${opts.role}"`);
242
+ if (opts.role === "user" && opts.userDid != null) validateId(opts.userDid);
243
+ const bareInstanceId = normalizeId(opts.instanceDid);
244
+ const cacheKey = opts.role === "system" ? `system:${bareInstanceId}` : `user:${bareInstanceId}`;
245
+ const existing = this.instanceSpaces.get(cacheKey);
246
+ if (existing) return existing;
247
+ const prefix = `blocklets/${bareInstanceId}/${opts.role}/`;
248
+ const accessMode = opts.accessMode ?? "readwrite";
249
+ const metadataDb = await this.getMetadataDb();
250
+ const treeIndex = new PrefixedTreeIndex(this.sqliteTreeIndex(metadataDb, { inlineCache: "on" }), prefix);
251
+ const gcQueue = await this.getGcQueue();
252
+ const shareResolver = opts.role === "user" ? this._shareResolverFactory?.(this.sqliteTreeIndex(metadataDb, { inlineCache: "on" })) : void 0;
253
+ const afs = new AFS();
254
+ await afs.mount(new DIDSpaceProvider({
255
+ did: bareInstanceId,
256
+ objectStore: this.getObjectStore(),
257
+ treeIndex,
258
+ name: "content",
259
+ accessMode,
260
+ deviceId: LOCAL_DEVICE_ID,
261
+ gcQueue,
262
+ ...opts.role === "user" ? {
263
+ shareTreePrefix: prefix,
264
+ ...shareResolver ? {
265
+ shareResolver,
266
+ shareOwnerDid: this._scope
267
+ } : {},
268
+ blockletResolver: this.resolver,
269
+ blockletOwnerDid: this.userDid
270
+ } : {},
271
+ ...opts.role === "user" ? this.uploadDeps(metadataDb) : {}
272
+ }), "/");
273
+ this.instanceSpaces.set(cacheKey, afs);
274
+ return afs;
275
+ }
276
+ /**
277
+ * Source-side dirty queue for an instance's `/user` fragment
278
+ * (index-follower-dirty-path §7, §9.2). Returns a `PrefixedTreeIndex` over the
279
+ * user-level `_metadata.db` (where `ds_changelog` + `ds_dirty_path` live), so
280
+ * the daemon can drain the merged dirty projection instead of replaying
281
+ * `/.changelog`. This is a RUNTIME-INTERNAL capability held directly by the
282
+ * daemon's follower — it is NEVER exposed as a public `/user` AFS action
283
+ * (ack/fail mutate scheduling state). Node has no D1 replica, so a single
284
+ * primary `_metadata.db` read is already read-your-write. The index writes stay
285
+ * in the separate `user-index.db`; only source/dirty rows are read here.
286
+ */
287
+ async getInstanceDirtyQueue(opts) {
288
+ validateId(opts.instanceDid);
289
+ const prefix = `blocklets/${normalizeId(opts.instanceDid)}/${opts.role}/`;
290
+ const metadataDb = await this.getMetadataDb();
291
+ return new PrefixedTreeIndex(this.sqliteTreeIndex(metadataDb), prefix);
292
+ }
293
+ /**
294
+ * Config fragment for an instance — a sibling of the role content tree at
295
+ * `blocklets/<i>/config/`, mounted at `/`. Runtime-internal (mount overrides
296
+ * etc.); NOT part of `/instance`. Stays a plain `AFSFS` blob fragment: config
297
+ * is opaque machine state, not content-addressed sync data (design §2.F/T3.9).
298
+ */
299
+ async getConfigSpace(instanceDid) {
300
+ validateId(instanceDid);
301
+ const bareInstanceId = normalizeId(instanceDid);
302
+ const configDir = join(this.userDir, "blocklets", bareInstanceId, "config");
303
+ mkdirSync(configDir, { recursive: true });
304
+ const afs = new AFS();
305
+ await afs.mount(new AFSFS({
306
+ localPath: configDir,
307
+ name: "config",
308
+ accessMode: "readwrite"
309
+ }), "/");
310
+ return afs;
311
+ }
312
+ /**
313
+ * List all instance DIDs that have storage in this user's DID space.
314
+ *
315
+ * Enumerated from the index (the source of truth) rather than `readdir` of
316
+ * the physical tree — the physical layout is now `objects/{cid}` + index, so
317
+ * there is no per-instance directory to walk. Direct children of the
318
+ * `blocklets/` prefix are the instance ids (trailing-slash dir rows).
319
+ */
320
+ async listApps() {
321
+ const metadataDb = await this.getMetadataDb();
322
+ const result = await this.sqliteTreeIndex(metadataDb).listChildren("blocklets/");
323
+ const ids = /* @__PURE__ */ new Set();
324
+ for (const entry of result.entries) {
325
+ const rel = entry.path.slice(10).replace(/\/$/, "");
326
+ if (rel) ids.add(rel);
327
+ }
328
+ return [...ids];
329
+ }
330
+ /**
331
+ * Resolve table — persistent + DAEMON-GLOBAL (`{rootPath}/_resolve.db`, NOT
332
+ * the per-space `_metadata.db`): `blocklet/<id> → owner` / `domain/<host> → …`
333
+ * are caller-less reverse lookups that need ONE shared registry, exactly like
334
+ * `_shares.db` and CF's single shared D1. Backed by `SqliteResolveStore` so
335
+ * entries survive across DID-Space instances and daemon restarts — symmetric
336
+ * with `CloudflareDIDSpace.resolver` (D1ResolveStore). Closed in {@link close}.
337
+ */
338
+ get resolver() {
339
+ if (!this._resolver) {
340
+ this._resolveStore = new SqliteResolveStore(join(this.rootPath, "_resolve.db"));
341
+ this._resolver = new DIDSpaceResolver(this._resolveStore);
342
+ }
343
+ return this._resolver;
344
+ }
345
+ /**
346
+ * Whole-space root (contract alias `getSpaceRoot`; legacy name `getUserSpace`)
347
+ * — a `DIDSpaceProvider` over the NO-PREFIX user-scoped `SQLiteTreeIndex`, so
348
+ * it sees every instance fragment in the space. Used by `arc space`
349
+ * export/import/diff (CLI manifest source) and observability. Mobile sync does
350
+ * NOT use it (mobile goes through the aside `/user` fragment).
351
+ */
352
+ async getSpaceRoot(options) {
353
+ return this.getUserSpace(options);
354
+ }
355
+ async getUserSpace(options) {
356
+ const metadataDb = await this.getMetadataDb();
357
+ const gcQueue = await this.getGcQueue();
358
+ const afs = new AFS();
359
+ const scopeTreeIndex = this.sqliteTreeIndex(metadataDb, { inlineCache: "on" });
360
+ await afs.mount(new DIDSpaceProvider({
361
+ did: this.userDid,
362
+ objectStore: this.getObjectStore(),
363
+ treeIndex: scopeTreeIndex,
364
+ name: "did-space-user",
365
+ accessMode: options?.accessMode ?? "readonly",
366
+ deviceId: LOCAL_DEVICE_ID,
367
+ gcQueue,
368
+ shareResolver: this._shareResolverFactory?.(scopeTreeIndex),
369
+ blockletResolver: this.resolver,
370
+ blockletOwnerDid: this.userDid,
371
+ ...this.uploadDeps(metadataDb)
372
+ }), "/");
373
+ attachMetadataContext(afs, {
374
+ metadataDb,
375
+ userDir: this.userDir,
376
+ scope: this._scope
377
+ });
378
+ return afs;
379
+ }
380
+ /**
381
+ * Share-out Phase 4: a READONLY AFS for public `/s/{slug}` serving, scoped by
382
+ * `prefix` (with trailing slash, e.g. `docs/`) or — when undefined — the whole
383
+ * owner tree (root share + sidecar). Reuses THIS space's metadataDb +
384
+ * objectStore (no second libsql client on `_metadata.db`, which would race).
385
+ * No shareResolver injected: serving is read-only, never reconciles.
386
+ */
387
+ async getShareServeAFS(prefix) {
388
+ const metadataDb = await this.getMetadataDb();
389
+ const base = this.sqliteTreeIndex(metadataDb, { inlineCache: "on" });
390
+ const treeIndex = prefix ? new PrefixedTreeIndex(base, prefix) : base;
391
+ const afs = new AFS();
392
+ await afs.mount(new DIDSpaceProvider({
393
+ did: this.userDid,
394
+ objectStore: this.getObjectStore(),
395
+ treeIndex,
396
+ name: "did-space-share-serve",
397
+ accessMode: "readonly",
398
+ deviceId: LOCAL_DEVICE_ID
399
+ }), "/");
400
+ return afs;
401
+ }
402
+ /**
403
+ * Clean up resources. Closes the user-level MetadataDB and clears the
404
+ * instance-space cache. No event subscriptions to tear down anymore.
405
+ */
406
+ close() {
407
+ if (this.metadataDb) {
408
+ try {
409
+ this.metadataDb.close();
410
+ } catch {}
411
+ this.metadataDb = null;
412
+ }
413
+ if (this._resolveStore) {
414
+ this._resolveStore.close();
415
+ this._resolveStore = void 0;
416
+ this._resolver = void 0;
417
+ }
418
+ this.gcQueue = null;
419
+ this.objectStore = null;
420
+ this.cachingStore = null;
421
+ this.instanceSpaces.clear();
422
+ }
423
+ };
424
+
425
+ //#endregion
426
+ export { DIDSpace, FsObjectStore, MetadataDB, SQLiteTreeIndex, migrateNodeMetadataDb, migrateNodeSpaces, normalizeId, reconcile, validateId };
427
+ //# sourceMappingURL=local.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.mjs","names":[],"sources":["../src/local.ts"],"sourcesContent":["/**\n * DIDSpace — local (Node) backend for DID-scoped storage.\n *\n * Phase 3 (mobile-didspace-transport): unified onto the single\n * `DIDSpaceProvider`, exactly like the Cloudflare backend — only the injected\n * storage primitives differ (Node: `FsObjectStore` + `SQLiteTreeIndex`; CF: R2\n * + `D1TreeIndex`). This makes the backend runtime-agnostic per CLAUDE.md and\n * gives Node the content-addressed `.objects/{cid}`, `.changelog`, `maxDepth:-1`\n * manifest, device-aware writes, and ifMatch that the bare `AFSFS` mount lacked.\n *\n * Layout (relative to `userDir`, all under `entries.scope = <storage scope>` —\n * the derived scope when de-id is on, else the plaintext DID; `userDir` itself\n * is `{rootPath}/{storage scope}`):\n * role:\"system\" → PrefixedTreeIndex `blocklets/{i}/system/` (mounted at `/`)\n * role:\"user\" → PrefixedTreeIndex `blocklets/{i}/user/` (mounted at `/`)\n * space root → no-prefix SQLiteTreeIndex (whole space; CLI manifest)\n * config → AFSFS `blocklets/{i}/config/` (blob fragment)\n * Content blobs live once at `{userDir}/objects/{cid}` (FsObjectStore); the\n * index (`_metadata.db`) is the source of truth. The old eventual-mirror\n * subscriber + `desktop:default` deviceId hardcode are gone — the provider's\n * `put` writes entries + changelog atomically.\n *\n * Usage:\n * const space = new DIDSpace({ rootPath: \"~/.afs/spaces\", userDid: \"did:abt:z1...\" });\n * const afs = await space.getInstanceSpace({ instanceDid: \"z1app\", role: \"system\" });\n * await afs.write(\"/hello.txt\", { content: \"hello\" });\n */\n\n// biome-ignore lint/style/noRestrictedImports: provider internal — DIDSpace manages physical directories\nimport { mkdirSync } from \"node:fs\";\n// biome-ignore lint/style/noRestrictedImports: provider internal — path joins for directory layout\nimport { join } from \"node:path\";\nimport { AFS, type AFSAccessMode } from \"@aigne/afs\";\nimport { AFSFS } from \"@aigne/afs-fs\";\nimport type { ObjectStore } from \"@aigne/afs-r2\";\nimport { attachMetadataContext } from \"./afs-metadata-context.js\";\nimport { CachingObjectStore, CidByteCache } from \"./cid-byte-cache.js\";\nimport type { DIDSpaceLike, InstanceSpaceOptions } from \"./did-space-like.js\";\nimport { normalizeId, validateId } from \"./did-utils.js\";\nimport { FsObjectStore } from \"./fs-object-store.js\";\nimport { DIDSpaceProvider } from \"./index.js\";\nimport { MetadataDB } from \"./metadata-db.js\";\nimport { PrefixedTreeIndex } from \"./prefixed-tree-index.js\";\nimport { referencedAnywhereLibsql } from \"./reference-check.js\";\nimport { DIDSpaceResolver } from \"./resolve.js\";\nimport { type GcStats, runGc } from \"./run-gc.js\";\nimport { deriveScope } from \"./scope-id.js\";\nimport type { ShareResolver } from \"./share-resolve.js\";\nimport { SqliteGcQueue } from \"./sqlite-gc-queue.js\";\nimport { SqliteResolveStore } from \"./sqlite-resolve-store.js\";\nimport { SQLiteTreeIndex } from \"./sqlite-tree-index.js\";\nimport type { TreeIndex } from \"./tree-index.js\";\nimport { SqliteUploadSessionStore } from \"./upload-session-store.js\";\n\n/**\n * Builds a {@link ShareResolver} for a user/owner space (share-out Phase 3).\n * The daemon-global share store is captured in the factory's closure (one\n * `_shares.db` across all owners); `scopeTreeIndex` is the per-space tree index\n * (WHOLE user tree, no prefix) the resolver uses for sidecar/.web pre-io checks.\n * Injected into `getUserSpace` (whole-space) AND `getInstanceSpace(role:\"user\")`\n * (the caller's own per-user blocklet fragment — `/user` mount + mobile-sync\n * target); the fragment provider remaps owner→userDid + restores the full decl\n * path. role:\"system\" instance spaces are NEVER shared (installer data).\n */\nexport type ShareResolverFactory = (scopeTreeIndex: TreeIndex) => ShareResolver;\n\nexport { normalizeId, validateId } from \"./did-utils.js\";\nexport { FsObjectStore } from \"./fs-object-store.js\";\nexport type { CidCacheRow, EntryRow } from \"./metadata-db.js\";\nexport { MetadataDB } from \"./metadata-db.js\";\nexport type { ReconcileOptions, ReconcileStats } from \"./reconcile.js\";\nexport { reconcile } from \"./reconcile.js\";\n// Node scope de-id migration (uses node:fs) — lives behind ./local, NOT index,\n// so it never reaches the CF worker bundle's node:fs stub (README §5.4).\nexport {\n migrateNodeMetadataDb,\n migrateNodeSpaces,\n type NodeScopeMigrationOptions,\n type NodeScopeMigrationReport,\n} from \"./scope-migration-node.js\";\nexport { SQLiteTreeIndex } from \"./sqlite-tree-index.js\";\n\n/** Local desktop device label written into changelog rows for local writes. */\nconst LOCAL_DEVICE_ID = \"desktop:default\";\n\nexport interface DIDSpaceOptions {\n /** Root directory for all DID spaces (e.g., ~/.afs/spaces) */\n rootPath: string;\n /** Owner DID of this space */\n userDid: string;\n /**\n * Per-deployment HMAC secret for scope de-identification (README §5.3). When\n * set, the storage scope — the directory name `{rootPath}/{scope}`, the\n * `_metadata.db` scope columns, and the `_shares.db` owner_did — becomes\n * `deriveScope(userDid, secret)`. When absent, scope stays the plaintext DID\n * (de-id off, back-compat). Each deployment has its own secret (§5.5).\n */\n scopeHmacSecret?: Uint8Array;\n /**\n * Share-out Phase 3: optional factory that builds a ShareResolver for this\n * space's `getUserSpace` provider (closure over the daemon-global share\n * store). Can also be late-injected via {@link DIDSpace.setShareResolverFactory}\n * for spaces constructed before the store exists (installer space).\n */\n shareResolverFactory?: ShareResolverFactory;\n /**\n * upload-limits Phase 5 per-scope byte quota (bytes). 0 / unset = OFF (the\n * opt-in default). Threaded into every SQLiteTreeIndex this space builds.\n * The Node daemon resolves it from `resolveUploadLimits(process.env)`.\n */\n maxBytesPerScope?: number;\n}\n\nexport type { InstanceSpaceOptions } from \"./did-space-like.js\";\n\nexport class DIDSpace implements DIDSpaceLike {\n private readonly rootPath: string;\n /** Real normalizeId(DID) — identity, returned to callers (NOT a storage key). */\n private readonly userDid: string;\n /**\n * Storage scope — `deriveScope(userDid, secret)` when de-id is on, else the\n * plaintext `userDid`. Drives the directory name (`userDir`), every\n * SQLiteTreeIndex scope, and `_shares.db` owner_did. Named `_scope` to mirror\n * the CF backend (cloudflare.ts).\n */\n private readonly _scope: string;\n readonly userDir: string;\n private readonly instanceSpaces = new Map<string, AFS>();\n private _resolver?: DIDSpaceResolver;\n /** Backing store for {@link resolver}; closed in {@link close}. */\n private _resolveStore?: SqliteResolveStore;\n\n /** User-level MetadataDB; lazy-opened on first getInstanceSpace/getUserSpace */\n private metadataDb: MetadataDB | null = null;\n /** Shared content-addressed object store (one objects/ dir per user space). */\n private objectStore: FsObjectStore | null = null;\n /**\n * `objectStore` wrapped with a content-addressed byte cache (#674 perf). Keyed\n * by cid (immutable) → no staleness, no TTL; bounded LRU keeps memory flat.\n * Persists for this DID-Space's lifetime (the Node daemon reuses it per user).\n */\n private cachingStore: ObjectStore | null = null;\n private readonly objectByteCache = new CidByteCache();\n /** GC queue over `_metadata.db` (deferred object cleanup); lazy, ensureTable once. */\n private gcQueue: SqliteGcQueue | null = null;\n /**\n * Share-out Phase 3: builds the ShareResolver for getUserSpace providers.\n * Read lazily at provider-construction time, so a late {@link\n * setShareResolverFactory} (installer space) still covers providers built\n * after the call. Undefined → no share-out (back-compat: dark-wired off).\n */\n private _shareResolverFactory?: ShareResolverFactory;\n /** upload-limits Phase 5 per-scope byte quota (0 = off, the opt-in default). */\n private readonly maxBytesPerScope: number;\n\n constructor(options: DIDSpaceOptions) {\n validateId(options.userDid);\n this.rootPath = options.rootPath;\n this.maxBytesPerScope = options.maxBytesPerScope ?? 0;\n this.userDid = normalizeId(options.userDid);\n // de-id opt-in (README §5.3): derive an opaque storage scope only when a\n // secret is provided; otherwise keep the plaintext DID (back-compat). The\n // directory name + all DB scope keys use `_scope`; identity uses `userDid`.\n this._scope = options.scopeHmacSecret\n ? deriveScope(this.userDid, options.scopeHmacSecret)\n : this.userDid;\n this.userDir = join(this.rootPath, this._scope);\n this._shareResolverFactory = options.shareResolverFactory;\n }\n\n /**\n * Storage scope — mirrors `CloudflareDIDSpace.scope` (cloudflare.ts:443).\n * De-id on → derived HMAC hash; de-id off → plaintext `userDid`.\n * Used by consumers (e.g. the Node daemon `/user/index` mount) that need\n * the same de-identified partition key used for physical storage.\n */\n get scope(): string {\n return this._scope;\n }\n\n /**\n * Construct a SQLiteTreeIndex for this scope with the per-scope byte quota\n * (upload-limits Phase 5) always injected — so every write path enforces it\n * without each call site threading it. 0 = quota off (the opt-in default).\n */\n private sqliteTreeIndex(metadataDb: MetadataDB, opts?: { inlineCache?: \"on\" }): SQLiteTreeIndex {\n return new SQLiteTreeIndex(metadataDb, this._scope, {\n ...opts,\n maxBytesPerScope: this.maxBytesPerScope,\n });\n }\n\n /**\n * Open a space DIRECTLY by its storage scope, WITHOUT deriving (README §5.6\n * `/spaces` naming contract). For `/spaces/{X}` inspection where `X` is already\n * the storage scope (a base32 hash under de-id, or a plaintext DID otherwise):\n * the directory `{rootPath}/{storageScope}` is opened verbatim, `did` returns\n * the opaque `storageScope`, and NO `deriveScope` runs (re-deriving would point\n * at a non-existent `{rootPath}/{derive(storageScope)}`). Constructing with no\n * secret guarantees `_scope === storageScope`. Callers mount it readonly.\n */\n static openStorageScope(opts: { rootPath: string; storageScope: string }): DIDSpace {\n return new DIDSpace({ rootPath: opts.rootPath, userDid: opts.storageScope });\n }\n\n /**\n * Late-inject the share-resolver factory (share-out Phase 3). Used for the\n * installer space, which `resolveLocalDIDSpace()` builds BEFORE the\n * daemon-global share store exists. Lazy provider construction means this\n * covers every getUserSpace provider built after the call.\n */\n setShareResolverFactory(factory: ShareResolverFactory): void {\n this._shareResolverFactory = factory;\n }\n\n /** Expose constructor options for daemon integration (rootPath, userDid). */\n get config(): Readonly<DIDSpaceOptions> {\n return { rootPath: this.rootPath, userDid: this.userDid };\n }\n\n /**\n * Lazily open (or reuse) the user-level `_metadata.db`.\n * Creates `userDir` if missing. Only one instance per DIDSpace.\n */\n async getMetadataDb(): Promise<MetadataDB> {\n if (this.metadataDb) return this.metadataDb;\n mkdirSync(this.userDir, { recursive: true });\n this.metadataDb = await MetadataDB.open(this.userDir);\n return this.metadataDb;\n }\n\n /**\n * Upload-plane deps for the user-facing provider (design §7 / §15 decision 8).\n * Node is FALLBACK-ONLY: it implements the actions + SQLite session table\n * (protocol parity) but injects NO signer, so `prepare-upload` returns\n * `grant: null` and the native client falls back to batchWrite.\n */\n private uploadDeps(metadataDb: MetadataDB) {\n return {\n uploadSessionStore: new SqliteUploadSessionStore(metadataDb.rawClient),\n uploadScope: this._scope,\n };\n }\n\n /**\n * Lazily create the shared FsObjectStore rooted at `{userDir}/objects`, wrapped\n * with the content-addressed byte cache. Returns the caching wrapper so every\n * provider read benefits; the raw {@link objectStore} field is kept only so\n * {@link close} can drop it.\n */\n private getObjectStore(): ObjectStore {\n if (!this.cachingStore) {\n mkdirSync(this.userDir, { recursive: true });\n this.objectStore = new FsObjectStore(this.userDir);\n this.cachingStore = new CachingObjectStore(this.objectStore, this.objectByteCache);\n }\n return this.cachingStore;\n }\n\n /**\n * Lazily create the `SqliteGcQueue` over the user-level `_metadata.db`\n * (via the `MetadataDB.rawClient` getter — never reaches into private fields).\n * One queue per DIDSpace; injected into every DIDSpaceProvider so overwrites/\n * deletes enqueue the orphaned oldCid for the deferred sweep.\n */\n private async getGcQueue(): Promise<SqliteGcQueue> {\n if (!this.gcQueue) {\n const metadataDb = await this.getMetadataDb();\n const queue = new SqliteGcQueue(metadataDb.rawClient);\n await queue.ensureTable();\n this.gcQueue = queue;\n }\n return this.gcQueue;\n }\n\n /**\n * Run one object-GC sweep over this space's single owner scope (design §4.6).\n * Scope-agnostic: `referencedAnywhere` is scope-blind over the one\n * `_metadata.db`, so one sweep covers the whole daemon. Used by both the\n * `arc space gc` CLI and the daemon's scheduled GC task (AFSScheduler, #742 S3).\n */\n async runGc(opts?: {\n graceMs?: number;\n limit?: number;\n log?: (line: string) => void;\n }): Promise<GcStats> {\n const metadataDb = await this.getMetadataDb();\n const queue = await this.getGcQueue();\n const stats = await runGc({\n queue,\n referencedAnywhere: (cid, nowMs) =>\n referencedAnywhereLibsql(metadataDb.rawClient, cid, nowMs),\n objectStore: this.getObjectStore(),\n graceMs: opts?.graceMs,\n limit: opts?.limit,\n log: opts?.log,\n });\n // Sweep terminal upload sessions (committed, aborted, expired-prepared) once\n // GC finishes. Their expires_at is the 2-hour window from session creation;\n // past that, the records serve no purpose (GC in-flight protection already\n // requires expires_at > nowMs, so expired sessions never blocked GC anyway).\n await new SqliteUploadSessionStore(metadataDb.rawClient).sweepExpired(Date.now());\n return stats;\n }\n\n /**\n * Back-compat no-op: the eventual-mirror subscriber is gone (the provider now\n * writes entries + changelog atomically), so there is no in-flight work to\n * drain. Kept so existing test/daemon call sites don't break.\n */\n async drainSubscribers(): Promise<void> {\n return;\n }\n\n /**\n * Get an isolated AFS instance for a blocklet instance under this DID Space,\n * scoped by role (system = installer-side data, user = caller-side data).\n *\n * Mounts a single `DIDSpaceProvider` whose treeIndex is a `PrefixedTreeIndex`\n * over the user-scoped `SQLiteTreeIndex` at `blocklets/{i}/{role}/`. The\n * prefix is MANDATORY — without it the role root would degrade to the whole\n * space root (data越界). Blobs share the user-level `FsObjectStore`.\n *\n * `userDid` is @deprecated for role:\"user\" — per-user isolation comes from\n * WHICH space backs the call (the space is already the caller's), so it never\n * enters the path; validated only as defense if supplied.\n */\n async getInstanceSpace(opts: InstanceSpaceOptions): Promise<AFS> {\n validateId(opts.instanceDid);\n if (opts.role !== \"system\" && opts.role !== \"user\") {\n throw new Error(`getInstanceSpace: invalid role \"${opts.role}\"`);\n }\n if (opts.role === \"user\" && opts.userDid != null) {\n validateId(opts.userDid);\n }\n\n const bareInstanceId = normalizeId(opts.instanceDid);\n const cacheKey = opts.role === \"system\" ? `system:${bareInstanceId}` : `user:${bareInstanceId}`;\n const existing = this.instanceSpaces.get(cacheKey);\n if (existing) return existing;\n\n const prefix = `blocklets/${bareInstanceId}/${opts.role}/`;\n const accessMode: AFSAccessMode = opts.accessMode ?? \"readwrite\";\n\n const metadataDb = await this.getMetadataDb();\n // inlineCache \"on\" = write-through (settings-layering 1d): text content\n // ≤ threshold rides the index row as a cid-verified read-acceleration\n // copy. The `objects/{cid}` blob is ALWAYS written regardless\n // (cas-blob-always-written) — sync/GC semantics are untouched.\n const treeIndex = new PrefixedTreeIndex(\n this.sqliteTreeIndex(metadataDb, { inlineCache: \"on\" }),\n prefix,\n );\n const gcQueue = await this.getGcQueue();\n\n // Share-out (symmetric with CloudflareDIDSpace.getInstanceSpace): a `.share`\n // written into the caller's OWN per-user fragment (role:\"user\" — the `/user`\n // session mount + the mobile-sync target) reconciles into the shares index.\n // The resolver is built over a NO-PREFIX whole-user-tree index (NOT the\n // prefixed `treeIndex` above) so sidecar/.web checks see full paths; the\n // provider remaps owner→userDid + restores the full decl path via the\n // prefix. role:\"system\" stays excluded (installer data is never public).\n const shareResolver =\n opts.role === \"user\"\n ? this._shareResolverFactory?.(this.sqliteTreeIndex(metadataDb, { inlineCache: \"on\" }))\n : undefined;\n\n const afs = new AFS();\n await afs.mount(\n new DIDSpaceProvider({\n did: bareInstanceId,\n objectStore: this.getObjectStore(),\n treeIndex,\n name: \"content\",\n accessMode,\n deviceId: LOCAL_DEVICE_ID,\n gcQueue,\n // Share-out + blocklet publish (#674) on the caller's own `/user`\n // fragment. shareTreePrefix restores the whole-tree physical path for\n // BOTH hooks; set for all role:\"user\" fragments so the blocklet hook\n // captures the full path even when shares are off. blocklet owner =\n // PLAINTEXT userDid (the instance `did` would be wrong — serve re-derives\n // the scope from it).\n ...(opts.role === \"user\"\n ? {\n shareTreePrefix: prefix,\n ...(shareResolver ? { shareResolver, shareOwnerDid: this._scope } : {}),\n blockletResolver: this.resolver,\n blockletOwnerDid: this.userDid,\n }\n : {}),\n // Upload plane only on the caller's own `/user` fragment (fallback-only\n // on Node: no signer). role:\"system\" (installer) never gets it.\n ...(opts.role === \"user\" ? this.uploadDeps(metadataDb) : {}),\n }),\n \"/\",\n );\n\n this.instanceSpaces.set(cacheKey, afs);\n return afs;\n }\n\n /**\n * Source-side dirty queue for an instance's `/user` fragment\n * (index-follower-dirty-path §7, §9.2). Returns a `PrefixedTreeIndex` over the\n * user-level `_metadata.db` (where `ds_changelog` + `ds_dirty_path` live), so\n * the daemon can drain the merged dirty projection instead of replaying\n * `/.changelog`. This is a RUNTIME-INTERNAL capability held directly by the\n * daemon's follower — it is NEVER exposed as a public `/user` AFS action\n * (ack/fail mutate scheduling state). Node has no D1 replica, so a single\n * primary `_metadata.db` read is already read-your-write. The index writes stay\n * in the separate `user-index.db`; only source/dirty rows are read here.\n */\n async getInstanceDirtyQueue(opts: {\n instanceDid: string;\n role: \"user\";\n }): Promise<PrefixedTreeIndex> {\n validateId(opts.instanceDid);\n const bareInstanceId = normalizeId(opts.instanceDid);\n const prefix = `blocklets/${bareInstanceId}/${opts.role}/`;\n const metadataDb = await this.getMetadataDb();\n return new PrefixedTreeIndex(this.sqliteTreeIndex(metadataDb), prefix);\n }\n\n /**\n * Config fragment for an instance — a sibling of the role content tree at\n * `blocklets/<i>/config/`, mounted at `/`. Runtime-internal (mount overrides\n * etc.); NOT part of `/instance`. Stays a plain `AFSFS` blob fragment: config\n * is opaque machine state, not content-addressed sync data (design §2.F/T3.9).\n */\n async getConfigSpace(instanceDid: string): Promise<AFS> {\n validateId(instanceDid);\n const bareInstanceId = normalizeId(instanceDid);\n const configDir = join(this.userDir, \"blocklets\", bareInstanceId, \"config\");\n mkdirSync(configDir, { recursive: true });\n const afs = new AFS();\n await afs.mount(\n new AFSFS({ localPath: configDir, name: \"config\", accessMode: \"readwrite\" }),\n \"/\",\n );\n return afs;\n }\n\n /**\n * List all instance DIDs that have storage in this user's DID space.\n *\n * Enumerated from the index (the source of truth) rather than `readdir` of\n * the physical tree — the physical layout is now `objects/{cid}` + index, so\n * there is no per-instance directory to walk. Direct children of the\n * `blocklets/` prefix are the instance ids (trailing-slash dir rows).\n */\n async listApps(): Promise<string[]> {\n const metadataDb = await this.getMetadataDb();\n const index = this.sqliteTreeIndex(metadataDb);\n const result = await index.listChildren(\"blocklets/\");\n // Children come back as \"blocklets/<i>/\" (dir) — strip prefix + trailing /.\n const ids = new Set<string>();\n for (const entry of result.entries) {\n const rel = entry.path.slice(\"blocklets/\".length).replace(/\\/$/, \"\");\n if (rel) ids.add(rel);\n }\n return [...ids];\n }\n\n /**\n * Resolve table — persistent + DAEMON-GLOBAL (`{rootPath}/_resolve.db`, NOT\n * the per-space `_metadata.db`): `blocklet/<id> → owner` / `domain/<host> → …`\n * are caller-less reverse lookups that need ONE shared registry, exactly like\n * `_shares.db` and CF's single shared D1. Backed by `SqliteResolveStore` so\n * entries survive across DID-Space instances and daemon restarts — symmetric\n * with `CloudflareDIDSpace.resolver` (D1ResolveStore). Closed in {@link close}.\n */\n get resolver(): DIDSpaceResolver {\n if (!this._resolver) {\n this._resolveStore = new SqliteResolveStore(join(this.rootPath, \"_resolve.db\"));\n this._resolver = new DIDSpaceResolver(this._resolveStore);\n }\n return this._resolver;\n }\n\n /**\n * Whole-space root (contract alias `getSpaceRoot`; legacy name `getUserSpace`)\n * — a `DIDSpaceProvider` over the NO-PREFIX user-scoped `SQLiteTreeIndex`, so\n * it sees every instance fragment in the space. Used by `arc space`\n * export/import/diff (CLI manifest source) and observability. Mobile sync does\n * NOT use it (mobile goes through the aside `/user` fragment).\n */\n async getSpaceRoot(options?: { accessMode?: AFSAccessMode }): Promise<AFS> {\n return this.getUserSpace(options);\n }\n\n async getUserSpace(options?: { accessMode?: AFSAccessMode }): Promise<AFS> {\n const metadataDb = await this.getMetadataDb();\n const gcQueue = await this.getGcQueue();\n const afs = new AFS();\n // Write-through on (1d) — see getInstanceSpace.\n const scopeTreeIndex = this.sqliteTreeIndex(metadataDb, { inlineCache: \"on\" });\n await afs.mount(\n new DIDSpaceProvider({\n did: this.userDid,\n objectStore: this.getObjectStore(),\n treeIndex: scopeTreeIndex,\n name: \"did-space-user\",\n accessMode: options?.accessMode ?? \"readonly\",\n deviceId: LOCAL_DEVICE_ID,\n gcQueue,\n // Share-out Phase 3: the whole-space provider gets a ShareResolver\n // (write hook + `.shares/` virtual view). The per-user instance fragment\n // (getInstanceSpace role:\"user\") ALSO reconciles `.share` writes — see\n // there — but role:\"system\" never does. Read the factory lazily so a\n // late setShareResolverFactory still applies.\n shareResolver: this._shareResolverFactory?.(scopeTreeIndex),\n // #674: blocklet publish/resolve on whole-space writes (e.g.\n // `arc deploy --publish` local → `/blocklets/<id>/blocklet.yaml`).\n // owner = plaintext userDid (== this.did here, whole-space provider).\n blockletResolver: this.resolver,\n blockletOwnerDid: this.userDid,\n ...this.uploadDeps(metadataDb),\n }),\n \"/\",\n );\n\n // buildManifestFromDB reads the index (SoT) directly; the context just\n // hands it the metadataDb + scope (no FS reconcile anymore — see rsync.ts).\n attachMetadataContext(afs, {\n metadataDb,\n userDir: this.userDir,\n scope: this._scope,\n });\n return afs;\n }\n\n /**\n * Share-out Phase 4: a READONLY AFS for public `/s/{slug}` serving, scoped by\n * `prefix` (with trailing slash, e.g. `docs/`) or — when undefined — the whole\n * owner tree (root share + sidecar). Reuses THIS space's metadataDb +\n * objectStore (no second libsql client on `_metadata.db`, which would race).\n * No shareResolver injected: serving is read-only, never reconciles.\n */\n async getShareServeAFS(prefix?: string): Promise<AFS> {\n const metadataDb = await this.getMetadataDb();\n const base = this.sqliteTreeIndex(metadataDb, { inlineCache: \"on\" });\n const treeIndex = prefix ? new PrefixedTreeIndex(base, prefix) : base;\n const afs = new AFS();\n await afs.mount(\n new DIDSpaceProvider({\n did: this.userDid,\n objectStore: this.getObjectStore(),\n treeIndex,\n name: \"did-space-share-serve\",\n accessMode: \"readonly\",\n deviceId: LOCAL_DEVICE_ID,\n }),\n \"/\",\n );\n return afs;\n }\n\n /**\n * Clean up resources. Closes the user-level MetadataDB and clears the\n * instance-space cache. No event subscriptions to tear down anymore.\n */\n close(): void {\n if (this.metadataDb) {\n try {\n this.metadataDb.close();\n } catch {\n // best effort — DB may already be closed\n }\n this.metadataDb = null;\n }\n if (this._resolveStore) {\n // async close (libsql client) — fire-and-forget; nothing awaits teardown.\n void this._resolveStore.close();\n this._resolveStore = undefined;\n this._resolver = undefined;\n }\n this.gcQueue = null;\n this.objectStore = null;\n this.cachingStore = null;\n this.instanceSpaces.clear();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+
2
+ //#region src/memory-gc-queue.ts
3
+ /** Matches `D1GCQueue` / `SqliteGcQueue` (2h) for cross-impl parity. */
4
+ const DEFAULT_GRACE_MS = 7200 * 1e3;
5
+ var MemoryGcQueue = class {
6
+ /** cid → queued_at (epoch ms). A Map gives idempotent enqueue for free. */
7
+ rows = /* @__PURE__ */ new Map();
8
+ async ensureTable() {}
9
+ async enqueue(cids) {
10
+ await this.enqueueAt(cids, Date.now());
11
+ }
12
+ /**
13
+ * Enqueue with an explicit `queued_at` — used by the conformance suite to
14
+ * exercise the grace boundary deterministically. Not part of `GcQueue`.
15
+ * Idempotent: an existing cid keeps its original `queued_at`.
16
+ */
17
+ async enqueueAt(cids, queuedAt) {
18
+ for (const cid of cids) if (!this.rows.has(cid)) this.rows.set(cid, queuedAt);
19
+ }
20
+ async fetchCandidates(limit = 500, graceMs = DEFAULT_GRACE_MS) {
21
+ if (limit === 0) return [];
22
+ const cutoff = Date.now() - graceMs;
23
+ const out = [];
24
+ for (const [cid, queuedAt] of this.rows) if (queuedAt <= cutoff) {
25
+ out.push({ cid });
26
+ if (out.length >= limit) break;
27
+ }
28
+ return out;
29
+ }
30
+ async remove(cid) {
31
+ this.rows.delete(cid);
32
+ }
33
+ };
34
+
35
+ //#endregion
36
+ exports.MemoryGcQueue = MemoryGcQueue;
@@ -0,0 +1,22 @@
1
+ import { GcQueue } from "./gc-queue.cjs";
2
+
3
+ //#region src/memory-gc-queue.d.ts
4
+ declare class MemoryGcQueue implements GcQueue {
5
+ /** cid → queued_at (epoch ms). A Map gives idempotent enqueue for free. */
6
+ private readonly rows;
7
+ ensureTable(): Promise<void>;
8
+ enqueue(cids: string[]): Promise<void>;
9
+ /**
10
+ * Enqueue with an explicit `queued_at` — used by the conformance suite to
11
+ * exercise the grace boundary deterministically. Not part of `GcQueue`.
12
+ * Idempotent: an existing cid keeps its original `queued_at`.
13
+ */
14
+ enqueueAt(cids: string[], queuedAt: number): Promise<void>;
15
+ fetchCandidates(limit?: number, graceMs?: number): Promise<Array<{
16
+ cid: string;
17
+ }>>;
18
+ remove(cid: string): Promise<void>;
19
+ }
20
+ //#endregion
21
+ export { MemoryGcQueue };
22
+ //# sourceMappingURL=memory-gc-queue.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-gc-queue.d.cts","names":[],"sources":["../src/memory-gc-queue.ts"],"mappings":";;;cAaa,aAAA,YAAyB,OAAA;EAuBoC;EAAA,iBArBvD,IAAA;EAEX,WAAA,CAAA,GAAe,OAAA;EAIf,OAAA,CAAQ,IAAA,aAAiB,OAAA;EARY;;;;;EAiBrC,SAAA,CAAU,IAAA,YAAgB,QAAA,WAAmB,OAAA;EAM7C,eAAA,CAAgB,KAAA,WAAa,OAAA,YAA6B,OAAA,CAAQ,KAAA;IAAQ,GAAA;EAAA;EAa1E,MAAA,CAAO,GAAA,WAAc,OAAA;AAAA"}
@@ -0,0 +1,22 @@
1
+ import { GcQueue } from "./gc-queue.mjs";
2
+
3
+ //#region src/memory-gc-queue.d.ts
4
+ declare class MemoryGcQueue implements GcQueue {
5
+ /** cid → queued_at (epoch ms). A Map gives idempotent enqueue for free. */
6
+ private readonly rows;
7
+ ensureTable(): Promise<void>;
8
+ enqueue(cids: string[]): Promise<void>;
9
+ /**
10
+ * Enqueue with an explicit `queued_at` — used by the conformance suite to
11
+ * exercise the grace boundary deterministically. Not part of `GcQueue`.
12
+ * Idempotent: an existing cid keeps its original `queued_at`.
13
+ */
14
+ enqueueAt(cids: string[], queuedAt: number): Promise<void>;
15
+ fetchCandidates(limit?: number, graceMs?: number): Promise<Array<{
16
+ cid: string;
17
+ }>>;
18
+ remove(cid: string): Promise<void>;
19
+ }
20
+ //#endregion
21
+ export { MemoryGcQueue };
22
+ //# sourceMappingURL=memory-gc-queue.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-gc-queue.d.mts","names":[],"sources":["../src/memory-gc-queue.ts"],"mappings":";;;cAaa,aAAA,YAAyB,OAAA;EAuBoC;EAAA,iBArBvD,IAAA;EAEX,WAAA,CAAA,GAAe,OAAA;EAIf,OAAA,CAAQ,IAAA,aAAiB,OAAA;EARY;;;;;EAiBrC,SAAA,CAAU,IAAA,YAAgB,QAAA,WAAmB,OAAA;EAM7C,eAAA,CAAgB,KAAA,WAAa,OAAA,YAA6B,OAAA,CAAQ,KAAA;IAAQ,GAAA;EAAA;EAa1E,MAAA,CAAO,GAAA,WAAc,OAAA;AAAA"}
@@ -0,0 +1,36 @@
1
+ //#region src/memory-gc-queue.ts
2
+ /** Matches `D1GCQueue` / `SqliteGcQueue` (2h) for cross-impl parity. */
3
+ const DEFAULT_GRACE_MS = 7200 * 1e3;
4
+ var MemoryGcQueue = class {
5
+ /** cid → queued_at (epoch ms). A Map gives idempotent enqueue for free. */
6
+ rows = /* @__PURE__ */ new Map();
7
+ async ensureTable() {}
8
+ async enqueue(cids) {
9
+ await this.enqueueAt(cids, Date.now());
10
+ }
11
+ /**
12
+ * Enqueue with an explicit `queued_at` — used by the conformance suite to
13
+ * exercise the grace boundary deterministically. Not part of `GcQueue`.
14
+ * Idempotent: an existing cid keeps its original `queued_at`.
15
+ */
16
+ async enqueueAt(cids, queuedAt) {
17
+ for (const cid of cids) if (!this.rows.has(cid)) this.rows.set(cid, queuedAt);
18
+ }
19
+ async fetchCandidates(limit = 500, graceMs = DEFAULT_GRACE_MS) {
20
+ if (limit === 0) return [];
21
+ const cutoff = Date.now() - graceMs;
22
+ const out = [];
23
+ for (const [cid, queuedAt] of this.rows) if (queuedAt <= cutoff) {
24
+ out.push({ cid });
25
+ if (out.length >= limit) break;
26
+ }
27
+ return out;
28
+ }
29
+ async remove(cid) {
30
+ this.rows.delete(cid);
31
+ }
32
+ };
33
+
34
+ //#endregion
35
+ export { MemoryGcQueue };
36
+ //# sourceMappingURL=memory-gc-queue.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-gc-queue.mjs","names":[],"sources":["../src/memory-gc-queue.ts"],"sourcesContent":["/**\n * MemoryGcQueue — in-memory `GcQueue` for unit tests.\n *\n * Pairs with `MemoryObjectStore` (`@aigne/afs-r2`) and `MemoryTreeIndex` so the\n * shared `runGc` can be exercised with zero I/O. Pure storage — no reference\n * method (design §4.4).\n */\n\nimport type { GcQueue } from \"./gc-queue.js\";\n\n/** Matches `D1GCQueue` / `SqliteGcQueue` (2h) for cross-impl parity. */\nconst DEFAULT_GRACE_MS = 2 * 60 * 60 * 1000;\n\nexport class MemoryGcQueue implements GcQueue {\n /** cid → queued_at (epoch ms). A Map gives idempotent enqueue for free. */\n private readonly rows = new Map<string, number>();\n\n async ensureTable(): Promise<void> {\n // no-op — the Map is the table.\n }\n\n async enqueue(cids: string[]): Promise<void> {\n await this.enqueueAt(cids, Date.now());\n }\n\n /**\n * Enqueue with an explicit `queued_at` — used by the conformance suite to\n * exercise the grace boundary deterministically. Not part of `GcQueue`.\n * Idempotent: an existing cid keeps its original `queued_at`.\n */\n async enqueueAt(cids: string[], queuedAt: number): Promise<void> {\n for (const cid of cids) {\n if (!this.rows.has(cid)) this.rows.set(cid, queuedAt);\n }\n }\n\n async fetchCandidates(limit = 500, graceMs = DEFAULT_GRACE_MS): Promise<Array<{ cid: string }>> {\n if (limit === 0) return [];\n const cutoff = Date.now() - graceMs;\n const out: Array<{ cid: string }> = [];\n for (const [cid, queuedAt] of this.rows) {\n if (queuedAt <= cutoff) {\n out.push({ cid });\n if (out.length >= limit) break;\n }\n }\n return out;\n }\n\n async remove(cid: string): Promise<void> {\n this.rows.delete(cid);\n }\n}\n"],"mappings":";;AAWA,MAAM,mBAAmB,OAAc;AAEvC,IAAa,gBAAb,MAA8C;;CAE5C,AAAiB,uBAAO,IAAI,KAAqB;CAEjD,MAAM,cAA6B;CAInC,MAAM,QAAQ,MAA+B;AAC3C,QAAM,KAAK,UAAU,MAAM,KAAK,KAAK,CAAC;;;;;;;CAQxC,MAAM,UAAU,MAAgB,UAAiC;AAC/D,OAAK,MAAM,OAAO,KAChB,KAAI,CAAC,KAAK,KAAK,IAAI,IAAI,CAAE,MAAK,KAAK,IAAI,KAAK,SAAS;;CAIzD,MAAM,gBAAgB,QAAQ,KAAK,UAAU,kBAAmD;AAC9F,MAAI,UAAU,EAAG,QAAO,EAAE;EAC1B,MAAM,SAAS,KAAK,KAAK,GAAG;EAC5B,MAAM,MAA8B,EAAE;AACtC,OAAK,MAAM,CAAC,KAAK,aAAa,KAAK,KACjC,KAAI,YAAY,QAAQ;AACtB,OAAI,KAAK,EAAE,KAAK,CAAC;AACjB,OAAI,IAAI,UAAU,MAAO;;AAG7B,SAAO;;CAGT,MAAM,OAAO,KAA4B;AACvC,OAAK,KAAK,OAAO,IAAI"}