@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/index.cjs ADDED
@@ -0,0 +1,2712 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ const require_blocklet_hook = require('./blocklet-hook.cjs');
3
+ const require_changelog_schema = require('./changelog-schema.cjs');
4
+ const require_cid = require('./cid.cjs');
5
+ const require_did_utils = require('./did-utils.cjs');
6
+ const require_download_actions = require('./download-actions.cjs');
7
+ require('./mime-utils.cjs');
8
+ const require_inline_content = require('./inline-content.cjs');
9
+ const require_share_declaration = require('./share-declaration.cjs');
10
+ const require_share_hook = require('./share-hook.cjs');
11
+ const require_share_virtual_path = require('./share-virtual-path.cjs');
12
+ const require_timestamp = require('./timestamp.cjs');
13
+ const require_tree_index = require('./tree-index.cjs');
14
+ const require_tree_query = require('./tree-query.cjs');
15
+ const require_d1_schema = require('./d1-schema.cjs');
16
+ const require_upload_session_store = require('./upload-session-store.cjs');
17
+ const require_upload_actions = require('./upload-actions.cjs');
18
+ const require_url_normalize = require('./url-normalize.cjs');
19
+ const require_decorate = require('./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs');
20
+ const require_afs_metadata_context = require('./afs-metadata-context.cjs');
21
+ const require_changelog_write = require('./changelog-write.cjs');
22
+ const require_cid_byte_cache = require('./cid-byte-cache.cjs');
23
+ const require_remote_afs = require('./remote-afs.cjs');
24
+ const require_http = require('./transports/http.cjs');
25
+ const require_static = require('./transports/static.cjs');
26
+ const require_ws = require('./transports/ws.cjs');
27
+ const require_client = require('./client.cjs');
28
+ const require_d1_gc_queue = require('./d1-gc-queue.cjs');
29
+ const require_prefix_range = require('./prefix-range.cjs');
30
+ const require_d1_resolve_store = require('./d1-resolve-store.cjs');
31
+ const require_object_key = require('./object-key.cjs');
32
+ const require_share_resolve = require('./share-resolve.cjs');
33
+ const require_share_store = require('./share-store.cjs');
34
+ const require_d1_share_store = require('./d1-share-store.cjs');
35
+ const require_dirty_path_schema = require('./dirty-path-schema.cjs');
36
+ const require_d1_tree_index = require('./d1-tree-index.cjs');
37
+ const require_data_backend = require('./data-backend.cjs');
38
+ const require_dirty_path_queue = require('./dirty-path-queue.cjs');
39
+ const require_local_changelog = require('./local-changelog.cjs');
40
+ const require_memory_gc_queue = require('./memory-gc-queue.cjs');
41
+ const require_memory_tree_index = require('./memory-tree-index.cjs');
42
+ const require_prefixed_tree_index = require('./prefixed-tree-index.cjs');
43
+ const require_presign_signer = require('./presign-signer.cjs');
44
+ const require_reference_check = require('./reference-check.cjs');
45
+ const require_resolve = require('./resolve.cjs');
46
+ const require_rsync = require('./rsync.cjs');
47
+ const require_run_gc = require('./run-gc.cjs');
48
+ const require_scope_id = require('./scope-id.cjs');
49
+ const require_scope_migration = require('./scope-migration.cjs');
50
+ const require_share_serve = require('./share-serve.cjs');
51
+ const require_sqlite_gc_queue = require('./sqlite-gc-queue.cjs');
52
+ const require_sqlite_resolve_store = require('./sqlite-resolve-store.cjs');
53
+ const require_sqlite_share_store = require('./sqlite-share-store.cjs');
54
+ const require_sync_engine = require('./sync-engine.cjs');
55
+ const require_sync_state = require('./sync-state.cjs');
56
+ const require_versioned_content_backends = require('./versioned-content-backends.cjs');
57
+ const require_versioned_content_index = require('./versioned-content-index.cjs');
58
+ const require_versioned_content_store = require('./versioned-content-store.cjs');
59
+ const require_versioned_scope = require('./versioned-scope.cjs');
60
+ let _aigne_afs = require("@aigne/afs");
61
+ let _aigne_afs_provider = require("@aigne/afs/provider");
62
+ let _aigne_afs_timing = require("@aigne/afs-timing");
63
+ let ufo = require("ufo");
64
+ let zod = require("zod");
65
+ let _aigne_afs_provider_utils = require("@aigne/afs-provider-utils");
66
+
67
+ //#region src/index.ts
68
+ const log = (0, _aigne_afs.makeNsLog)("provider:did-space");
69
+ const LIST_MAX_LIMIT = 1e3;
70
+ /**
71
+ * AFS entry `id` is the basename (last path segment), matching AFSFS/AFSJSON —
72
+ * NOT the full path. `AFSBaseProvider.buildEntry` defaults `id` to the full
73
+ * normalized path, so the list handlers below MUST pass an explicit basename id;
74
+ * otherwise consumers that labelize `entry.id` (e.g. the auto-surface settings
75
+ * tabs) render the whole path instead of "Input"/"Sort"/"View". The stat handler
76
+ * already does this (`afsPath.split("/").pop()`); the list handlers were the gap.
77
+ */
78
+ function basenameId(path) {
79
+ return path.split("/").filter(Boolean).pop() ?? "/";
80
+ }
81
+ /**
82
+ * Provider-reserved meta keys (planning/agent-hub/metadata-foundation.md §
83
+ * 白名单). These are computed by DID Space at write time and overwrite
84
+ * anything the application tries to put under the same name. The application
85
+ * may write any OTHER key into `payload.meta` and it'll be persisted to the
86
+ * `entries.metadata` column.
87
+ *
88
+ * Note: `ctime / mtime` are NOT in `entry.meta` (they live at AFSEntry top
89
+ * level as `createdAt / updatedAt`), so they're not in the reserved set —
90
+ * the application can write `meta.ctime` and it'll be persisted as just
91
+ * another application field, NOT conflated with the entry's timestamp.
92
+ */
93
+ const PROVIDER_RESERVED_META_KEYS = new Set([
94
+ "kind",
95
+ "size",
96
+ "cid",
97
+ "mimeType"
98
+ ]);
99
+ /**
100
+ * Strip provider-reserved keys from a write payload's meta, leaving only the
101
+ * application-owned fields that should go to `entries.metadata`.
102
+ * Returns undefined when nothing application-owned remains (avoids storing
103
+ * `{}` blobs).
104
+ */
105
+ function extractApplicationMeta(meta) {
106
+ if (!meta) return void 0;
107
+ const out = {};
108
+ let any = false;
109
+ for (const [k, v] of Object.entries(meta)) {
110
+ if (PROVIDER_RESERVED_META_KEYS.has(k)) continue;
111
+ out[k] = v;
112
+ any = true;
113
+ }
114
+ return any ? out : void 0;
115
+ }
116
+ /**
117
+ * Loose upper bound on serialized application metadata (did-space-metadata
118
+ * NEEDS DECISION 3). `metadata` rides `z.record(z.unknown())` and is NOT
119
+ * digest-checked (it's application data, not bytes — design §5), so the wire
120
+ * imposes no size limit of its own. Aside's real metadata is tiny (a few ISO
121
+ * timestamps, ~50B), but a malicious / buggy client could otherwise stuff a
122
+ * huge JSON blob straight into the D1 `metadata` column (row-size / write-
123
+ * amplification risk, cf. the D1 burn-through lesson). 64KB is far above any
124
+ * legitimate use yet blocks abuse. Shared by the two new server ingress points
125
+ * (batchWrite + upload-plane commit) so the threshold can't drift.
126
+ */
127
+ const METADATA_MAX_BYTES = 64 * 1024;
128
+ /**
129
+ * Reject abusively large application metadata gracefully (an `AFSValidationError`,
130
+ * which afs-rpc maps to 400 — never an uncaught 500). Call AFTER
131
+ * `extractApplicationMeta` so the reserved-key strip can't be gamed to dodge
132
+ * the bound. No-op for absent metadata.
133
+ */
134
+ function assertMetadataSize(meta, path) {
135
+ if (!meta) return;
136
+ const bytes = new TextEncoder().encode(JSON.stringify(meta)).length;
137
+ if (bytes > METADATA_MAX_BYTES) throw new _aigne_afs.AFSValidationError(`Application metadata for ${path} is ${bytes} bytes, over the ${METADATA_MAX_BYTES}-byte limit`);
138
+ }
139
+ /** Plain JSON object (not array, not null) — the deep-merge branch. */
140
+ function isPlainObject(v) {
141
+ return typeof v === "object" && v !== null && !Array.isArray(v);
142
+ }
143
+ /** An incoming scalar that carries no information and must never overwrite. */
144
+ function isEmptyScalar(v) {
145
+ return v === void 0 || v === null || v === "";
146
+ }
147
+ /** Union two arrays, dedup by structural identity, existing entries first. */
148
+ function unionArrays(existing, incoming) {
149
+ const seen = new Set(existing.map((x) => JSON.stringify(x)));
150
+ const out = [...existing];
151
+ for (const item of incoming) {
152
+ const key = JSON.stringify(item);
153
+ if (!seen.has(key)) {
154
+ out.push(item);
155
+ seen.add(key);
156
+ }
157
+ }
158
+ return out;
159
+ }
160
+ /**
161
+ * Merge incoming item content into the existing item on a duplicate-key write
162
+ * (#492). A re-save is ADDITIVE and must never DEGRADE the stored item:
163
+ *
164
+ * - keys in `immutable` (always `createdAt`, plus caller-supplied lifecycle
165
+ * fields like `status`) keep the existing value — a re-clip never resets an
166
+ * archived item or rewrites its creation time;
167
+ * - arrays union-dedup (existing first) so tags / collections accrete;
168
+ * - plain objects deep-merge recursively, so a partial re-clip of `og` /
169
+ * `content` keeps fields the new capture omitted;
170
+ * - scalars take the incoming value EXCEPT empty ones (`undefined` / `null` /
171
+ * `""`), which keep the existing value — a bookmarklet that failed to grab a
172
+ * title must not wipe the good title already saved;
173
+ * - type conflicts (array↔scalar, object↔scalar) take the incoming value.
174
+ */
175
+ function mergeItemContent(existing, incoming, immutable = new Set(["createdAt"])) {
176
+ const merged = { ...existing };
177
+ for (const [k, v] of Object.entries(incoming)) {
178
+ if (immutable.has(k)) continue;
179
+ const cur = merged[k];
180
+ if (Array.isArray(v) && Array.isArray(cur)) merged[k] = unionArrays(cur, v);
181
+ else if (isPlainObject(v) && isPlainObject(cur)) merged[k] = mergeItemContent(cur, v, immutable);
182
+ else if (isEmptyScalar(v)) {} else merged[k] = v;
183
+ }
184
+ return merged;
185
+ }
186
+ var DIDSpaceProvider = class extends _aigne_afs_provider.AFSBaseProvider {
187
+ static manifest() {
188
+ return {
189
+ name: "did-space",
190
+ description: "DID-scoped persistent storage with CID-based content deduplication.\n- Per-user data isolation via DID prefix\n- Content-addressable deduplication (CID v1, raw codec + SHA-256)\n- R2 for objects, D1 for tree index",
191
+ uriTemplate: "did-space://{did}/{path*}",
192
+ category: "storage",
193
+ schema: zod.z.object({
194
+ did: zod.z.string(),
195
+ path: zod.z.string().optional()
196
+ }),
197
+ tags: [
198
+ "did",
199
+ "storage",
200
+ "cid",
201
+ "dedup",
202
+ "cloud"
203
+ ],
204
+ capabilityTags: [
205
+ "read-write",
206
+ "crud",
207
+ "search",
208
+ "auth:token"
209
+ ],
210
+ security: {
211
+ riskLevel: "sandboxed",
212
+ resourceAccess: []
213
+ },
214
+ capabilities: { filesystem: {
215
+ read: true,
216
+ write: true
217
+ } },
218
+ noPerEntryActions: true
219
+ };
220
+ }
221
+ static treeSchema() {
222
+ return {
223
+ operations: [
224
+ "list",
225
+ "read",
226
+ "write",
227
+ "delete",
228
+ "search",
229
+ "stat",
230
+ "explain"
231
+ ],
232
+ tree: {
233
+ "/": { kind: "did-space:root" },
234
+ "/{path+}": { kind: "did-space:file" }
235
+ },
236
+ auth: { type: "custom" },
237
+ bestFor: [
238
+ "user files",
239
+ "persistent storage",
240
+ "content-addressed data"
241
+ ],
242
+ notFor: ["large binary blobs", "real-time streaming"]
243
+ };
244
+ }
245
+ name;
246
+ description;
247
+ accessMode;
248
+ did;
249
+ objectStore;
250
+ treeIndex;
251
+ gcQueue;
252
+ /** See `DIDSpaceProviderOptions.deviceId`. Read by writeHandler/deleteHandler. */
253
+ deviceId;
254
+ /** Optional ShareResolver for .share reconcile hook (Phase 0.4) */
255
+ shareResolver;
256
+ /** See `DIDSpaceProviderOptions.shareOwnerDid` / `shareTreePrefix`. */
257
+ shareOwnerDid;
258
+ shareTreePrefix;
259
+ /** See `DIDSpaceProviderOptions.blockletResolver` / `blockletOwnerDid` (#674). */
260
+ blockletResolver;
261
+ blockletOwnerDid;
262
+ /** Upload plane deps (design §7). See `DIDSpaceProviderOptions`. */
263
+ signer;
264
+ uploadSessionStore;
265
+ uploadScope;
266
+ constructor(options) {
267
+ super();
268
+ require_did_utils.validateId(options.did);
269
+ this.name = options.name || "did-space";
270
+ this.description = options.description;
271
+ this.accessMode = options.accessMode ?? "readwrite";
272
+ this.did = require_did_utils.normalizeId(options.did);
273
+ this.objectStore = options.objectStore;
274
+ this.treeIndex = options.treeIndex;
275
+ this.gcQueue = options.gcQueue;
276
+ this.deviceId = options.deviceId ?? "server:default";
277
+ this.shareResolver = options.shareResolver;
278
+ this.shareOwnerDid = options.shareOwnerDid;
279
+ this.shareTreePrefix = options.shareTreePrefix;
280
+ this.blockletResolver = options.blockletResolver;
281
+ this.blockletOwnerDid = options.blockletOwnerDid;
282
+ this.signer = options.signer;
283
+ this.uploadSessionStore = options.uploadSessionStore;
284
+ this.uploadScope = options.uploadScope;
285
+ }
286
+ /** Convert AFS path to tree-index path (strip leading /) */
287
+ treePath(afsPath) {
288
+ if (afsPath.includes("\0")) throw new Error("Null byte in path");
289
+ if (/(?:^|\/)\.\.(?:\/|$)/.test(afsPath)) throw new Error("Path traversal in path");
290
+ return afsPath.replace(/^\/+/, "");
291
+ }
292
+ /** Convert AFS path to tree-index prefix for directory listing */
293
+ treeListPrefix(afsPath) {
294
+ if (afsPath === "/") return "";
295
+ return `${this.treePath(afsPath)}/`;
296
+ }
297
+ /**
298
+ * Remap a write/delete into the arguments the .share reconcile hook expects.
299
+ * On a whole-space provider this is identity. On a prefixed instance-fragment
300
+ * provider (`shareTreePrefix` set) it restores the WHOLE-TREE decl path and
301
+ * keys the share to the space owner (`shareOwnerDid`), so reconcile stores the
302
+ * same `owner_did` + full `decl_path` the public serving path resolves by.
303
+ */
304
+ shareHookArgs(afsPath, treePath) {
305
+ const fullTreePath = this.shareTreePrefix ? (0, ufo.joinURL)(this.shareTreePrefix, treePath) : treePath;
306
+ return {
307
+ afsPath: this.shareTreePrefix ? `/${fullTreePath}` : afsPath,
308
+ treePath: fullTreePath,
309
+ ownerDid: this.shareOwnerDid ?? this.did
310
+ };
311
+ }
312
+ /**
313
+ * Remap a write/delete into the arguments the blocklet hook expects. Same
314
+ * whole-tree path restoration as {@link shareHookArgs}, but the recorded
315
+ * `ownerDid` is the PLAINTEXT space owner (`blockletOwnerDid`), not the de-id
316
+ * scope — the serve path re-derives the storage scope from the plaintext DID.
317
+ */
318
+ blockletHookArgs(afsPath, treePath) {
319
+ const fullTreePath = this.shareTreePrefix ? (0, ufo.joinURL)(this.shareTreePrefix, treePath) : treePath;
320
+ return {
321
+ afsPath: this.shareTreePrefix ? `/${fullTreePath}` : afsPath,
322
+ treePath: fullTreePath,
323
+ ownerDid: this.blockletOwnerDid ?? this.did
324
+ };
325
+ }
326
+ objectKey(cid) {
327
+ if (!/^[bz][a-zA-Z0-9]{20,100}$/.test(cid)) throw new Error(`Invalid CID format: ${cid.slice(0, 30)}`);
328
+ return `objects/${cid}`;
329
+ }
330
+ async isDirectory(path) {
331
+ if (path === "/") return true;
332
+ return this.treeIndex.hasChildren(this.treeListPrefix(path));
333
+ }
334
+ /** Collect all CIDs under a prefix for GC queuing before bulk delete. */
335
+ async collectCIDsByPrefix(prefix) {
336
+ const cids = [];
337
+ let cursor;
338
+ do {
339
+ const result = await this.treeIndex.listEntries(prefix, {
340
+ limit: 1e3,
341
+ cursor
342
+ });
343
+ for (const e of result.entries) if (e.cid) cids.push(e.cid);
344
+ cursor = result.hasMore ? result.cursor : void 0;
345
+ } while (cursor);
346
+ return cids;
347
+ }
348
+ async readMeta(ctx) {
349
+ const afsPath = (0, ufo.joinURL)("/", ctx.params.path ?? "");
350
+ if (afsPath === "/") {
351
+ const childrenCount = await this.treeIndex.countChildren(this.treeListPrefix("/"));
352
+ return this.buildEntry((0, ufo.joinURL)(afsPath, ".meta"), {
353
+ content: {
354
+ type: "directory",
355
+ path: "/"
356
+ },
357
+ meta: {
358
+ kind: "did-space:root",
359
+ childrenCount
360
+ }
361
+ });
362
+ }
363
+ const entry = await this.treeIndex.get(this.treePath(afsPath), { includeContent: false });
364
+ if (entry) return this.buildEntry((0, ufo.joinURL)(afsPath, ".meta"), {
365
+ content: {
366
+ type: "file",
367
+ path: afsPath,
368
+ size: entry.size,
369
+ cid: entry.cid
370
+ },
371
+ meta: {
372
+ kind: "did-space:file",
373
+ size: entry.size,
374
+ cid: entry.cid,
375
+ mimeType: entry.mimeType
376
+ },
377
+ createdAt: new Date(entry.ctime),
378
+ updatedAt: new Date(entry.mtime)
379
+ });
380
+ if (await this.isDirectory(afsPath)) {
381
+ const childrenCount = await this.treeIndex.countChildren(this.treeListPrefix(afsPath));
382
+ return this.buildEntry((0, ufo.joinURL)(afsPath, ".meta"), {
383
+ content: {
384
+ type: "directory",
385
+ path: afsPath
386
+ },
387
+ meta: {
388
+ kind: "did-space:directory",
389
+ childrenCount
390
+ }
391
+ });
392
+ }
393
+ throw new _aigne_afs.AFSNotFoundError(afsPath);
394
+ }
395
+ /**
396
+ * Override to implement the `maxDepth: -1` snapshot contract.
397
+ *
398
+ * When called with `maxDepth: -1`, collects ALL file entries under `path`
399
+ * using cursor-based pagination through the treeIndex and returns them in
400
+ * a single `AFSListResult` with `meta.cursor = String(manifestSeq)` so
401
+ * `SyncEngine.fullResync()` can advance `cloudSeq` correctly.
402
+ *
403
+ * > 10_000 entries → sets `meta.truncated: true` (HTTP 200, SyncEngine
404
+ * throws `ManifestTooLargeError`).
405
+ *
406
+ * All other options fall through to `super.list()` → `listHandler`.
407
+ */
408
+ async list(path, options) {
409
+ if (options?.maxDepth !== -1) return super.list(path, options);
410
+ const normPath = path.startsWith("/") ? path.slice(1) : path;
411
+ if (normPath === ".changelog" || normPath.startsWith(".changelog/")) throw new _aigne_afs.AFSValidationError(".changelog path does not support maxDepth=-1 (changelog uses cursor-based incremental access)");
412
+ if (options.cursor !== void 0 || options.limit !== void 0 && Number.isFinite(options.limit) || options.offset !== void 0 && Number.isFinite(options.offset)) throw new _aigne_afs.AFSValidationError("list with maxDepth=-1 cannot be combined with cursor, limit, or offset");
413
+ const SNAPSHOT_MAX_ENTRIES = 1e4;
414
+ const prefix = this.treeListPrefix(path);
415
+ const allEntries = [];
416
+ let cursor;
417
+ do {
418
+ const result = await this.treeIndex.listEntries(prefix, {
419
+ limit: LIST_MAX_LIMIT,
420
+ cursor
421
+ });
422
+ for (const treeEntry of result.entries) {
423
+ if (allEntries.length >= SNAPSHOT_MAX_ENTRIES) return {
424
+ data: allEntries,
425
+ meta: {
426
+ truncated: true,
427
+ ...(0, _aigne_afs.orderByRequested)(options.orderBy) ? { orderByIgnored: true } : {}
428
+ }
429
+ };
430
+ allEntries.push(this.buildEntry(`/${treeEntry.path}`, {
431
+ id: basenameId(treeEntry.path),
432
+ meta: require_changelog_schema.serializeManifestEntry({
433
+ cid: treeEntry.cid,
434
+ size: treeEntry.size,
435
+ mtime: treeEntry.mtime,
436
+ mimeType: treeEntry.mimeType
437
+ }),
438
+ createdAt: new Date(treeEntry.ctime),
439
+ updatedAt: new Date(treeEntry.mtime)
440
+ }));
441
+ }
442
+ cursor = result.hasMore ? result.cursor : void 0;
443
+ } while (cursor);
444
+ const manifestSeq = await this.treeIndex.getMaxSeq();
445
+ const snapshotMeta = { cursor: String(manifestSeq) };
446
+ if ((0, _aigne_afs.orderByRequested)(options.orderBy)) snapshotMeta.orderByIgnored = true;
447
+ return {
448
+ data: allEntries,
449
+ meta: snapshotMeta
450
+ };
451
+ }
452
+ async listHandler(ctx) {
453
+ const afsPath = ctx.params.path ? `/${ctx.params.path}` : "/";
454
+ const options = ctx.options;
455
+ const includeContent = options?.includeContent === true;
456
+ const includeChildrenCounts = options?.includeChildrenCounts === true;
457
+ const orderRequested = (0, _aigne_afs.orderByRequested)(options?.orderBy);
458
+ const orderBy = orderRequested && (0, _aigne_afs.isValidOrderBy)(options?.orderBy) ? options.orderBy : void 0;
459
+ /** Merge the no-silent-drop flag into a result when ordering was requested but not applied. */
460
+ const flagIgnored = (result) => orderRequested ? {
461
+ ...result,
462
+ meta: {
463
+ ...result.meta,
464
+ orderByIgnored: true
465
+ }
466
+ } : result;
467
+ const sharesMatch = require_share_virtual_path.matchSharesVirtualPath(afsPath);
468
+ if (sharesMatch && this.shareResolver && !this.shareTreePrefix) {
469
+ const { list } = await new require_share_virtual_path.ShareVirtualHandler(this.shareResolver, this.did).list(sharesMatch);
470
+ return flagIgnored({ data: list });
471
+ }
472
+ const normPath = afsPath.startsWith("/") ? afsPath.slice(1) : afsPath;
473
+ const isChangelog = normPath === ".changelog" || normPath.startsWith(".changelog/");
474
+ if (isChangelog && options?.cursor !== void 0) return flagIgnored(await this.listChangelog(options.cursor, options.limit));
475
+ const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);
476
+ const offset = Math.max(0, Number(options?.offset) || 0);
477
+ const maxDepth = options?.maxDepth ?? 1;
478
+ const collectLimit = offset + limit;
479
+ if (maxDepth === 0) return { data: [] };
480
+ const prefix = this.treeListPrefix(afsPath);
481
+ if (afsPath !== "/") {
482
+ if (!await this.treeIndex.get(this.treePath(afsPath), { includeContent: false })) {
483
+ if (!await this.treeIndex.hasChildren(prefix)) throw new _aigne_afs.AFSNotFoundError(afsPath);
484
+ }
485
+ }
486
+ const entries = [];
487
+ if (maxDepth === 1) {
488
+ if (orderBy) {
489
+ const scan$1 = await this.listShallowOrdered(prefix, collectLimit, entries, orderBy, includeContent);
490
+ if (includeChildrenCounts) await this.fillChildrenCounts(entries);
491
+ return this.withLiveSeq({
492
+ data: entries,
493
+ total: await this.shallowTotal(prefix, scan$1)
494
+ }, prefix, isChangelog);
495
+ }
496
+ const scan = await this.listShallow(prefix, collectLimit, entries, void 0, includeContent);
497
+ if (includeChildrenCounts) await this.fillChildrenCounts(entries);
498
+ return this.withLiveSeq(flagIgnored({
499
+ data: entries,
500
+ total: await this.shallowTotal(prefix, scan)
501
+ }), prefix, isChangelog);
502
+ }
503
+ await this.listDeep(afsPath, prefix, collectLimit, maxDepth, entries, includeContent);
504
+ return flagIgnored({ data: entries });
505
+ }
506
+ /**
507
+ * True direct-children total for a shallow listing.
508
+ *
509
+ * AFSListResult contract (`packages/core/src/type.ts`): absent `total`
510
+ * means "data IS the complete result" — so a handler that truncates its
511
+ * collection MUST report `total`, or every consumer downstream (base.ts
512
+ * pagination, afs-list pager) mistakes the page for the whole set.
513
+ *
514
+ * Cost model (D1: per-query round-trips dominate): `listShallow` scans
515
+ * the index in LIST_MAX_LIMIT batches and keeps counting rows past the
516
+ * build cap, so when the cursor drained (directories ≤ LIST_MAX_LIMIT
517
+ * children — the common case) the scan itself yields the exact total:
518
+ * ZERO extra queries. Only a directory truncated mid-cursor (> 1000
519
+ * children) pays ONE COUNT round-trip (same `countChildren(prefix)`
520
+ * call and parent-key format `readMeta` already relies on).
521
+ */
522
+ async shallowTotal(prefix, scan) {
523
+ if (scan.exhausted) return scan.seen;
524
+ return this.treeIndex.countChildren(prefix);
525
+ }
526
+ /**
527
+ * Attach the per-prefix watermark `version` as the result's top-level
528
+ * `liveSeq` (Live Update Resilience §4 / 0a'). Read ONE row via the
529
+ * TreeIndex — O(1), independent of changelog history/global write volume.
530
+ *
531
+ * - Skipped for `.changelog` listings (sync-internal, no live-update scope).
532
+ * - Skipped when the index has no `getPrefixWatermark` (rollout) or reports
533
+ * no watermark key for the path (`null` — no collection root): the
534
+ * consumer then falls back to full re-list on the next edge/poll, still
535
+ * correct.
536
+ * - A valid-but-never-written collection root reports `"0"` so consumers
537
+ * baseline at 0 instead of conservatively refetching every poll tick on a
538
+ * read-only collection (the first write bumps to 1 > 0 and is detected).
539
+ * - The value is prefix-LOCAL (only this collection root's write count), so
540
+ * it never leaks another blocklet's timing (invariant I3).
541
+ */
542
+ async withLiveSeq(result, prefix, isChangelog) {
543
+ if (isChangelog || !this.treeIndex.getPrefixWatermark) return result;
544
+ const version = await this.treeIndex.getPrefixWatermark(prefix);
545
+ if (version != null) result.liveSeq = String(version);
546
+ return result;
547
+ }
548
+ /**
549
+ * Shallow listing with a VALID orderBy (did-space-sorting Phase 3).
550
+ *
551
+ * - Single tuple → forwarded to the TreeIndex (`listChildren` orders
552
+ * natively over the (scope,parent,col,name) indexes; keyset cursor
553
+ * pages stay globally ordered, so collecting offset+limit entries is
554
+ * the correct order-before-pagination slice).
555
+ * - 2-3 tuples → the index can order by one column only, so collect the
556
+ * FULL child set and apply the canonical core sort before truncation
557
+ * (order → offset → limit still holds).
558
+ */
559
+ async listShallowOrdered(prefix, collectLimit, entries, orderBy, includeContent = false) {
560
+ if (orderBy.length === 1) {
561
+ const [field, order] = orderBy[0];
562
+ return this.listShallow(prefix, collectLimit, entries, {
563
+ orderBy: field,
564
+ order
565
+ }, includeContent);
566
+ }
567
+ const all = [];
568
+ await this.listShallow(prefix, Number.MAX_SAFE_INTEGER, all, void 0, includeContent);
569
+ const { entries: sorted } = (0, _aigne_afs.orderEntries)(all, orderBy);
570
+ entries.push(...sorted.slice(0, collectLimit));
571
+ return {
572
+ exhausted: true,
573
+ seen: all.length
574
+ };
575
+ }
576
+ async listShallow(prefix, limit, entries, order, includeContent = false) {
577
+ let cursor;
578
+ let seen = 0;
579
+ do {
580
+ const result = await this.treeIndex.listChildren(prefix, {
581
+ limit: LIST_MAX_LIMIT,
582
+ cursor,
583
+ includeContent,
584
+ ...order ?? {}
585
+ });
586
+ seen += result.entries.length;
587
+ for (const child of result.entries) {
588
+ if (entries.length >= limit) continue;
589
+ if (child.path.endsWith("/")) {
590
+ const displayPath = `/${child.path.slice(0, -1)}`;
591
+ entries.push(this.buildEntry(displayPath, {
592
+ id: basenameId(displayPath),
593
+ meta: {
594
+ kind: "did-space:directory",
595
+ childrenCount: -1,
596
+ size: child.size
597
+ },
598
+ createdAt: new Date(child.ctime),
599
+ updatedAt: new Date(child.mtime)
600
+ }));
601
+ } else entries.push(this.buildEntry(`/${child.path}`, {
602
+ id: basenameId(child.path),
603
+ ...includeContent && await this.verifiedInline(child) !== null ? { content: child.content } : {},
604
+ meta: {
605
+ kind: "did-space:file",
606
+ cid: child.cid,
607
+ size: child.size,
608
+ mimeType: child.mimeType
609
+ },
610
+ createdAt: new Date(child.ctime),
611
+ updatedAt: new Date(child.mtime)
612
+ }));
613
+ }
614
+ cursor = result.hasMore ? result.cursor : void 0;
615
+ } while (cursor && entries.length < limit);
616
+ return {
617
+ exhausted: cursor === void 0,
618
+ seen
619
+ };
620
+ }
621
+ /**
622
+ * Patch EXACT `meta.childrenCount` onto the directory entries of a
623
+ * collected shallow page — the opt-in half of the two-tier childrenCount
624
+ * contract (`AFSListOptions.includeChildrenCounts`; default tier is the
625
+ * `-1` sentinel stamped by listShallow). Page-bounded by construction:
626
+ * only the dirs actually collected are counted, ONE `countChildrenMany`
627
+ * round-trip (per-dir `countChildren` fallback for indexes without it),
628
+ * so the cost is Σ direct children of the page's dirs — never the
629
+ * whole-subtree scan the retired `countChildrenGrouped` performed.
630
+ */
631
+ async fillChildrenCounts(entries) {
632
+ const dirs = entries.filter((e) => e.meta?.kind === "did-space:directory");
633
+ if (dirs.length === 0) return;
634
+ const keyOf = (e) => `${this.treePath(e.path)}/`;
635
+ if (this.treeIndex.countChildrenMany) {
636
+ const counts = await this.treeIndex.countChildrenMany(dirs.map(keyOf));
637
+ for (const e of dirs) e.meta.childrenCount = counts.get(keyOf(e)) ?? 0;
638
+ return;
639
+ }
640
+ for (const e of dirs) e.meta.childrenCount = await this.treeIndex.countChildren(keyOf(e));
641
+ }
642
+ async listDeep(afsBasePath, prefix, limit, maxDepth, entries, includeContent = false) {
643
+ const prefixDepth = afsBasePath === "/" ? 0 : afsBasePath.split("/").filter(Boolean).length;
644
+ const seenDirs = /* @__PURE__ */ new Set();
645
+ let cursor;
646
+ do {
647
+ const result = await this.treeIndex.listEntries(prefix, {
648
+ limit: LIST_MAX_LIMIT,
649
+ cursor,
650
+ includeContent
651
+ });
652
+ for (const treeEntry of result.entries) {
653
+ if (entries.length >= limit) return;
654
+ const parts = treeEntry.path.slice(prefix.length).split("/").filter(Boolean);
655
+ for (let i = 0; i < parts.length - 1; i++) {
656
+ if (i + 1 > maxDepth) break;
657
+ const dirKey = prefix + parts.slice(0, i + 1).join("/");
658
+ if (!seenDirs.has(dirKey)) {
659
+ seenDirs.add(dirKey);
660
+ entries.push(this.buildEntry(`/${dirKey}`, {
661
+ id: basenameId(dirKey),
662
+ meta: {
663
+ kind: "did-space:directory",
664
+ childrenCount: -1
665
+ }
666
+ }));
667
+ }
668
+ }
669
+ const path = `/${treeEntry.path}`;
670
+ if (path.split("/").filter(Boolean).length - prefixDepth <= maxDepth) entries.push(this.buildEntry(path, {
671
+ id: basenameId(path),
672
+ ...includeContent && await this.verifiedInline(treeEntry) !== null ? { content: treeEntry.content } : {},
673
+ meta: {
674
+ kind: "did-space:file",
675
+ cid: treeEntry.cid,
676
+ size: treeEntry.size,
677
+ mimeType: treeEntry.mimeType
678
+ },
679
+ createdAt: new Date(treeEntry.ctime),
680
+ updatedAt: new Date(treeEntry.mtime)
681
+ }));
682
+ }
683
+ cursor = result.hasMore ? result.cursor : void 0;
684
+ } while (cursor && entries.length < limit);
685
+ }
686
+ /**
687
+ * Task 1.9: list changelog rows starting after `cursor` (seq number).
688
+ * Returns rows as AFSEntry objects + meta.cursor pointing to the last row's seq.
689
+ */
690
+ async listChangelog(cursor, limit) {
691
+ const sinceSeq = Number(cursor) || 0;
692
+ const effectiveLimit = Math.min(limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);
693
+ const watermark = await this.treeIndex.getCompactedBelowSeq?.() ?? 0;
694
+ if (watermark > 0 && sinceSeq < watermark) throw new _aigne_afs.AFSCompactedError("/.changelog", String(watermark));
695
+ const changelog = await this.treeIndex.getChangelogSince(sinceSeq, effectiveLimit);
696
+ return {
697
+ data: changelog.entries.map((row) => this.buildEntry(`/.changelog/${row.seq}`, {
698
+ content: null,
699
+ meta: {
700
+ kind: require_changelog_schema.CHANGELOG_ROW_KIND,
701
+ extra: require_changelog_schema.serializeChangelogEntry(row)
702
+ }
703
+ })),
704
+ meta: {
705
+ cursor: String(changelog.cursor),
706
+ hasMore: changelog.hasMore
707
+ }
708
+ };
709
+ }
710
+ /**
711
+ * Promote write + delete to the object form so callers can discover the
712
+ * `ifMatch` sub-capability (CLAUDE.md "Sub-capability schema"). The base impl
713
+ * (base.ts) returns plain booleans; without this override the if-match
714
+ * conformance suite's `getCapabilityFeature("write","ifMatch")` reads nothing
715
+ * and the suite skips. Mirrors AFSFS's override (fs/src/index.ts).
716
+ */
717
+ getOperationsDeclaration() {
718
+ const ops = super.getOperationsDeclaration();
719
+ if (typeof ops.write === "boolean" ? ops.write : ops.write?.supported === true) ops.write = {
720
+ supported: true,
721
+ features: {
722
+ ifMatch: true,
723
+ treeMerge: true,
724
+ treeToggle: true,
725
+ treeIncrement: true,
726
+ treeListAdd: true,
727
+ treeListRemove: true,
728
+ batchPushdown: true
729
+ }
730
+ };
731
+ if (typeof ops.delete === "boolean" ? ops.delete : ops.delete?.supported === true) ops.delete = {
732
+ supported: true,
733
+ features: { ifMatch: true }
734
+ };
735
+ if (typeof ops.list === "boolean" ? ops.list : ops.list?.supported === true) ops.list = {
736
+ supported: true,
737
+ features: {
738
+ orderBy: true,
739
+ includeContent: true
740
+ }
741
+ };
742
+ return ops;
743
+ }
744
+ async readCapabilities(_ctx) {
745
+ const operations = this.getOperationsDeclaration();
746
+ const queryable = typeof this.treeIndex.query === "function";
747
+ if (queryable) operations.query = true;
748
+ return {
749
+ id: "/.meta/.capabilities",
750
+ path: "/.meta/.capabilities",
751
+ content: {
752
+ schemaVersion: 1,
753
+ provider: this.name,
754
+ description: this.description || "DID-scoped persistent storage with CID-based deduplication",
755
+ tools: [],
756
+ actions: [],
757
+ operations,
758
+ ...queryable ? { standardActions: [(0, _aigne_afs.collectionQueryStandardAction)({
759
+ doc: true,
760
+ groupBy: true,
761
+ annotate: true,
762
+ aggregate: true
763
+ })] } : {}
764
+ },
765
+ meta: {
766
+ kind: "afs:capabilities",
767
+ operations
768
+ }
769
+ };
770
+ }
771
+ /**
772
+ * Typed collection query over a directory of records (design §3.5).
773
+ *
774
+ * - dir-per-record (`spec.doc`): direct child dirs joined with their
775
+ * `<dir>/<doc>` document; file-per-record (no doc): direct child files'
776
+ * inline content is the record.
777
+ * - where/orderBy/pagination/total/groupBy execute inside the TreeIndex
778
+ * engine (SQL or in-memory — lockstep-tested). select + annotate.memberOf
779
+ * are page-level post-processing here, identical across backends.
780
+ * - Strict contract: spec arrives core-validated; provider constraints
781
+ * (single orderBy key, no text) throw AFSValidationError. An index
782
+ * without the engine (e.g. not-yet-migrated backends) throws
783
+ * AFSUnsupportedError — and is never declared in capabilities.
784
+ */
785
+ async query(path, spec, _options) {
786
+ if (!this.treeIndex.query) throw new _aigne_afs.AFSUnsupportedError("query", "Collection query is not supported by this index backend");
787
+ const afsPath = (0, ufo.joinURL)("/", path);
788
+ if (this.isVirtualPath(afsPath) || require_share_virtual_path.matchSharesVirtualPath(afsPath)) throw new _aigne_afs.AFSValidationError(`Invalid query spec: path: not a queryable collection: ${afsPath}`);
789
+ require_tree_query.assertTreeQuerySpec(spec);
790
+ const prefix = this.treeListPrefix(afsPath);
791
+ if (afsPath !== "/") {
792
+ if (!await this.treeIndex.get(this.treePath(afsPath), { includeContent: false }) && !await this.treeIndex.hasChildren(prefix)) throw new _aigne_afs.AFSNotFoundError(afsPath);
793
+ }
794
+ const result = await this.treeIndex.query(prefix, spec);
795
+ let memberValues;
796
+ if (spec.annotate) {
797
+ memberValues = /* @__PURE__ */ new Set();
798
+ try {
799
+ const ref = await this.read((0, ufo.joinURL)("/", spec.annotate.memberOf.doc));
800
+ const raw = (0, _aigne_afs.resolveFieldPath)(require_tree_query.parseRecordText(typeof ref.data?.content === "string" ? ref.data.content : void 0), spec.annotate.memberOf.field);
801
+ if (Array.isArray(raw)) for (const v of raw) memberValues.add(typeof v === "string" ? v.toLowerCase() : String(v));
802
+ } catch {}
803
+ }
804
+ const entries = [];
805
+ for (const row of result.rows) entries.push(await this.queryRowToEntry(row, spec, memberValues));
806
+ const out = {
807
+ entries,
808
+ total: result.total
809
+ };
810
+ if (result.cursor) out.cursor = result.cursor;
811
+ if (result.groups) out.groups = result.groups;
812
+ if (result.aggregateGroups) out.aggregateGroups = result.aggregateGroups;
813
+ if (result.seekOffset !== void 0) out.seekOffset = result.seekOffset;
814
+ if (this.treeIndex.getPrefixWatermark) {
815
+ const version = await this.treeIndex.getPrefixWatermark(prefix);
816
+ if (version != null) out.liveSeq = String(version);
817
+ }
818
+ return out;
819
+ }
820
+ /** Map an engine row to an AFSEntry (mirrors the list handlers' shapes). */
821
+ async queryRowToEntry(row, spec, memberValues) {
822
+ const isDir = row.kind === "dir";
823
+ const displayPath = isDir ? `/${row.path.slice(0, -1)}` : `/${row.path}`;
824
+ const verifyCid = isDir ? row.contentCid : row.cid;
825
+ const verified = row.content !== void 0 && verifyCid ? await this.verifiedInline({
826
+ cid: verifyCid,
827
+ content: row.content
828
+ }) : null;
829
+ let content;
830
+ if (spec.select) content = require_tree_query.projectRecordFields(require_tree_query.parseRecordText(verified ?? void 0), spec.select);
831
+ else if (verified !== null) content = verified;
832
+ const entry = this.buildEntry(displayPath, {
833
+ id: basenameId(displayPath),
834
+ ...content !== void 0 ? { content } : {},
835
+ meta: isDir ? {
836
+ ...row.metadata ?? {},
837
+ kind: "did-space:directory",
838
+ childrenCount: -1,
839
+ size: row.size
840
+ } : {
841
+ ...row.metadata ?? {},
842
+ kind: "did-space:file",
843
+ cid: row.cid,
844
+ size: row.size,
845
+ mimeType: row.mimeType
846
+ },
847
+ createdAt: new Date(row.ctime),
848
+ updatedAt: new Date(row.mtime)
849
+ });
850
+ if (spec.annotate && memberValues) {
851
+ const { by, as } = spec.annotate.memberOf;
852
+ const byValue = by === "$id" ? row.name : (0, _aigne_afs.resolveFieldPath)(require_tree_query.parseRecordText(verified ?? row.content), by);
853
+ const member = byValue != null && memberValues.has(typeof byValue === "string" ? byValue.toLowerCase() : String(byValue));
854
+ entry._derived = { [as]: member ? "1" : "" };
855
+ }
856
+ return entry;
857
+ }
858
+ async readHandler(ctx) {
859
+ const afsPath = (0, ufo.joinURL)("/", ctx.params.path ?? "");
860
+ const sharesMatch = require_share_virtual_path.matchSharesVirtualPath(afsPath);
861
+ if (sharesMatch && this.shareResolver && !this.shareTreePrefix) {
862
+ const result = await new require_share_virtual_path.ShareVirtualHandler(this.shareResolver, this.did).read(sharesMatch);
863
+ if (!result) throw new _aigne_afs.AFSNotFoundError(afsPath);
864
+ return result.data;
865
+ }
866
+ const normPath = afsPath.startsWith("/") ? afsPath.slice(1) : afsPath;
867
+ if (normPath.startsWith(".objects/")) {
868
+ const cid = normPath.slice(9);
869
+ if (!await (0, _aigne_afs_timing.time)("ds.ref", () => this.treeIndex.isReferenced(cid))) throw new _aigne_afs.AFSNotFoundError(afsPath);
870
+ const obj = await (0, _aigne_afs_timing.time)("ds.obj", () => this.objectStore.get(this.objectKey(cid)));
871
+ if (!obj) throw new _aigne_afs.AFSNotFoundError(afsPath);
872
+ const bytes = new Uint8Array(await (0, _aigne_afs_timing.time)("ds.body", () => obj.arrayBuffer()));
873
+ return this.buildEntry(afsPath, {
874
+ content: bytes,
875
+ meta: {
876
+ kind: "did-space:object",
877
+ cid,
878
+ size: bytes.length
879
+ }
880
+ });
881
+ }
882
+ if (afsPath === "/") {
883
+ const childrenCount = await this.treeIndex.countChildren(this.treeListPrefix("/"));
884
+ return this.buildEntry(afsPath, {
885
+ content: `DID Space root for ${this.did} (${childrenCount} entries)`,
886
+ meta: {
887
+ kind: "did-space:root",
888
+ childrenCount
889
+ }
890
+ });
891
+ }
892
+ const entry = await (0, _aigne_afs_timing.time)("ds.idx", () => this.treeIndex.get(this.treePath(afsPath)));
893
+ if (entry) {
894
+ if (entry.content !== void 0 && (0, _aigne_afs_provider_utils.isTextMime)(entry.mimeType)) {
895
+ if (await (0, _aigne_afs_timing.time)("ds.inl", async () => {
896
+ return await require_cid.computeCID(entry.content) === entry.cid;
897
+ })) return this.buildEntry(afsPath, {
898
+ content: entry.content,
899
+ meta: {
900
+ ...entry.metadata ?? {},
901
+ kind: "did-space:file",
902
+ size: entry.size,
903
+ cid: entry.cid,
904
+ mimeType: entry.mimeType,
905
+ version: entry.cid
906
+ },
907
+ createdAt: new Date(entry.ctime),
908
+ updatedAt: new Date(entry.mtime)
909
+ });
910
+ log.warn(`[did-space] inline content failed cid verification at ${afsPath} (cid=${entry.cid}) — serving from objects; row will self-repair on next write`);
911
+ }
912
+ const obj = await (0, _aigne_afs_timing.time)("ds.obj", () => this.objectStore.get(this.objectKey(entry.cid)));
913
+ const content = await (0, _aigne_afs_timing.time)("ds.body", async () => obj ? (0, _aigne_afs_provider_utils.isTextMime)(entry.mimeType) ? await obj.text() : await obj.arrayBuffer() : (0, _aigne_afs_provider_utils.isTextMime)(entry.mimeType) ? "" : /* @__PURE__ */ new ArrayBuffer(0));
914
+ return this.buildEntry(afsPath, {
915
+ content,
916
+ meta: {
917
+ ...entry.metadata ?? {},
918
+ kind: "did-space:file",
919
+ size: entry.size,
920
+ cid: entry.cid,
921
+ mimeType: entry.mimeType,
922
+ version: entry.cid
923
+ },
924
+ createdAt: new Date(entry.ctime),
925
+ updatedAt: new Date(entry.mtime)
926
+ });
927
+ }
928
+ if (await this.isDirectory(afsPath)) {
929
+ const childrenCount = await this.treeIndex.countChildren(this.treeListPrefix(afsPath));
930
+ return this.buildEntry(afsPath, {
931
+ content: `Directory: ${afsPath} (${childrenCount} entries)`,
932
+ meta: {
933
+ kind: "did-space:directory",
934
+ childrenCount
935
+ }
936
+ });
937
+ }
938
+ throw new _aigne_afs.AFSNotFoundError(afsPath);
939
+ }
940
+ /**
941
+ * presignRead — path-keyed read-grant capability (preview-r2-direct §3).
942
+ *
943
+ * Resolves `path` in THIS provider's (prefix-isolated) view exactly like
944
+ * `read`/`stat` (`treeIndex.get(treePath(path))`, incl. the same null-byte /
945
+ * `..`-segment guards). iff the resolved entry has a cid AND a signer with
946
+ * `presignGet` is present → mint a short-TTL presigned GET with the
947
+ * Content-Type baked in, and return `{ url, expiresAt }`.
948
+ *
949
+ * The path resolution IS the authorization: a caller can only presign what
950
+ * `read` would already return. Prefix isolation is inherited from the view's
951
+ * `PrefixedTreeIndex` — no separate object-level `isReferenced(cid)` is needed
952
+ * (that primitive is for the cid-keyed `.objects/{cid}` path, which has no
953
+ * tree path). NEVER throws to the caller: no signer (Node) / non-CAS / no cid /
954
+ * unresolved / any internal error → `null`, and the raw handler then falls back
955
+ * to the worker serve path. TTL = `DOWNLOAD_GRANT_TTL_SEC` (2h) — never the 7d
956
+ * signer cap (§8.3).
957
+ */
958
+ async presignRead(path, opts) {
959
+ try {
960
+ const entry = await this.treeIndex.get(this.treePath(path), { includeContent: false });
961
+ if (!entry?.cid) return null;
962
+ if (this.signer == null || typeof this.signer.presignGet !== "function") return null;
963
+ const g = await this.signer.presignGet({
964
+ objectKey: this.objectKey(entry.cid),
965
+ expiresInSec: require_download_actions.DOWNLOAD_GRANT_TTL_SEC,
966
+ responseContentType: opts?.responseContentType
967
+ });
968
+ return {
969
+ url: g.url,
970
+ expiresAt: g.expiresAt
971
+ };
972
+ } catch {
973
+ return null;
974
+ }
975
+ }
976
+ /**
977
+ * Re-verify an entry's inline copy against its cid (§2.I list contract).
978
+ * Returns the content when verified, null otherwise (best-effort miss).
979
+ */
980
+ async verifiedInline(entry) {
981
+ if (entry.content === void 0) return null;
982
+ return await require_cid.computeCID(entry.content) === entry.cid ? entry.content : null;
983
+ }
984
+ /**
985
+ * Returns true if the given AFS path is a virtual D4-protected path
986
+ * (.objects/* or .changelog/*).
987
+ */
988
+ isVirtualPath(afsPath) {
989
+ const norm = afsPath.startsWith("/") ? afsPath.slice(1) : afsPath;
990
+ return norm === ".objects" || norm.startsWith(".objects/") || norm === ".changelog" || norm.startsWith(".changelog/");
991
+ }
992
+ async writeHandler(ctx, payload) {
993
+ const afsPath = (0, ufo.joinURL)("/", ctx.params.path ?? "");
994
+ if (this.isVirtualPath(afsPath)) throw new _aigne_afs.AFSForbiddenError(afsPath, "virtual_path");
995
+ if (require_share_declaration.isShareReservedPath(afsPath)) throw new _aigne_afs.AFSForbiddenError(afsPath, "share-path-reserved");
996
+ const writeIfMatch = ctx.options?.ifMatch;
997
+ if (writeIfMatch !== void 0) {
998
+ const current = await this.treeIndex.get(this.treePath(afsPath), { includeContent: false });
999
+ if (require_upload_actions.ifMatchPreconditionFailed(writeIfMatch, current)) throw new _aigne_afs.AFSConflictError(afsPath, {
1000
+ cid: current?.cid ?? null,
1001
+ mtime: current?.mtime ?? null
1002
+ });
1003
+ }
1004
+ if (payload.content === void 0 && payload.meta !== void 0) return { data: this.buildEntry(afsPath, { meta: {
1005
+ kind: "did-space:file",
1006
+ ...payload.meta
1007
+ } }) };
1008
+ let bytes;
1009
+ let storeValue;
1010
+ if (payload.content instanceof Uint8Array) {
1011
+ bytes = payload.content;
1012
+ storeValue = bytes.buffer;
1013
+ } else if (payload.content instanceof ArrayBuffer) {
1014
+ bytes = new Uint8Array(payload.content);
1015
+ storeValue = payload.content;
1016
+ } else {
1017
+ const text = typeof payload.content === "string" ? payload.content : JSON.stringify(payload.content);
1018
+ bytes = new TextEncoder().encode(text);
1019
+ storeValue = text;
1020
+ }
1021
+ const cid = await require_cid.computeCID(bytes);
1022
+ const treePath = this.treePath(afsPath);
1023
+ await require_share_hook.applyShareWriteHook({
1024
+ ...this.shareHookArgs(afsPath, treePath),
1025
+ bytes,
1026
+ cid,
1027
+ resolver: this.shareResolver,
1028
+ phase: "pre-io"
1029
+ });
1030
+ if (!await this.objectStore.head(this.objectKey(cid))) await this.objectStore.put(this.objectKey(cid), storeValue);
1031
+ const writeMeta = ctx.options?.meta;
1032
+ const writeDeviceId = typeof writeMeta?.deviceId === "string" && writeMeta.deviceId ? writeMeta.deviceId : this.deviceId;
1033
+ const now = typeof writeMeta?.mtime === "number" ? writeMeta.mtime : Date.now();
1034
+ let mimeType = payload.meta?.mimeType ?? (0, _aigne_afs_provider_utils.detectMimeType)(afsPath) ?? void 0;
1035
+ if (!mimeType && (payload.content instanceof Uint8Array || payload.content instanceof ArrayBuffer)) mimeType = "application/octet-stream";
1036
+ const ctime = require_timestamp.resolveCtime(payload.meta, now);
1037
+ const treeEntry = {
1038
+ cid,
1039
+ size: bytes.length,
1040
+ mtime: now,
1041
+ ctime,
1042
+ mimeType
1043
+ };
1044
+ const appMeta = extractApplicationMeta(payload.meta);
1045
+ if (appMeta) {
1046
+ if (typeof appMeta.dedupScope === "string") {
1047
+ treeEntry.dedupScope = appMeta.dedupScope;
1048
+ delete appMeta.dedupScope;
1049
+ }
1050
+ if (typeof appMeta.dedupKey === "string") treeEntry.dedupKey = appMeta.dedupKey;
1051
+ if (Object.keys(appMeta).length > 0) treeEntry.metadata = appMeta;
1052
+ }
1053
+ if (typeof storeValue === "string") treeEntry.content = storeValue;
1054
+ else {
1055
+ const inlineText = require_inline_content.decodeInlineText(bytes, mimeType);
1056
+ if (inlineText !== null) treeEntry.content = inlineText;
1057
+ }
1058
+ const { oldCid } = await this.treeIndex.put(treePath, treeEntry, { deviceId: writeDeviceId });
1059
+ if (this.gcQueue) {
1060
+ await this.gcQueue.remove(cid);
1061
+ if (oldCid && oldCid !== cid) await this.gcQueue.enqueue([oldCid]);
1062
+ }
1063
+ await require_share_hook.applyShareWriteHook({
1064
+ ...this.shareHookArgs(afsPath, treePath),
1065
+ bytes,
1066
+ cid,
1067
+ resolver: this.shareResolver,
1068
+ phase: "post-io"
1069
+ });
1070
+ await require_blocklet_hook.applyBlockletWriteHook({
1071
+ ...this.blockletHookArgs(afsPath, treePath),
1072
+ bytes,
1073
+ resolver: this.blockletResolver
1074
+ });
1075
+ if (afsPath.endsWith(".ash")) this.emit({
1076
+ type: _aigne_afs.SCRIPT_EVENT_TYPES.REGISTERED,
1077
+ path: afsPath,
1078
+ data: { runtime: "ash" }
1079
+ });
1080
+ const meta = {
1081
+ ...appMeta ?? {},
1082
+ kind: "did-space:file",
1083
+ size: treeEntry.size,
1084
+ cid,
1085
+ mimeType: treeEntry.mimeType
1086
+ };
1087
+ if (this.treeIndex.getPrefixWatermark) {
1088
+ const version = await this.treeIndex.getPrefixWatermark(this.treePath(afsPath));
1089
+ if (version != null) meta.liveSeq = String(version);
1090
+ }
1091
+ return { data: this.buildEntry(afsPath, {
1092
+ content: storeValue,
1093
+ meta,
1094
+ createdAt: new Date(treeEntry.ctime),
1095
+ updatedAt: new Date(treeEntry.mtime)
1096
+ }) };
1097
+ }
1098
+ async deleteHandler(ctx) {
1099
+ const afsPath = (0, ufo.joinURL)("/", ctx.params.path ?? "");
1100
+ if (this.isVirtualPath(afsPath)) throw new _aigne_afs.AFSForbiddenError(afsPath, "virtual_path");
1101
+ if (require_share_declaration.isShareReservedPath(afsPath)) throw new _aigne_afs.AFSForbiddenError(afsPath, "share-path-reserved");
1102
+ const treePath = this.treePath(afsPath);
1103
+ const deleteMeta = ctx.options?.meta;
1104
+ const deleteIfMatch = deleteMeta?.ifMatch;
1105
+ if (deleteIfMatch !== void 0) {
1106
+ const current = await this.treeIndex.get(treePath, { includeContent: false });
1107
+ if (!current || current.cid !== deleteIfMatch) throw new _aigne_afs.AFSConflictError(afsPath, {
1108
+ cid: current?.cid ?? null,
1109
+ mtime: current?.mtime ?? null
1110
+ });
1111
+ }
1112
+ const deleteOpts = {
1113
+ deviceId: typeof deleteMeta?.deviceId === "string" && deleteMeta.deviceId ? deleteMeta.deviceId : this.deviceId,
1114
+ ...typeof deleteMeta?.mtime === "number" ? { mtime: deleteMeta.mtime } : {}
1115
+ };
1116
+ const entry = await this.treeIndex.get(treePath, { includeContent: false });
1117
+ if (entry) {
1118
+ await this.gcQueue?.enqueue([entry.cid]);
1119
+ await this.treeIndex.delete(treePath, deleteOpts);
1120
+ await require_share_hook.applyShareDeleteHook({
1121
+ ...this.shareHookArgs(afsPath, treePath),
1122
+ resolver: this.shareResolver
1123
+ });
1124
+ await require_blocklet_hook.applyBlockletDeleteHook({
1125
+ ...this.blockletHookArgs(afsPath, treePath),
1126
+ resolver: this.blockletResolver
1127
+ });
1128
+ if (afsPath.endsWith(".ash")) this.emit({
1129
+ type: _aigne_afs.SCRIPT_EVENT_TYPES.UNREGISTERED,
1130
+ path: afsPath,
1131
+ data: { runtime: "ash" }
1132
+ });
1133
+ return {
1134
+ path: afsPath,
1135
+ deleted: true
1136
+ };
1137
+ }
1138
+ const prefix = this.treeListPrefix(afsPath);
1139
+ if (this.gcQueue) {
1140
+ const cids = await this.collectCIDsByPrefix(prefix);
1141
+ if (cids.length > 0) await this.gcQueue.enqueue(cids);
1142
+ }
1143
+ if (await this.treeIndex.deleteByPrefix(prefix, deleteOpts) === 0) throw new _aigne_afs.AFSNotFoundError(afsPath);
1144
+ return {
1145
+ path: afsPath,
1146
+ deleted: true
1147
+ };
1148
+ }
1149
+ /** Conflict failure shaped exactly like the single-op AFSConflictError surface. */
1150
+ batchConflictFailure(afsPath, conflict) {
1151
+ return {
1152
+ path: afsPath,
1153
+ success: false,
1154
+ error: `Write conflict at ${afsPath}`,
1155
+ code: "AFS_CONFLICT",
1156
+ conflict
1157
+ };
1158
+ }
1159
+ /** ONE preflight read for a batch — getMany when the index has it, per-path get otherwise. */
1160
+ async batchPreread(treePaths) {
1161
+ if (this.treeIndex.getMany) return this.treeIndex.getMany(treePaths);
1162
+ return new Map(await Promise.all(treePaths.map(async (tp) => [tp, await this.treeIndex.get(tp)])));
1163
+ }
1164
+ /** Run `fn` over `items` with bounded concurrency (Workers cap: 6 sockets). */
1165
+ async boundedAll(items, limit, fn) {
1166
+ let next = 0;
1167
+ const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
1168
+ while (next < items.length) {
1169
+ const item = items[next++];
1170
+ await fn(item);
1171
+ }
1172
+ });
1173
+ await Promise.all(workers);
1174
+ }
1175
+ /**
1176
+ * Native batch read (issue #808 follow-up) — the render fast path. Resolves the
1177
+ * whole path set with ONE `treeIndex.getMany` (chunked `IN(…)`, which carries
1178
+ * inline content when the index has it), so small cid-verified inline files are
1179
+ * served with ZERO R2 gets; only the (few, large) non-inline entries fall back
1180
+ * to bounded-concurrent `objectStore.get`. Mirrors the single-read handler's
1181
+ * inline-verify → objects cascade per entry. File-oriented: getMany matches
1182
+ * `kind='file'`, so directory / absent paths come back AFS_NOT_FOUND (callers
1183
+ * reading directories use `list`/`read`).
1184
+ */
1185
+ async batchRead(entries, _options) {
1186
+ const afsPaths = entries.map((e) => (0, ufo.joinURL)("/", e.path));
1187
+ const treePaths = afsPaths.map((p) => this.treePath(p));
1188
+ const preread = await this.batchPreread(treePaths);
1189
+ const results = new Array(entries.length);
1190
+ const fileEntry = (afsPath, entry, content) => this.buildEntry(afsPath, {
1191
+ content,
1192
+ meta: {
1193
+ ...entry.metadata ?? {},
1194
+ kind: "did-space:file",
1195
+ size: entry.size,
1196
+ cid: entry.cid,
1197
+ mimeType: entry.mimeType,
1198
+ version: entry.cid
1199
+ },
1200
+ createdAt: new Date(entry.ctime),
1201
+ updatedAt: new Date(entry.mtime)
1202
+ });
1203
+ const needObject = [];
1204
+ for (let i = 0; i < entries.length; i++) {
1205
+ const entry = preread.get(treePaths[i]);
1206
+ if (!entry) {
1207
+ results[i] = {
1208
+ path: afsPaths[i],
1209
+ success: false,
1210
+ error: `Path not found: ${afsPaths[i]}`,
1211
+ code: "AFS_NOT_FOUND"
1212
+ };
1213
+ continue;
1214
+ }
1215
+ if (entry.content !== void 0 && (0, _aigne_afs_provider_utils.isTextMime)(entry.mimeType)) {
1216
+ if (await require_cid.computeCID(entry.content) === entry.cid) {
1217
+ results[i] = {
1218
+ path: afsPaths[i],
1219
+ success: true,
1220
+ data: fileEntry(afsPaths[i], entry, entry.content)
1221
+ };
1222
+ continue;
1223
+ }
1224
+ log.warn(`[did-space] inline content failed cid verification at ${afsPaths[i]} (cid=${entry.cid}) — serving from objects; row will self-repair on next write`);
1225
+ }
1226
+ needObject.push(i);
1227
+ }
1228
+ await this.boundedAll(needObject, 6, async (i) => {
1229
+ const entry = preread.get(treePaths[i]);
1230
+ const obj = await this.objectStore.get(this.objectKey(entry.cid));
1231
+ const isText = (0, _aigne_afs_provider_utils.isTextMime)(entry.mimeType);
1232
+ const content = obj ? isText ? await obj.text() : await obj.arrayBuffer() : isText ? "" : /* @__PURE__ */ new ArrayBuffer(0);
1233
+ results[i] = {
1234
+ path: afsPaths[i],
1235
+ success: true,
1236
+ data: fileEntry(afsPaths[i], entry, content)
1237
+ };
1238
+ });
1239
+ const succeeded = results.filter((r) => r.success).length;
1240
+ return {
1241
+ results,
1242
+ succeeded,
1243
+ failed: entries.length - succeeded
1244
+ };
1245
+ }
1246
+ /**
1247
+ * Native batch write — the CF-subrequest-budget merge (§4.3): eight-step
1248
+ * order aligned point-for-point with the single write path (preread BEFORE
1249
+ * share pre-io, so a stale ifMatch on a `.share` declaration yields 409,
1250
+ * never a share error):
1251
+ * 1 shape/decode/computeCID → 2 ONE preread (conflict/skip exit here —
1252
+ * no share hook, no R2 upload) → 3 share pre-io (survivors) → 4 in-batch
1253
+ * CID dedup → 5 unconditional R2 put (unique cids, bounded ≤6 —
1254
+ * P0-measured wall-time) → 6 treeIndex.batchPutEntries (single db.batch)
1255
+ * → 7 gcQueue removeMany/enqueue → 8 share post-io + per-entry results.
1256
+ */
1257
+ async batchWrite(entries, _options) {
1258
+ if (entries.some((e) => e.options?.dedupBy || e.options?.dedupKey)) {
1259
+ const seq = [];
1260
+ let succeeded$1 = 0;
1261
+ for (const entry of entries) {
1262
+ const afsPath = (0, ufo.joinURL)("/", entry.path);
1263
+ try {
1264
+ const payload = entry.content;
1265
+ if (!payload || payload.content === void 0) throw new _aigne_afs.AFSValidationError("Batch write requires entry content");
1266
+ const opts = {
1267
+ ...entry.options ?? {},
1268
+ ...entry.mode ? { mode: entry.mode } : {}
1269
+ };
1270
+ const res = await this.write(entry.path, payload, opts);
1271
+ seq.push({
1272
+ path: res.data.path,
1273
+ success: true,
1274
+ data: res.data
1275
+ });
1276
+ succeeded$1++;
1277
+ } catch (err) {
1278
+ seq.push({
1279
+ path: afsPath,
1280
+ success: false,
1281
+ error: err instanceof Error ? err.message : String(err),
1282
+ code: err instanceof _aigne_afs.AFSConflictError ? "AFS_CONFLICT" : err?.code ?? "AFS_WRITE_ERROR"
1283
+ });
1284
+ }
1285
+ }
1286
+ return {
1287
+ results: seq,
1288
+ succeeded: succeeded$1,
1289
+ failed: entries.length - succeeded$1
1290
+ };
1291
+ }
1292
+ const results = new Array(entries.length);
1293
+ const prepared = [];
1294
+ for (let i = 0; i < entries.length; i++) {
1295
+ const entry = entries[i];
1296
+ const afsPath = (0, ufo.joinURL)("/", entry.path);
1297
+ if (entry.patches || entry.mode && entry.mode !== "replace") {
1298
+ results[i] = {
1299
+ path: afsPath,
1300
+ success: false,
1301
+ error: `Batch write supports replace-mode content writes only (got mode '${entry.mode ?? "patches"}')`,
1302
+ code: "AFS_UNSUPPORTED"
1303
+ };
1304
+ continue;
1305
+ }
1306
+ if (this.isVirtualPath(afsPath) || require_share_declaration.isShareReservedPath(afsPath)) {
1307
+ results[i] = {
1308
+ path: afsPath,
1309
+ success: false,
1310
+ error: `Forbidden at ${afsPath}: virtual_path`,
1311
+ code: "AFS_FORBIDDEN"
1312
+ };
1313
+ continue;
1314
+ }
1315
+ const payload = entry.content ?? {};
1316
+ if (payload.content === void 0) {
1317
+ results[i] = {
1318
+ path: afsPath,
1319
+ success: false,
1320
+ error: "Batch write requires entry content (meta-only writes are single-op)",
1321
+ code: "AFS_VALIDATION_ERROR"
1322
+ };
1323
+ continue;
1324
+ }
1325
+ let bytes;
1326
+ let storeValue;
1327
+ if (payload.content instanceof Uint8Array) {
1328
+ bytes = payload.content;
1329
+ storeValue = bytes.buffer;
1330
+ } else if (payload.content instanceof ArrayBuffer) {
1331
+ bytes = new Uint8Array(payload.content);
1332
+ storeValue = payload.content;
1333
+ } else {
1334
+ const text = typeof payload.content === "string" ? payload.content : JSON.stringify(payload.content);
1335
+ bytes = new TextEncoder().encode(text);
1336
+ storeValue = text;
1337
+ }
1338
+ const cid = await require_cid.computeCID(bytes);
1339
+ const writeOptions = entry.options;
1340
+ const writeMeta = writeOptions?.meta;
1341
+ const deviceId = typeof writeMeta?.deviceId === "string" && writeMeta.deviceId ? writeMeta.deviceId : this.deviceId;
1342
+ const now = typeof writeMeta?.mtime === "number" ? writeMeta.mtime : Date.now();
1343
+ let mimeType = payload.meta?.mimeType ?? (0, _aigne_afs_provider_utils.detectMimeType)(afsPath) ?? void 0;
1344
+ if (!mimeType && (payload.content instanceof Uint8Array || payload.content instanceof ArrayBuffer)) mimeType = "application/octet-stream";
1345
+ const ctime = require_timestamp.resolveCtime(payload.meta, now);
1346
+ const treeEntry = {
1347
+ cid,
1348
+ size: bytes.length,
1349
+ mtime: now,
1350
+ ctime,
1351
+ mimeType
1352
+ };
1353
+ const appMeta = extractApplicationMeta(payload.meta);
1354
+ if (appMeta) {
1355
+ try {
1356
+ assertMetadataSize(appMeta, afsPath);
1357
+ } catch (err) {
1358
+ results[i] = {
1359
+ path: afsPath,
1360
+ success: false,
1361
+ error: err instanceof Error ? err.message : String(err),
1362
+ code: err.code ?? "AFS_VALIDATION_ERROR"
1363
+ };
1364
+ continue;
1365
+ }
1366
+ treeEntry.metadata = appMeta;
1367
+ }
1368
+ if (typeof storeValue === "string") treeEntry.content = storeValue;
1369
+ else {
1370
+ const inlineText = require_inline_content.decodeInlineText(bytes, mimeType);
1371
+ if (inlineText !== null) treeEntry.content = inlineText;
1372
+ }
1373
+ prepared.push({
1374
+ i,
1375
+ afsPath,
1376
+ treePath: this.treePath(afsPath),
1377
+ bytes,
1378
+ storeValue,
1379
+ cid,
1380
+ treeEntry,
1381
+ deviceId,
1382
+ ifMatch: writeOptions?.ifMatch
1383
+ });
1384
+ }
1385
+ const preread = await this.batchPreread(prepared.map((p) => p.treePath));
1386
+ const skipped = [];
1387
+ let live = [];
1388
+ for (const p of prepared) {
1389
+ const existing = preread.get(p.treePath) ?? null;
1390
+ if (require_upload_actions.ifMatchPreconditionFailed(p.ifMatch, existing)) {
1391
+ results[p.i] = this.batchConflictFailure(p.afsPath, {
1392
+ currentCid: existing?.cid ?? null,
1393
+ currentMtime: existing?.mtime ?? null,
1394
+ currentDeviceId: null
1395
+ });
1396
+ continue;
1397
+ }
1398
+ live.push(p);
1399
+ }
1400
+ const preIoOk = [];
1401
+ for (const p of live) try {
1402
+ await require_share_hook.applyShareWriteHook({
1403
+ ...this.shareHookArgs(p.afsPath, p.treePath),
1404
+ bytes: p.bytes,
1405
+ cid: p.cid,
1406
+ resolver: this.shareResolver,
1407
+ phase: "pre-io"
1408
+ });
1409
+ preIoOk.push(p);
1410
+ } catch (err) {
1411
+ results[p.i] = {
1412
+ path: p.afsPath,
1413
+ success: false,
1414
+ error: err instanceof Error ? err.message : String(err),
1415
+ code: err.code
1416
+ };
1417
+ }
1418
+ live = preIoOk;
1419
+ const uniqueCids = /* @__PURE__ */ new Map();
1420
+ for (const p of live) if (!uniqueCids.has(p.cid)) uniqueCids.set(p.cid, p);
1421
+ const uploadFailed = /* @__PURE__ */ new Map();
1422
+ const uploaded = [];
1423
+ await this.boundedAll([...uniqueCids.values()], 6, async (p) => {
1424
+ try {
1425
+ await this.objectStore.put(this.objectKey(p.cid), p.storeValue);
1426
+ uploaded.push(p.cid);
1427
+ } catch (err) {
1428
+ uploadFailed.set(p.cid, err instanceof Error ? err.message : String(err));
1429
+ }
1430
+ });
1431
+ if (uploadFailed.size > 0) {
1432
+ const stillLive = [];
1433
+ for (const p of live) {
1434
+ const failure = uploadFailed.get(p.cid);
1435
+ if (failure !== void 0) results[p.i] = {
1436
+ path: p.afsPath,
1437
+ success: false,
1438
+ error: `R2 put failed: ${failure}`
1439
+ };
1440
+ else stillLive.push(p);
1441
+ }
1442
+ live = stillLive;
1443
+ }
1444
+ let indexResults = [];
1445
+ if (live.length > 0) {
1446
+ const batchEntries = live.map((p) => ({
1447
+ path: p.treePath,
1448
+ entry: p.treeEntry,
1449
+ opts: { deviceId: p.deviceId }
1450
+ }));
1451
+ try {
1452
+ indexResults = this.treeIndex.batchPutEntries ? await this.treeIndex.batchPutEntries(batchEntries, { preread }) : await require_tree_index.loopBatchPutEntries(this.treeIndex, batchEntries, { preread });
1453
+ } catch (err) {
1454
+ await this.gcQueue?.enqueue(uploaded);
1455
+ const message = err instanceof Error ? err.message : String(err);
1456
+ for (const p of live) results[p.i] = {
1457
+ path: p.afsPath,
1458
+ success: false,
1459
+ error: message
1460
+ };
1461
+ live = [];
1462
+ }
1463
+ }
1464
+ const written = [];
1465
+ live.forEach((p, j) => {
1466
+ const r = indexResults[j];
1467
+ if (!r) {
1468
+ results[p.i] = {
1469
+ path: p.afsPath,
1470
+ success: false,
1471
+ error: "index batch returned no result"
1472
+ };
1473
+ return;
1474
+ }
1475
+ if (r.conflict) {
1476
+ results[p.i] = this.batchConflictFailure(p.afsPath, r.conflict);
1477
+ return;
1478
+ }
1479
+ if (r.error) {
1480
+ results[p.i] = {
1481
+ path: p.afsPath,
1482
+ success: false,
1483
+ error: r.error
1484
+ };
1485
+ return;
1486
+ }
1487
+ if (r.skipped) {
1488
+ skipped.push(p);
1489
+ return;
1490
+ }
1491
+ written.push({
1492
+ p,
1493
+ oldCid: r.oldCid,
1494
+ liveSeq: r.liveSeq
1495
+ });
1496
+ });
1497
+ if (this.gcQueue) {
1498
+ const reReferenced = [...new Set([...written.map((w) => w.p.cid), ...skipped.map((s) => s.cid)])];
1499
+ if (reReferenced.length > 0) if (this.gcQueue.removeMany) await this.gcQueue.removeMany(reReferenced);
1500
+ else for (const cid of reReferenced) await this.gcQueue.remove(cid);
1501
+ const replaced = [...new Set(written.filter((w) => w.oldCid && w.oldCid !== w.p.cid).map((w) => w.oldCid))];
1502
+ if (replaced.length > 0) await this.gcQueue.enqueue(replaced);
1503
+ }
1504
+ for (const w of written) {
1505
+ try {
1506
+ await require_share_hook.applyShareWriteHook({
1507
+ ...this.shareHookArgs(w.p.afsPath, w.p.treePath),
1508
+ bytes: w.p.bytes,
1509
+ cid: w.p.cid,
1510
+ resolver: this.shareResolver,
1511
+ phase: "post-io"
1512
+ });
1513
+ } catch (err) {
1514
+ w.error = err instanceof Error ? err.message : String(err);
1515
+ }
1516
+ if (w.p.afsPath.endsWith(".ash")) this.emit({
1517
+ type: _aigne_afs.SCRIPT_EVENT_TYPES.REGISTERED,
1518
+ path: w.p.afsPath,
1519
+ data: { runtime: "ash" }
1520
+ });
1521
+ }
1522
+ const maxSeqByPrefix = /* @__PURE__ */ new Map();
1523
+ for (const w of written) {
1524
+ if (typeof w.liveSeq !== "number") continue;
1525
+ const wm = require_changelog_schema.watermarkWritePrefix(w.p.treePath);
1526
+ if (wm === null) continue;
1527
+ maxSeqByPrefix.set(wm, Math.max(maxSeqByPrefix.get(wm) ?? 0, w.liveSeq));
1528
+ }
1529
+ for (const s of skipped) {
1530
+ const wm = require_changelog_schema.watermarkWritePrefix(s.treePath);
1531
+ let liveSeq = wm !== null ? maxSeqByPrefix.get(wm) : void 0;
1532
+ if (liveSeq === void 0 && this.treeIndex.getPrefixWatermark) liveSeq = await this.treeIndex.getPrefixWatermark(s.treePath) ?? void 0;
1533
+ results[s.i] = {
1534
+ path: s.afsPath,
1535
+ success: true,
1536
+ data: this.buildBatchWriteData(s, liveSeq)
1537
+ };
1538
+ }
1539
+ for (const w of written) {
1540
+ if (w.error) {
1541
+ results[w.p.i] = {
1542
+ path: w.p.afsPath,
1543
+ success: false,
1544
+ error: w.error
1545
+ };
1546
+ continue;
1547
+ }
1548
+ results[w.p.i] = {
1549
+ path: w.p.afsPath,
1550
+ success: true,
1551
+ data: this.buildBatchWriteData(w.p, w.liveSeq)
1552
+ };
1553
+ }
1554
+ const succeeded = results.filter((r) => r?.success).length;
1555
+ return {
1556
+ results,
1557
+ succeeded,
1558
+ failed: results.length - succeeded
1559
+ };
1560
+ }
1561
+ /** Write-echo entry — field-for-field the single-op writeHandler result. */
1562
+ buildBatchWriteData(p, liveSeq) {
1563
+ const meta = {
1564
+ kind: "did-space:file",
1565
+ size: p.treeEntry.size,
1566
+ cid: p.cid,
1567
+ mimeType: p.treeEntry.mimeType
1568
+ };
1569
+ if (liveSeq != null) meta.liveSeq = String(liveSeq);
1570
+ return this.buildEntry(p.afsPath, {
1571
+ content: p.storeValue,
1572
+ meta,
1573
+ createdAt: new Date(p.treeEntry.ctime),
1574
+ updatedAt: new Date(p.treeEntry.mtime)
1575
+ });
1576
+ }
1577
+ /** Build a failed AFSExecResult (shared across the three action handlers). */
1578
+ uploadErr(code, message) {
1579
+ return {
1580
+ success: false,
1581
+ error: {
1582
+ code,
1583
+ message
1584
+ }
1585
+ };
1586
+ }
1587
+ /**
1588
+ * Session ownership isolation (§9 / §10): a session belongs to THIS view only
1589
+ * when its scope equals this provider's storage scope. A different-scope (or
1590
+ * unknown) session is treated as not-found — never confirm another user's
1591
+ * session exists, and never let one view commit into another.
1592
+ */
1593
+ ownsSession(session) {
1594
+ return !!session && session.scope === (this.uploadScope ?? this.did);
1595
+ }
1596
+ /**
1597
+ * Validate an action-supplied `path`. Unlike `@Write` handlers, action args
1598
+ * carry NO router-layer traversal protection — `args.entries[].path` is raw
1599
+ * client input, so we MUST normalize + reject traversal / reserved virtual
1600
+ * prefixes explicitly (design §10). Returns the tree path on success.
1601
+ */
1602
+ validateActionPath(afsPath) {
1603
+ const norm = afsPath.startsWith("/") ? afsPath.slice(1) : afsPath;
1604
+ if (this.isVirtualPath(afsPath) || require_share_declaration.isShareReservedPath(afsPath) || norm === ".actions" || norm.startsWith(".actions/")) return {
1605
+ ok: false,
1606
+ reason: `reserved path: ${afsPath}`
1607
+ };
1608
+ let treePath;
1609
+ try {
1610
+ treePath = this.treePath(afsPath);
1611
+ } catch (err) {
1612
+ return {
1613
+ ok: false,
1614
+ reason: err instanceof Error ? err.message : String(err)
1615
+ };
1616
+ }
1617
+ return {
1618
+ ok: true,
1619
+ treePath
1620
+ };
1621
+ }
1622
+ /**
1623
+ * Action catalogue — the upload-plane + download-plane actions, always listed
1624
+ * (protocol parity, T2.5). Each plane advertises its OWN capability flag:
1625
+ * `grantCapability` for prepare-upload, the INDEPENDENT `downloadGrantCapability`
1626
+ * for prepare-download (design §7.3 / Finding 4 — never reuse the upload flag,
1627
+ * so the two planes can be advertised separately).
1628
+ */
1629
+ listObjectPlaneActions(ctx) {
1630
+ const grantCapable = this.signer != null;
1631
+ const downloadGrantCapable = this.signer != null && typeof this.signer.presignGet === "function";
1632
+ return { data: [...require_upload_actions.UPLOAD_ACTIONS, ...require_download_actions.DOWNLOAD_ACTIONS].map((name) => this.buildEntry((0, ufo.joinURL)(ctx.path, name), {
1633
+ id: name,
1634
+ meta: {
1635
+ kind: "afs:executable",
1636
+ ...name === "prepare-upload" ? { grantCapability: grantCapable } : {},
1637
+ ...name === "prepare-download" ? { downloadGrantCapability: downloadGrantCapable } : {}
1638
+ }
1639
+ })) };
1640
+ }
1641
+ async execPrepareUpload(_ctx, args) {
1642
+ if (!this.uploadSessionStore) return this.uploadErr("AFS_UNSUPPORTED", "upload plane not available");
1643
+ const parsed = require_upload_actions.PrepareUploadArgsSchema.safeParse(args);
1644
+ if (!parsed.success) return this.uploadErr("AFS_VALIDATION_ERROR", require_upload_actions.zodMessage(parsed.error));
1645
+ const outEntries = [];
1646
+ const storeEntries = [];
1647
+ const seenPaths = /* @__PURE__ */ new Set();
1648
+ for (const e of parsed.data.entries) {
1649
+ const afsPath = (0, ufo.joinURL)("/", e.path);
1650
+ const pv = this.validateActionPath(afsPath);
1651
+ if (!pv.ok) return this.uploadErr("AFS_VALIDATION_ERROR", pv.reason);
1652
+ if (seenPaths.has(afsPath)) return this.uploadErr("AFS_VALIDATION_ERROR", `duplicate path: ${afsPath}`);
1653
+ seenPaths.add(afsPath);
1654
+ let checksum;
1655
+ try {
1656
+ checksum = require_cid.cidToChecksumSha256(e.cid);
1657
+ } catch (err) {
1658
+ return this.uploadErr("AFS_VALIDATION_ERROR", `bad cid for ${afsPath}: ${err instanceof Error ? err.message : err}`);
1659
+ }
1660
+ const objectKey = this.objectKey(e.cid);
1661
+ let grant = null;
1662
+ if (this.signer) {
1663
+ const g = await this.signer.presignPut({
1664
+ objectKey,
1665
+ checksumSha256: checksum,
1666
+ expiresInSec: require_upload_actions.GRANT_EXPIRES_SEC,
1667
+ contentLength: e.size
1668
+ });
1669
+ grant = {
1670
+ kind: "presigned-put",
1671
+ url: g.url,
1672
+ method: g.method,
1673
+ signedHeaders: g.signedHeaders,
1674
+ expiresAt: g.expiresAt
1675
+ };
1676
+ }
1677
+ outEntries.push({
1678
+ path: afsPath,
1679
+ cid: e.cid,
1680
+ status: "needs-upload",
1681
+ objectKey,
1682
+ checksumSha256: checksum,
1683
+ grant
1684
+ });
1685
+ storeEntries.push({
1686
+ path: afsPath,
1687
+ cid: e.cid,
1688
+ objectKey,
1689
+ checksumSha256: checksum,
1690
+ size: e.size,
1691
+ mimeType: e.mimeType ?? null,
1692
+ ifMatch: e.ifMatch ?? null,
1693
+ deviceId: e.deviceId ?? null,
1694
+ mtime: e.mtime ?? null,
1695
+ status: "needs_upload"
1696
+ });
1697
+ }
1698
+ const sessionId = crypto.randomUUID();
1699
+ const expiresAt = Date.now() + require_upload_session_store.UPLOAD_SESSION_TTL_MS;
1700
+ await this.uploadSessionStore.createSession({
1701
+ id: sessionId,
1702
+ scope: this.uploadScope ?? this.did,
1703
+ caller: null,
1704
+ expiresAt,
1705
+ entries: storeEntries
1706
+ });
1707
+ return {
1708
+ success: true,
1709
+ data: {
1710
+ sessionId,
1711
+ expiresAt: new Date(expiresAt).toISOString(),
1712
+ entries: outEntries
1713
+ }
1714
+ };
1715
+ }
1716
+ async execPrepareDownload(_ctx, args) {
1717
+ const parsed = require_download_actions.PrepareDownloadArgsSchema.safeParse(args);
1718
+ if (!parsed.success) return this.uploadErr("AFS_VALIDATION_ERROR", require_upload_actions.zodMessage(parsed.error));
1719
+ const inputEntries = parsed.data.entries;
1720
+ const cidValid = /* @__PURE__ */ new Map();
1721
+ const validCids = [];
1722
+ for (const e of inputEntries) {
1723
+ if (cidValid.has(e.cid)) continue;
1724
+ let ok = true;
1725
+ try {
1726
+ require_cid.cidToChecksumSha256(e.cid);
1727
+ } catch {
1728
+ ok = false;
1729
+ }
1730
+ cidValid.set(e.cid, ok);
1731
+ if (ok) validCids.push(e.cid);
1732
+ }
1733
+ let referenced = /* @__PURE__ */ new Set();
1734
+ if (validCids.length > 0) try {
1735
+ referenced = await this.treeIndex.isReferencedMany(validCids);
1736
+ } catch {
1737
+ return this.uploadErr("AFS_INTERNAL_ERROR", "prepare-download authorization unavailable");
1738
+ }
1739
+ const canSign = this.signer != null && typeof this.signer.presignGet === "function";
1740
+ const grantByCid = /* @__PURE__ */ new Map();
1741
+ for (const cid of referenced) if (canSign) {
1742
+ const g = await this.signer.presignGet({
1743
+ objectKey: this.objectKey(cid),
1744
+ expiresInSec: require_download_actions.DOWNLOAD_GRANT_TTL_SEC
1745
+ });
1746
+ grantByCid.set(cid, {
1747
+ kind: "presigned-get",
1748
+ url: g.url,
1749
+ method: g.method,
1750
+ signedHeaders: g.signedHeaders,
1751
+ expiresAt: g.expiresAt
1752
+ });
1753
+ } else grantByCid.set(cid, null);
1754
+ return {
1755
+ success: true,
1756
+ data: { entries: inputEntries.map((e) => {
1757
+ if (!cidValid.get(e.cid)) return {
1758
+ cid: e.cid,
1759
+ status: "invalid"
1760
+ };
1761
+ if (!referenced.has(e.cid)) return {
1762
+ cid: e.cid,
1763
+ status: "forbidden"
1764
+ };
1765
+ return {
1766
+ cid: e.cid,
1767
+ status: "ok",
1768
+ objectKey: this.objectKey(e.cid),
1769
+ grant: grantByCid.get(e.cid) ?? null
1770
+ };
1771
+ }) }
1772
+ };
1773
+ }
1774
+ async execCommitUpload(_ctx, args) {
1775
+ if (!this.uploadSessionStore) return this.uploadErr("AFS_UNSUPPORTED", "upload plane not available");
1776
+ const parsed = require_upload_actions.CommitUploadArgsSchema.safeParse(args);
1777
+ if (!parsed.success) return this.uploadErr("AFS_VALIDATION_ERROR", require_upload_actions.zodMessage(parsed.error));
1778
+ const { sessionId, entries } = parsed.data;
1779
+ const session = await this.uploadSessionStore.getSession(sessionId);
1780
+ if (!this.ownsSession(session)) return this.uploadErr("AFS_NOT_FOUND", "unknown upload session");
1781
+ if (session.status === "aborted") return this.uploadErr("AFS_UNSUPPORTED", "session aborted");
1782
+ if (session.status === "prepared" && session.expiresAt <= Date.now()) return this.uploadErr("AFS_UNSUPPORTED", "session expired");
1783
+ const result = await this.commitSessionEntries(session.id, session.entries, entries);
1784
+ await this.uploadSessionStore.markCommitted(session.id);
1785
+ return {
1786
+ success: true,
1787
+ data: result
1788
+ };
1789
+ }
1790
+ async execAbortUpload(_ctx, args) {
1791
+ if (!this.uploadSessionStore) return this.uploadErr("AFS_UNSUPPORTED", "upload plane not available");
1792
+ const parsed = require_upload_actions.AbortUploadArgsSchema.safeParse(args);
1793
+ if (!parsed.success) return this.uploadErr("AFS_VALIDATION_ERROR", require_upload_actions.zodMessage(parsed.error));
1794
+ const session = await this.uploadSessionStore.getSession(parsed.data.sessionId);
1795
+ if (!this.ownsSession(session)) return this.uploadErr("AFS_NOT_FOUND", "unknown upload session");
1796
+ await this.uploadSessionStore.markAborted(session.id);
1797
+ if (this.gcQueue) {
1798
+ const orphanCids = [...new Set(session.entries.filter((e) => e.status !== "committed").map((e) => e.cid))];
1799
+ if (orphanCids.length > 0) await this.gcQueue.enqueue(orphanCids);
1800
+ }
1801
+ return {
1802
+ success: true,
1803
+ data: {
1804
+ sessionId: session.id,
1805
+ status: "aborted"
1806
+ }
1807
+ };
1808
+ }
1809
+ /**
1810
+ * Resolve the immutable-merge-key set: `createdAt` is always preserved, plus
1811
+ * any caller-supplied lifecycle fields (e.g. `status`).
1812
+ */
1813
+ resolveImmutableMergeKeys(extra) {
1814
+ const keys = new Set(["createdAt"]);
1815
+ for (const k of extra ?? []) keys.add(k);
1816
+ return keys;
1817
+ }
1818
+ /** Extract the JSON record object from a write payload's `content`. */
1819
+ parseRecordBody(content) {
1820
+ if (content !== null && typeof content === "object" && !Array.isArray(content) && !(content instanceof Uint8Array) && !(content instanceof ArrayBuffer)) return content;
1821
+ if (typeof content === "string") try {
1822
+ const v = JSON.parse(content);
1823
+ return v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
1824
+ } catch {
1825
+ return null;
1826
+ }
1827
+ return null;
1828
+ }
1829
+ /**
1830
+ * Stamp the dedup identity into a write's meta so the writeHandler can project
1831
+ * it onto the entry's `dedup_scope` / `dedup_key` columns (#1159). `dedupKey`
1832
+ * stays in the metadata blob (observability / collection-query); on a merge
1833
+ * we drop `createdAt` so the index keeps the original file ctime (R1,
1834
+ * feed-sort §5.1).
1835
+ */
1836
+ stampDedupMeta(base, dedupScope, dedupKey, isDuplicate) {
1837
+ const meta = { ...base ?? {} };
1838
+ meta.dedupKey = dedupKey;
1839
+ meta.dedupScope = dedupScope;
1840
+ if (isDuplicate) delete meta.createdAt;
1841
+ return meta;
1842
+ }
1843
+ /**
1844
+ * Dedup-on-write core (#492 / #1159) — the `options.dedupBy` upsert behind the
1845
+ * standard `write` / `batchWrite`. The record's `dedupBy` field (URL-valued)
1846
+ * is normalized into a `dedupKey`; the record lands at a STABLE readable path
1847
+ * (`{collection}/{uuid}.json`) and dedup resolves through the tree-index's
1848
+ * `(scope, dedup_scope, dedup_key)` secondary UNIQUE index — NOT a
1849
+ * cid-in-path filename (which silently orphaned records written under any
1850
+ * other layout, #1159). On a hit the incoming record is MERGED
1851
+ * (non-degrading) into the existing entry AT ITS REAL PATH. Concurrency-safe:
1852
+ * a racing create collides on the UNIQUE index, and the loser re-queries the
1853
+ * index and merges (bounded retry). Records missing the field are written
1854
+ * normally under the collection (no dedup).
1855
+ */
1856
+ async upsertByDedupKey(path, content, options, dedupBy) {
1857
+ if (!this.treeIndex.findByDedupKey) throw new _aigne_afs.AFSValidationError("dedup-on-write (dedupBy) is not supported by this DID Space index");
1858
+ const { dedupBy: _by, dedupKey: _dk, mergeImmutableFields, ...baseOpts } = options;
1859
+ const body = this.parseRecordBody(content.content);
1860
+ const rawVal = body && typeof body[dedupBy] === "string" ? body[dedupBy] : null;
1861
+ if (!body || !rawVal) {
1862
+ const fresh = (0, ufo.joinURL)("/", path, `${crypto.randomUUID()}.json`);
1863
+ return {
1864
+ ...await super.write(fresh, content, baseOpts),
1865
+ deduped: false
1866
+ };
1867
+ }
1868
+ const dedupKey = require_url_normalize.normalizeItemUrl(rawVal);
1869
+ const dedupScope = this.treePath((0, ufo.joinURL)("/", path));
1870
+ const immutable = this.resolveImmutableMergeKeys(mergeImmutableFields);
1871
+ const MAX_ATTEMPTS = 4;
1872
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
1873
+ const existing = await this.treeIndex.findByDedupKey(dedupScope, dedupKey);
1874
+ if (existing) {
1875
+ const target = `/${existing.path}`;
1876
+ let existingBody = null;
1877
+ let version;
1878
+ try {
1879
+ const readResult = await this.read(target);
1880
+ existingBody = this.parseRecordBody(readResult.data?.content);
1881
+ version = (readResult.data?.meta)?.version;
1882
+ } catch (err) {
1883
+ if (err instanceof _aigne_afs.AFSNotFoundError) continue;
1884
+ throw err;
1885
+ }
1886
+ const mergedBody = existingBody !== null ? mergeItemContent(existingBody, body, immutable) : { ...body };
1887
+ const meta$1 = this.stampDedupMeta(content.meta, dedupScope, dedupKey, true);
1888
+ const callerIfMatch = baseOpts.ifMatch;
1889
+ const ifMatch = callerIfMatch ?? version;
1890
+ try {
1891
+ return {
1892
+ ...await super.write(target, {
1893
+ ...content,
1894
+ content: mergedBody,
1895
+ meta: meta$1
1896
+ }, ifMatch ? {
1897
+ ...baseOpts,
1898
+ ifMatch
1899
+ } : baseOpts),
1900
+ deduped: true
1901
+ };
1902
+ } catch (err) {
1903
+ if (err instanceof _aigne_afs.AFSConflictError && callerIfMatch === void 0) continue;
1904
+ throw err;
1905
+ }
1906
+ }
1907
+ const fresh = (0, ufo.joinURL)("/", path, `${crypto.randomUUID()}.json`);
1908
+ const meta = this.stampDedupMeta(content.meta, dedupScope, dedupKey, false);
1909
+ try {
1910
+ return {
1911
+ ...await super.write(fresh, {
1912
+ ...content,
1913
+ content: body,
1914
+ meta
1915
+ }, baseOpts),
1916
+ deduped: false
1917
+ };
1918
+ } catch (err) {
1919
+ if (await this.treeIndex.findByDedupKey(dedupScope, dedupKey)) continue;
1920
+ throw err;
1921
+ }
1922
+ }
1923
+ throw new _aigne_afs.AFSConflictError((0, ufo.joinURL)("/", path), {});
1924
+ }
1925
+ /**
1926
+ * One-time reindex/backfill seam (#1159 — run from a deferred one-shot
1927
+ * script, NOT on the write hot path). Populates the dedup columns for
1928
+ * EXISTING records under `collection` so pre-feature entries (old
1929
+ * `{slug}.json` / `{uuid}/item.json` / `{cid}.json` layouts) become
1930
+ * dedup-able — fixing the "an already-saved URL's first re-save duplicates"
1931
+ * bug. Reads each record, extracts + normalizes `record[dedupBy]`, and writes
1932
+ * the dedup columns INDEX-ONLY (no changelog / no cid change) via
1933
+ * `TreeIndex.setDedupKey`. Records missing the field, and genuine
1934
+ * pre-existing duplicates (which would violate the UNIQUE index), are
1935
+ * skipped. Idempotent.
1936
+ */
1937
+ async reindexDedupKeys(collection, dedupBy) {
1938
+ if (!this.treeIndex.setDedupKey) throw new _aigne_afs.AFSValidationError("reindexDedupKeys is not supported by this DID Space index");
1939
+ const dedupScope = this.treePath((0, ufo.joinURL)("/", collection));
1940
+ const listPrefix = dedupScope.endsWith("/") ? dedupScope : `${dedupScope}/`;
1941
+ let scanned = 0;
1942
+ let indexed = 0;
1943
+ let skipped = 0;
1944
+ let cursor;
1945
+ do {
1946
+ const page = await this.treeIndex.listEntries(listPrefix, {
1947
+ limit: 500,
1948
+ cursor
1949
+ });
1950
+ for (const e of page.entries) {
1951
+ scanned++;
1952
+ let body = null;
1953
+ try {
1954
+ const r = await this.read(`/${e.path}`);
1955
+ body = this.parseRecordBody(r.data?.content);
1956
+ } catch {
1957
+ skipped++;
1958
+ continue;
1959
+ }
1960
+ const raw = body && typeof body[dedupBy] === "string" ? body[dedupBy] : null;
1961
+ if (!raw) {
1962
+ skipped++;
1963
+ continue;
1964
+ }
1965
+ try {
1966
+ await this.treeIndex.setDedupKey(e.path, dedupScope, require_url_normalize.normalizeItemUrl(raw), { deviceId: this.deviceId });
1967
+ indexed++;
1968
+ } catch {
1969
+ skipped++;
1970
+ }
1971
+ }
1972
+ cursor = page.hasMore ? page.cursor : void 0;
1973
+ } while (cursor);
1974
+ return {
1975
+ scanned,
1976
+ indexed,
1977
+ skipped
1978
+ };
1979
+ }
1980
+ /**
1981
+ * Standard write, extended with the dedup-on-write upsert policy (#492 /
1982
+ * #1159). When `options.dedupBy` (canonical) or the deprecated `dedupKey`
1983
+ * alias is set the write becomes a secondary-index upsert (see
1984
+ * {@link upsertByDedupKey}); otherwise it is the base provider's write.
1985
+ */
1986
+ async write(path, content, options) {
1987
+ const dedupBy = options?.dedupBy ?? options?.dedupKey;
1988
+ if (dedupBy) return this.upsertByDedupKey(path, content, options, dedupBy);
1989
+ return super.write(path, content, options);
1990
+ }
1991
+ /**
1992
+ * The commit write — mirrors `batchWrite` steps 2/6/7/8 WITHOUT the R2 byte
1993
+ * leg (existence is trusted, §8): preread → ifMatch/skip → batchPutEntries →
1994
+ * GC bookkeeping → share hooks → per-entry results. `cid`/`size`/`mimeType`
1995
+ * come from the SESSION record (the prepared contract, re-validated against
1996
+ * the client's entries); `deviceId`/`mtime` are client provenance threaded to
1997
+ * the changelog (echo防回环, §6.1).
1998
+ */
1999
+ async commitSessionEntries(sessionId, sessionEntries, clientEntries) {
2000
+ const byPath = new Map(sessionEntries.map((e) => [(0, ufo.joinURL)("/", e.path), e]));
2001
+ const results = new Array(clientEntries.length);
2002
+ const prepared = [];
2003
+ for (let i = 0; i < clientEntries.length; i++) {
2004
+ const c = clientEntries[i];
2005
+ const afsPath = (0, ufo.joinURL)("/", c.path);
2006
+ const pv = this.validateActionPath(afsPath);
2007
+ if (!pv.ok) {
2008
+ results[i] = {
2009
+ path: afsPath,
2010
+ success: false,
2011
+ error: pv.reason,
2012
+ code: "AFS_VALIDATION_ERROR"
2013
+ };
2014
+ continue;
2015
+ }
2016
+ const se = byPath.get(afsPath);
2017
+ if (!se || se.cid !== c.cid) {
2018
+ results[i] = {
2019
+ path: afsPath,
2020
+ success: false,
2021
+ error: "entry not in session, or cid mismatch with prepared contract",
2022
+ code: "AFS_VALIDATION_ERROR"
2023
+ };
2024
+ continue;
2025
+ }
2026
+ const deviceId = typeof c.deviceId === "string" && c.deviceId ? c.deviceId : se.deviceId ?? this.deviceId;
2027
+ const mtime = typeof c.mtime === "number" ? c.mtime : se.mtime ?? Date.now();
2028
+ const ctime = require_timestamp.resolveCtime(c.metadata, mtime);
2029
+ const treeEntry = {
2030
+ cid: se.cid,
2031
+ size: se.size,
2032
+ mtime,
2033
+ ctime,
2034
+ mimeType: se.mimeType ?? void 0
2035
+ };
2036
+ if (c.content !== void 0 && (0, _aigne_afs_provider_utils.isTextMime)(se.mimeType ?? void 0)) {
2037
+ if (await require_cid.checksumSha256OfContent(c.content) === se.checksumSha256) treeEntry.content = c.content;
2038
+ }
2039
+ if (c.metadata) {
2040
+ const appMeta = extractApplicationMeta(c.metadata);
2041
+ if (appMeta) {
2042
+ try {
2043
+ assertMetadataSize(appMeta, afsPath);
2044
+ } catch (err) {
2045
+ results[i] = {
2046
+ path: afsPath,
2047
+ success: false,
2048
+ error: err instanceof Error ? err.message : String(err),
2049
+ code: err.code ?? "AFS_VALIDATION_ERROR"
2050
+ };
2051
+ continue;
2052
+ }
2053
+ treeEntry.metadata = appMeta;
2054
+ }
2055
+ }
2056
+ prepared.push({
2057
+ i,
2058
+ afsPath,
2059
+ treePath: pv.treePath,
2060
+ cid: se.cid,
2061
+ treeEntry,
2062
+ deviceId,
2063
+ ...se.ifMatch ? { ifMatch: se.ifMatch } : {}
2064
+ });
2065
+ }
2066
+ const preread = await this.batchPreread(prepared.map((p) => p.treePath));
2067
+ const skipped = [];
2068
+ let live = [];
2069
+ for (const p of prepared) {
2070
+ const existing = preread.get(p.treePath) ?? null;
2071
+ if (require_upload_actions.ifMatchPreconditionFailed(p.ifMatch, existing)) {
2072
+ results[p.i] = this.batchConflictFailure(p.afsPath, {
2073
+ currentCid: existing?.cid ?? null,
2074
+ currentMtime: existing?.mtime ?? null,
2075
+ currentDeviceId: null
2076
+ });
2077
+ continue;
2078
+ }
2079
+ live.push(p);
2080
+ }
2081
+ const preIoOk = [];
2082
+ for (const p of live) try {
2083
+ await require_share_hook.applyShareWriteHook({
2084
+ ...this.shareHookArgs(p.afsPath, p.treePath),
2085
+ bytes: new Uint8Array(0),
2086
+ cid: p.cid,
2087
+ resolver: this.shareResolver,
2088
+ phase: "pre-io"
2089
+ });
2090
+ preIoOk.push(p);
2091
+ } catch (err) {
2092
+ results[p.i] = {
2093
+ path: p.afsPath,
2094
+ success: false,
2095
+ error: err instanceof Error ? err.message : String(err)
2096
+ };
2097
+ }
2098
+ live = preIoOk;
2099
+ let indexResults = [];
2100
+ if (live.length > 0) {
2101
+ const batchEntries = live.map((p) => ({
2102
+ path: p.treePath,
2103
+ entry: p.treeEntry,
2104
+ opts: { deviceId: p.deviceId }
2105
+ }));
2106
+ indexResults = this.treeIndex.batchPutEntries ? await this.treeIndex.batchPutEntries(batchEntries, { preread }) : await require_tree_index.loopBatchPutEntries(this.treeIndex, batchEntries, { preread });
2107
+ }
2108
+ const written = [];
2109
+ live.forEach((p, j) => {
2110
+ const r = indexResults[j];
2111
+ if (!r) {
2112
+ results[p.i] = {
2113
+ path: p.afsPath,
2114
+ success: false,
2115
+ error: "index batch returned no result"
2116
+ };
2117
+ return;
2118
+ }
2119
+ if (r.conflict) {
2120
+ results[p.i] = this.batchConflictFailure(p.afsPath, r.conflict);
2121
+ return;
2122
+ }
2123
+ if (r.error) {
2124
+ results[p.i] = {
2125
+ path: p.afsPath,
2126
+ success: false,
2127
+ error: r.error
2128
+ };
2129
+ return;
2130
+ }
2131
+ if (r.skipped) {
2132
+ skipped.push(p);
2133
+ return;
2134
+ }
2135
+ written.push({
2136
+ p,
2137
+ oldCid: r.oldCid,
2138
+ liveSeq: r.liveSeq
2139
+ });
2140
+ });
2141
+ if (this.gcQueue) {
2142
+ const reReferenced = [...new Set([...written.map((w) => w.p.cid), ...skipped.map((s) => s.cid)])];
2143
+ if (reReferenced.length > 0) if (this.gcQueue.removeMany) await this.gcQueue.removeMany(reReferenced);
2144
+ else for (const cid of reReferenced) await this.gcQueue.remove(cid);
2145
+ const replaced = [...new Set(written.filter((w) => w.oldCid && w.oldCid !== w.p.cid).map((w) => w.oldCid))];
2146
+ if (replaced.length > 0) await this.gcQueue.enqueue(replaced);
2147
+ }
2148
+ for (const w of written) try {
2149
+ await require_share_hook.applyShareWriteHook({
2150
+ ...this.shareHookArgs(w.p.afsPath, w.p.treePath),
2151
+ bytes: new Uint8Array(0),
2152
+ cid: w.p.cid,
2153
+ resolver: this.shareResolver,
2154
+ phase: "post-io"
2155
+ });
2156
+ } catch {}
2157
+ const commitOk = (p, liveSeq) => ({
2158
+ status: 200,
2159
+ ok: true,
2160
+ data: {
2161
+ path: p.afsPath,
2162
+ meta: {
2163
+ cid: p.cid,
2164
+ liveSeq: liveSeq != null ? String(liveSeq) : void 0
2165
+ }
2166
+ }
2167
+ });
2168
+ for (const w of written) results[w.p.i] = commitOk(w.p, w.liveSeq);
2169
+ for (const s of skipped) {
2170
+ let liveSeq;
2171
+ if (this.treeIndex.getPrefixWatermark) liveSeq = await this.treeIndex.getPrefixWatermark(s.treePath) ?? void 0;
2172
+ results[s.i] = commitOk(s, liveSeq);
2173
+ }
2174
+ const committedPaths = [...written, ...skipped.map((s) => ({ p: s }))].map((w) => ({
2175
+ path: w.p.afsPath,
2176
+ status: "committed"
2177
+ }));
2178
+ if (committedPaths.length > 0) await this.uploadSessionStore?.markEntries(sessionId, committedPaths);
2179
+ const succeeded = results.filter((r) => r?.ok === true).length;
2180
+ return {
2181
+ ok: true,
2182
+ results,
2183
+ succeeded,
2184
+ failed: results.length - succeeded
2185
+ };
2186
+ }
2187
+ /**
2188
+ * Native batch delete — same merge without the R2 leg: ONE preread → GC
2189
+ * enqueue (before delete, single-op ordering) → ONE atomic index batch →
2190
+ * share delete hooks. FILE entries only (no directory fallback — sync
2191
+ * never dir-deletes; a dir path fails per entry with 404).
2192
+ */
2193
+ async batchDelete(entries, _options) {
2194
+ const results = new Array(entries.length);
2195
+ const prepared = [];
2196
+ for (let i = 0; i < entries.length; i++) {
2197
+ const entry = entries[i];
2198
+ const afsPath = (0, ufo.joinURL)("/", entry.path);
2199
+ if (this.isVirtualPath(afsPath) || require_share_declaration.isShareReservedPath(afsPath)) {
2200
+ results[i] = {
2201
+ path: afsPath,
2202
+ success: false,
2203
+ error: `Forbidden at ${afsPath}: virtual_path`,
2204
+ code: "AFS_FORBIDDEN"
2205
+ };
2206
+ continue;
2207
+ }
2208
+ const deleteMeta = entry.options?.meta;
2209
+ prepared.push({
2210
+ i,
2211
+ afsPath,
2212
+ treePath: this.treePath(afsPath),
2213
+ deviceId: typeof deleteMeta?.deviceId === "string" && deleteMeta.deviceId ? deleteMeta.deviceId : this.deviceId,
2214
+ ...typeof deleteMeta?.mtime === "number" ? { mtime: deleteMeta.mtime } : {},
2215
+ ...typeof deleteMeta?.ifMatch === "string" ? { ifMatch: deleteMeta.ifMatch } : {}
2216
+ });
2217
+ }
2218
+ const preread = await this.batchPreread(prepared.map((p) => p.treePath));
2219
+ const live = [];
2220
+ for (const p of prepared) {
2221
+ const existing = preread.get(p.treePath) ?? null;
2222
+ if (p.ifMatch !== void 0 && (!existing || existing.cid !== p.ifMatch)) {
2223
+ results[p.i] = this.batchConflictFailure(p.afsPath, {
2224
+ currentCid: existing?.cid ?? null,
2225
+ currentMtime: existing?.mtime ?? null,
2226
+ currentDeviceId: null
2227
+ });
2228
+ continue;
2229
+ }
2230
+ if (!existing) {
2231
+ results[p.i] = {
2232
+ path: p.afsPath,
2233
+ success: false,
2234
+ error: `Path not found: ${p.afsPath}`,
2235
+ code: "AFS_NOT_FOUND"
2236
+ };
2237
+ continue;
2238
+ }
2239
+ live.push(p);
2240
+ }
2241
+ if (this.gcQueue && live.length > 0) {
2242
+ const cids = [...new Set(live.map((p) => (preread.get(p.treePath) ?? null)?.cid).filter(Boolean))];
2243
+ if (cids.length > 0) await this.gcQueue.enqueue(cids);
2244
+ }
2245
+ if (live.length > 0) {
2246
+ const batchEntries = live.map((p) => ({
2247
+ path: p.treePath,
2248
+ opts: {
2249
+ deviceId: p.deviceId,
2250
+ ...p.mtime !== void 0 ? { mtime: p.mtime } : {}
2251
+ }
2252
+ }));
2253
+ let indexResults;
2254
+ try {
2255
+ indexResults = this.treeIndex.batchDeleteEntries ? await this.treeIndex.batchDeleteEntries(batchEntries, { preread }) : await require_tree_index.loopBatchDeleteEntries(this.treeIndex, batchEntries, { preread });
2256
+ } catch (err) {
2257
+ const message = err instanceof Error ? err.message : String(err);
2258
+ for (const p of live) results[p.i] = {
2259
+ path: p.afsPath,
2260
+ success: false,
2261
+ error: message
2262
+ };
2263
+ const succeededEarly = results.filter((r) => r?.success).length;
2264
+ return {
2265
+ results,
2266
+ succeeded: succeededEarly,
2267
+ failed: results.length - succeededEarly
2268
+ };
2269
+ }
2270
+ for (let j = 0; j < live.length; j++) {
2271
+ const p = live[j];
2272
+ const r = indexResults[j];
2273
+ if (!r || r.error || r.conflict || r.notFound) {
2274
+ results[p.i] = r?.conflict ? this.batchConflictFailure(p.afsPath, r.conflict) : {
2275
+ path: p.afsPath,
2276
+ success: false,
2277
+ error: r?.error ?? `Path not found: ${p.afsPath}`,
2278
+ ...r?.notFound ? { code: "AFS_NOT_FOUND" } : {}
2279
+ };
2280
+ continue;
2281
+ }
2282
+ await require_share_hook.applyShareDeleteHook({
2283
+ ...this.shareHookArgs(p.afsPath, p.treePath),
2284
+ resolver: this.shareResolver
2285
+ });
2286
+ if (p.afsPath.endsWith(".ash")) this.emit({
2287
+ type: _aigne_afs.SCRIPT_EVENT_TYPES.UNREGISTERED,
2288
+ path: p.afsPath,
2289
+ data: { runtime: "ash" }
2290
+ });
2291
+ results[p.i] = {
2292
+ path: p.afsPath,
2293
+ success: true
2294
+ };
2295
+ }
2296
+ }
2297
+ const succeeded = results.filter((r) => r?.success).length;
2298
+ return {
2299
+ results,
2300
+ succeeded,
2301
+ failed: results.length - succeeded
2302
+ };
2303
+ }
2304
+ async statHandler(ctx) {
2305
+ const afsPath = (0, ufo.joinURL)("/", ctx.params.path ?? "");
2306
+ if (afsPath === "/") return { data: {
2307
+ id: "/",
2308
+ path: "/",
2309
+ meta: {
2310
+ kind: "did-space:root",
2311
+ childrenCount: await this.treeIndex.countChildren(this.treeListPrefix("/"))
2312
+ }
2313
+ } };
2314
+ const entry = await this.treeIndex.get(this.treePath(afsPath), { includeContent: false });
2315
+ if (entry) return { data: {
2316
+ id: afsPath.split("/").pop() || afsPath,
2317
+ path: afsPath,
2318
+ meta: {
2319
+ kind: "did-space:file",
2320
+ size: entry.size,
2321
+ cid: entry.cid,
2322
+ mimeType: entry.mimeType,
2323
+ version: entry.cid
2324
+ },
2325
+ createdAt: new Date(entry.ctime),
2326
+ updatedAt: new Date(entry.mtime)
2327
+ } };
2328
+ if (await this.isDirectory(afsPath)) {
2329
+ const prefix = this.treeListPrefix(afsPath);
2330
+ const meta = {
2331
+ kind: "did-space:directory",
2332
+ childrenCount: await this.treeIndex.countChildren(prefix)
2333
+ };
2334
+ if (this.treeIndex.getPrefixWatermark) {
2335
+ const version = await this.treeIndex.getPrefixWatermark(prefix);
2336
+ if (version != null) meta.liveSeq = String(version);
2337
+ }
2338
+ const stat = {
2339
+ id: afsPath.split("/").pop() || afsPath,
2340
+ path: afsPath,
2341
+ meta
2342
+ };
2343
+ const dirTimes = await this.treeIndex.getDirTimes?.(prefix);
2344
+ if (dirTimes) {
2345
+ stat.createdAt = new Date(dirTimes.ctime);
2346
+ stat.updatedAt = new Date(dirTimes.mtime);
2347
+ }
2348
+ return { data: stat };
2349
+ }
2350
+ throw new _aigne_afs.AFSNotFoundError(afsPath);
2351
+ }
2352
+ async searchHandler(ctx, query, options) {
2353
+ const afsPath = ctx.params.path ? `/${ctx.params.path}` : "/";
2354
+ const pattern = query || "";
2355
+ const limit = options?.limit || 50;
2356
+ if (!pattern) return {
2357
+ data: [],
2358
+ total: 0
2359
+ };
2360
+ const prefix = this.treeListPrefix(afsPath);
2361
+ const results = [];
2362
+ let cursor;
2363
+ const searchQuery = pattern.toLowerCase();
2364
+ do {
2365
+ const listResult = await this.treeIndex.list(prefix, {
2366
+ limit: LIST_MAX_LIMIT,
2367
+ cursor
2368
+ });
2369
+ for (const treeListEntry of listResult.entries) {
2370
+ if (results.length >= limit) break;
2371
+ const path = `/${treeListEntry.path}`;
2372
+ if ((path.split("/").pop() || "").toLowerCase().includes(searchQuery)) {
2373
+ results.push(this.buildEntry(path, {}));
2374
+ continue;
2375
+ }
2376
+ const treeEntry = await this.treeIndex.get(treeListEntry.path, { includeContent: false });
2377
+ if (treeEntry) {
2378
+ if (!(0, _aigne_afs_provider_utils.isTextMime)(treeEntry.mimeType)) continue;
2379
+ const obj = await this.objectStore.get(this.objectKey(treeEntry.cid));
2380
+ if (obj) {
2381
+ const content = await obj.text();
2382
+ if (content.toLowerCase().includes(searchQuery)) results.push(this.buildEntry(path, { content }));
2383
+ }
2384
+ }
2385
+ }
2386
+ cursor = listResult.hasMore ? listResult.cursor : void 0;
2387
+ } while (cursor && results.length < limit);
2388
+ return {
2389
+ data: results,
2390
+ total: results.length
2391
+ };
2392
+ }
2393
+ async explainHandler(ctx) {
2394
+ const afsPath = (0, ufo.joinURL)("/", ctx.params.path ?? "");
2395
+ const lines = [];
2396
+ lines.push(`# DID Space: ${afsPath}`);
2397
+ lines.push("");
2398
+ if (afsPath === "/") {
2399
+ lines.push(`DID: ${this.did}`);
2400
+ lines.push("");
2401
+ lines.push(this.description || "DID-scoped persistent storage with CID-based deduplication");
2402
+ lines.push("");
2403
+ lines.push("## Operations");
2404
+ lines.push("- `list /` — List root files and directories");
2405
+ lines.push("- `read /{path}` — Read file content");
2406
+ lines.push("- `write /{path}` — Write file (content-addressed dedup)");
2407
+ lines.push("- `delete /{path}` — Delete file entry (lazy GC)");
2408
+ } else {
2409
+ const entry = await this.treeIndex.get(this.treePath(afsPath), { includeContent: false });
2410
+ if (entry) {
2411
+ lines.push(`DID: ${this.did}`);
2412
+ lines.push(`Path: ${afsPath}`);
2413
+ lines.push(`Size: ${entry.size} bytes`);
2414
+ lines.push(`CID: ${entry.cid}`);
2415
+ lines.push(`MIME: ${entry.mimeType ?? "unknown"}`);
2416
+ lines.push(`Created: ${new Date(entry.ctime).toISOString()}`);
2417
+ lines.push(`Modified: ${new Date(entry.mtime).toISOString()}`);
2418
+ } else if (await this.isDirectory(afsPath)) {
2419
+ lines.push(`DID: ${this.did}`);
2420
+ lines.push(`Virtual directory: ${afsPath}`);
2421
+ lines.push("Use `list` to see contents.");
2422
+ } else lines.push("Path not found.");
2423
+ }
2424
+ return {
2425
+ format: "markdown",
2426
+ content: lines.join("\n")
2427
+ };
2428
+ }
2429
+ /**
2430
+ * Write content to the provider, bypassing D4 virtual path protection.
2431
+ * Stores content to objectStore and treeIndex with the given deviceId.
2432
+ */
2433
+ async internalWrite(afsPath, opts) {
2434
+ if (require_share_declaration.isShareReservedPath(afsPath)) throw new _aigne_afs.AFSForbiddenError(afsPath, "share-path-reserved");
2435
+ const { content, deviceId, mtime, mimeType: explicitMimeType, metadata } = opts;
2436
+ let bytes;
2437
+ let storeValue;
2438
+ if (content instanceof Uint8Array) {
2439
+ bytes = content;
2440
+ storeValue = bytes.buffer;
2441
+ } else {
2442
+ bytes = new Uint8Array(content);
2443
+ storeValue = bytes.buffer;
2444
+ }
2445
+ const cid = await require_cid.computeCID(bytes);
2446
+ const objKey = this.objectKey(cid);
2447
+ const treePath = this.treePath(afsPath);
2448
+ await require_share_hook.applyShareWriteHook({
2449
+ ...this.shareHookArgs(afsPath, treePath),
2450
+ bytes,
2451
+ cid,
2452
+ resolver: this.shareResolver,
2453
+ phase: "pre-io"
2454
+ });
2455
+ if (!await this.objectStore.head(objKey)) await this.objectStore.put(objKey, storeValue);
2456
+ const now = mtime ?? Date.now();
2457
+ const detectedMime = explicitMimeType ?? (0, _aigne_afs_provider_utils.detectMimeType)(afsPath) ?? void 0;
2458
+ const ctime = require_timestamp.resolveCtime(metadata, now);
2459
+ const treeEntry = {
2460
+ cid,
2461
+ size: bytes.length,
2462
+ mtime: now,
2463
+ ctime,
2464
+ mimeType: detectedMime
2465
+ };
2466
+ const appMeta = extractApplicationMeta(metadata);
2467
+ if (appMeta) treeEntry.metadata = appMeta;
2468
+ const inlineText = require_inline_content.decodeInlineText(bytes, detectedMime);
2469
+ if (inlineText !== null) treeEntry.content = inlineText;
2470
+ const { oldCid } = await this.treeIndex.put(treePath, treeEntry, { deviceId });
2471
+ if (this.gcQueue) {
2472
+ await this.gcQueue.remove(cid);
2473
+ if (oldCid && oldCid !== cid) await this.gcQueue.enqueue([oldCid]);
2474
+ }
2475
+ await require_share_hook.applyShareWriteHook({
2476
+ ...this.shareHookArgs(afsPath, treePath),
2477
+ bytes,
2478
+ cid,
2479
+ resolver: this.shareResolver,
2480
+ phase: "post-io"
2481
+ });
2482
+ await require_blocklet_hook.applyBlockletWriteHook({
2483
+ ...this.blockletHookArgs(afsPath, treePath),
2484
+ bytes,
2485
+ resolver: this.blockletResolver
2486
+ });
2487
+ return oldCid === void 0 ? { cid } : {
2488
+ cid,
2489
+ oldCid
2490
+ };
2491
+ }
2492
+ /**
2493
+ * Delete a file from the provider, bypassing D4 virtual path protection.
2494
+ */
2495
+ async internalDelete(afsPath, opts) {
2496
+ if (require_share_declaration.isShareReservedPath(afsPath)) throw new _aigne_afs.AFSForbiddenError(afsPath, "share-path-reserved");
2497
+ const treePath = this.treePath(afsPath);
2498
+ const entry = await this.treeIndex.get(treePath, { includeContent: false });
2499
+ if (!entry) return { deleted: false };
2500
+ await this.gcQueue?.enqueue([entry.cid]);
2501
+ await this.treeIndex.delete(treePath, { deviceId: opts.deviceId });
2502
+ await require_share_hook.applyShareDeleteHook({
2503
+ ...this.shareHookArgs(afsPath, treePath),
2504
+ resolver: this.shareResolver
2505
+ });
2506
+ await require_blocklet_hook.applyBlockletDeleteHook({
2507
+ ...this.blockletHookArgs(afsPath, treePath),
2508
+ resolver: this.blockletResolver
2509
+ });
2510
+ return { deleted: true };
2511
+ }
2512
+ capabilities() {
2513
+ return {
2514
+ schemaVersion: 1,
2515
+ provider: this.name,
2516
+ tools: [],
2517
+ actions: [],
2518
+ operations: this.getOperationsDeclaration()
2519
+ };
2520
+ }
2521
+ };
2522
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/:path*")], DIDSpaceProvider.prototype, "readMeta", null);
2523
+ require_decorate.__decorate([(0, _aigne_afs_provider.List)("/:path*", { handleDepth: true })], DIDSpaceProvider.prototype, "listHandler", null);
2524
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/.meta/.capabilities")], DIDSpaceProvider.prototype, "readCapabilities", null);
2525
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/:path*")], DIDSpaceProvider.prototype, "readHandler", null);
2526
+ require_decorate.__decorate([(0, _aigne_afs_provider.Write)("/:path*")], DIDSpaceProvider.prototype, "writeHandler", null);
2527
+ require_decorate.__decorate([(0, _aigne_afs_provider.Delete)("/:path*")], DIDSpaceProvider.prototype, "deleteHandler", null);
2528
+ require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/")], DIDSpaceProvider.prototype, "listObjectPlaneActions", null);
2529
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "prepare-upload", "Reserve an upload session + (CF) mint exact-key checksum-pinned PUT grants", { effect: "write" })], DIDSpaceProvider.prototype, "execPrepareUpload", null);
2530
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "prepare-download", "Mint exact-key short-TTL presigned GET grants for view-referenced objects", { effect: "write" })], DIDSpaceProvider.prototype, "execPrepareDownload", null);
2531
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "commit-upload", "Commit uploaded objects' metadata (trust existence, 0 R2 subrequests)", { effect: "write" })], DIDSpaceProvider.prototype, "execCommitUpload", null);
2532
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "abort-upload", "Abort an upload session; staged objects become GC candidates", { effect: "write" })], DIDSpaceProvider.prototype, "execAbortUpload", null);
2533
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/:path*")], DIDSpaceProvider.prototype, "statHandler", null);
2534
+ require_decorate.__decorate([(0, _aigne_afs_provider.Search)("/:path*")], DIDSpaceProvider.prototype, "searchHandler", null);
2535
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/:path*")], DIDSpaceProvider.prototype, "explainHandler", null);
2536
+
2537
+ //#endregion
2538
+ exports.AFS_IFMATCH_ABSENT = require_upload_actions.AFS_IFMATCH_ABSENT;
2539
+ exports.BLOCKLET_PUBLISH_MARKER = require_blocklet_hook.BLOCKLET_PUBLISH_MARKER;
2540
+ exports.BatchOverBudgetError = require_remote_afs.BatchOverBudgetError;
2541
+ exports.BatchUnsupportedError = require_remote_afs.BatchUnsupportedError;
2542
+ exports.CHANGELOG_DDL = require_changelog_schema.CHANGELOG_DDL;
2543
+ exports.CHANGELOG_INDEX_SQL = require_changelog_schema.CHANGELOG_INDEX_SQL;
2544
+ exports.CHANGELOG_INSERT_SQL = require_changelog_write.CHANGELOG_INSERT_SQL;
2545
+ exports.CHANGELOG_ROW_KIND = require_changelog_schema.CHANGELOG_ROW_KIND;
2546
+ exports.CHANGELOG_SCOPE_CID_MIGRATION_SQL = require_changelog_schema.CHANGELOG_SCOPE_CID_MIGRATION_SQL;
2547
+ exports.CHANGELOG_TABLE_SQL = require_changelog_schema.CHANGELOG_TABLE_SQL;
2548
+ exports.CHANGELOG_WIRE_EXTRA_FIELDS = require_changelog_schema.CHANGELOG_WIRE_EXTRA_FIELDS;
2549
+ exports.CachingObjectStore = require_cid_byte_cache.CachingObjectStore;
2550
+ exports.CidByteCache = require_cid_byte_cache.CidByteCache;
2551
+ exports.ConflictResolutionError = require_sync_engine.ConflictResolutionError;
2552
+ exports.CurrentObjectMissingError = require_sync_engine.CurrentObjectMissingError;
2553
+ exports.D1GCQueue = require_d1_gc_queue.D1GCQueue;
2554
+ exports.D1ResolveStore = require_d1_resolve_store.D1ResolveStore;
2555
+ exports.D1ShareReconciler = require_d1_share_store.D1ShareReconciler;
2556
+ exports.D1ShareStore = require_d1_share_store.D1ShareStore;
2557
+ exports.D1TreeIndex = require_d1_tree_index.D1TreeIndex;
2558
+ exports.D1UploadSessionStore = require_upload_session_store.D1UploadSessionStore;
2559
+ exports.D1VersionedScopeBackend = require_versioned_content_backends.D1VersionedScopeBackend;
2560
+ exports.D1_DID_SPACE_SCHEMA_SQL = require_d1_schema.D1_DID_SPACE_SCHEMA_SQL;
2561
+ exports.DEFAULT_GC_GRACE_MS = require_run_gc.DEFAULT_GC_GRACE_MS;
2562
+ exports.DEFAULT_INLINE_THRESHOLD = require_inline_content.DEFAULT_INLINE_THRESHOLD;
2563
+ exports.DEVICE_ID_FORMAT_RE = require_sync_state.DEVICE_ID_FORMAT_RE;
2564
+ exports.DEVICE_SYNC_STATE_INDEX_SQL = require_changelog_schema.DEVICE_SYNC_STATE_INDEX_SQL;
2565
+ exports.DEVICE_SYNC_STATE_TABLE_SQL = require_changelog_schema.DEVICE_SYNC_STATE_TABLE_SQL;
2566
+ exports.DIDSpaceBlockletDataBackend = require_data_backend.DIDSpaceBlockletDataBackend;
2567
+ exports.DIDSpaceClient = require_client.DIDSpaceClient;
2568
+ exports.DIDSpaceProvider = DIDSpaceProvider;
2569
+ exports.DIDSpaceResolver = require_resolve.DIDSpaceResolver;
2570
+ exports.DIRTY_PATH_DDL = require_dirty_path_schema.DIRTY_PATH_DDL;
2571
+ exports.DIRTY_PATH_INDEX_SQL = require_dirty_path_schema.DIRTY_PATH_INDEX_SQL;
2572
+ exports.DIRTY_PATH_TABLE_SQL = require_dirty_path_schema.DIRTY_PATH_TABLE_SQL;
2573
+ exports.DIRTY_PATH_TRIGGER_SQL = require_dirty_path_schema.DIRTY_PATH_TRIGGER_SQL;
2574
+ exports.ENTRIES_CONTENT_TYPE_COLUMN_SQL = require_d1_schema.ENTRIES_CONTENT_TYPE_COLUMN_SQL;
2575
+ exports.ENTRIES_CONTENT_TYPE_INDEX_DROP_SQL = require_d1_schema.ENTRIES_CONTENT_TYPE_INDEX_DROP_SQL;
2576
+ exports.ENTRIES_CONTENT_TYPE_INDEX_SQL = require_d1_schema.ENTRIES_CONTENT_TYPE_INDEX_SQL;
2577
+ exports.ENTRIES_DEDUP_INDEX_SQL = require_d1_schema.ENTRIES_DEDUP_INDEX_SQL;
2578
+ exports.ENTRIES_DEDUP_KEY_COLUMN_SQL = require_d1_schema.ENTRIES_DEDUP_KEY_COLUMN_SQL;
2579
+ exports.ENTRIES_DEDUP_SCOPE_COLUMN_SQL = require_d1_schema.ENTRIES_DEDUP_SCOPE_COLUMN_SQL;
2580
+ exports.ENTRIES_INDEX_SQL = require_d1_schema.ENTRIES_INDEX_SQL;
2581
+ exports.ENTRIES_INLINE_CONTENT_COLUMN_SQL = require_d1_schema.ENTRIES_INLINE_CONTENT_COLUMN_SQL;
2582
+ exports.ENTRIES_METADATA_COLUMN_SQL = require_d1_schema.ENTRIES_METADATA_COLUMN_SQL;
2583
+ exports.ENTRIES_TABLE_SQL = require_d1_schema.ENTRIES_TABLE_SQL;
2584
+ exports.GC_QUEUE_INDEX_SQL = require_d1_schema.GC_QUEUE_INDEX_SQL;
2585
+ exports.GC_QUEUE_TABLE_SQL = require_d1_schema.GC_QUEUE_TABLE_SQL;
2586
+ exports.HttpTransport = require_http.HttpTransport;
2587
+ exports.INGEST_DEVICE_ID = require_inline_content.INGEST_DEVICE_ID;
2588
+ exports.INLINE_CONTENT_HARD_CAP = require_inline_content.INLINE_CONTENT_HARD_CAP;
2589
+ exports.LOCAL_CHANGELOG_DEFAULT_LIMIT = require_local_changelog.LOCAL_CHANGELOG_DEFAULT_LIMIT;
2590
+ exports.LOCAL_CHANGELOG_MAX_LIMIT = require_local_changelog.LOCAL_CHANGELOG_MAX_LIMIT;
2591
+ exports.LibsqlVersionedScopeBackend = require_versioned_content_backends.LibsqlVersionedScopeBackend;
2592
+ exports.LocalChangelog = require_local_changelog.LocalChangelog;
2593
+ exports.MANIFEST_FILE_KIND = require_changelog_schema.MANIFEST_FILE_KIND;
2594
+ exports.MANIFEST_WIRE_EXTRA_FIELDS = require_changelog_schema.MANIFEST_WIRE_EXTRA_FIELDS;
2595
+ exports.MAX_DEVICE_ID_BYTES = require_changelog_write.MAX_DEVICE_ID_BYTES;
2596
+ exports.MAX_ENTRIES_PER_SCOPE = require_d1_tree_index.MAX_ENTRIES_PER_SCOPE;
2597
+ exports.MemoryGcQueue = require_memory_gc_queue.MemoryGcQueue;
2598
+ exports.MemoryTreeIndex = require_memory_tree_index.MemoryTreeIndex;
2599
+ exports.MemoryVersionedScopeBackend = require_versioned_content_backends.MemoryVersionedScopeBackend;
2600
+ exports.PREFIX_WATERMARK_TABLE_SQL = require_changelog_schema.PREFIX_WATERMARK_TABLE_SQL;
2601
+ exports.PRESIGN_MAX_EXPIRES_SEC = require_presign_signer.PRESIGN_MAX_EXPIRES_SEC;
2602
+ exports.PULL_BATCH_LIMIT = require_sync_engine.PULL_BATCH_LIMIT;
2603
+ exports.PULL_FETCH_CONCURRENCY = require_sync_engine.PULL_FETCH_CONCURRENCY;
2604
+ exports.PUSH_BATCH_LIMIT = require_sync_engine.PUSH_BATCH_LIMIT;
2605
+ exports.PUSH_PROGRESS_FLUSH_INTERVAL = require_sync_engine.PUSH_PROGRESS_FLUSH_INTERVAL;
2606
+ exports.PrefixedTreeIndex = require_prefixed_tree_index.PrefixedTreeIndex;
2607
+ exports.RESOLVE_ENTRIES_INDEX_SQL = require_d1_schema.RESOLVE_ENTRIES_INDEX_SQL;
2608
+ exports.RESOLVE_ENTRIES_TABLE_SQL = require_d1_schema.RESOLVE_ENTRIES_TABLE_SQL;
2609
+ exports.RpcUserSpaceAFS = require_remote_afs.RpcUserSpaceAFS;
2610
+ exports.SCOPE_META_FILE_COUNT_BACKFILL_SQL = require_changelog_schema.SCOPE_META_FILE_COUNT_BACKFILL_SQL;
2611
+ exports.SCOPE_META_FILE_COUNT_DELETE_ONE_SQL = require_changelog_schema.SCOPE_META_FILE_COUNT_DELETE_ONE_SQL;
2612
+ exports.SCOPE_META_FILE_COUNT_DELETE_RANGE_SQL = require_changelog_schema.SCOPE_META_FILE_COUNT_DELETE_RANGE_SQL;
2613
+ exports.SCOPE_META_FILE_COUNT_TRIGGER_SQL = require_changelog_schema.SCOPE_META_FILE_COUNT_TRIGGER_SQL;
2614
+ exports.SCOPE_META_INDEX_SQL = require_changelog_schema.SCOPE_META_INDEX_SQL;
2615
+ exports.SCOPE_META_TABLE_SQL = require_changelog_schema.SCOPE_META_TABLE_SQL;
2616
+ exports.SCOPE_META_TOTAL_BYTES_BACKFILL_SQL = require_changelog_schema.SCOPE_META_TOTAL_BYTES_BACKFILL_SQL;
2617
+ exports.SCOPE_META_TOTAL_BYTES_COLUMN_SQL = require_changelog_schema.SCOPE_META_TOTAL_BYTES_COLUMN_SQL;
2618
+ exports.SCOPE_META_TOTAL_BYTES_TRIGGER_SQL = require_changelog_schema.SCOPE_META_TOTAL_BYTES_TRIGGER_SQL;
2619
+ exports.SHARES_INDEX_SQL = require_d1_schema.SHARES_INDEX_SQL;
2620
+ exports.SHARES_TABLE_SQL = require_d1_schema.SHARES_TABLE_SQL;
2621
+ exports.SYNC_STATE_DDL = require_changelog_schema.SYNC_STATE_DDL;
2622
+ exports.SYNC_STATE_INDEX_SQL = require_changelog_schema.SYNC_STATE_INDEX_SQL;
2623
+ exports.SYNC_STATE_KEYS = require_sync_state.SYNC_STATE_KEYS;
2624
+ exports.SYNC_STATE_TABLE_SQL = require_changelog_schema.SYNC_STATE_TABLE_SQL;
2625
+ exports.SYNC_WALL_CLOCK_BUDGET_MS = require_sync_engine.SYNC_WALL_CLOCK_BUDGET_MS;
2626
+ exports.ShareResolver = require_share_resolve.ShareResolver;
2627
+ exports.SqliteGcQueue = require_sqlite_gc_queue.SqliteGcQueue;
2628
+ exports.SqliteResolveStore = require_sqlite_resolve_store.SqliteResolveStore;
2629
+ exports.SqliteShareStore = require_sqlite_share_store.SqliteShareStore;
2630
+ exports.SqliteUploadSessionStore = require_upload_session_store.SqliteUploadSessionStore;
2631
+ exports.StaticReadonlyTransport = require_static.StaticReadonlyTransport;
2632
+ exports.SyncEngine = require_sync_engine.SyncEngine;
2633
+ exports.SyncStateStore = require_sync_state.SyncStateStore;
2634
+ exports.UPLOAD_SESSIONS_INDEX_SQL = require_d1_schema.UPLOAD_SESSIONS_INDEX_SQL;
2635
+ exports.UPLOAD_SESSIONS_TABLE_SQL = require_d1_schema.UPLOAD_SESSIONS_TABLE_SQL;
2636
+ exports.UPLOAD_SESSION_ENTRIES_TABLE_SQL = require_d1_schema.UPLOAD_SESSION_ENTRIES_TABLE_SQL;
2637
+ exports.UPLOAD_SESSION_TTL_MS = require_upload_session_store.UPLOAD_SESSION_TTL_MS;
2638
+ exports.VERSIONED_SCOPES_INDEX_SQL = require_d1_schema.VERSIONED_SCOPES_INDEX_SQL;
2639
+ exports.VERSIONED_SCOPES_TABLE_SQL = require_d1_schema.VERSIONED_SCOPES_TABLE_SQL;
2640
+ exports.VersionedContentIndex = require_versioned_content_index.VersionedContentIndex;
2641
+ exports.VersionedContentStore = require_versioned_content_store.VersionedContentStore;
2642
+ exports.WsTransport = require_ws.WsTransport;
2643
+ exports.ancestorDirs = require_d1_tree_index.ancestorDirs;
2644
+ exports.applyDirtyUpsert = require_dirty_path_queue.applyDirtyUpsert;
2645
+ exports.assertInlineThreshold = require_inline_content.assertInlineThreshold;
2646
+ exports.assertValidCid = require_object_key.assertValidCid;
2647
+ exports.attachMetadataContext = require_afs_metadata_context.attachMetadataContext;
2648
+ exports.buildChangelogBinds = require_changelog_write.buildChangelogBinds;
2649
+ exports.buildD1MigrationSql = require_scope_migration.buildD1MigrationSql;
2650
+ exports.buildKeysetCursor = require_tree_index.buildKeysetCursor;
2651
+ exports.buildManifest = require_rsync.buildManifest;
2652
+ exports.buildOrderedQueryParts = require_tree_index.buildOrderedQueryParts;
2653
+ exports.cidToChecksumSha256 = require_cid.cidToChecksumSha256;
2654
+ exports.classifyMissingObject = require_sync_engine.classifyMissingObject;
2655
+ exports.classifyScopes = require_scope_migration.classifyScopes;
2656
+ exports.compareTreeSortValues = require_tree_index.compareTreeSortValues;
2657
+ exports.computeCID = require_cid.computeCID;
2658
+ exports.computeDiff = require_rsync.computeDiff;
2659
+ exports.connectDIDSpace = require_client.connectDIDSpace;
2660
+ exports.createWsTransport = require_ws.createWsTransport;
2661
+ exports.decodeInlineText = require_inline_content.decodeInlineText;
2662
+ exports.decodeScopeHmacSecret = require_scope_id.decodeScopeHmacSecret;
2663
+ exports.defaultAppState = require_sync_state.defaultAppState;
2664
+ exports.deriveScope = require_scope_id.deriveScope;
2665
+ exports.detachMetadataContext = require_afs_metadata_context.detachMetadataContext;
2666
+ Object.defineProperty(exports, 'detectMimeType', {
2667
+ enumerable: true,
2668
+ get: function () {
2669
+ return _aigne_afs_provider_utils.detectMimeType;
2670
+ }
2671
+ });
2672
+ exports.fetchRemoteManifest = require_rsync.fetchRemoteManifest;
2673
+ exports.generateDesktopDeviceId = require_sync_state.generateDesktopDeviceId;
2674
+ exports.getMetadataContext = require_afs_metadata_context.getMetadataContext;
2675
+ exports.isPlainDidScope = require_scope_id.isPlainDidScope;
2676
+ exports.isShareDeclarationPath = require_share_declaration.isShareDeclarationPath;
2677
+ Object.defineProperty(exports, 'isTextMime', {
2678
+ enumerable: true,
2679
+ get: function () {
2680
+ return _aigne_afs_provider_utils.isTextMime;
2681
+ }
2682
+ });
2683
+ exports.isValidDeviceId = require_sync_state.isValidDeviceId;
2684
+ exports.keysetAfter = require_tree_index.keysetAfter;
2685
+ exports.migrateD1Scopes = require_scope_migration.migrateD1Scopes;
2686
+ exports.nameOf = require_d1_tree_index.nameOf;
2687
+ exports.normalizeDdl = require_changelog_schema.normalizeDdl;
2688
+ exports.normalizeId = require_did_utils.normalizeId;
2689
+ exports.normalizeItemUrl = require_url_normalize.normalizeItemUrl;
2690
+ exports.objectKeyForCid = require_object_key.objectKeyForCid;
2691
+ exports.openDaemonShareStore = require_sqlite_share_store.openDaemonShareStore;
2692
+ exports.parentOf = require_d1_tree_index.parentOf;
2693
+ exports.parseChangelogEntry = require_changelog_schema.parseChangelogEntry;
2694
+ exports.parseManifestEntry = require_changelog_schema.parseManifestEntry;
2695
+ exports.parseSyncStatus = require_sync_state.parseSyncStatus;
2696
+ exports.parseTreeCursor = require_tree_index.parseTreeCursor;
2697
+ exports.prefixRange = require_prefix_range.prefixRange;
2698
+ exports.referencedAnywhereD1 = require_reference_check.referencedAnywhereD1;
2699
+ exports.referencedAnywhereLibsql = require_reference_check.referencedAnywhereLibsql;
2700
+ exports.resolveVersionToken = require_versioned_scope.resolveVersionToken;
2701
+ exports.rowToEntry = require_share_store.rowToEntry;
2702
+ exports.runGc = require_run_gc.runGc;
2703
+ exports.runWithConcurrency = require_rsync.runWithConcurrency;
2704
+ exports.serializeChangelogEntry = require_changelog_schema.serializeChangelogEntry;
2705
+ exports.serializeManifestEntry = require_changelog_schema.serializeManifestEntry;
2706
+ exports.serializeSyncStatus = require_sync_state.serializeSyncStatus;
2707
+ exports.serveShare = require_share_serve.serveShare;
2708
+ exports.syncDIDSpace = require_rsync.syncDIDSpace;
2709
+ exports.validateDeviceId = require_changelog_write.validateDeviceId;
2710
+ exports.validateId = require_did_utils.validateId;
2711
+ exports.versionedScope = require_versioned_scope.versionedScope;
2712
+ exports.versionedScopeGroup = require_versioned_scope.versionedScopeGroup;