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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (510) hide show
  1. package/LICENSE.md +26 -0
  2. package/README.md +129 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
  5. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  6. package/dist/afs-metadata-context.cjs +20 -0
  7. package/dist/afs-metadata-context.d.cts +21 -0
  8. package/dist/afs-metadata-context.d.cts.map +1 -0
  9. package/dist/afs-metadata-context.d.mts +21 -0
  10. package/dist/afs-metadata-context.d.mts.map +1 -0
  11. package/dist/afs-metadata-context.mjs +18 -0
  12. package/dist/afs-metadata-context.mjs.map +1 -0
  13. package/dist/blocklet-hook.cjs +180 -0
  14. package/dist/blocklet-hook.d.cts +29 -0
  15. package/dist/blocklet-hook.d.cts.map +1 -0
  16. package/dist/blocklet-hook.d.mts +29 -0
  17. package/dist/blocklet-hook.d.mts.map +1 -0
  18. package/dist/blocklet-hook.mjs +178 -0
  19. package/dist/blocklet-hook.mjs.map +1 -0
  20. package/dist/changelog-schema.cjs +603 -0
  21. package/dist/changelog-schema.d.cts +395 -0
  22. package/dist/changelog-schema.d.cts.map +1 -0
  23. package/dist/changelog-schema.d.mts +395 -0
  24. package/dist/changelog-schema.d.mts.map +1 -0
  25. package/dist/changelog-schema.mjs +570 -0
  26. package/dist/changelog-schema.mjs.map +1 -0
  27. package/dist/changelog-write.cjs +80 -0
  28. package/dist/changelog-write.d.cts +70 -0
  29. package/dist/changelog-write.d.cts.map +1 -0
  30. package/dist/changelog-write.d.mts +70 -0
  31. package/dist/changelog-write.d.mts.map +1 -0
  32. package/dist/changelog-write.mjs +77 -0
  33. package/dist/changelog-write.mjs.map +1 -0
  34. package/dist/cid-byte-cache.cjs +126 -0
  35. package/dist/cid-byte-cache.d.cts +46 -0
  36. package/dist/cid-byte-cache.d.cts.map +1 -0
  37. package/dist/cid-byte-cache.d.mts +47 -0
  38. package/dist/cid-byte-cache.d.mts.map +1 -0
  39. package/dist/cid-byte-cache.mjs +125 -0
  40. package/dist/cid-byte-cache.mjs.map +1 -0
  41. package/dist/cid.cjs +68 -0
  42. package/dist/cid.d.cts +22 -0
  43. package/dist/cid.d.cts.map +1 -0
  44. package/dist/cid.d.mts +22 -0
  45. package/dist/cid.d.mts.map +1 -0
  46. package/dist/cid.mjs +65 -0
  47. package/dist/cid.mjs.map +1 -0
  48. package/dist/client.cjs +221 -0
  49. package/dist/client.d.cts +72 -0
  50. package/dist/client.d.cts.map +1 -0
  51. package/dist/client.d.mts +72 -0
  52. package/dist/client.d.mts.map +1 -0
  53. package/dist/client.mjs +220 -0
  54. package/dist/client.mjs.map +1 -0
  55. package/dist/cloudflare.cjs +510 -0
  56. package/dist/cloudflare.d.cts +307 -0
  57. package/dist/cloudflare.d.cts.map +1 -0
  58. package/dist/cloudflare.d.mts +308 -0
  59. package/dist/cloudflare.d.mts.map +1 -0
  60. package/dist/cloudflare.mjs +507 -0
  61. package/dist/cloudflare.mjs.map +1 -0
  62. package/dist/ctime-backfill.cjs +272 -0
  63. package/dist/ctime-backfill.d.cts +169 -0
  64. package/dist/ctime-backfill.d.cts.map +1 -0
  65. package/dist/ctime-backfill.d.mts +169 -0
  66. package/dist/ctime-backfill.d.mts.map +1 -0
  67. package/dist/ctime-backfill.mjs +267 -0
  68. package/dist/ctime-backfill.mjs.map +1 -0
  69. package/dist/d1-gc-queue.cjs +80 -0
  70. package/dist/d1-gc-queue.d.cts +59 -0
  71. package/dist/d1-gc-queue.d.cts.map +1 -0
  72. package/dist/d1-gc-queue.d.mts +59 -0
  73. package/dist/d1-gc-queue.d.mts.map +1 -0
  74. package/dist/d1-gc-queue.mjs +81 -0
  75. package/dist/d1-gc-queue.mjs.map +1 -0
  76. package/dist/d1-resolve-store.cjs +189 -0
  77. package/dist/d1-resolve-store.d.cts +93 -0
  78. package/dist/d1-resolve-store.d.cts.map +1 -0
  79. package/dist/d1-resolve-store.d.mts +93 -0
  80. package/dist/d1-resolve-store.d.mts.map +1 -0
  81. package/dist/d1-resolve-store.mjs +190 -0
  82. package/dist/d1-resolve-store.mjs.map +1 -0
  83. package/dist/d1-schema.cjs +361 -0
  84. package/dist/d1-schema.d.cts +237 -0
  85. package/dist/d1-schema.d.cts.map +1 -0
  86. package/dist/d1-schema.d.mts +237 -0
  87. package/dist/d1-schema.d.mts.map +1 -0
  88. package/dist/d1-schema.mjs +341 -0
  89. package/dist/d1-schema.mjs.map +1 -0
  90. package/dist/d1-share-store.cjs +0 -0
  91. package/dist/d1-share-store.d.cts +53 -0
  92. package/dist/d1-share-store.d.cts.map +1 -0
  93. package/dist/d1-share-store.d.mts +53 -0
  94. package/dist/d1-share-store.d.mts.map +1 -0
  95. package/dist/d1-share-store.mjs +0 -0
  96. package/dist/d1-share-store.mjs.map +1 -0
  97. package/dist/d1-tree-index.cjs +1098 -0
  98. package/dist/d1-tree-index.d.cts +203 -0
  99. package/dist/d1-tree-index.d.cts.map +1 -0
  100. package/dist/d1-tree-index.d.mts +203 -0
  101. package/dist/d1-tree-index.d.mts.map +1 -0
  102. package/dist/d1-tree-index.mjs +1094 -0
  103. package/dist/d1-tree-index.mjs.map +1 -0
  104. package/dist/data-backend.cjs +147 -0
  105. package/dist/data-backend.d.cts +30 -0
  106. package/dist/data-backend.d.cts.map +1 -0
  107. package/dist/data-backend.d.mts +30 -0
  108. package/dist/data-backend.d.mts.map +1 -0
  109. package/dist/data-backend.mjs +148 -0
  110. package/dist/data-backend.mjs.map +1 -0
  111. package/dist/dedup-backfill.cjs +278 -0
  112. package/dist/dedup-backfill.d.cts +165 -0
  113. package/dist/dedup-backfill.d.cts.map +1 -0
  114. package/dist/dedup-backfill.d.mts +165 -0
  115. package/dist/dedup-backfill.d.mts.map +1 -0
  116. package/dist/dedup-backfill.mjs +273 -0
  117. package/dist/dedup-backfill.mjs.map +1 -0
  118. package/dist/did-space-like.d.cts +2 -0
  119. package/dist/did-space-like.d.mts +2 -0
  120. package/dist/did-utils.cjs +44 -0
  121. package/dist/did-utils.d.cts +25 -0
  122. package/dist/did-utils.d.cts.map +1 -0
  123. package/dist/did-utils.d.mts +25 -0
  124. package/dist/did-utils.d.mts.map +1 -0
  125. package/dist/did-utils.mjs +43 -0
  126. package/dist/did-utils.mjs.map +1 -0
  127. package/dist/dirty-path-queue.cjs +31 -0
  128. package/dist/dirty-path-queue.d.cts +104 -0
  129. package/dist/dirty-path-queue.d.cts.map +1 -0
  130. package/dist/dirty-path-queue.d.mts +104 -0
  131. package/dist/dirty-path-queue.d.mts.map +1 -0
  132. package/dist/dirty-path-queue.mjs +31 -0
  133. package/dist/dirty-path-queue.mjs.map +1 -0
  134. package/dist/dirty-path-schema.cjs +142 -0
  135. package/dist/dirty-path-schema.d.cts +90 -0
  136. package/dist/dirty-path-schema.d.cts.map +1 -0
  137. package/dist/dirty-path-schema.d.mts +90 -0
  138. package/dist/dirty-path-schema.d.mts.map +1 -0
  139. package/dist/dirty-path-schema.mjs +136 -0
  140. package/dist/dirty-path-schema.mjs.map +1 -0
  141. package/dist/download-actions.cjs +43 -0
  142. package/dist/download-actions.mjs +40 -0
  143. package/dist/download-actions.mjs.map +1 -0
  144. package/dist/fs-object-store.cjs +153 -0
  145. package/dist/fs-object-store.d.cts +21 -0
  146. package/dist/fs-object-store.d.cts.map +1 -0
  147. package/dist/fs-object-store.d.mts +22 -0
  148. package/dist/fs-object-store.d.mts.map +1 -0
  149. package/dist/fs-object-store.mjs +153 -0
  150. package/dist/fs-object-store.mjs.map +1 -0
  151. package/dist/gc-queue.d.cts +43 -0
  152. package/dist/gc-queue.d.cts.map +1 -0
  153. package/dist/gc-queue.d.mts +43 -0
  154. package/dist/gc-queue.d.mts.map +1 -0
  155. package/dist/index.cjs +2712 -0
  156. package/dist/index.d.cts +504 -0
  157. package/dist/index.d.cts.map +1 -0
  158. package/dist/index.d.mts +505 -0
  159. package/dist/index.d.mts.map +1 -0
  160. package/dist/index.mjs +2537 -0
  161. package/dist/index.mjs.map +1 -0
  162. package/dist/inline-content-migration.cjs +212 -0
  163. package/dist/inline-content-migration.d.cts +110 -0
  164. package/dist/inline-content-migration.d.cts.map +1 -0
  165. package/dist/inline-content-migration.d.mts +111 -0
  166. package/dist/inline-content-migration.d.mts.map +1 -0
  167. package/dist/inline-content-migration.mjs +207 -0
  168. package/dist/inline-content-migration.mjs.map +1 -0
  169. package/dist/inline-content.cjs +145 -0
  170. package/dist/inline-content.d.cts +98 -0
  171. package/dist/inline-content.d.cts.map +1 -0
  172. package/dist/inline-content.d.mts +98 -0
  173. package/dist/inline-content.d.mts.map +1 -0
  174. package/dist/inline-content.mjs +137 -0
  175. package/dist/inline-content.mjs.map +1 -0
  176. package/dist/local-changelog.cjs +241 -0
  177. package/dist/local-changelog.d.cts +119 -0
  178. package/dist/local-changelog.d.cts.map +1 -0
  179. package/dist/local-changelog.d.mts +119 -0
  180. package/dist/local-changelog.d.mts.map +1 -0
  181. package/dist/local-changelog.mjs +240 -0
  182. package/dist/local-changelog.mjs.map +1 -0
  183. package/dist/local.cjs +435 -0
  184. package/dist/local.d.cts +254 -0
  185. package/dist/local.d.cts.map +1 -0
  186. package/dist/local.d.mts +254 -0
  187. package/dist/local.d.mts.map +1 -0
  188. package/dist/local.mjs +427 -0
  189. package/dist/local.mjs.map +1 -0
  190. package/dist/memory-gc-queue.cjs +36 -0
  191. package/dist/memory-gc-queue.d.cts +22 -0
  192. package/dist/memory-gc-queue.d.cts.map +1 -0
  193. package/dist/memory-gc-queue.d.mts +22 -0
  194. package/dist/memory-gc-queue.d.mts.map +1 -0
  195. package/dist/memory-gc-queue.mjs +36 -0
  196. package/dist/memory-gc-queue.mjs.map +1 -0
  197. package/dist/memory-tree-index.cjs +734 -0
  198. package/dist/memory-tree-index.d.cts +159 -0
  199. package/dist/memory-tree-index.d.cts.map +1 -0
  200. package/dist/memory-tree-index.d.mts +159 -0
  201. package/dist/memory-tree-index.d.mts.map +1 -0
  202. package/dist/memory-tree-index.mjs +735 -0
  203. package/dist/memory-tree-index.mjs.map +1 -0
  204. package/dist/metadata-db.cjs +562 -0
  205. package/dist/metadata-db.d.cts +102 -0
  206. package/dist/metadata-db.d.cts.map +1 -0
  207. package/dist/metadata-db.d.mts +102 -0
  208. package/dist/metadata-db.d.mts.map +1 -0
  209. package/dist/metadata-db.mjs +562 -0
  210. package/dist/metadata-db.mjs.map +1 -0
  211. package/dist/mime-utils.cjs +2 -0
  212. package/dist/mime-utils.d.cts +2 -0
  213. package/dist/mime-utils.d.mts +2 -0
  214. package/dist/mime-utils.mjs +3 -0
  215. package/dist/object-key.cjs +41 -0
  216. package/dist/object-key.d.cts +25 -0
  217. package/dist/object-key.d.cts.map +1 -0
  218. package/dist/object-key.d.mts +25 -0
  219. package/dist/object-key.d.mts.map +1 -0
  220. package/dist/object-key.mjs +40 -0
  221. package/dist/object-key.mjs.map +1 -0
  222. package/dist/packages/session/dist/index.cjs +788 -0
  223. package/dist/packages/session/dist/index.mjs +782 -0
  224. package/dist/packages/session/dist/index.mjs.map +1 -0
  225. package/dist/platform/r2/dist/types.d.cts +49 -0
  226. package/dist/platform/r2/dist/types.d.cts.map +1 -0
  227. package/dist/prefix-range.cjs +33 -0
  228. package/dist/prefix-range.d.cts +29 -0
  229. package/dist/prefix-range.d.cts.map +1 -0
  230. package/dist/prefix-range.d.mts +29 -0
  231. package/dist/prefix-range.d.mts.map +1 -0
  232. package/dist/prefix-range.mjs +33 -0
  233. package/dist/prefix-range.mjs.map +1 -0
  234. package/dist/prefixed-tree-index.cjs +238 -0
  235. package/dist/prefixed-tree-index.d.cts +85 -0
  236. package/dist/prefixed-tree-index.d.cts.map +1 -0
  237. package/dist/prefixed-tree-index.d.mts +85 -0
  238. package/dist/prefixed-tree-index.d.mts.map +1 -0
  239. package/dist/prefixed-tree-index.mjs +239 -0
  240. package/dist/prefixed-tree-index.mjs.map +1 -0
  241. package/dist/presign-signer.cjs +7 -0
  242. package/dist/presign-signer.d.cts +99 -0
  243. package/dist/presign-signer.d.cts.map +1 -0
  244. package/dist/presign-signer.d.mts +99 -0
  245. package/dist/presign-signer.d.mts.map +1 -0
  246. package/dist/presign-signer.mjs +7 -0
  247. package/dist/presign-signer.mjs.map +1 -0
  248. package/dist/providers/platform/r2/dist/index.d.mts +3 -0
  249. package/dist/providers/platform/r2/dist/types.d.mts +49 -0
  250. package/dist/providers/platform/r2/dist/types.d.mts.map +1 -0
  251. package/dist/reconcile.cjs +184 -0
  252. package/dist/reconcile.d.cts +26 -0
  253. package/dist/reconcile.d.cts.map +1 -0
  254. package/dist/reconcile.d.mts +26 -0
  255. package/dist/reconcile.d.mts.map +1 -0
  256. package/dist/reconcile.mjs +184 -0
  257. package/dist/reconcile.mjs.map +1 -0
  258. package/dist/reference-check.cjs +49 -0
  259. package/dist/reference-check.d.cts +10 -0
  260. package/dist/reference-check.d.cts.map +1 -0
  261. package/dist/reference-check.d.mts +10 -0
  262. package/dist/reference-check.d.mts.map +1 -0
  263. package/dist/reference-check.mjs +48 -0
  264. package/dist/reference-check.mjs.map +1 -0
  265. package/dist/remote-afs.cjs +681 -0
  266. package/dist/remote-afs.d.cts +236 -0
  267. package/dist/remote-afs.d.cts.map +1 -0
  268. package/dist/remote-afs.d.mts +236 -0
  269. package/dist/remote-afs.d.mts.map +1 -0
  270. package/dist/remote-afs.mjs +676 -0
  271. package/dist/remote-afs.mjs.map +1 -0
  272. package/dist/rename-guard.cjs +18 -0
  273. package/dist/rename-guard.mjs +18 -0
  274. package/dist/rename-guard.mjs.map +1 -0
  275. package/dist/resolve.cjs +260 -0
  276. package/dist/resolve.d.cts +113 -0
  277. package/dist/resolve.d.cts.map +1 -0
  278. package/dist/resolve.d.mts +113 -0
  279. package/dist/resolve.d.mts.map +1 -0
  280. package/dist/resolve.mjs +260 -0
  281. package/dist/resolve.mjs.map +1 -0
  282. package/dist/rsync-content.cjs +33 -0
  283. package/dist/rsync-content.mjs +33 -0
  284. package/dist/rsync-content.mjs.map +1 -0
  285. package/dist/rsync.cjs +400 -0
  286. package/dist/rsync.d.cts +157 -0
  287. package/dist/rsync.d.cts.map +1 -0
  288. package/dist/rsync.d.mts +157 -0
  289. package/dist/rsync.d.mts.map +1 -0
  290. package/dist/rsync.mjs +397 -0
  291. package/dist/rsync.mjs.map +1 -0
  292. package/dist/run-gc.cjs +57 -0
  293. package/dist/run-gc.d.cts +40 -0
  294. package/dist/run-gc.d.cts.map +1 -0
  295. package/dist/run-gc.d.mts +41 -0
  296. package/dist/run-gc.d.mts.map +1 -0
  297. package/dist/run-gc.mjs +57 -0
  298. package/dist/run-gc.mjs.map +1 -0
  299. package/dist/scope-id.cjs +70 -0
  300. package/dist/scope-id.d.cts +35 -0
  301. package/dist/scope-id.d.cts.map +1 -0
  302. package/dist/scope-id.d.mts +35 -0
  303. package/dist/scope-id.d.mts.map +1 -0
  304. package/dist/scope-id.mjs +68 -0
  305. package/dist/scope-id.mjs.map +1 -0
  306. package/dist/scope-migration-node.cjs +172 -0
  307. package/dist/scope-migration-node.d.cts +44 -0
  308. package/dist/scope-migration-node.d.cts.map +1 -0
  309. package/dist/scope-migration-node.d.mts +44 -0
  310. package/dist/scope-migration-node.d.mts.map +1 -0
  311. package/dist/scope-migration-node.mjs +171 -0
  312. package/dist/scope-migration-node.mjs.map +1 -0
  313. package/dist/scope-migration.cjs +155 -0
  314. package/dist/scope-migration.d.cts +65 -0
  315. package/dist/scope-migration.d.cts.map +1 -0
  316. package/dist/scope-migration.d.mts +65 -0
  317. package/dist/scope-migration.d.mts.map +1 -0
  318. package/dist/scope-migration.mjs +154 -0
  319. package/dist/scope-migration.mjs.map +1 -0
  320. package/dist/share-declaration.cjs +225 -0
  321. package/dist/share-declaration.d.cts +30 -0
  322. package/dist/share-declaration.d.cts.map +1 -0
  323. package/dist/share-declaration.d.mts +32 -0
  324. package/dist/share-declaration.d.mts.map +1 -0
  325. package/dist/share-declaration.mjs +221 -0
  326. package/dist/share-declaration.mjs.map +1 -0
  327. package/dist/share-hook.cjs +63 -0
  328. package/dist/share-hook.mjs +62 -0
  329. package/dist/share-hook.mjs.map +1 -0
  330. package/dist/share-resolve.cjs +176 -0
  331. package/dist/share-resolve.d.cts +36 -0
  332. package/dist/share-resolve.d.cts.map +1 -0
  333. package/dist/share-resolve.d.mts +36 -0
  334. package/dist/share-resolve.d.mts.map +1 -0
  335. package/dist/share-resolve.mjs +177 -0
  336. package/dist/share-resolve.mjs.map +1 -0
  337. package/dist/share-serve.cjs +145 -0
  338. package/dist/share-serve.d.cts +30 -0
  339. package/dist/share-serve.d.cts.map +1 -0
  340. package/dist/share-serve.d.mts +30 -0
  341. package/dist/share-serve.d.mts.map +1 -0
  342. package/dist/share-serve.mjs +144 -0
  343. package/dist/share-serve.mjs.map +1 -0
  344. package/dist/share-store.cjs +25 -0
  345. package/dist/share-store.d.cts +65 -0
  346. package/dist/share-store.d.cts.map +1 -0
  347. package/dist/share-store.d.mts +65 -0
  348. package/dist/share-store.d.mts.map +1 -0
  349. package/dist/share-store.mjs +25 -0
  350. package/dist/share-store.mjs.map +1 -0
  351. package/dist/share-virtual-path.cjs +102 -0
  352. package/dist/share-virtual-path.mjs +101 -0
  353. package/dist/share-virtual-path.mjs.map +1 -0
  354. package/dist/space-resolver.cjs +54 -0
  355. package/dist/space-resolver.d.cts +34 -0
  356. package/dist/space-resolver.d.cts.map +1 -0
  357. package/dist/space-resolver.d.mts +34 -0
  358. package/dist/space-resolver.d.mts.map +1 -0
  359. package/dist/space-resolver.mjs +55 -0
  360. package/dist/space-resolver.mjs.map +1 -0
  361. package/dist/sqlite-gc-queue.cjs +51 -0
  362. package/dist/sqlite-gc-queue.d.cts +22 -0
  363. package/dist/sqlite-gc-queue.d.cts.map +1 -0
  364. package/dist/sqlite-gc-queue.d.mts +22 -0
  365. package/dist/sqlite-gc-queue.d.mts.map +1 -0
  366. package/dist/sqlite-gc-queue.mjs +52 -0
  367. package/dist/sqlite-gc-queue.mjs.map +1 -0
  368. package/dist/sqlite-resolve-store.cjs +163 -0
  369. package/dist/sqlite-resolve-store.d.cts +50 -0
  370. package/dist/sqlite-resolve-store.d.cts.map +1 -0
  371. package/dist/sqlite-resolve-store.d.mts +50 -0
  372. package/dist/sqlite-resolve-store.d.mts.map +1 -0
  373. package/dist/sqlite-resolve-store.mjs +164 -0
  374. package/dist/sqlite-resolve-store.mjs.map +1 -0
  375. package/dist/sqlite-share-store.cjs +157 -0
  376. package/dist/sqlite-share-store.d.cts +31 -0
  377. package/dist/sqlite-share-store.d.cts.map +1 -0
  378. package/dist/sqlite-share-store.d.mts +31 -0
  379. package/dist/sqlite-share-store.d.mts.map +1 -0
  380. package/dist/sqlite-share-store.mjs +157 -0
  381. package/dist/sqlite-share-store.mjs.map +1 -0
  382. package/dist/sqlite-tree-index.cjs +901 -0
  383. package/dist/sqlite-tree-index.d.cts +107 -0
  384. package/dist/sqlite-tree-index.d.cts.map +1 -0
  385. package/dist/sqlite-tree-index.d.mts +107 -0
  386. package/dist/sqlite-tree-index.d.mts.map +1 -0
  387. package/dist/sqlite-tree-index.mjs +902 -0
  388. package/dist/sqlite-tree-index.mjs.map +1 -0
  389. package/dist/sync-engine.cjs +1324 -0
  390. package/dist/sync-engine.d.cts +473 -0
  391. package/dist/sync-engine.d.cts.map +1 -0
  392. package/dist/sync-engine.d.mts +473 -0
  393. package/dist/sync-engine.d.mts.map +1 -0
  394. package/dist/sync-engine.mjs +1315 -0
  395. package/dist/sync-engine.mjs.map +1 -0
  396. package/dist/sync-state.cjs +303 -0
  397. package/dist/sync-state.d.cts +224 -0
  398. package/dist/sync-state.d.cts.map +1 -0
  399. package/dist/sync-state.d.mts +224 -0
  400. package/dist/sync-state.d.mts.map +1 -0
  401. package/dist/sync-state.mjs +297 -0
  402. package/dist/sync-state.mjs.map +1 -0
  403. package/dist/timestamp.cjs +92 -0
  404. package/dist/timestamp.mjs +91 -0
  405. package/dist/timestamp.mjs.map +1 -0
  406. package/dist/transports/http.cjs +54 -0
  407. package/dist/transports/http.d.cts +26 -0
  408. package/dist/transports/http.d.cts.map +1 -0
  409. package/dist/transports/http.d.mts +26 -0
  410. package/dist/transports/http.d.mts.map +1 -0
  411. package/dist/transports/http.mjs +54 -0
  412. package/dist/transports/http.mjs.map +1 -0
  413. package/dist/transports/static.cjs +55 -0
  414. package/dist/transports/static.d.cts +28 -0
  415. package/dist/transports/static.d.cts.map +1 -0
  416. package/dist/transports/static.d.mts +28 -0
  417. package/dist/transports/static.d.mts.map +1 -0
  418. package/dist/transports/static.mjs +55 -0
  419. package/dist/transports/static.mjs.map +1 -0
  420. package/dist/transports/types.cjs +13 -0
  421. package/dist/transports/types.d.cts +72 -0
  422. package/dist/transports/types.d.cts.map +1 -0
  423. package/dist/transports/types.d.mts +72 -0
  424. package/dist/transports/types.d.mts.map +1 -0
  425. package/dist/transports/types.mjs +13 -0
  426. package/dist/transports/types.mjs.map +1 -0
  427. package/dist/transports/ws.cjs +207 -0
  428. package/dist/transports/ws.d.cts +47 -0
  429. package/dist/transports/ws.d.cts.map +1 -0
  430. package/dist/transports/ws.d.mts +47 -0
  431. package/dist/transports/ws.d.mts.map +1 -0
  432. package/dist/transports/ws.mjs +206 -0
  433. package/dist/transports/ws.mjs.map +1 -0
  434. package/dist/tree-index.cjs +333 -0
  435. package/dist/tree-index.d.cts +524 -0
  436. package/dist/tree-index.d.cts.map +1 -0
  437. package/dist/tree-index.d.mts +524 -0
  438. package/dist/tree-index.d.mts.map +1 -0
  439. package/dist/tree-index.mjs +322 -0
  440. package/dist/tree-index.mjs.map +1 -0
  441. package/dist/tree-query.cjs +655 -0
  442. package/dist/tree-query.d.cts +70 -0
  443. package/dist/tree-query.d.cts.map +1 -0
  444. package/dist/tree-query.d.mts +70 -0
  445. package/dist/tree-query.d.mts.map +1 -0
  446. package/dist/tree-query.mjs +648 -0
  447. package/dist/tree-query.mjs.map +1 -0
  448. package/dist/upload-actions.cjs +97 -0
  449. package/dist/upload-actions.d.cts +19 -0
  450. package/dist/upload-actions.d.cts.map +1 -0
  451. package/dist/upload-actions.d.mts +21 -0
  452. package/dist/upload-actions.d.mts.map +1 -0
  453. package/dist/upload-actions.mjs +90 -0
  454. package/dist/upload-actions.mjs.map +1 -0
  455. package/dist/upload-session-store.cjs +185 -0
  456. package/dist/upload-session-store.d.cts +99 -0
  457. package/dist/upload-session-store.d.cts.map +1 -0
  458. package/dist/upload-session-store.d.mts +99 -0
  459. package/dist/upload-session-store.d.mts.map +1 -0
  460. package/dist/upload-session-store.mjs +184 -0
  461. package/dist/upload-session-store.mjs.map +1 -0
  462. package/dist/url-normalize.cjs +61 -0
  463. package/dist/url-normalize.d.cts +33 -0
  464. package/dist/url-normalize.d.cts.map +1 -0
  465. package/dist/url-normalize.d.mts +33 -0
  466. package/dist/url-normalize.d.mts.map +1 -0
  467. package/dist/url-normalize.mjs +61 -0
  468. package/dist/url-normalize.mjs.map +1 -0
  469. package/dist/versioned-content-backends.cjs +155 -0
  470. package/dist/versioned-content-backends.d.cts +49 -0
  471. package/dist/versioned-content-backends.d.cts.map +1 -0
  472. package/dist/versioned-content-backends.d.mts +49 -0
  473. package/dist/versioned-content-backends.d.mts.map +1 -0
  474. package/dist/versioned-content-backends.mjs +154 -0
  475. package/dist/versioned-content-backends.mjs.map +1 -0
  476. package/dist/versioned-content-index.cjs +102 -0
  477. package/dist/versioned-content-index.d.cts +46 -0
  478. package/dist/versioned-content-index.d.cts.map +1 -0
  479. package/dist/versioned-content-index.d.mts +46 -0
  480. package/dist/versioned-content-index.d.mts.map +1 -0
  481. package/dist/versioned-content-index.mjs +103 -0
  482. package/dist/versioned-content-index.mjs.map +1 -0
  483. package/dist/versioned-content-store.cjs +121 -0
  484. package/dist/versioned-content-store.d.cts +90 -0
  485. package/dist/versioned-content-store.d.cts.map +1 -0
  486. package/dist/versioned-content-store.d.mts +90 -0
  487. package/dist/versioned-content-store.d.mts.map +1 -0
  488. package/dist/versioned-content-store.mjs +122 -0
  489. package/dist/versioned-content-store.mjs.map +1 -0
  490. package/dist/versioned-scope.cjs +39 -0
  491. package/dist/versioned-scope.d.cts +57 -0
  492. package/dist/versioned-scope.d.cts.map +1 -0
  493. package/dist/versioned-scope.d.mts +57 -0
  494. package/dist/versioned-scope.d.mts.map +1 -0
  495. package/dist/versioned-scope.mjs +37 -0
  496. package/dist/versioned-scope.mjs.map +1 -0
  497. package/migrations/0001_tree_entries.sql +17 -0
  498. package/migrations/0002_resolve_entries.sql +7 -0
  499. package/migrations/0003_entries_v2.sql +38 -0
  500. package/migrations/0004_gc_queue.sql +5 -0
  501. package/migrations/0006_resolve_enhance.sql +14 -0
  502. package/migrations/0007_resolve_created_at.sql +10 -0
  503. package/migrations/0008_changelog.sql +34 -0
  504. package/migrations/0009_device_sync_state.sql +29 -0
  505. package/migrations/0010_scope_meta.sql +33 -0
  506. package/migrations/0011_shares.sql +25 -0
  507. package/migrations/0012_scope_meta_file_count.sql +41 -0
  508. package/migrations/0013_scope_meta_total_bytes.sql +47 -0
  509. package/migrations/0014_rename_tables.sql +62 -0
  510. package/package.json +102 -0
