@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
@@ -0,0 +1,507 @@
1
+ import { applyBlockletDeleteHook, applyBlockletWriteHook } from "./blocklet-hook.mjs";
2
+ import { computeCID } from "./cid.mjs";
3
+ import { normalizeId, validateId } from "./did-utils.mjs";
4
+ import { detectMimeType } from "./mime-utils.mjs";
5
+ import { decodeInlineText } from "./inline-content.mjs";
6
+ import { isShareReservedPath } from "./share-declaration.mjs";
7
+ import { applyShareDeleteHook, applyShareWriteHook } from "./share-hook.mjs";
8
+ import { D1UploadSessionStore } from "./upload-session-store.mjs";
9
+ import { CachingObjectStore, CidByteCache } from "./cid-byte-cache.mjs";
10
+ import { D1GCQueue } from "./d1-gc-queue.mjs";
11
+ import { D1ResolveStore } from "./d1-resolve-store.mjs";
12
+ import { objectKeyForCid } from "./object-key.mjs";
13
+ import { D1TreeIndex } from "./d1-tree-index.mjs";
14
+ import { PrefixedTreeIndex } from "./prefixed-tree-index.mjs";
15
+ import { DIDSpaceResolver } from "./resolve.mjs";
16
+ import { deriveScope } from "./scope-id.mjs";
17
+ import { DIDSpaceProvider } from "./index.mjs";
18
+ import { AFS, AFSForbiddenError, nsLog } from "@aigne/afs";
19
+
20
+ //#region src/cloudflare.ts
21
+ /**
22
+ * CloudflareDIDSpace — Cloudflare backend for DID-scoped storage.
23
+ *
24
+ * Composes DIDSpaceProvider (files via R2 + D1 TreeIndex) per app,
25
+ * with logical isolation (separate D1 scope per user+app DID).
26
+ *
27
+ * Same interface as local DIDSpace for 100% compatibility.
28
+ *
29
+ * Usage:
30
+ * const space = new CloudflareDIDSpace({ userDid: "did:abt:z1...", objectStore, indexDb });
31
+ * const afs = await space.getInstanceSpace({ instanceDid: "z1app", role: "system" });
32
+ * await afs.write("/hello.txt", { content: "hello" });
33
+ */
34
+ /** provider:did-space structured logger (debug self-guards; off by default). */
35
+ const log = nsLog("provider:did-space");
36
+ var CloudflareDIDSpace = class {
37
+ /** Real normalizeId(DID) — identity, returned to callers (NOT a storage key). */
38
+ userDid;
39
+ /**
40
+ * Storage scope — `deriveScope(userDid, secret)` when de-id is on, else the
41
+ * plaintext `userDid`. Every D1 storage/query key uses this, never `userDid`.
42
+ * Named `_scope` to avoid colliding with the `get scope()` accessor (README §4.1).
43
+ */
44
+ _scope;
45
+ objectStore;
46
+ indexDb;
47
+ _resolver;
48
+ /** Optional ShareResolver for .share reconcile hook (Phase 0.4) */
49
+ shareResolver;
50
+ /** Read replica opt-in for read-serving paths only — see options doc. */
51
+ useReplica;
52
+ /** upload-limits Phase 5 per-scope byte quota (0 = off, the opt-in default). */
53
+ maxBytesPerScope;
54
+ /**
55
+ * Instance space cache keyed by (role, useReplica) — same shape as `local.ts`
56
+ * `instanceSpaces`. Without it, `getInstanceSpace` returns a fresh `AFS`
57
+ * (and fresh `EventBus`) every call, so propBind subscribe (registered on
58
+ * AFS_A at session start) and the agent's subsequent write (executing on
59
+ * AFS_B for that exec) cross different buses — the write's `afs:write`
60
+ * event never reaches the subscriber, so the live patch silently drops and
61
+ * the user only sees the new value after a manual page reload (Layer 2
62
+ * staleness on staging). Bounded by the lifetime of this DID-Space (per
63
+ * caller in the resolver), the same memory budget the resolver already
64
+ * spends on `spaces`.
65
+ */
66
+ instanceSpaces = /* @__PURE__ */ new Map();
67
+ /**
68
+ * User-space cache keyed by (accessMode, useReplica) — the getUserSpace
69
+ * counterpart of {@link instanceSpaces}. Without it, every `getUserSpace()`
70
+ * builds a fresh `AFS` and re-runs `AFS.mount()`'s async provider health check
71
+ * (a root `stat` → `countChildren` + list). Blocklet serve calls getUserSpace
72
+ * once PER stage (reader / afsRoot), so a cold page paid that root enumeration
73
+ * 3× (issue #808 #4). Caching the handle reuses the mounted provider; data
74
+ * reads stay fresh (path→cid D1 lookups are never cached), so zero staleness.
75
+ */
76
+ userSpaces = /* @__PURE__ */ new Map();
77
+ /** Shared GC queue instance — created once per CloudflareDIDSpace instance */
78
+ gcQueue;
79
+ /** Injected presigned-PUT signer (upload plane). Undefined → no grant capability. */
80
+ signer;
81
+ constructor(options) {
82
+ validateId(options.userDid);
83
+ this.userDid = normalizeId(options.userDid);
84
+ this._scope = options.scopeHmacSecret ? deriveScope(this.userDid, options.scopeHmacSecret) : this.userDid;
85
+ this.objectStore = new CachingObjectStore(options.objectStore, new CidByteCache());
86
+ this.indexDb = options.indexDb;
87
+ this.gcQueue = new D1GCQueue(options.indexDb);
88
+ this.shareResolver = options.shareResolver;
89
+ this.useReplica = options.useReplica ?? false;
90
+ this.maxBytesPerScope = options.maxBytesPerScope ?? 0;
91
+ this.signer = options.signer;
92
+ }
93
+ /**
94
+ * Construct a D1TreeIndex for this scope with the per-scope byte quota
95
+ * (upload-limits Phase 5) always injected — so every write path enforces it
96
+ * uniformly without each call site remembering to thread it. 0 = quota off.
97
+ */
98
+ makeTreeIndex(opts) {
99
+ return new D1TreeIndex(this.indexDb, this._scope, {
100
+ ...opts,
101
+ maxBytesPerScope: this.maxBytesPerScope
102
+ });
103
+ }
104
+ /**
105
+ * The injected presigned-PUT signer, or undefined when no R2 signer
106
+ * credentials are configured. The seam through which `prepare-upload` (P2)
107
+ * mints upload grants; also the capability signal clients use to decide
108
+ * grant-path vs batchWrite fallback (§15 decision 7/8).
109
+ */
110
+ get presignSigner() {
111
+ return this.signer;
112
+ }
113
+ /**
114
+ * Upload-plane deps for the user-facing `DIDSpaceProvider` (design §7): the
115
+ * injected signer (CF mints grants; absent → fallback), a D1-backed session
116
+ * store, and the de-identified storage scope written into `upload_sessions`.
117
+ * Only the user space gets these — `/instance` (installer) + config don't.
118
+ */
119
+ uploadDeps() {
120
+ return {
121
+ signer: this.signer,
122
+ uploadSessionStore: new D1UploadSessionStore(this.indexDb),
123
+ uploadScope: this._scope
124
+ };
125
+ }
126
+ /**
127
+ * Get an isolated AFS instance for a blocklet instance under this DID Space,
128
+ * scoped by role (system = installer-side data, user = caller-side data).
129
+ *
130
+ * Layout (PrefixedTreeIndex prefix relative to `entries.scope = userDid`):
131
+ * role:"system" → blocklets/{instanceDid}/system/ (content tree, mounted at `/`)
132
+ * role:"user" → blocklets/{instanceDid}/user/ (content tree, mounted at `/`)
133
+ * config → blocklets/{instanceDid}/config/ (sibling fragment, getConfigSpace)
134
+ *
135
+ * /files-prefix-removal: content tree is the role root (no `files/` wrapper);
136
+ * config is a sibling fragment (invisible to `/instance`); vault removed.
137
+ *
138
+ * did-space-composition Phase 2 (symmetric with the local backend): the user
139
+ * fragment is a SINGLE `user` subtree. Per-user isolation comes from scope =
140
+ * caller's userDid (the resolver hands back a CloudflareDIDSpace scoped to the
141
+ * caller), so a `users/{userDid}` path segment would be redundant. `userDid`
142
+ * is @deprecated for this call and ignored for the path.
143
+ *
144
+ * Cached per (instanceDid, role, useReplica) — first caller's accessMode
145
+ * wins for that triple (mirrors local.ts). See `instanceSpaces` for the
146
+ * live-update rationale.
147
+ */
148
+ async getInstanceSpace(opts) {
149
+ log.debug({
150
+ message: "getInstanceSpace",
151
+ instanceDid: opts.instanceDid,
152
+ role: opts.role
153
+ });
154
+ validateId(opts.instanceDid);
155
+ if (opts.role !== "system" && opts.role !== "user") throw new Error(`getInstanceSpace: invalid role "${opts.role}"`);
156
+ if (opts.role === "user" && opts.userDid != null) validateId(opts.userDid);
157
+ const bareInstanceId = normalizeId(opts.instanceDid);
158
+ const useReplica = opts.consistency === "primary" ? false : this.useReplica;
159
+ const cacheKey = `${bareInstanceId}::${opts.role}::${useReplica ? "r" : "p"}`;
160
+ const cached = this.instanceSpaces.get(cacheKey);
161
+ if (cached) return cached;
162
+ const subtree = opts.role === "system" ? `blocklets/${bareInstanceId}/system` : `blocklets/${bareInstanceId}/user`;
163
+ const accessMode = opts.accessMode ?? "readwrite";
164
+ const afs = new AFS();
165
+ const baseTreeIndex = this.makeTreeIndex({
166
+ useReplica,
167
+ inlineCache: "on"
168
+ });
169
+ const fileProvider = new DIDSpaceProvider({
170
+ did: bareInstanceId,
171
+ objectStore: this.objectStore,
172
+ treeIndex: new PrefixedTreeIndex(baseTreeIndex, `${subtree}/`),
173
+ name: "content",
174
+ accessMode,
175
+ gcQueue: this.gcQueue,
176
+ ...opts.role === "user" ? {
177
+ shareTreePrefix: `${subtree}/`,
178
+ ...this.shareResolver ? {
179
+ shareResolver: this.shareResolver,
180
+ shareOwnerDid: this._scope
181
+ } : {},
182
+ blockletResolver: this.resolver,
183
+ blockletOwnerDid: this.userDid
184
+ } : {},
185
+ ...opts.role === "user" ? this.uploadDeps() : {}
186
+ });
187
+ await afs.mount(fileProvider, "/", { skipHealthCheck: true });
188
+ this.instanceSpaces.set(cacheKey, afs);
189
+ return afs;
190
+ }
191
+ /**
192
+ * Runtime-internal config fragment (`blocklets/<i>/config/`, mounted at
193
+ * `/`). Mirrors local `getConfigSpace`. Invisible to `/instance`; holds
194
+ * per-instance machine state like `mounts.toml`.
195
+ */
196
+ async getConfigSpace(instanceDid) {
197
+ validateId(instanceDid);
198
+ const bareInstanceId = normalizeId(instanceDid);
199
+ const afs = new AFS();
200
+ const baseTreeIndex = this.makeTreeIndex({
201
+ useReplica: this.useReplica,
202
+ inlineCache: "on"
203
+ });
204
+ const configProvider = new DIDSpaceProvider({
205
+ did: bareInstanceId,
206
+ objectStore: this.objectStore,
207
+ treeIndex: new PrefixedTreeIndex(baseTreeIndex, `blocklets/${bareInstanceId}/config/`),
208
+ name: "config",
209
+ accessMode: "readwrite",
210
+ gcQueue: this.gcQueue
211
+ });
212
+ await afs.mount(configProvider, "/", { skipHealthCheck: true });
213
+ return afs;
214
+ }
215
+ /**
216
+ * List all instance DIDs that have storage in this user's space.
217
+ *
218
+ * Enumerates entries under the `blocklets/<instanceDid>/...` prefix and
219
+ * returns the distinct instance-DID segment. This mirrors the local
220
+ * backend's `listApps`, which enumerates `{userDir}/blocklets/*`. Legacy
221
+ * top-level `getAppSpace` segments are NOT enumerated — that layout is
222
+ * deprecated and removed in P4.
223
+ */
224
+ async listApps() {
225
+ const result = await this.makeTreeIndex({ useReplica: this.useReplica }).list("", { limit: 1e3 });
226
+ const apps = /* @__PURE__ */ new Set();
227
+ for (const entry of result.entries) {
228
+ const parts = entry.path.split("/");
229
+ if (parts[0] === "blocklets" && parts[1]) apps.add(parts[1]);
230
+ }
231
+ return [...apps];
232
+ }
233
+ /**
234
+ * Contract alias (分叉 A, observability Phase 2) for the whole-space root.
235
+ * Delegates to {@link getUserSpace}; satisfies `DIDSpaceLike.getSpaceRoot`
236
+ * without renaming the existing CF method. Defaults readonly via getUserSpace.
237
+ */
238
+ async getSpaceRoot(options) {
239
+ return this.getUserSpace(options);
240
+ }
241
+ /**
242
+ * Get an AFS instance scoped to the user's entire DID Space.
243
+ * No appDid sub-partitioning.
244
+ */
245
+ async getUserSpace(options) {
246
+ log.debug({
247
+ message: "getUserSpace",
248
+ userDid: this.userDid
249
+ });
250
+ const accessMode = options?.accessMode ?? "readonly";
251
+ const cacheKey = `${accessMode}::${this.useReplica ? "r" : "p"}`;
252
+ const cached = this.userSpaces.get(cacheKey);
253
+ if (cached) return cached;
254
+ const afs = new AFS();
255
+ const provider = new DIDSpaceProvider({
256
+ did: this.userDid,
257
+ objectStore: this.objectStore,
258
+ treeIndex: this.makeTreeIndex({
259
+ useReplica: this.useReplica,
260
+ inlineCache: "on"
261
+ }),
262
+ name: "did-space-user",
263
+ accessMode,
264
+ shareResolver: this.shareResolver,
265
+ blockletResolver: this.resolver,
266
+ blockletOwnerDid: this.userDid,
267
+ ...this.uploadDeps()
268
+ });
269
+ await afs.mount(provider, "/", { skipHealthCheck: true });
270
+ this.userSpaces.set(cacheKey, afs);
271
+ return afs;
272
+ }
273
+ get resolver() {
274
+ if (!this._resolver) this._resolver = new DIDSpaceResolver(new D1ResolveStore(this.indexDb, { useReplica: this.useReplica }));
275
+ return this._resolver;
276
+ }
277
+ /**
278
+ * Expose the underlying storage scope — used by worker handlers for
279
+ * cid-permission queries. MUST return the derived scope (not the plaintext
280
+ * DID) so the worker's `entries.scope` lookups match what was written.
281
+ */
282
+ get scope() {
283
+ return this._scope;
284
+ }
285
+ /**
286
+ * Direct write path for the Worker HTTP handler (Task 2.4b).
287
+ *
288
+ * Bypasses the generic `afs.write()` path because `AFSWriteEntryPayload` has
289
+ * no `deviceId`/`skipChangelog` field — the `PutOpts` cannot be smuggled
290
+ * through the public AFS API. The Worker handler therefore constructs a
291
+ * `CloudflareDIDSpace` and calls this method directly, passing the per-
292
+ * request `deviceId` read from the `X-Device-Id` header.
293
+ *
294
+ * Semantics mirror `DIDSpaceProvider.writeHandler`:
295
+ * 1. Compute the content-addressed CID from the payload bytes.
296
+ * 2. R2 put `objects/{cid}` if not already stored (head-check for dedup).
297
+ * 3. `treeIndex.put` — atomic `entries` + `changelog` write (Task 2.4a).
298
+ * 4. Enqueue the previous CID (if any) onto the GC queue for deferred
299
+ * cleanup. Enqueuing an overwritten CID is a no-op if the CID is still
300
+ * referenced by another row (the GC job's reference check handles it).
301
+ *
302
+ * Path conventions:
303
+ * - `afsPath` is an AFS-style path WITH leading slash (e.g. `/foo.json`
304
+ * or `/blocklets/z1abc/index.html`).
305
+ * - The leading slash is stripped before `treeIndex.put`; the remainder is
306
+ * stored verbatim under `entries.scope = this._scope` (the derived storage
307
+ * scope when de-id is on, else the plaintext DID). This matches the
308
+ * `getUserSpace()` layout (no appDid sub-partition) that blocklet publish
309
+ * and general user-space CRUD share.
310
+ *
311
+ * Returns `{ cid, oldCid }`. `oldCid` is present only when an existing row
312
+ * was overwritten; the caller can treat its presence as "something was
313
+ * replaced" for change-tracking. `seq` is not returned — Task 2.4a's
314
+ * `treeIndex.put` does not expose the changelog's AUTOINCREMENT `seq`, and
315
+ * adding a separate SELECT round trip here is wasted work for the current
316
+ * sync path (which only cares about cid + oldCid).
317
+ *
318
+ * No event dispatch is performed (Decision 3A round 5): CloudflareDIDSpace
319
+ * has no event bus reference, the Worker runtime is request/response with
320
+ * no long-lived subscriber, and the sync protocol polls `changelog` rows
321
+ * by `seq` rather than reacting to transient event fan-out.
322
+ */
323
+ async internalWrite(afsPath, content, opts) {
324
+ let bytes;
325
+ let storeValue;
326
+ if (content instanceof Uint8Array) {
327
+ bytes = content;
328
+ storeValue = bytes.buffer;
329
+ } else if (content instanceof ArrayBuffer) {
330
+ bytes = new Uint8Array(content);
331
+ storeValue = content;
332
+ } else {
333
+ bytes = new TextEncoder().encode(content);
334
+ storeValue = content;
335
+ }
336
+ const cid = await computeCID(bytes);
337
+ const objectKey = objectKeyForCid(cid);
338
+ const treePath = afsPath.replace(/^\/+/, "");
339
+ if (isShareReservedPath(afsPath)) throw new AFSForbiddenError(afsPath, "share-path-reserved");
340
+ await applyShareWriteHook({
341
+ afsPath,
342
+ treePath,
343
+ bytes,
344
+ cid,
345
+ ownerDid: this._scope,
346
+ resolver: this.shareResolver,
347
+ phase: "pre-io"
348
+ });
349
+ if (!await this.objectStore.head(objectKey)) await this.objectStore.put(objectKey, storeValue);
350
+ const now = Date.now();
351
+ const mimeType = detectMimeType(afsPath) ?? void 0;
352
+ const treeIndex = this.makeTreeIndex({ inlineCache: "on" });
353
+ const entry = {
354
+ cid,
355
+ size: bytes.length,
356
+ mtime: now,
357
+ ctime: now,
358
+ mimeType
359
+ };
360
+ const inlineText = decodeInlineText(bytes, mimeType);
361
+ if (inlineText !== null) entry.content = inlineText;
362
+ const { oldCid } = await treeIndex.put(treePath, entry, opts);
363
+ await this.gcQueue.remove(cid);
364
+ if (oldCid && oldCid !== cid) await this.gcQueue.enqueue([oldCid]);
365
+ await applyShareWriteHook({
366
+ afsPath,
367
+ treePath,
368
+ bytes,
369
+ cid,
370
+ ownerDid: this._scope,
371
+ resolver: this.shareResolver,
372
+ phase: "post-io"
373
+ });
374
+ await applyBlockletWriteHook({
375
+ afsPath,
376
+ treePath,
377
+ bytes,
378
+ ownerDid: this.userDid,
379
+ resolver: this.resolver,
380
+ purgeCachesForDomains: typeof caches !== "undefined" ? async (domains) => {
381
+ const cfCaches = caches;
382
+ for (const domain of domains) {
383
+ const rootKey = new Request(`https://${domain}/`, { method: "GET" });
384
+ await cfCaches.default.delete(rootKey);
385
+ }
386
+ } : void 0
387
+ });
388
+ return oldCid === void 0 ? { cid } : {
389
+ cid,
390
+ oldCid
391
+ };
392
+ }
393
+ /**
394
+ * Read the current entry's cid + mtime + last-write deviceId for an
395
+ * If-Match precondition check (Task 3.3).
396
+ *
397
+ * Used by `_createSpaceHandler` BEFORE issuing a write when the client
398
+ * sends `If-Match: <cid>` for optimistic concurrency control. The
399
+ * handler compares the returned `cid` to the header value and returns
400
+ * 409 (with the full snapshot below) on mismatch, or 412 if the row
401
+ * doesn't exist (first-create with If-Match).
402
+ *
403
+ * The query joins `entries` (cid + mtime authoritative) with the most
404
+ * recent put row in `changelog` (device_id of last writer). Both
405
+ * indices used (`PRIMARY KEY (scope, path)` on entries +
406
+ * `idx_changelog_scope_seq` on changelog) keep this O(log n).
407
+ *
408
+ * `deviceId` is `null` when the entry was written via the blocklet
409
+ * exemption path (skipChangelog = true) — there's no changelog row to
410
+ * read the device id from. The handler exposes `null` to clients in
411
+ * the 409 body.
412
+ *
413
+ * Returns `null` when no entry exists at this path.
414
+ */
415
+ async getCurrentEntry(afsPath) {
416
+ const treePath = afsPath.replace(/^\/+/, "");
417
+ const row = await this.indexDb.prepare(`SELECT
418
+ e.cid AS cid,
419
+ e.mtime AS mtime,
420
+ (SELECT device_id FROM ds_changelog
421
+ WHERE scope = ? AND path = ? AND op = 'put'
422
+ ORDER BY seq DESC LIMIT 1) AS device_id
423
+ FROM ds_entries e
424
+ WHERE e.scope = ? AND e.path = ? AND e.kind = 'file'`).bind(this._scope, treePath, this._scope, treePath).first();
425
+ if (!row || row.cid === null) return null;
426
+ return {
427
+ cid: row.cid,
428
+ mtime: row.mtime,
429
+ deviceId: row.device_id
430
+ };
431
+ }
432
+ /**
433
+ * Read the changelog `seq` of the most recent write by `(scope, path,
434
+ * deviceId)` (Task 3.3).
435
+ *
436
+ * The Worker handler calls this AFTER `internalWrite/internalDelete`
437
+ * to recover the seq of the row just inserted, so the PUT/DELETE
438
+ * response can echo `{cid, seq}` for the client.
439
+ *
440
+ * The triple WHERE clause (scope + path + device_id) is the safety
441
+ * mechanism for concurrent writers: each writer is guaranteed to have
442
+ * a unique `device_id` (validated by the Worker handler's strict
443
+ * format regex BEFORE we get here), so even if two PUTs on the same
444
+ * path race, each call's getLastSeq sees ONLY its own write — never
445
+ * the other writer's.
446
+ *
447
+ * Returns `0` when no row matches (impossible if called immediately
448
+ * after a successful internalWrite, but defensive against
449
+ * skipChangelog = true callers — blocklet exempt PUTs have no
450
+ * changelog row at all and the handler should NOT call this for them).
451
+ */
452
+ async getLastSeq(afsPath, deviceId) {
453
+ const treePath = afsPath.replace(/^\/+/, "");
454
+ return (await this.indexDb.prepare(`SELECT MAX(seq) AS seq FROM ds_changelog
455
+ WHERE scope = ? AND path = ? AND device_id = ?`).bind(this._scope, treePath, deviceId).first())?.seq ?? 0;
456
+ }
457
+ /**
458
+ * Direct delete path for the Worker HTTP handler (Task 2.4b).
459
+ *
460
+ * Semantics mirror `DIDSpaceProvider.deleteHandler`:
461
+ * 1. Read the current entry (for GC enqueue of the soon-to-be-orphaned CID).
462
+ * 2. `treeIndex.delete` — atomic `entries` delete + `changelog` delete row
463
+ * (Task 2.4a's `INSERT INTO ds_changelog SELECT ... FROM ds_entries` pattern
464
+ * captures the old CID in-batch).
465
+ * 3. Enqueue the old CID for GC. Enqueueing is safe even under races —
466
+ * the GC job's `isReferenced` check skips CIDs still referenced by
467
+ * other rows.
468
+ *
469
+ * Returns an empty object (no `seq` for the same reason as `internalWrite`).
470
+ * Callers that need to know whether anything was actually deleted can rely
471
+ * on the absence of an `AFSNotFoundError` — Task 2.4b's handler converts
472
+ * that to a 404 response.
473
+ */
474
+ async internalDelete(afsPath, opts) {
475
+ const treePath = afsPath.replace(/^\/+/, "");
476
+ const treeIndex = this.makeTreeIndex();
477
+ if (isShareReservedPath(afsPath)) throw new AFSForbiddenError(afsPath, "share-path-reserved");
478
+ const entry = await treeIndex.get(treePath, { includeContent: false });
479
+ if (!entry) return { deleted: false };
480
+ await this.gcQueue.enqueue([entry.cid]);
481
+ await treeIndex.delete(treePath, opts);
482
+ await applyShareDeleteHook({
483
+ afsPath,
484
+ treePath,
485
+ ownerDid: this._scope,
486
+ resolver: this.shareResolver
487
+ });
488
+ await applyBlockletDeleteHook({
489
+ afsPath,
490
+ treePath,
491
+ ownerDid: this.userDid,
492
+ resolver: this.resolver
493
+ });
494
+ return { deleted: true };
495
+ }
496
+ /**
497
+ * Clean up resources. Drops the cached instance-space map so retained AFS
498
+ * instances are GC-eligible alongside this DID-Space.
499
+ */
500
+ close() {
501
+ this.instanceSpaces.clear();
502
+ }
503
+ };
504
+
505
+ //#endregion
506
+ export { CloudflareDIDSpace, normalizeId, validateId };
507
+ //# sourceMappingURL=cloudflare.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.mjs","names":[],"sources":["../src/cloudflare.ts"],"sourcesContent":["/**\n * CloudflareDIDSpace — Cloudflare backend for DID-scoped storage.\n *\n * Composes DIDSpaceProvider (files via R2 + D1 TreeIndex) per app,\n * with logical isolation (separate D1 scope per user+app DID).\n *\n * Same interface as local DIDSpace for 100% compatibility.\n *\n * Usage:\n * const space = new CloudflareDIDSpace({ userDid: \"did:abt:z1...\", objectStore, indexDb });\n * const afs = await space.getInstanceSpace({ instanceDid: \"z1app\", role: \"system\" });\n * await afs.write(\"/hello.txt\", { content: \"hello\" });\n */\n\nimport { AFS, type AFSAccessMode, AFSForbiddenError, nsLog } from \"@aigne/afs\";\nimport type { ObjectStore } from \"@aigne/afs-r2\";\nimport { applyBlockletDeleteHook, applyBlockletWriteHook } from \"./blocklet-hook.js\";\nimport { computeCID } from \"./cid.js\";\nimport { CachingObjectStore, CidByteCache } from \"./cid-byte-cache.js\";\nimport { D1GCQueue } from \"./d1-gc-queue.js\";\nimport { D1ResolveStore } from \"./d1-resolve-store.js\";\nimport { D1TreeIndex } from \"./d1-tree-index.js\";\nimport type { DIDSpaceLike, InstanceSpaceOptions } from \"./did-space-like.js\";\nimport { normalizeId, validateId } from \"./did-utils.js\";\nimport { DIDSpaceProvider } from \"./index.js\";\nimport { decodeInlineText } from \"./inline-content.js\";\nimport { detectMimeType } from \"./mime-utils.js\";\nimport { objectKeyForCid } from \"./object-key.js\";\nimport { PrefixedTreeIndex } from \"./prefixed-tree-index.js\";\nimport type { PresignSigner } from \"./presign-signer.js\";\nimport { DIDSpaceResolver } from \"./resolve.js\";\nimport { deriveScope } from \"./scope-id.js\";\nimport { isShareReservedPath } from \"./share-declaration.js\";\nimport { applyShareDeleteHook, applyShareWriteHook } from \"./share-hook.js\";\nimport type { ShareResolver } from \"./share-resolve.js\";\nimport type { PutOpts, TreeEntry } from \"./tree-index.js\";\nimport { D1UploadSessionStore } from \"./upload-session-store.js\";\n\nexport { normalizeId, validateId } from \"./did-utils.js\";\n\n/** provider:did-space structured logger (debug self-guards; off by default). */\nconst log = nsLog(\"provider:did-space\");\n\nexport interface CloudflareDIDSpaceOptions {\n /** Owner DID of this space */\n userDid: string;\n /** Shared R2 object store (content-addressed, shared across all apps) */\n objectStore: ObjectStore;\n /** D1 database for tree index + resolve entries */\n indexDb: D1Database;\n /**\n * Optional per-deployment HMAC secret for scope de-identification (README §4.4).\n * When set, all D1 storage keys (`entries.scope` / `changelog.scope` /\n * `shares.owner_did`) become `deriveScope(userDid, secret)` — an opaque HMAC —\n * instead of the plaintext DID. When absent, scope stays plaintext (de-id off,\n * back-compat). Injected along `objectStore` / `indexDb` by the resolver.\n */\n scopeHmacSecret?: Uint8Array;\n /** Optional: ShareResolver for .share reconcile hook (Phase 0.4) */\n shareResolver?: ShareResolver;\n /**\n * Route READ-serving D1 queries through a D1 Session\n * (`withSession(\"first-unconstrained\")`) so they can be served by the\n * nearest read replica. Requires `read_replication: { mode: \"auto\" }` on the\n * D1 database (a DB-level setting, NOT wrangler.toml) — otherwise every query\n * stays on primary regardless of this flag.\n *\n * Only the read paths honor this (getInstanceSpace / getConfigSpace /\n * getUserSpace / listApps / resolver). The write + GC paths (internalWrite /\n * internalDelete / gcQueue) ALWAYS use the primary: they read the old entry to\n * drive GC, and a stale replica read there would compute the wrong `oldCid`.\n *\n * Consistency note: each tree-index opens its own session with no bookmark\n * threading, so this is \"first-unconstrained\" (potentially-stale) reads.\n * Read-your-write holds WITHIN a single mounted AFS (same session) but NOT\n * across requests. Default false (last-write-wins on primary, back-compat).\n */\n useReplica?: boolean;\n /**\n * Object-store presigned-PUT signer (DID Space upload plane, §15 decision 7).\n * Injected by the CF runtime when R2 signer credentials are configured;\n * `prepare-upload` (P2) mints exact-key checksum-pinned grants through it.\n * Absent → no grant capability (clients fall back to batchWrite). Node never\n * injects one (decision 8, fallback-only).\n */\n signer?: PresignSigner;\n /**\n * upload-limits Phase 5 per-scope byte quota (bytes). 0 / unset = OFF (the\n * opt-in default). Injected by the CF runtime from `resolveUploadLimits`'s\n * `maxBytesPerScope`; enforced on every write through this space's tree index.\n */\n maxBytesPerScope?: number;\n}\n\nexport type { InstanceSpaceOptions } from \"./did-space-like.js\";\n\nexport class CloudflareDIDSpace implements DIDSpaceLike {\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`. Every D1 storage/query key uses this, never `userDid`.\n * Named `_scope` to avoid colliding with the `get scope()` accessor (README §4.1).\n */\n private readonly _scope: string;\n private readonly objectStore: ObjectStore;\n private readonly indexDb: D1Database;\n private _resolver?: DIDSpaceResolver;\n /** Optional ShareResolver for .share reconcile hook (Phase 0.4) */\n private readonly shareResolver?: ShareResolver;\n /** Read replica opt-in for read-serving paths only — see options doc. */\n private readonly useReplica: boolean;\n /** upload-limits Phase 5 per-scope byte quota (0 = off, the opt-in default). */\n private readonly maxBytesPerScope: number;\n /**\n * Instance space cache keyed by (role, useReplica) — same shape as `local.ts`\n * `instanceSpaces`. Without it, `getInstanceSpace` returns a fresh `AFS`\n * (and fresh `EventBus`) every call, so propBind subscribe (registered on\n * AFS_A at session start) and the agent's subsequent write (executing on\n * AFS_B for that exec) cross different buses — the write's `afs:write`\n * event never reaches the subscriber, so the live patch silently drops and\n * the user only sees the new value after a manual page reload (Layer 2\n * staleness on staging). Bounded by the lifetime of this DID-Space (per\n * caller in the resolver), the same memory budget the resolver already\n * spends on `spaces`.\n */\n private readonly instanceSpaces = new Map<string, AFS>();\n /**\n * User-space cache keyed by (accessMode, useReplica) — the getUserSpace\n * counterpart of {@link instanceSpaces}. Without it, every `getUserSpace()`\n * builds a fresh `AFS` and re-runs `AFS.mount()`'s async provider health check\n * (a root `stat` → `countChildren` + list). Blocklet serve calls getUserSpace\n * once PER stage (reader / afsRoot), so a cold page paid that root enumeration\n * 3× (issue #808 #4). Caching the handle reuses the mounted provider; data\n * reads stay fresh (path→cid D1 lookups are never cached), so zero staleness.\n */\n private readonly userSpaces = new Map<string, AFS>();\n /** Shared GC queue instance — created once per CloudflareDIDSpace instance */\n readonly gcQueue: D1GCQueue;\n /** Injected presigned-PUT signer (upload plane). Undefined → no grant capability. */\n private readonly signer?: PresignSigner;\n\n constructor(options: CloudflareDIDSpaceOptions) {\n validateId(options.userDid);\n this.userDid = normalizeId(options.userDid);\n // de-id opt-in (README §4.4): derive an opaque storage scope only when a\n // secret is provided; otherwise keep the plaintext DID (back-compat). The\n // identity (`userDid`) is always the real DID.\n this._scope = options.scopeHmacSecret\n ? deriveScope(this.userDid, options.scopeHmacSecret)\n : this.userDid;\n // GC plan §4.3 — Model B global object pool: inject the raw R2 store\n // directly (no per-user key prefix), so identical bytes dedup across users\n // at the canonical key `objects/{cid}`. Cross-user read isolation is now\n // enforced at the index layer (`DIDSpaceProvider.readHandler` gates\n // `.objects/{cid}` on a view-local `treeIndex.isReferenced`), NOT by the\n // store key shape. (Was `ScopedObjectStore`, removed in the GC plan.)\n //\n // #674 perf: wrap the store with a content-addressed byte cache so repeat\n // reads of the same blob (e.g. a published blocklet's files across requests)\n // skip the R2 GET. Keyed by cid (immutable) → no staleness, no TTL. This\n // instance is cached per-user by the space resolver, so the cache persists\n // for the isolate lifetime — but bounded (LRU) so memory stays flat.\n this.objectStore = new CachingObjectStore(options.objectStore, new CidByteCache());\n this.indexDb = options.indexDb;\n // GC queue always on primary: dequeue + isReferenced reference-counting must\n // see the latest writes, never a replica.\n this.gcQueue = new D1GCQueue(options.indexDb);\n this.shareResolver = options.shareResolver;\n this.useReplica = options.useReplica ?? false;\n this.maxBytesPerScope = options.maxBytesPerScope ?? 0;\n this.signer = options.signer;\n }\n\n /**\n * Construct a D1TreeIndex for this scope with the per-scope byte quota\n * (upload-limits Phase 5) always injected — so every write path enforces it\n * uniformly without each call site remembering to thread it. 0 = quota off.\n */\n private makeTreeIndex(opts?: { useReplica?: boolean; inlineCache?: \"on\" }): D1TreeIndex {\n return new D1TreeIndex(this.indexDb, this._scope, {\n ...opts,\n maxBytesPerScope: this.maxBytesPerScope,\n });\n }\n\n /**\n * The injected presigned-PUT signer, or undefined when no R2 signer\n * credentials are configured. The seam through which `prepare-upload` (P2)\n * mints upload grants; also the capability signal clients use to decide\n * grant-path vs batchWrite fallback (§15 decision 7/8).\n */\n get presignSigner(): PresignSigner | undefined {\n return this.signer;\n }\n\n /**\n * Upload-plane deps for the user-facing `DIDSpaceProvider` (design §7): the\n * injected signer (CF mints grants; absent → fallback), a D1-backed session\n * store, and the de-identified storage scope written into `upload_sessions`.\n * Only the user space gets these — `/instance` (installer) + config don't.\n */\n private uploadDeps() {\n return {\n signer: this.signer,\n uploadSessionStore: new D1UploadSessionStore(this.indexDb),\n uploadScope: this._scope,\n };\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 * Layout (PrefixedTreeIndex prefix relative to `entries.scope = userDid`):\n * role:\"system\" → blocklets/{instanceDid}/system/ (content tree, mounted at `/`)\n * role:\"user\" → blocklets/{instanceDid}/user/ (content tree, mounted at `/`)\n * config → blocklets/{instanceDid}/config/ (sibling fragment, getConfigSpace)\n *\n * /files-prefix-removal: content tree is the role root (no `files/` wrapper);\n * config is a sibling fragment (invisible to `/instance`); vault removed.\n *\n * did-space-composition Phase 2 (symmetric with the local backend): the user\n * fragment is a SINGLE `user` subtree. Per-user isolation comes from scope =\n * caller's userDid (the resolver hands back a CloudflareDIDSpace scoped to the\n * caller), so a `users/{userDid}` path segment would be redundant. `userDid`\n * is @deprecated for this call and ignored for the path.\n *\n * Cached per (instanceDid, role, useReplica) — first caller's accessMode\n * wins for that triple (mirrors local.ts). See `instanceSpaces` for the\n * live-update rationale.\n */\n async getInstanceSpace(opts: InstanceSpaceOptions): Promise<AFS> {\n log.debug({ message: \"getInstanceSpace\", instanceDid: opts.instanceDid, role: opts.role });\n validateId(opts.instanceDid);\n if (opts.role !== \"system\" && opts.role !== \"user\") {\n throw new Error(`getInstanceSpace: invalid role \"${opts.role}\"`);\n }\n // userDid is @deprecated for role:\"user\" — validated as defense if present,\n // but never enters the path (the space's scope IS the user). See local.ts.\n if (opts.role === \"user\" && opts.userDid != null) {\n validateId(opts.userDid);\n }\n\n const bareInstanceId = normalizeId(opts.instanceDid);\n // Cache key splits on useReplica because the D1TreeIndex's session is\n // fixed at construction — a replica-vs-primary mismatch can't be served\n // by sharing one AFS instance (a reconcile pass asking for \"primary\" must\n // get a primary-bound space). Within a single (role, useReplica) bucket,\n // every caller shares the same eventBus → propBind sees its sibling\n // writes.\n const useReplica = opts.consistency === \"primary\" ? false : this.useReplica;\n const cacheKey = `${bareInstanceId}::${opts.role}::${useReplica ? \"r\" : \"p\"}`;\n const cached = this.instanceSpaces.get(cacheKey);\n if (cached) return cached;\n\n const subtree =\n opts.role === \"system\"\n ? `blocklets/${bareInstanceId}/system`\n : `blocklets/${bareInstanceId}/user`;\n const accessMode: AFSAccessMode = opts.accessMode ?? \"readwrite\";\n\n const afs = new AFS();\n // Read replica opt-in: the mounted AFS reuses this single session, so\n // write→read within it stays sequentially consistent even on a replica.\n // inlineCache \"on\" = write-through (settings-layering 1d): text content\n // ≤ threshold rides the index row as a cid-verified read-acceleration\n // copy (saves an R2 GET per hit). `objects/{cid}` is ALWAYS written\n // regardless (cas-blob-always-written) — sync/GC semantics untouched.\n //\n // Live Update Resilience 1d / DEC-5 (read-your-watermark) — RESERVED HOOK,\n // currently UNWIRED. Passing `consistency: \"primary\"` pins this space to the\n // primary DB (useReplica:false) so a reconcile observes its own watermark\n // rather than a replica snapshot (replica lag ~30-75ms, no SLA). The choice\n // must be made HERE (the layer that holds indexDb) because the D1TreeIndex\n // conn is fixed at construction — a per-op flag would not retarget an\n // already-built session.\n //\n // No caller passes `consistency` today (cf-session-view's getInstanceSpace\n // omits it), and reading the watermark from the replica is SAFE without this\n // pin: the client (web-page/live-update.ts) makes a stale read cost at most\n // one redundant refetch / one poll cycle, NEVER a missed update —\n // 1. self-write refetches UNCONDITIONALLY (never gates on the watermark);\n // 2. foreign-write detection rides the ~45s visible-only poll, so the\n // ~75ms replica lag is in the noise and the next poll reads the caught-\n // up value;\n // 3. the refetch predicate is monotonic (`raw > lastVersion`) — a stale\n // read skips one cycle, never lowers the baseline;\n // 4. pushes carry their own prefix-local liveSeq and reconcile on any\n // gap/forged/bulk value, never adopting a replica read.\n // The \"(with no further edge) permanently stale\" failure this would guard\n // against needs NO poll backstop, which does not hold — so the pin is\n // redundant defense-in-depth, kept only as an opt-in for a caller that ever\n // needs strict read-your-watermark. Default (omitted/\"replica\") keeps the\n // backend's standing useReplica. (useReplica is computed above as part of\n // the cache key so subscribe + write share an AFS — see ~line 150.)\n const baseTreeIndex = this.makeTreeIndex({ useReplica, inlineCache: \"on\" });\n\n // /files-prefix-removal: content tree IS the role root (prefix `${subtree}/`,\n // mounted at `/`). config moved to a sibling fragment (getConfigSpace);\n // vault removed (real vault = @aigne/afs-vault provider).\n const fileProvider = new DIDSpaceProvider({\n did: bareInstanceId,\n objectStore: this.objectStore,\n treeIndex: new PrefixedTreeIndex(baseTreeIndex, `${subtree}/`),\n name: \"content\",\n accessMode,\n gcQueue: this.gcQueue,\n // Share-out + blocklet publish on the caller's OWN per-user fragment\n // (role:\"user\" — the `/user` session mount, mobile-sync + remote-publish\n // target). The provider's treeIndex is prefixed to `blocklets/<i>/user/`\n // and its `did` is the instance, so both hooks need the whole-tree path\n // restored: `shareTreePrefix` does that (read by share+blocklet hookArgs).\n // It is set for ALL role:\"user\" fragments (independent of shareResolver),\n // so the blocklet hook captures the full physical path even when shares\n // are off. role:\"system\" stays excluded (installer data is never public).\n // - share: `shareOwnerDid` keys the share to the whole-space user scope.\n // - blocklet: `blockletOwnerDid` records the PLAINTEXT userDid (the serve\n // path re-derives the storage scope from it); the instance `did` would\n // be wrong here.\n ...(opts.role === \"user\"\n ? {\n shareTreePrefix: `${subtree}/`,\n ...(this.shareResolver\n ? { shareResolver: this.shareResolver, shareOwnerDid: this._scope }\n : {}),\n blockletResolver: this.resolver,\n blockletOwnerDid: this.userDid,\n }\n : {}),\n // Upload plane only on the caller's own `/user` fragment (the native-sync\n // target); `/instance` (role:system, installer data) never gets grants.\n ...(opts.role === \"user\" ? this.uploadDeps() : {}),\n });\n await afs.mount(fileProvider, \"/\", { skipHealthCheck: true });\n\n this.instanceSpaces.set(cacheKey, afs);\n return afs;\n }\n\n /**\n * Runtime-internal config fragment (`blocklets/<i>/config/`, mounted at\n * `/`). Mirrors local `getConfigSpace`. Invisible to `/instance`; holds\n * per-instance machine state like `mounts.toml`.\n */\n async getConfigSpace(instanceDid: string): Promise<AFS> {\n validateId(instanceDid);\n const bareInstanceId = normalizeId(instanceDid);\n const afs = new AFS();\n const baseTreeIndex = this.makeTreeIndex({ useReplica: this.useReplica, inlineCache: \"on\" });\n const configProvider = new DIDSpaceProvider({\n did: bareInstanceId,\n objectStore: this.objectStore,\n treeIndex: new PrefixedTreeIndex(baseTreeIndex, `blocklets/${bareInstanceId}/config/`),\n name: \"config\",\n accessMode: \"readwrite\",\n gcQueue: this.gcQueue,\n });\n await afs.mount(configProvider, \"/\", { skipHealthCheck: true });\n return afs;\n }\n\n /**\n * List all instance DIDs that have storage in this user's space.\n *\n * Enumerates entries under the `blocklets/<instanceDid>/...` prefix and\n * returns the distinct instance-DID segment. This mirrors the local\n * backend's `listApps`, which enumerates `{userDir}/blocklets/*`. Legacy\n * top-level `getAppSpace` segments are NOT enumerated — that layout is\n * deprecated and removed in P4.\n */\n async listApps(): Promise<string[]> {\n const idx = this.makeTreeIndex({ useReplica: this.useReplica });\n const result = await idx.list(\"\", { limit: 1000 });\n\n const apps = new Set<string>();\n for (const entry of result.entries) {\n const parts = entry.path.split(\"/\");\n // blocklets/<instanceDid>/{system|user}/...\n if (parts[0] === \"blocklets\" && parts[1]) {\n apps.add(parts[1]);\n }\n }\n return [...apps];\n }\n\n /**\n * Contract alias (分叉 A, observability Phase 2) for the whole-space root.\n * Delegates to {@link getUserSpace}; satisfies `DIDSpaceLike.getSpaceRoot`\n * without renaming the existing CF method. Defaults readonly via getUserSpace.\n */\n async getSpaceRoot(options?: { accessMode?: AFSAccessMode }): Promise<AFS> {\n return this.getUserSpace(options);\n }\n\n /**\n * Get an AFS instance scoped to the user's entire DID Space.\n * No appDid sub-partitioning.\n */\n async getUserSpace(options?: { accessMode?: AFSAccessMode }): Promise<AFS> {\n log.debug({ message: \"getUserSpace\", userDid: this.userDid });\n const accessMode: AFSAccessMode = options?.accessMode ?? \"readonly\";\n // Cache key splits on useReplica for the same reason as instanceSpaces (the\n // D1TreeIndex session is fixed at construction) and on accessMode (readonly\n // vs readwrite build different providers). See userSpaces doc.\n const cacheKey = `${accessMode}::${this.useReplica ? \"r\" : \"p\"}`;\n const cached = this.userSpaces.get(cacheKey);\n if (cached) return cached;\n const afs = new AFS();\n const provider = new DIDSpaceProvider({\n did: this.userDid,\n objectStore: this.objectStore,\n treeIndex: this.makeTreeIndex({ useReplica: this.useReplica, inlineCache: \"on\" }),\n name: \"did-space-user\",\n accessMode,\n shareResolver: this.shareResolver,\n // #674: blocklet publish/resolve — whole-space writes (e.g. local-style\n // `getUserSpace().write(\"/blocklets/<id>/blocklet.yaml\")`) register into\n // the global resolve table. owner = plaintext userDid (NOT the de-id scope).\n blockletResolver: this.resolver,\n blockletOwnerDid: this.userDid,\n ...this.uploadDeps(),\n });\n await afs.mount(provider, \"/\", { skipHealthCheck: true });\n this.userSpaces.set(cacheKey, afs);\n return afs;\n }\n\n get resolver(): DIDSpaceResolver {\n if (!this._resolver) {\n this._resolver = new DIDSpaceResolver(\n new D1ResolveStore(this.indexDb, { useReplica: this.useReplica }),\n );\n }\n return this._resolver;\n }\n\n /**\n * Expose the underlying storage scope — used by worker handlers for\n * cid-permission queries. MUST return the derived scope (not the plaintext\n * DID) so the worker's `entries.scope` lookups match what was written.\n */\n get scope(): string {\n return this._scope;\n }\n\n /**\n * Direct write path for the Worker HTTP handler (Task 2.4b).\n *\n * Bypasses the generic `afs.write()` path because `AFSWriteEntryPayload` has\n * no `deviceId`/`skipChangelog` field — the `PutOpts` cannot be smuggled\n * through the public AFS API. The Worker handler therefore constructs a\n * `CloudflareDIDSpace` and calls this method directly, passing the per-\n * request `deviceId` read from the `X-Device-Id` header.\n *\n * Semantics mirror `DIDSpaceProvider.writeHandler`:\n * 1. Compute the content-addressed CID from the payload bytes.\n * 2. R2 put `objects/{cid}` if not already stored (head-check for dedup).\n * 3. `treeIndex.put` — atomic `entries` + `changelog` write (Task 2.4a).\n * 4. Enqueue the previous CID (if any) onto the GC queue for deferred\n * cleanup. Enqueuing an overwritten CID is a no-op if the CID is still\n * referenced by another row (the GC job's reference check handles it).\n *\n * Path conventions:\n * - `afsPath` is an AFS-style path WITH leading slash (e.g. `/foo.json`\n * or `/blocklets/z1abc/index.html`).\n * - The leading slash is stripped before `treeIndex.put`; the remainder is\n * stored verbatim under `entries.scope = this._scope` (the derived storage\n * scope when de-id is on, else the plaintext DID). This matches the\n * `getUserSpace()` layout (no appDid sub-partition) that blocklet publish\n * and general user-space CRUD share.\n *\n * Returns `{ cid, oldCid }`. `oldCid` is present only when an existing row\n * was overwritten; the caller can treat its presence as \"something was\n * replaced\" for change-tracking. `seq` is not returned — Task 2.4a's\n * `treeIndex.put` does not expose the changelog's AUTOINCREMENT `seq`, and\n * adding a separate SELECT round trip here is wasted work for the current\n * sync path (which only cares about cid + oldCid).\n *\n * No event dispatch is performed (Decision 3A round 5): CloudflareDIDSpace\n * has no event bus reference, the Worker runtime is request/response with\n * no long-lived subscriber, and the sync protocol polls `changelog` rows\n * by `seq` rather than reacting to transient event fan-out.\n */\n async internalWrite(\n afsPath: string,\n content: string | Uint8Array | ArrayBuffer,\n opts: PutOpts,\n ): Promise<{ cid: string; oldCid?: string }> {\n // Normalise content to bytes + storage value (same branches as\n // DIDSpaceProvider.writeHandler). Text stored as text; binary stored as\n // ArrayBuffer — both are acceptable inputs to the R2 put API.\n let bytes: Uint8Array;\n let storeValue: string | ArrayBuffer;\n if (content instanceof Uint8Array) {\n bytes = content;\n storeValue = bytes.buffer as ArrayBuffer;\n } else if (content instanceof ArrayBuffer) {\n bytes = new Uint8Array(content);\n storeValue = content;\n } else {\n bytes = new TextEncoder().encode(content);\n storeValue = content;\n }\n\n const cid = await computeCID(bytes);\n // Single hardened key builder (validates the cid) — never raw `'objects/'+cid`.\n const objectKey = objectKeyForCid(cid);\n\n // Strip leading slash to match the tree-index path convention used by\n // DIDSpaceProvider (see `treePath()` in index.ts:182).\n const treePath = afsPath.replace(/^\\/+/, \"\");\n\n // Phase 0.4: reserved path check + pre-io share hook (before R2 PUT)\n if (isShareReservedPath(afsPath)) {\n throw new AFSForbiddenError(afsPath, \"share-path-reserved\");\n }\n await applyShareWriteHook({\n afsPath,\n treePath,\n bytes,\n cid,\n ownerDid: this._scope,\n resolver: this.shareResolver,\n phase: \"pre-io\",\n });\n\n // Content-addressed dedup — skip R2 put when the object already exists.\n const existingObj = await this.objectStore.head(objectKey);\n if (!existingObj) {\n await this.objectStore.put(objectKey, storeValue);\n }\n\n const now = Date.now();\n const mimeType = detectMimeType(afsPath) ?? undefined;\n\n // Primary only (no useReplica): `put` reads the prior row to return oldCid\n // for GC enqueue; a stale replica read here would leak/misroute GC.\n const treeIndex = this.makeTreeIndex({ inlineCache: \"on\" });\n // Write-through (1d): offer the payload as inline text when it decodes as\n // strict UTF-8 (text mime, within cap). Best-effort — undecodable payloads\n // simply aren't inlined; the blob above is ALWAYS written either way.\n const entry: TreeEntry = { cid, size: bytes.length, mtime: now, ctime: now, mimeType };\n const inlineText = decodeInlineText(bytes, mimeType);\n if (inlineText !== null) entry.content = inlineText;\n const { oldCid } = await treeIndex.put(treePath, entry, opts);\n\n // §4.7.1 write-dequeue: a write re-references `cid` — remove it from the GC\n // queue (if a prior delete/overwrite queued it) so a concurrent sweep can't\n // delete a now-live object.\n await this.gcQueue.remove(cid);\n if (oldCid && oldCid !== cid) {\n await this.gcQueue.enqueue([oldCid]);\n }\n\n // Phase 0.4: post-io share hook (after tree_entries written)\n await applyShareWriteHook({\n afsPath,\n treePath,\n bytes,\n cid,\n ownerDid: this._scope,\n resolver: this.shareResolver,\n phase: \"post-io\",\n });\n\n // #674: blocklet publish/resolve hook (CF direct write path). `treePath` is\n // already whole-space scope-absolute here; owner = plaintext userDid.\n // #1110: inject a CF-specific purge callback so a re-publish immediately\n // invalidates the pre-rendered pages in the CF edge cache (`caches.default`).\n await applyBlockletWriteHook({\n afsPath,\n treePath,\n bytes,\n ownerDid: this.userDid,\n resolver: this.resolver,\n // Guard: `caches` is CF-Worker-only global; undefined in Node/Bun context\n // (e.g. Miniflare tests that instantiate CloudflareDIDSpace directly from\n // Node rather than going through the Worker fetch handler).\n purgeCachesForDomains:\n typeof caches !== \"undefined\"\n ? async (domains) => {\n // Purge the root entry for each domain from the CF Cache API so\n // the next request fetches the freshly published content (#1110).\n // The CF Cache API has no wildcard/prefix delete — only exact URLs.\n // We purge the root (most important) path; deeper paths expire at TTL.\n // Cast: bun's CacheStorage lacks `.default`; CF workers-types has it.\n const cfCaches = caches as unknown as {\n default: { delete(r: Request): Promise<boolean> };\n };\n for (const domain of domains) {\n const rootKey = new Request(`https://${domain}/`, { method: \"GET\" });\n await cfCaches.default.delete(rootKey);\n }\n }\n : undefined,\n });\n\n return oldCid === undefined ? { cid } : { cid, oldCid };\n }\n\n /**\n * Read the current entry's cid + mtime + last-write deviceId for an\n * If-Match precondition check (Task 3.3).\n *\n * Used by `_createSpaceHandler` BEFORE issuing a write when the client\n * sends `If-Match: <cid>` for optimistic concurrency control. The\n * handler compares the returned `cid` to the header value and returns\n * 409 (with the full snapshot below) on mismatch, or 412 if the row\n * doesn't exist (first-create with If-Match).\n *\n * The query joins `entries` (cid + mtime authoritative) with the most\n * recent put row in `changelog` (device_id of last writer). Both\n * indices used (`PRIMARY KEY (scope, path)` on entries +\n * `idx_changelog_scope_seq` on changelog) keep this O(log n).\n *\n * `deviceId` is `null` when the entry was written via the blocklet\n * exemption path (skipChangelog = true) — there's no changelog row to\n * read the device id from. The handler exposes `null` to clients in\n * the 409 body.\n *\n * Returns `null` when no entry exists at this path.\n */\n async getCurrentEntry(afsPath: string): Promise<{\n cid: string;\n mtime: number;\n deviceId: string | null;\n } | null> {\n const treePath = afsPath.replace(/^\\/+/, \"\");\n const row = await this.indexDb\n .prepare(\n `SELECT\n e.cid AS cid,\n e.mtime AS mtime,\n (SELECT device_id FROM ds_changelog\n WHERE scope = ? AND path = ? AND op = 'put'\n ORDER BY seq DESC LIMIT 1) AS device_id\n FROM ds_entries e\n WHERE e.scope = ? AND e.path = ? AND e.kind = 'file'`,\n )\n .bind(this._scope, treePath, this._scope, treePath)\n .first<{ cid: string | null; mtime: number; device_id: string | null }>();\n if (!row || row.cid === null) return null;\n return { cid: row.cid, mtime: row.mtime, deviceId: row.device_id };\n }\n\n /**\n * Read the changelog `seq` of the most recent write by `(scope, path,\n * deviceId)` (Task 3.3).\n *\n * The Worker handler calls this AFTER `internalWrite/internalDelete`\n * to recover the seq of the row just inserted, so the PUT/DELETE\n * response can echo `{cid, seq}` for the client.\n *\n * The triple WHERE clause (scope + path + device_id) is the safety\n * mechanism for concurrent writers: each writer is guaranteed to have\n * a unique `device_id` (validated by the Worker handler's strict\n * format regex BEFORE we get here), so even if two PUTs on the same\n * path race, each call's getLastSeq sees ONLY its own write — never\n * the other writer's.\n *\n * Returns `0` when no row matches (impossible if called immediately\n * after a successful internalWrite, but defensive against\n * skipChangelog = true callers — blocklet exempt PUTs have no\n * changelog row at all and the handler should NOT call this for them).\n */\n async getLastSeq(afsPath: string, deviceId: string): Promise<number> {\n const treePath = afsPath.replace(/^\\/+/, \"\");\n const row = await this.indexDb\n .prepare(\n `SELECT MAX(seq) AS seq FROM ds_changelog\n WHERE scope = ? AND path = ? AND device_id = ?`,\n )\n .bind(this._scope, treePath, deviceId)\n .first<{ seq: number | null }>();\n return row?.seq ?? 0;\n }\n\n /**\n * Direct delete path for the Worker HTTP handler (Task 2.4b).\n *\n * Semantics mirror `DIDSpaceProvider.deleteHandler`:\n * 1. Read the current entry (for GC enqueue of the soon-to-be-orphaned CID).\n * 2. `treeIndex.delete` — atomic `entries` delete + `changelog` delete row\n * (Task 2.4a's `INSERT INTO ds_changelog SELECT ... FROM ds_entries` pattern\n * captures the old CID in-batch).\n * 3. Enqueue the old CID for GC. Enqueueing is safe even under races —\n * the GC job's `isReferenced` check skips CIDs still referenced by\n * other rows.\n *\n * Returns an empty object (no `seq` for the same reason as `internalWrite`).\n * Callers that need to know whether anything was actually deleted can rely\n * on the absence of an `AFSNotFoundError` — Task 2.4b's handler converts\n * that to a 404 response.\n */\n async internalDelete(afsPath: string, opts: PutOpts): Promise<{ deleted: boolean }> {\n const treePath = afsPath.replace(/^\\/+/, \"\");\n // Primary only (no useReplica): reads the entry to GC its CID after delete;\n // a stale replica read here would leak/misroute GC.\n const treeIndex = this.makeTreeIndex();\n\n // Phase 0.4: reserved path check\n if (isShareReservedPath(afsPath)) {\n throw new AFSForbiddenError(afsPath, \"share-path-reserved\");\n }\n\n // Read first so we can GC the CID AFTER the delete succeeds. Matches the\n // enqueue-before-delete ordering in DIDSpaceProvider.deleteHandler — the\n // rationale there is that a failing enqueue leaves entries untouched, and\n // a failing delete after a successful enqueue is safe because the CID is\n // still referenced. We preserve the same ordering here.\n // GC enqueue path — needs only entry.cid, never the inline bytes.\n const entry = await treeIndex.get(treePath, { includeContent: false });\n if (!entry) {\n return { deleted: false };\n }\n\n await this.gcQueue.enqueue([entry.cid]);\n await treeIndex.delete(treePath, opts);\n\n // Phase 0.4: delete hook (after tree_entries removed)\n await applyShareDeleteHook({\n afsPath,\n treePath,\n ownerDid: this._scope,\n resolver: this.shareResolver,\n });\n\n // #674: blocklet unregister (CF direct delete path).\n await applyBlockletDeleteHook({\n afsPath,\n treePath,\n ownerDid: this.userDid,\n resolver: this.resolver,\n });\n\n return { deleted: true };\n }\n\n /**\n * Clean up resources. Drops the cached instance-space map so retained AFS\n * instances are GC-eligible alongside this DID-Space.\n */\n close(): void {\n this.instanceSpaces.clear();\n }\n}\n"],"mappings":"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"}