@@ -0,0 +1,177 @@
1
+ import { deriveSourcePath, isShareReservedPath, parseShareDeclaration } from "./share-declaration.mjs";
2
+
3
+ //#region src/share-resolve.ts
4
+ /**
5
+ * Phase 0.4 — ShareResolver: storage-agnostic share reconcile orchestrator.
6
+ *
7
+ * Holds the reconcile BUSINESS LOGIC (YAML parse + schema validation, reserved
8
+ * / forbidden / traversal path checks, sourcePath derivation, nested-share
9
+ * detection, kind auto-detection, slug determination + conflict check) and
10
+ * delegates ALL `shares` table access to an injected {@link ShareStore}. This
11
+ * mirrors the repo's `DIDSpaceResolver + KeyValueStore` precedent — one
12
+ * orchestrator, swappable backend (D1 / libsql), no adapter.
13
+ *
14
+ * Identity is owner-scoped: `(ownerDid, declPath)` is the composite key, so two
15
+ * owners that share one D1 / one daemon-global `_shares.db` never clobber each
16
+ * other (CRITICAL #1 fix). `slug` stays globally unique.
17
+ *
18
+ * Offline full rebuild (`reconcileAll`) is intentionally NOT here — see
19
+ * `D1ShareReconciler` in d1-share-store.ts (D1-only, needs an ObjectStore).
20
+ */
21
+ /** 21-character random ID using Web Crypto (base-36, no external deps). */
22
+ function generateId() {
23
+ return Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) => b.toString(36).padStart(2, "0")).join("").slice(0, 21);
24
+ }
25
+ /** Paths under these prefixes are forbidden from having shares. */
26
+ const FORBIDDEN_PREFIXES = ["config/", "vault/"];
27
+ function isForbiddenPath(path) {
28
+ const p = path.startsWith("/") ? path.slice(1) : path;
29
+ return FORBIDDEN_PREFIXES.some((prefix) => p.startsWith(prefix));
30
+ }
31
+ function hasPathTraversal(path) {
32
+ return path.split("/").includes("..");
33
+ }
34
+ /** Returns true if the declPath is a sidecar (not root, not in-tree /.share). */
35
+ function isSidecarPath(declPath) {
36
+ const p = declPath.startsWith("/") ? declPath.slice(1) : declPath;
37
+ return p !== ".share" && !p.endsWith("/.share");
38
+ }
39
+ var ShareResolver = class {
40
+ constructor(store, treeIndex) {
41
+ this.store = store;
42
+ this.treeIndex = treeIndex;
43
+ }
44
+ resolveBySlug(slug) {
45
+ return this.store.resolveBySlug(slug);
46
+ }
47
+ listByOwner(ownerDid) {
48
+ return this.store.listByOwner(ownerDid);
49
+ }
50
+ listByInstance(ownerDid, instanceDid) {
51
+ return this.store.listByInstance(ownerDid, instanceDid);
52
+ }
53
+ readShare(ownerDid, slug) {
54
+ return this.store.readByOwnerAndSlug(ownerDid, slug);
55
+ }
56
+ findByOwnerAndDeclPath(ownerDid, declPath) {
57
+ return this.store.findByOwnerAndDeclPath(ownerDid, declPath);
58
+ }
59
+ async reconcileFromDecl(params) {
60
+ const { declPath, declContent, declCid, ownerDid, dryRun = false } = params;
61
+ const parsed = parseShareDeclaration(declContent);
62
+ if (!parsed.ok) return parsed;
63
+ if (isShareReservedPath(declPath)) return {
64
+ ok: false,
65
+ code: "share-path-reserved",
66
+ message: `Path is reserved and cannot have a share declaration: ${declPath}`
67
+ };
68
+ if (isForbiddenPath(declPath)) return {
69
+ ok: false,
70
+ code: "share-path-forbidden",
71
+ message: `Path prefix is forbidden for share declarations: ${declPath}`
72
+ };
73
+ if (hasPathTraversal(declPath)) return {
74
+ ok: false,
75
+ code: "share-path-forbidden",
76
+ message: `Path traversal detected in declaration path: ${declPath}`
77
+ };
78
+ const sourcePath = deriveSourcePath(declPath);
79
+ const isSidecar = isSidecarPath(declPath);
80
+ if (sourcePath !== "") {
81
+ const nested = await this.store.findNestedConflict(ownerDid, declPath, sourcePath);
82
+ if (nested) return {
83
+ ok: false,
84
+ code: "share-nested",
85
+ message: `Share declaration conflicts with ${nested}`
86
+ };
87
+ }
88
+ if (isSidecar && this.treeIndex && sourcePath) {
89
+ if (!await this.treeIndex.get(sourcePath, { includeContent: false })) return {
90
+ ok: false,
91
+ code: "share-sidecar-target-missing",
92
+ message: `Sidecar target file not found: ${sourcePath}`
93
+ };
94
+ }
95
+ let resolvedKind;
96
+ if (parsed.data.kind) {
97
+ resolvedKind = parsed.data.kind;
98
+ if (resolvedKind === "web" && this.treeIndex) {
99
+ const webPrefix = sourcePath ? `${sourcePath}/.web/` : ".web/";
100
+ const { entries } = await this.treeIndex.list(webPrefix, { limit: 1 });
101
+ if (entries.length === 0) return {
102
+ ok: false,
103
+ code: "share-web-no-decl",
104
+ message: `kind:web requires a .web/ directory at ${webPrefix}`
105
+ };
106
+ }
107
+ } else if (isSidecar) resolvedKind = "raw";
108
+ else if (this.treeIndex) {
109
+ const webPrefix = sourcePath ? `${sourcePath}/.web/` : ".web/";
110
+ const { entries } = await this.treeIndex.list(webPrefix, { limit: 1 });
111
+ resolvedKind = entries.length > 0 ? "web" : "raw";
112
+ } else resolvedKind = "raw";
113
+ const existing = await this.store.findByOwnerAndDeclPath(ownerDid, declPath);
114
+ let slug;
115
+ if (parsed.data.slug) slug = parsed.data.slug;
116
+ else if (existing) slug = existing.slug;
117
+ else slug = generateId();
118
+ if (!existing || existing.slug !== slug) {
119
+ const conflicting = await this.store.findSlugConflict(slug, ownerDid, declPath);
120
+ if (conflicting) return {
121
+ ok: false,
122
+ code: "share-slug-conflict",
123
+ message: `Slug "${slug}" is already claimed by ${conflicting}`
124
+ };
125
+ }
126
+ const now = Date.now();
127
+ const id = existing?.id ?? generateId();
128
+ const status = parsed.data.status ?? "active";
129
+ if (dryRun) return {
130
+ ok: true,
131
+ entry: {
132
+ id,
133
+ ownerDid,
134
+ sourcePath,
135
+ declPath,
136
+ slug,
137
+ kind: resolvedKind,
138
+ status,
139
+ declCid,
140
+ config: parsed.data,
141
+ createdAt: existing?.createdAt ?? now,
142
+ updatedAt: now
143
+ }
144
+ };
145
+ const row = {
146
+ id,
147
+ owner_did: ownerDid,
148
+ source_path: sourcePath,
149
+ decl_path: declPath,
150
+ slug,
151
+ kind: resolvedKind,
152
+ status,
153
+ decl_cid: declCid,
154
+ config_json: JSON.stringify(parsed.data),
155
+ created_at: existing?.createdAt ?? now,
156
+ updated_at: now
157
+ };
158
+ await this.store.upsert(row);
159
+ const final = await this.store.findByOwnerAndDeclPath(ownerDid, declPath);
160
+ if (!final) return {
161
+ ok: false,
162
+ code: "share-internal-error",
163
+ message: "Failed to read back the upserted share row"
164
+ };
165
+ return {
166
+ ok: true,
167
+ entry: final
168
+ };
169
+ }
170
+ reconcileDelete(ownerDid, declPath) {
171
+ return this.store.deleteByOwnerAndDeclPath(ownerDid, declPath);
172
+ }
173
+ };
174
+
175
+ //#endregion
176
+ export { ShareResolver };
177
+ //# sourceMappingURL=share-resolve.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share-resolve.mjs","names":[],"sources":["../src/share-resolve.ts"],"sourcesContent":["/**\n * Phase 0.4 — ShareResolver: storage-agnostic share reconcile orchestrator.\n *\n * Holds the reconcile BUSINESS LOGIC (YAML parse + schema validation, reserved\n * / forbidden / traversal path checks, sourcePath derivation, nested-share\n * detection, kind auto-detection, slug determination + conflict check) and\n * delegates ALL `shares` table access to an injected {@link ShareStore}. This\n * mirrors the repo's `DIDSpaceResolver + KeyValueStore` precedent — one\n * orchestrator, swappable backend (D1 / libsql), no adapter.\n *\n * Identity is owner-scoped: `(ownerDid, declPath)` is the composite key, so two\n * owners that share one D1 / one daemon-global `_shares.db` never clobber each\n * other (CRITICAL #1 fix). `slug` stays globally unique.\n *\n * Offline full rebuild (`reconcileAll`) is intentionally NOT here — see\n * `D1ShareReconciler` in d1-share-store.ts (D1-only, needs an ObjectStore).\n */\n\nimport {\n deriveSourcePath,\n isShareReservedPath,\n parseShareDeclaration,\n} from \"./share-declaration.js\";\nimport type { ShareEntry, ShareRow, ShareStore } from \"./share-store.js\";\nimport type { TreeIndex } from \"./tree-index.js\";\n\nexport type { ShareEntry } from \"./share-store.js\";\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/** 21-character random ID using Web Crypto (base-36, no external deps). */\nfunction generateId(): string {\n return Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) =>\n b.toString(36).padStart(2, \"0\"),\n )\n .join(\"\")\n .slice(0, 21);\n}\n\n/** Paths under these prefixes are forbidden from having shares. */\nconst FORBIDDEN_PREFIXES = [\"config/\", \"vault/\"];\n\nfunction isForbiddenPath(path: string): boolean {\n const p = path.startsWith(\"/\") ? path.slice(1) : path;\n return FORBIDDEN_PREFIXES.some((prefix) => p.startsWith(prefix));\n}\n\nfunction hasPathTraversal(path: string): boolean {\n return path.split(\"/\").includes(\"..\");\n}\n\n/** Returns true if the declPath is a sidecar (not root, not in-tree /.share). */\nfunction isSidecarPath(declPath: string): boolean {\n const p = declPath.startsWith(\"/\") ? declPath.slice(1) : declPath;\n return p !== \".share\" && !p.endsWith(\"/.share\");\n}\n\ntype ReconcileOk = { ok: true; entry: ShareEntry };\ntype ReconcileFail = { ok: false; code: string; message: string; detail?: unknown };\ntype ReconcileResult = ReconcileOk | ReconcileFail;\n\n// ─── ShareResolver ────────────────────────────────────────────────────────────\n\nexport class ShareResolver {\n constructor(\n private store: ShareStore,\n // treeIndex: optional — used only by reconcileFromDecl for .web/ + sidecar\n // checks. Production write path MUST pass a treeIndex. Omitting is only\n // valid for tests that don't exercise those checks. Widened from\n // D1TreeIndex to the TreeIndex interface (only .get()/.list() are used) so\n // the libsql SQLiteTreeIndex works unchanged.\n private treeIndex?: TreeIndex,\n ) {}\n\n // ① Public serving hot path ─────────────────────────────────────────────────\n\n resolveBySlug(slug: string): Promise<ShareEntry | null> {\n return this.store.resolveBySlug(slug);\n }\n\n // ② Virtual path handlers ───────────────────────────────────────────────────\n\n listByOwner(ownerDid: string): Promise<ShareEntry[]> {\n return this.store.listByOwner(ownerDid);\n }\n\n listByInstance(ownerDid: string, instanceDid: string): Promise<ShareEntry[]> {\n return this.store.listByInstance(ownerDid, instanceDid);\n }\n\n readShare(ownerDid: string, slug: string): Promise<ShareEntry | null> {\n return this.store.readByOwnerAndSlug(ownerDid, slug);\n }\n\n findByOwnerAndDeclPath(ownerDid: string, declPath: string): Promise<ShareEntry | null> {\n return this.store.findByOwnerAndDeclPath(ownerDid, declPath);\n }\n\n // ③ Internal upsert ─────────────────────────────────────────────────────────\n\n async reconcileFromDecl(params: {\n declPath: string;\n declContent: Uint8Array;\n declCid: string;\n ownerDid: string;\n dryRun?: boolean;\n }): Promise<ReconcileResult> {\n const { declPath, declContent, declCid, ownerDid, dryRun = false } = params;\n\n // Step 1: YAML parse + schema validation\n const parsed = parseShareDeclaration(declContent);\n if (!parsed.ok) return parsed;\n\n // Step 2: reserved path check (.shares/, instances/{iid}/.shares/)\n if (isShareReservedPath(declPath)) {\n return {\n ok: false,\n code: \"share-path-reserved\",\n message: `Path is reserved and cannot have a share declaration: ${declPath}`,\n };\n }\n\n // Step 3: blacklist prefix check (config/, vault/, ...)\n if (isForbiddenPath(declPath)) {\n return {\n ok: false,\n code: \"share-path-forbidden\",\n message: `Path prefix is forbidden for share declarations: ${declPath}`,\n };\n }\n\n // Step 4: path traversal check\n if (hasPathTraversal(declPath)) {\n return {\n ok: false,\n code: \"share-path-forbidden\",\n message: `Path traversal detected in declaration path: ${declPath}`,\n };\n }\n\n const sourcePath = deriveSourcePath(declPath);\n const isSidecar = isSidecarPath(declPath);\n\n // Step 5: nested share detection (skip for root share — sourcePath === \"\")\n if (sourcePath !== \"\") {\n const nested = await this.store.findNestedConflict(ownerDid, declPath, sourcePath);\n if (nested) {\n return {\n ok: false,\n code: \"share-nested\",\n message: `Share declaration conflicts with ${nested}`,\n };\n }\n }\n\n // Step 6: sidecar target existence check\n if (isSidecar && this.treeIndex && sourcePath) {\n const target = await this.treeIndex.get(sourcePath, { includeContent: false });\n if (!target) {\n return {\n ok: false,\n code: \"share-sidecar-target-missing\",\n message: `Sidecar target file not found: ${sourcePath}`,\n };\n }\n }\n\n // Step 6b / 7: kind detection + validation\n let resolvedKind: \"raw\" | \"web\";\n\n if (parsed.data.kind) {\n resolvedKind = parsed.data.kind;\n // Validate explicit kind:web — .web/ directory must exist\n if (resolvedKind === \"web\" && this.treeIndex) {\n const webPrefix = sourcePath ? `${sourcePath}/.web/` : \".web/\";\n const { entries } = await this.treeIndex.list(webPrefix, { limit: 1 });\n if (entries.length === 0) {\n return {\n ok: false,\n code: \"share-web-no-decl\",\n message: `kind:web requires a .web/ directory at ${webPrefix}`,\n };\n }\n }\n } else if (isSidecar) {\n // Sidecar always defaults to raw\n resolvedKind = \"raw\";\n } else if (this.treeIndex) {\n // in-tree: auto-detect from .web/ presence\n const webPrefix = sourcePath ? `${sourcePath}/.web/` : \".web/\";\n const { entries } = await this.treeIndex.list(webPrefix, { limit: 1 });\n resolvedKind = entries.length > 0 ? \"web\" : \"raw\";\n } else {\n resolvedKind = \"raw\";\n }\n\n // Step 8: slug determination\n const existing = await this.store.findByOwnerAndDeclPath(ownerDid, declPath);\n let slug: string;\n\n if (parsed.data.slug) {\n slug = parsed.data.slug;\n } else if (existing) {\n slug = existing.slug; // preserve existing slug\n } else {\n slug = generateId(); // first write — generate\n }\n\n // Slug conflict check (only when the slug differs from the existing row).\n // Excludes the row by IDENTITY (ownerDid, declPath) — never decl_path alone\n // — so a sibling owner's same-decl_path row isn't wrongly excluded.\n if (!existing || existing.slug !== slug) {\n const conflicting = await this.store.findSlugConflict(slug, ownerDid, declPath);\n if (conflicting) {\n return {\n ok: false,\n code: \"share-slug-conflict\",\n message: `Slug \"${slug}\" is already claimed by ${conflicting}`,\n };\n }\n }\n\n // Build the entry we would write\n const now = Date.now();\n const id = existing?.id ?? generateId();\n const status = parsed.data.status ?? \"active\";\n\n if (dryRun) {\n // Return what would be written without touching the store\n const entry: ShareEntry = {\n id,\n ownerDid,\n sourcePath,\n declPath,\n slug,\n kind: resolvedKind,\n status,\n declCid,\n config: parsed.data,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n };\n return { ok: true, entry };\n }\n\n // Step 9: UPSERT — ON CONFLICT(owner_did, decl_path) keyed in the store so\n // it never clobbers a sibling owner's row that owns a conflicting slug.\n const row: ShareRow = {\n id,\n owner_did: ownerDid,\n source_path: sourcePath,\n decl_path: declPath,\n slug,\n kind: resolvedKind,\n status,\n decl_cid: declCid,\n config_json: JSON.stringify(parsed.data),\n created_at: existing?.createdAt ?? now,\n updated_at: now,\n };\n await this.store.upsert(row);\n\n // Read back the final row\n const final = await this.store.findByOwnerAndDeclPath(ownerDid, declPath);\n if (!final) {\n return {\n ok: false,\n code: \"share-internal-error\",\n message: \"Failed to read back the upserted share row\",\n };\n }\n\n return { ok: true, entry: final };\n }\n\n reconcileDelete(ownerDid: string, declPath: string): Promise<void> {\n return this.store.deleteByOwnerAndDeclPath(ownerDid, declPath);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ require('./mime-utils.cjs');
3
+ const require_share_declaration = require('./share-declaration.cjs');
4
+ let _aigne_afs = require("@aigne/afs");
5
+ let ufo = require("ufo");
6
+ let _aigne_afs_provider_utils = require("@aigne/afs-provider-utils");
7
+
8
+ //#region src/share-serve.ts
9
+ /**
10
+ * Phase 4.1 — serveShare: runtime-agnostic public serving of `kind:"raw"` shares.
11
+ *
12
+ * `GET /s/{slug}/{subPath}` content serving as a pure-AFS function. Both runtimes
13
+ * register a reserved `/s/` route and call this with a runtime-specific
14
+ * `buildScopedAFS` (CF: D1TreeIndex + R2; Node: the owner DIDSpace's
15
+ * SQLiteTreeIndex + FsObjectStore). Everything else — slug lookup, revoked/web
16
+ * handling, `.share` blocking, the sidecar vs directory/root split, the
17
+ * index.html / SPA fallback lookup order, mime/etag/cache headers — lives here
18
+ * so the two runtimes never fork it (design §serving 逻辑单一来源).
19
+ *
20
+ * Decoupled from any runtime binding.
21
+ *
22
+ * Security (sealed here, not per-runtime):
23
+ * - revoked === nonexistent → 404 (no existence leak).
24
+ * - `.share` / `*.share` declaration files are never served (404).
25
+ * - reads are scoped by the caller's `buildScopedAFS` to the share subtree
26
+ * (PrefixedTreeIndex with a trailing slash) — no escape to sibling paths.
27
+ * - Content-Type is ALWAYS detectMimeType(path), never trusted storage
28
+ * metadata (MIME-sniff defense).
29
+ */
30
+ /** True for fingerprinted immutable assets: `[.-][0-9a-f]{8,}.(js|css|woff2?|ttf|eot)`. */
31
+ function isImmutableAsset(path) {
32
+ return /[.-][0-9a-f]{8,}\.(js|css|woff2?|ttf|eot)$/.test(path);
33
+ }
34
+ /** Sidecar = declPath ends with ".share" but is neither bare ".share" nor a "/.share". */
35
+ function isSidecarShare(declPath) {
36
+ const p = declPath.startsWith("/") ? declPath.slice(1) : declPath;
37
+ return p !== ".share" && p.endsWith(".share") && !p.endsWith("/.share");
38
+ }
39
+ async function serveShare(opts) {
40
+ const { lookup, buildScopedAFS, slug, subPath: rawSubPath, request } = opts;
41
+ if (!slug) return new Response("Not Found", { status: 404 });
42
+ const share = await lookup.resolveBySlug(slug);
43
+ if (!share || share.status !== "active") return new Response("Not Found", { status: 404 });
44
+ if (share.kind === "web") return new Response("Share not yet rendered", {
45
+ status: 503,
46
+ headers: {
47
+ "X-Share-State": "rendering-pending",
48
+ "Retry-After": "30"
49
+ }
50
+ });
51
+ const subPath = (0, ufo.cleanDoubleSlashes)(`/${(0, ufo.withoutLeadingSlash)(rawSubPath)}`);
52
+ if (require_share_declaration.isShareDeclarationPath(subPath)) return new Response("Not Found", { status: 404 });
53
+ if (isSidecarShare(share.declPath)) return serveRaw(await buildScopedAFS({
54
+ ownerDid: share.ownerDid,
55
+ prefix: null
56
+ }), `/${share.sourcePath}`, request);
57
+ const prefix = share.sourcePath ? `${share.sourcePath}/` : null;
58
+ return serveRaw(await buildScopedAFS({
59
+ ownerDid: share.ownerDid,
60
+ prefix
61
+ }), subPath, request);
62
+ }
63
+ /**
64
+ * Serve a file from a readonly AFS. Lookup order:
65
+ * 1. subPath directly
66
+ * 2. subPath/index.html
67
+ * 3. SPA fallback: /index.html at root
68
+ * 4. 404
69
+ */
70
+ async function serveRaw(afs, subPath, request) {
71
+ const readEntry = async (path) => {
72
+ try {
73
+ const entry = (await afs.read(path)).data;
74
+ if (!entry) return null;
75
+ if (entry.meta?.kind === "did-space:directory" || entry.meta?.kind === "did-space:root") return null;
76
+ return entry;
77
+ } catch (err) {
78
+ if (err instanceof _aigne_afs.AFSNotFoundError) return null;
79
+ throw err;
80
+ }
81
+ };
82
+ try {
83
+ const entry = subPath === "/" ? null : await readEntry(subPath);
84
+ if (entry) return buildShareResponse(entry, subPath, request);
85
+ const indexPath = subPath === "/" ? "/index.html" : (0, ufo.cleanDoubleSlashes)(`${subPath}/index.html`);
86
+ const indexEntry = await readEntry(indexPath);
87
+ if (indexEntry) return buildShareResponse(indexEntry, indexPath, request);
88
+ if (subPath !== "/" && indexPath !== "/index.html") {
89
+ const spaEntry = await readEntry("/index.html");
90
+ if (spaEntry) return buildShareResponse(spaEntry, "/index.html", request);
91
+ }
92
+ return new Response("Not Found", { status: 404 });
93
+ } catch {
94
+ return new Response("Internal Server Error", { status: 500 });
95
+ }
96
+ }
97
+ /**
98
+ * Build the 200/304 Response for a read file entry.
99
+ * - ETag = WEAK validator `W/"<cid>"`; If-None-Match match → 304.
100
+ * - Cache-Control: immutable for fingerprinted assets, else max-age=300.
101
+ * - Content-Type ALWAYS from detectMimeType(path); X-Content-Type-Options:nosniff.
102
+ *
103
+ * WHY WEAK: a CDN (Cloudflare) may compress a compressible 200 body (html/js/css)
104
+ * and STRIPS a strong validator in the process — the strong ETag never reaches
105
+ * the browser, so it never revalidates and the 304 path is dead. A weak ETag
106
+ * survives compression (it only asserts semantic, not byte, equivalence), which
107
+ * is exactly right for content-addressed serving: the CID identifies the content
108
+ * regardless of transfer encoding. (Verified on staging: strong ETag was stripped
109
+ * from text/html but kept on image/png; weak survives both.)
110
+ */
111
+ function buildShareResponse(entry, targetPath, request) {
112
+ const cid = entry.meta?.cid;
113
+ const etag = cid ? `W/"${cid}"` : void 0;
114
+ const cacheControl = isImmutableAsset(targetPath) ? "public, max-age=31536000, immutable" : "public, max-age=300";
115
+ if (etag) {
116
+ const ifNoneMatch = request.headers.get("If-None-Match");
117
+ const bareTag = (t) => t.replace(/^W\//, "");
118
+ if (ifNoneMatch && bareTag(ifNoneMatch) === bareTag(etag)) return new Response(null, {
119
+ status: 304,
120
+ headers: {
121
+ ETag: etag,
122
+ "Cache-Control": cacheControl
123
+ }
124
+ });
125
+ }
126
+ const content = entry.content;
127
+ const mimeType = (0, _aigne_afs_provider_utils.detectMimeType)(targetPath) || "application/octet-stream";
128
+ const headers = new Headers();
129
+ headers.set("Content-Type", mimeType);
130
+ headers.set("Cache-Control", cacheControl);
131
+ headers.set("X-Content-Type-Options", "nosniff");
132
+ if (etag) headers.set("ETag", etag);
133
+ if (content instanceof ArrayBuffer || content instanceof Uint8Array) return new Response(content, {
134
+ status: 200,
135
+ headers
136
+ });
137
+ if (typeof content === "string") return new Response(content, {
138
+ status: 200,
139
+ headers
140
+ });
141
+ return new Response("Not Found", { status: 404 });
142
+ }
143
+
144
+ //#endregion
145
+ exports.serveShare = serveShare;
@@ -0,0 +1,30 @@
1
+ import { ShareEntry } from "./share-store.cjs";
2
+ import { AFS } from "@aigne/afs";
3
+
4
+ //#region src/share-serve.d.ts
5
+ /** Minimal slug → share lookup — satisfied by ShareResolver AND any ShareStore. */
6
+ interface ShareLookup {
7
+ resolveBySlug(slug: string): Promise<ShareEntry | null>;
8
+ }
9
+ /**
10
+ * Runtime-injected: build a READONLY AFS for `ownerDid`, scoped under `prefix`
11
+ * (with trailing slash) or — when `prefix` is null — over the owner's whole
12
+ * tree. The three-state separator contract (design H3) is handled by serveShare
13
+ * deciding the prefix; the runtime only wires the storage primitives.
14
+ */
15
+ type BuildScopedAFS = (args: {
16
+ ownerDid: string;
17
+ prefix: string | null;
18
+ }) => Promise<AFS> | AFS;
19
+ interface ServeShareOptions {
20
+ lookup: ShareLookup;
21
+ buildScopedAFS: BuildScopedAFS;
22
+ slug: string;
23
+ /** Raw subpath after `/s/{slug}` (may be "" or "/..."). */
24
+ subPath: string;
25
+ request: Request;
26
+ }
27
+ declare function serveShare(opts: ServeShareOptions): Promise<Response>;
28
+ //#endregion
29
+ export { BuildScopedAFS, ServeShareOptions, ShareLookup, serveShare };
30
+ //# sourceMappingURL=share-serve.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share-serve.d.cts","names":[],"sources":["../src/share-serve.ts"],"mappings":";;;;;UA6BiB,WAAA;EACf,aAAA,CAAc,IAAA,WAAe,OAAA,CAAQ,UAAA;AAAA;;;;AAcvC;;;KALY,cAAA,IAAkB,IAAA;EAC5B,QAAA;EACA,MAAA;AAAA,MACI,OAAA,CAAQ,GAAA,IAAO,GAAA;AAAA,UAEJ,iBAAA;EACf,MAAA,EAAQ,WAAA;EACR,cAAA,EAAgB,cAAA;EAChB,IAAA;EADgB;EAGhB,OAAA;EACA,OAAA,EAAS,OAAA;AAAA;AAAA,iBAcW,UAAA,CAAW,IAAA,EAAM,iBAAA,GAAoB,OAAA,CAAQ,QAAA"}
@@ -0,0 +1,30 @@
1
+ import { ShareEntry } from "./share-store.mjs";
2
+ import { AFS } from "@aigne/afs";
3
+
4
+ //#region src/share-serve.d.ts
5
+ /** Minimal slug → share lookup — satisfied by ShareResolver AND any ShareStore. */
6
+ interface ShareLookup {
7
+ resolveBySlug(slug: string): Promise<ShareEntry | null>;
8
+ }
9
+ /**
10
+ * Runtime-injected: build a READONLY AFS for `ownerDid`, scoped under `prefix`
11
+ * (with trailing slash) or — when `prefix` is null — over the owner's whole
12
+ * tree. The three-state separator contract (design H3) is handled by serveShare
13
+ * deciding the prefix; the runtime only wires the storage primitives.
14
+ */
15
+ type BuildScopedAFS = (args: {
16
+ ownerDid: string;
17
+ prefix: string | null;
18
+ }) => Promise<AFS> | AFS;
19
+ interface ServeShareOptions {
20
+ lookup: ShareLookup;
21
+ buildScopedAFS: BuildScopedAFS;
22
+ slug: string;
23
+ /** Raw subpath after `/s/{slug}` (may be "" or "/..."). */
24
+ subPath: string;
25
+ request: Request;
26
+ }
27
+ declare function serveShare(opts: ServeShareOptions): Promise<Response>;
28
+ //#endregion
29
+ export { BuildScopedAFS, ServeShareOptions, ShareLookup, serveShare };
30
+ //# sourceMappingURL=share-serve.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share-serve.d.mts","names":[],"sources":["../src/share-serve.ts"],"mappings":";;;;;UA6BiB,WAAA;EACf,aAAA,CAAc,IAAA,WAAe,OAAA,CAAQ,UAAA;AAAA;;;;AAcvC;;;KALY,cAAA,IAAkB,IAAA;EAC5B,QAAA;EACA,MAAA;AAAA,MACI,OAAA,CAAQ,GAAA,IAAO,GAAA;AAAA,UAEJ,iBAAA;EACf,MAAA,EAAQ,WAAA;EACR,cAAA,EAAgB,cAAA;EAChB,IAAA;EADgB;EAGhB,OAAA;EACA,OAAA,EAAS,OAAA;AAAA;AAAA,iBAcW,UAAA,CAAW,IAAA,EAAM,iBAAA,GAAoB,OAAA,CAAQ,QAAA"}
@@ -0,0 +1,144 @@
1
+ import { detectMimeType } from "./mime-utils.mjs";
2
+ import { isShareDeclarationPath } from "./share-declaration.mjs";
3
+ import { AFSNotFoundError } from "@aigne/afs";
4
+ import { cleanDoubleSlashes, withoutLeadingSlash } from "ufo";
5
+
6
+ //#region src/share-serve.ts
7
+ /**
8
+ * Phase 4.1 — serveShare: runtime-agnostic public serving of `kind:"raw"` shares.
9
+ *
10
+ * `GET /s/{slug}/{subPath}` content serving as a pure-AFS function. Both runtimes
11
+ * register a reserved `/s/` route and call this with a runtime-specific
12
+ * `buildScopedAFS` (CF: D1TreeIndex + R2; Node: the owner DIDSpace's
13
+ * SQLiteTreeIndex + FsObjectStore). Everything else — slug lookup, revoked/web
14
+ * handling, `.share` blocking, the sidecar vs directory/root split, the
15
+ * index.html / SPA fallback lookup order, mime/etag/cache headers — lives here
16
+ * so the two runtimes never fork it (design §serving 逻辑单一来源).
17
+ *
18
+ * Decoupled from any runtime binding.
19
+ *
20
+ * Security (sealed here, not per-runtime):
21
+ * - revoked === nonexistent → 404 (no existence leak).
22
+ * - `.share` / `*.share` declaration files are never served (404).
23
+ * - reads are scoped by the caller's `buildScopedAFS` to the share subtree
24
+ * (PrefixedTreeIndex with a trailing slash) — no escape to sibling paths.
25
+ * - Content-Type is ALWAYS detectMimeType(path), never trusted storage
26
+ * metadata (MIME-sniff defense).
27
+ */
28
+ /** True for fingerprinted immutable assets: `[.-][0-9a-f]{8,}.(js|css|woff2?|ttf|eot)`. */
29
+ function isImmutableAsset(path) {
30
+ return /[.-][0-9a-f]{8,}\.(js|css|woff2?|ttf|eot)$/.test(path);
31
+ }
32
+ /** Sidecar = declPath ends with ".share" but is neither bare ".share" nor a "/.share". */
33
+ function isSidecarShare(declPath) {
34
+ const p = declPath.startsWith("/") ? declPath.slice(1) : declPath;
35
+ return p !== ".share" && p.endsWith(".share") && !p.endsWith("/.share");
36
+ }
37
+ async function serveShare(opts) {
38
+ const { lookup, buildScopedAFS, slug, subPath: rawSubPath, request } = opts;
39
+ if (!slug) return new Response("Not Found", { status: 404 });
40
+ const share = await lookup.resolveBySlug(slug);
41
+ if (!share || share.status !== "active") return new Response("Not Found", { status: 404 });
42
+ if (share.kind === "web") return new Response("Share not yet rendered", {
43
+ status: 503,
44
+ headers: {
45
+ "X-Share-State": "rendering-pending",
46
+ "Retry-After": "30"
47
+ }
48
+ });
49
+ const subPath = cleanDoubleSlashes(`/${withoutLeadingSlash(rawSubPath)}`);
50
+ if (isShareDeclarationPath(subPath)) return new Response("Not Found", { status: 404 });
51
+ if (isSidecarShare(share.declPath)) return serveRaw(await buildScopedAFS({
52
+ ownerDid: share.ownerDid,
53
+ prefix: null
54
+ }), `/${share.sourcePath}`, request);
55
+ const prefix = share.sourcePath ? `${share.sourcePath}/` : null;
56
+ return serveRaw(await buildScopedAFS({
57
+ ownerDid: share.ownerDid,
58
+ prefix
59
+ }), subPath, request);
60
+ }
61
+ /**
62
+ * Serve a file from a readonly AFS. Lookup order:
63
+ * 1. subPath directly
64
+ * 2. subPath/index.html
65
+ * 3. SPA fallback: /index.html at root
66
+ * 4. 404
67
+ */
68
+ async function serveRaw(afs, subPath, request) {
69
+ const readEntry = async (path) => {
70
+ try {
71
+ const entry = (await afs.read(path)).data;
72
+ if (!entry) return null;
73
+ if (entry.meta?.kind === "did-space:directory" || entry.meta?.kind === "did-space:root") return null;
74
+ return entry;
75
+ } catch (err) {
76
+ if (err instanceof AFSNotFoundError) return null;
77
+ throw err;
78
+ }
79
+ };
80
+ try {
81
+ const entry = subPath === "/" ? null : await readEntry(subPath);
82
+ if (entry) return buildShareResponse(entry, subPath, request);
83
+ const indexPath = subPath === "/" ? "/index.html" : cleanDoubleSlashes(`${subPath}/index.html`);
84
+ const indexEntry = await readEntry(indexPath);
85
+ if (indexEntry) return buildShareResponse(indexEntry, indexPath, request);
86
+ if (subPath !== "/" && indexPath !== "/index.html") {
87
+ const spaEntry = await readEntry("/index.html");
88
+ if (spaEntry) return buildShareResponse(spaEntry, "/index.html", request);
89
+ }
90
+ return new Response("Not Found", { status: 404 });
91
+ } catch {
92
+ return new Response("Internal Server Error", { status: 500 });
93
+ }
94
+ }
95
+ /**
96
+ * Build the 200/304 Response for a read file entry.
97
+ * - ETag = WEAK validator `W/"<cid>"`; If-None-Match match → 304.
98
+ * - Cache-Control: immutable for fingerprinted assets, else max-age=300.
99
+ * - Content-Type ALWAYS from detectMimeType(path); X-Content-Type-Options:nosniff.
100
+ *
101
+ * WHY WEAK: a CDN (Cloudflare) may compress a compressible 200 body (html/js/css)
102
+ * and STRIPS a strong validator in the process — the strong ETag never reaches
103
+ * the browser, so it never revalidates and the 304 path is dead. A weak ETag
104
+ * survives compression (it only asserts semantic, not byte, equivalence), which
105
+ * is exactly right for content-addressed serving: the CID identifies the content
106
+ * regardless of transfer encoding. (Verified on staging: strong ETag was stripped
107
+ * from text/html but kept on image/png; weak survives both.)
108
+ */
109
+ function buildShareResponse(entry, targetPath, request) {
110
+ const cid = entry.meta?.cid;
111
+ const etag = cid ? `W/"${cid}"` : void 0;
112
+ const cacheControl = isImmutableAsset(targetPath) ? "public, max-age=31536000, immutable" : "public, max-age=300";
113
+ if (etag) {
114
+ const ifNoneMatch = request.headers.get("If-None-Match");
115
+ const bareTag = (t) => t.replace(/^W\//, "");
116
+ if (ifNoneMatch && bareTag(ifNoneMatch) === bareTag(etag)) return new Response(null, {
117
+ status: 304,
118
+ headers: {
119
+ ETag: etag,
120
+ "Cache-Control": cacheControl
121
+ }
122
+ });
123
+ }
124
+ const content = entry.content;
125
+ const mimeType = detectMimeType(targetPath) || "application/octet-stream";
126
+ const headers = new Headers();
127
+ headers.set("Content-Type", mimeType);
128
+ headers.set("Cache-Control", cacheControl);
129
+ headers.set("X-Content-Type-Options", "nosniff");
130
+ if (etag) headers.set("ETag", etag);
131
+ if (content instanceof ArrayBuffer || content instanceof Uint8Array) return new Response(content, {
132
+ status: 200,
133
+ headers
134
+ });
135
+ if (typeof content === "string") return new Response(content, {
136
+ status: 200,
137
+ headers
138
+ });
139
+ return new Response("Not Found", { status: 404 });
140
+ }
141
+
142
+ //#endregion
143
+ export { serveShare };
144
+ //# sourceMappingURL=share-serve.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share-serve.mjs","names":[],"sources":["../src/share-serve.ts"],"sourcesContent":["/**\n * Phase 4.1 — serveShare: runtime-agnostic public serving of `kind:\"raw\"` shares.\n *\n * `GET /s/{slug}/{subPath}` content serving as a pure-AFS function. Both runtimes\n * register a reserved `/s/` route and call this with a runtime-specific\n * `buildScopedAFS` (CF: D1TreeIndex + R2; Node: the owner DIDSpace's\n * SQLiteTreeIndex + FsObjectStore). Everything else — slug lookup, revoked/web\n * handling, `.share` blocking, the sidecar vs directory/root split, the\n * index.html / SPA fallback lookup order, mime/etag/cache headers — lives here\n * so the two runtimes never fork it (design §serving 逻辑单一来源).\n *\n * Decoupled from any runtime binding.\n *\n * Security (sealed here, not per-runtime):\n * - revoked === nonexistent → 404 (no existence leak).\n * - `.share` / `*.share` declaration files are never served (404).\n * - reads are scoped by the caller's `buildScopedAFS` to the share subtree\n * (PrefixedTreeIndex with a trailing slash) — no escape to sibling paths.\n * - Content-Type is ALWAYS detectMimeType(path), never trusted storage\n * metadata (MIME-sniff defense).\n */\n\nimport { type AFS, type AFSEntry, AFSNotFoundError } from \"@aigne/afs\";\nimport { cleanDoubleSlashes, withoutLeadingSlash } from \"ufo\";\nimport { detectMimeType } from \"./mime-utils.js\";\nimport { isShareDeclarationPath } from \"./share-declaration.js\";\nimport type { ShareEntry } from \"./share-store.js\";\n\n/** Minimal slug → share lookup — satisfied by ShareResolver AND any ShareStore. */\nexport interface ShareLookup {\n resolveBySlug(slug: string): Promise<ShareEntry | null>;\n}\n\n/**\n * Runtime-injected: build a READONLY AFS for `ownerDid`, scoped under `prefix`\n * (with trailing slash) or — when `prefix` is null — over the owner's whole\n * tree. The three-state separator contract (design H3) is handled by serveShare\n * deciding the prefix; the runtime only wires the storage primitives.\n */\nexport type BuildScopedAFS = (args: {\n ownerDid: string;\n prefix: string | null;\n}) => Promise<AFS> | AFS;\n\nexport interface ServeShareOptions {\n lookup: ShareLookup;\n buildScopedAFS: BuildScopedAFS;\n slug: string;\n /** Raw subpath after `/s/{slug}` (may be \"\" or \"/...\"). */\n subPath: string;\n request: Request;\n}\n\n/** True for fingerprinted immutable assets: `[.-][0-9a-f]{8,}.(js|css|woff2?|ttf|eot)`. */\nfunction isImmutableAsset(path: string): boolean {\n return /[.-][0-9a-f]{8,}\\.(js|css|woff2?|ttf|eot)$/.test(path);\n}\n\n/** Sidecar = declPath ends with \".share\" but is neither bare \".share\" nor a \"/.share\". */\nfunction isSidecarShare(declPath: string): boolean {\n const p = declPath.startsWith(\"/\") ? declPath.slice(1) : declPath;\n return p !== \".share\" && p.endsWith(\".share\") && !p.endsWith(\"/.share\");\n}\n\nexport async function serveShare(opts: ServeShareOptions): Promise<Response> {\n const { lookup, buildScopedAFS, slug, subPath: rawSubPath, request } = opts;\n\n if (!slug) return new Response(\"Not Found\", { status: 404 });\n\n const share = await lookup.resolveBySlug(slug);\n // Revoked === nonexistent — no existence leakage.\n if (!share || share.status !== \"active\") {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n if (share.kind === \"web\") {\n // kind:web rendering pipeline is a separate project — stub for now.\n return new Response(\"Share not yet rendered\", {\n status: 503,\n headers: { \"X-Share-State\": \"rendering-pending\", \"Retry-After\": \"30\" },\n });\n }\n\n const subPath = cleanDoubleSlashes(`/${withoutLeadingSlash(rawSubPath)}`);\n\n // Block \"/.share\", \"dir/.share\", and \"file.pdf.share\" from ever being served.\n if (isShareDeclarationPath(subPath)) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n // Three-state (design H3):\n // sidecar → whole-owner tree (prefix null), serve sourcePath, ignore subPath\n // directory → PrefixedTreeIndex(base, `${sourcePath}/`), serve subPath\n // root (src=\"\") → whole-owner tree (prefix null), serve subPath\n if (isSidecarShare(share.declPath)) {\n const afs = await buildScopedAFS({ ownerDid: share.ownerDid, prefix: null });\n return serveRaw(afs, `/${share.sourcePath}`, request);\n }\n\n const prefix = share.sourcePath ? `${share.sourcePath}/` : null;\n const afs = await buildScopedAFS({ ownerDid: share.ownerDid, prefix });\n return serveRaw(afs, subPath, request);\n}\n\n/**\n * Serve a file from a readonly AFS. Lookup order:\n * 1. subPath directly\n * 2. subPath/index.html\n * 3. SPA fallback: /index.html at root\n * 4. 404\n */\nasync function serveRaw(afs: AFS, subPath: string, request: Request): Promise<Response> {\n const readEntry = async (path: string): Promise<AFSEntry | null> => {\n try {\n const result = await afs.read(path);\n const entry = result.data;\n if (!entry) return null;\n // Directories return descriptive string content — not a file to serve.\n if (entry.meta?.kind === \"did-space:directory\" || entry.meta?.kind === \"did-space:root\") {\n return null;\n }\n return entry;\n } catch (err) {\n if (err instanceof AFSNotFoundError) return null;\n throw err;\n }\n };\n\n try {\n // `subPath === \"/\"` is always the share's directory ROOT — a read there can\n // only ever return the root/directory descriptor (never a servable file),\n // so skip that round-trip (a wasted D1 COUNT in the did-space provider) and\n // go straight to index.html.\n const entry = subPath === \"/\" ? null : await readEntry(subPath);\n if (entry) return buildShareResponse(entry, subPath, request);\n\n const indexPath = subPath === \"/\" ? \"/index.html\" : cleanDoubleSlashes(`${subPath}/index.html`);\n const indexEntry = await readEntry(indexPath);\n if (indexEntry) return buildShareResponse(indexEntry, indexPath, request);\n\n if (subPath !== \"/\" && indexPath !== \"/index.html\") {\n const spaEntry = await readEntry(\"/index.html\");\n if (spaEntry) return buildShareResponse(spaEntry, \"/index.html\", request);\n }\n\n return new Response(\"Not Found\", { status: 404 });\n } catch {\n return new Response(\"Internal Server Error\", { status: 500 });\n }\n}\n\n/**\n * Build the 200/304 Response for a read file entry.\n * - ETag = WEAK validator `W/\"<cid>\"`; If-None-Match match → 304.\n * - Cache-Control: immutable for fingerprinted assets, else max-age=300.\n * - Content-Type ALWAYS from detectMimeType(path); X-Content-Type-Options:nosniff.\n *\n * WHY WEAK: a CDN (Cloudflare) may compress a compressible 200 body (html/js/css)\n * and STRIPS a strong validator in the process — the strong ETag never reaches\n * the browser, so it never revalidates and the 304 path is dead. A weak ETag\n * survives compression (it only asserts semantic, not byte, equivalence), which\n * is exactly right for content-addressed serving: the CID identifies the content\n * regardless of transfer encoding. (Verified on staging: strong ETag was stripped\n * from text/html but kept on image/png; weak survives both.)\n */\nfunction buildShareResponse(entry: AFSEntry, targetPath: string, request: Request): Response {\n const cid = entry.meta?.cid as string | undefined;\n const etag = cid ? `W/\"${cid}\"` : undefined;\n const cacheControl = isImmutableAsset(targetPath)\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=300\";\n\n if (etag) {\n // RFC 7232 §3.2: If-None-Match uses the WEAK comparison function, so match\n // on the opaque-tag value regardless of the `W/` prefix on either side (a\n // client that cached a strong form, or echoes the weak form, both hit).\n const ifNoneMatch = request.headers.get(\"If-None-Match\");\n const bareTag = (t: string) => t.replace(/^W\\//, \"\");\n if (ifNoneMatch && bareTag(ifNoneMatch) === bareTag(etag)) {\n return new Response(null, {\n status: 304,\n headers: { ETag: etag, \"Cache-Control\": cacheControl },\n });\n }\n }\n\n const content = entry.content;\n // detectMimeType(path) wins over stored metadata (MIME-sniff defense).\n const mimeType = detectMimeType(targetPath) || \"application/octet-stream\";\n\n const headers = new Headers();\n headers.set(\"Content-Type\", mimeType);\n headers.set(\"Cache-Control\", cacheControl);\n headers.set(\"X-Content-Type-Options\", \"nosniff\");\n if (etag) headers.set(\"ETag\", etag);\n\n if (content instanceof ArrayBuffer || content instanceof Uint8Array) {\n // Uint8Array<ArrayBufferLike> isn't structurally BodyInit under every lib\n // target; the runtime accepts it (it's a valid body) so cast at the seam.\n return new Response(content as unknown as BodyInit, { status: 200, headers });\n }\n if (typeof content === \"string\") {\n return new Response(content, { status: 200, headers });\n }\n return new Response(\"Not Found\", { status: 404 });\n}\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+
2
+ //#region src/share-store.ts
3
+ /** Map a raw storage row to a public ShareEntry (malformed config_json → {}). */
4
+ function rowToEntry(row) {
5
+ let config = {};
6
+ try {
7
+ config = JSON.parse(row.config_json);
8
+ } catch {}
9
+ return {
10
+ id: row.id,
11
+ ownerDid: row.owner_did,
12
+ sourcePath: row.source_path,
13
+ declPath: row.decl_path,
14
+ slug: row.slug,
15
+ kind: row.kind,
16
+ status: row.status,
17
+ declCid: row.decl_cid ?? void 0,
18
+ config,
19
+ createdAt: row.created_at,
20
+ updatedAt: row.updated_at
21
+ };
22
+ }
23
+
24
+ //#endregion
25
+ exports.rowToEntry = rowToEntry;