@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,103 @@
1
+ import { computeCID } from "./cid.mjs";
2
+ import { DEFAULT_INLINE_THRESHOLD, INGEST_DEVICE_ID, assertInlineThreshold, utf8ByteLength } from "./inline-content.mjs";
3
+
4
+ //#region src/versioned-content-index.ts
5
+ /**
6
+ * VersionedContentIndex — a PURE DERIVED INDEX over a TreeIndex (Layer 1
7
+ * machinery; contract in `inline-content.ts` clause 2b + `versioned-scope.ts`).
8
+ *
9
+ * NOT a CAS store: there is no object backend; the inline `content` column IS
10
+ * the primary data and the cid is merely a checksum. The authority is the
11
+ * ingested source (a package's immutable, per-version content) — rebuilding
12
+ * from source is always possible, so:
13
+ *
14
+ * - writes NEVER touch the changelog (`skipChangelog: true`, enforced HERE,
15
+ * not at call sites — a derived index has nothing to sync);
16
+ * - `deviceId` is the fixed sentinel {@link INGEST_DEVICE_ID};
17
+ * - content is REQUIRED on every write, and content over the threshold is
18
+ * a HARD ERROR (there is no blob to fall back to);
19
+ * - the wrapped index MUST be constructed with `inlineCache: "on"` and the
20
+ * same threshold — the first write VERIFIES the content actually landed
21
+ * inline and fails loudly on a misconfigured store.
22
+ *
23
+ * Kind-agnostic: this class never interprets what it stores. Consumers pick a
24
+ * scope key via `versionedScope(kind, packageId, versionToken)` and bind the
25
+ * wrapped TreeIndex to that scope; ingest/stale/cleanup live one level up.
26
+ */
27
+ var VersionedContentIndex = class {
28
+ index;
29
+ inlineThreshold;
30
+ /** First successful write verifies the wrapped index actually inlines. */
31
+ verifiedInline = false;
32
+ constructor(opts) {
33
+ this.index = opts.index;
34
+ this.inlineThreshold = opts.inlineThreshold ?? DEFAULT_INLINE_THRESHOLD;
35
+ assertInlineThreshold(this.inlineThreshold);
36
+ }
37
+ /** Write one file (idempotent upsert by path). */
38
+ async put(file, mtime = 0) {
39
+ await this.putBatch([file], mtime);
40
+ }
41
+ /**
42
+ * Write a batch of files (idempotent upsert by path). Enforces the derived-
43
+ * index invariants: content required, over-threshold hard error, changelog
44
+ * skipped, ingest device sentinel.
45
+ */
46
+ async putBatch(files, mtime = 0) {
47
+ if (files.length === 0) return;
48
+ const entries = [];
49
+ for (const file of files) {
50
+ if (typeof file.content !== "string") throw new Error(`VersionedContentIndex: content is required for "${file.path}"`);
51
+ if (file.content.includes("\0")) throw new Error(`VersionedContentIndex: "${file.path}" contains an embedded NUL — not storable as derived text content`);
52
+ const bytes = utf8ByteLength(file.content);
53
+ if (bytes > this.inlineThreshold) throw new Error(`VersionedContentIndex: "${file.path}" is ${bytes} bytes — over the ${this.inlineThreshold}-byte inline threshold (a derived index has no blob fallback; raise the threshold or shrink the source)`);
54
+ const cid = await computeCID(file.content);
55
+ entries.push({
56
+ path: file.path,
57
+ entry: {
58
+ cid,
59
+ size: bytes,
60
+ mtime,
61
+ ctime: mtime,
62
+ mimeType: file.mimeType,
63
+ content: file.content
64
+ }
65
+ });
66
+ }
67
+ await this.index.putBatch(entries, {
68
+ deviceId: INGEST_DEVICE_ID,
69
+ skipChangelog: true
70
+ });
71
+ if (!this.verifiedInline) {
72
+ const probe = entries[0];
73
+ if ((await this.index.get(probe.path))?.content !== probe.entry.content) throw new Error("VersionedContentIndex: wrapped index did not store inline content — construct it with inlineCache: \"on\" and a matching threshold");
74
+ this.verifiedInline = true;
75
+ }
76
+ }
77
+ /** Read one file's content (null when absent). */
78
+ async get(path) {
79
+ return (await this.index.get(path))?.content ?? null;
80
+ }
81
+ /** Bulk read — one query, content included. */
82
+ async listEntries(prefix = "", limit = 1e3) {
83
+ return this.index.listEntries(prefix, {
84
+ includeContent: true,
85
+ limit
86
+ });
87
+ }
88
+ /** Does this scope hold any content? */
89
+ async hasContent() {
90
+ return this.index.hasChildren("");
91
+ }
92
+ /** Drop every row in the scope (version cleanup = one DELETE). */
93
+ async deleteAll() {
94
+ return this.index.deleteByPrefix("", {
95
+ deviceId: INGEST_DEVICE_ID,
96
+ skipChangelog: true
97
+ });
98
+ }
99
+ };
100
+
101
+ //#endregion
102
+ export { VersionedContentIndex };
103
+ //# sourceMappingURL=versioned-content-index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versioned-content-index.mjs","names":[],"sources":["../src/versioned-content-index.ts"],"sourcesContent":["/**\n * VersionedContentIndex — a PURE DERIVED INDEX over a TreeIndex (Layer 1\n * machinery; contract in `inline-content.ts` clause 2b + `versioned-scope.ts`).\n *\n * NOT a CAS store: there is no object backend; the inline `content` column IS\n * the primary data and the cid is merely a checksum. The authority is the\n * ingested source (a package's immutable, per-version content) — rebuilding\n * from source is always possible, so:\n *\n * - writes NEVER touch the changelog (`skipChangelog: true`, enforced HERE,\n * not at call sites — a derived index has nothing to sync);\n * - `deviceId` is the fixed sentinel {@link INGEST_DEVICE_ID};\n * - content is REQUIRED on every write, and content over the threshold is\n * a HARD ERROR (there is no blob to fall back to);\n * - the wrapped index MUST be constructed with `inlineCache: \"on\"` and the\n * same threshold — the first write VERIFIES the content actually landed\n * inline and fails loudly on a misconfigured store.\n *\n * Kind-agnostic: this class never interprets what it stores. Consumers pick a\n * scope key via `versionedScope(kind, packageId, versionToken)` and bind the\n * wrapped TreeIndex to that scope; ingest/stale/cleanup live one level up.\n */\n\nimport { computeCID } from \"./cid.js\";\nimport {\n assertInlineThreshold,\n DEFAULT_INLINE_THRESHOLD,\n INGEST_DEVICE_ID,\n utf8ByteLength,\n} from \"./inline-content.js\";\nimport type { TreeEntry, TreeIndex, TreeIndexListEntriesResult } from \"./tree-index.js\";\n\nexport interface VersionedContentFile {\n /** Index-relative path (e.g. `general/title.json`). */\n path: string;\n /** Primary data — required (this store has no other copy). */\n content: string;\n mimeType?: string;\n}\n\nexport interface VersionedContentIndexOptions {\n /**\n * Scope-bound TreeIndex over the shared store. MUST be constructed with\n * `inlineCache: \"on\"` and a threshold ≥ this index's threshold (verified on\n * first write).\n */\n index: TreeIndex;\n /** Max bytes per file — over it is a HARD ERROR. Default 4KB. */\n inlineThreshold?: number;\n}\n\nexport class VersionedContentIndex {\n private readonly index: TreeIndex;\n private readonly inlineThreshold: number;\n /** First successful write verifies the wrapped index actually inlines. */\n private verifiedInline = false;\n\n constructor(opts: VersionedContentIndexOptions) {\n this.index = opts.index;\n this.inlineThreshold = opts.inlineThreshold ?? DEFAULT_INLINE_THRESHOLD;\n assertInlineThreshold(this.inlineThreshold);\n }\n\n /** Write one file (idempotent upsert by path). */\n async put(file: VersionedContentFile, mtime = 0): Promise<void> {\n await this.putBatch([file], mtime);\n }\n\n /**\n * Write a batch of files (idempotent upsert by path). Enforces the derived-\n * index invariants: content required, over-threshold hard error, changelog\n * skipped, ingest device sentinel.\n */\n async putBatch(files: readonly VersionedContentFile[], mtime = 0): Promise<void> {\n if (files.length === 0) return;\n const entries: Array<{ path: string; entry: TreeEntry }> = [];\n for (const file of files) {\n if (typeof file.content !== \"string\") {\n throw new Error(`VersionedContentIndex: content is required for \"${file.path}\"`);\n }\n if (file.content.includes(\"\\0\")) {\n // No blob fallback exists here — and TEXT storage cannot hold an\n // embedded NUL faithfully (see inline-content.ts). Sources are\n // structured text (JSON/YAML), which never contains a raw NUL.\n throw new Error(\n `VersionedContentIndex: \"${file.path}\" contains an embedded NUL — not storable as derived text content`,\n );\n }\n const bytes = utf8ByteLength(file.content);\n if (bytes > this.inlineThreshold) {\n throw new Error(\n `VersionedContentIndex: \"${file.path}\" is ${bytes} bytes — over the ${this.inlineThreshold}-byte inline threshold (a derived index has no blob fallback; raise the threshold or shrink the source)`,\n );\n }\n const cid = await computeCID(file.content);\n entries.push({\n path: file.path,\n entry: {\n cid,\n size: bytes,\n mtime,\n ctime: mtime,\n mimeType: file.mimeType,\n content: file.content,\n },\n });\n }\n await this.index.putBatch(entries, { deviceId: INGEST_DEVICE_ID, skipChangelog: true });\n\n if (!this.verifiedInline) {\n // Loud-failure guard: a wrapped index without `inlineCache: \"on\"` would\n // silently drop the primary data. Verify the first write round-trips.\n const probe = entries[0]!;\n const back = await this.index.get(probe.path);\n if (back?.content !== probe.entry.content) {\n throw new Error(\n 'VersionedContentIndex: wrapped index did not store inline content — construct it with inlineCache: \"on\" and a matching threshold',\n );\n }\n this.verifiedInline = true;\n }\n }\n\n /** Read one file's content (null when absent). */\n async get(path: string): Promise<string | null> {\n const entry = await this.index.get(path);\n return entry?.content ?? null;\n }\n\n /** Bulk read — one query, content included. */\n async listEntries(prefix = \"\", limit = 1000): Promise<TreeIndexListEntriesResult> {\n return this.index.listEntries(prefix, { includeContent: true, limit });\n }\n\n /** Does this scope hold any content? */\n async hasContent(): Promise<boolean> {\n return this.index.hasChildren(\"\");\n }\n\n /** Drop every row in the scope (version cleanup = one DELETE). */\n async deleteAll(): Promise<number> {\n return this.index.deleteByPrefix(\"\", { deviceId: INGEST_DEVICE_ID, skipChangelog: true });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,wBAAb,MAAmC;CACjC,AAAiB;CACjB,AAAiB;;CAEjB,AAAQ,iBAAiB;CAEzB,YAAY,MAAoC;AAC9C,OAAK,QAAQ,KAAK;AAClB,OAAK,kBAAkB,KAAK,mBAAmB;AAC/C,wBAAsB,KAAK,gBAAgB;;;CAI7C,MAAM,IAAI,MAA4B,QAAQ,GAAkB;AAC9D,QAAM,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM;;;;;;;CAQpC,MAAM,SAAS,OAAwC,QAAQ,GAAkB;AAC/E,MAAI,MAAM,WAAW,EAAG;EACxB,MAAM,UAAqD,EAAE;AAC7D,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,OAAO,KAAK,YAAY,SAC1B,OAAM,IAAI,MAAM,mDAAmD,KAAK,KAAK,GAAG;AAElF,OAAI,KAAK,QAAQ,SAAS,KAAK,CAI7B,OAAM,IAAI,MACR,2BAA2B,KAAK,KAAK,mEACtC;GAEH,MAAM,QAAQ,eAAe,KAAK,QAAQ;AAC1C,OAAI,QAAQ,KAAK,gBACf,OAAM,IAAI,MACR,2BAA2B,KAAK,KAAK,OAAO,MAAM,oBAAoB,KAAK,gBAAgB,yGAC5F;GAEH,MAAM,MAAM,MAAM,WAAW,KAAK,QAAQ;AAC1C,WAAQ,KAAK;IACX,MAAM,KAAK;IACX,OAAO;KACL;KACA,MAAM;KACN;KACA,OAAO;KACP,UAAU,KAAK;KACf,SAAS,KAAK;KACf;IACF,CAAC;;AAEJ,QAAM,KAAK,MAAM,SAAS,SAAS;GAAE,UAAU;GAAkB,eAAe;GAAM,CAAC;AAEvF,MAAI,CAAC,KAAK,gBAAgB;GAGxB,MAAM,QAAQ,QAAQ;AAEtB,QADa,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,GACnC,YAAY,MAAM,MAAM,QAChC,OAAM,IAAI,MACR,qIACD;AAEH,QAAK,iBAAiB;;;;CAK1B,MAAM,IAAI,MAAsC;AAE9C,UADc,MAAM,KAAK,MAAM,IAAI,KAAK,GAC1B,WAAW;;;CAI3B,MAAM,YAAY,SAAS,IAAI,QAAQ,KAA2C;AAChF,SAAO,KAAK,MAAM,YAAY,QAAQ;GAAE,gBAAgB;GAAM;GAAO,CAAC;;;CAIxE,MAAM,aAA+B;AACnC,SAAO,KAAK,MAAM,YAAY,GAAG;;;CAInC,MAAM,YAA6B;AACjC,SAAO,KAAK,MAAM,eAAe,IAAI;GAAE,UAAU;GAAkB,eAAe;GAAM,CAAC"}
@@ -0,0 +1,121 @@
1
+ const require_inline_content = require('./inline-content.cjs');
2
+ const require_versioned_content_index = require('./versioned-content-index.cjs');
3
+
4
+ //#region src/versioned-content-store.ts
5
+ /**
6
+ * VersionedContentStore — multi-scope manager over {@link VersionedContentIndex}
7
+ * (Layer 1 machinery: versioned package-content materialization).
8
+ *
9
+ * One store instance manages MANY scopes (`<kind>:<packageId>:<versionToken>`,
10
+ * see `versioned-scope.ts`) inside ONE shared database — Node: a daemon-level
11
+ * SQLite file; CF: the shared D1 binding; tests: in-memory. The machinery is
12
+ * KIND-AGNOSTIC: scope and group are opaque keys; consumers map their source
13
+ * content (via AFS) into `VersionedContentFile[]` and never touch the DB.
14
+ *
15
+ * Ingest contract (idempotent, lazily triggered):
16
+ * - `ensure(scope)` is a HIT when the scope's metadata row exists (and, when
17
+ * digest verification is requested, the stored digest matches) — zero
18
+ * source I/O on the hot path.
19
+ * - a MISS loads the source files, upserts every row, THEN writes the
20
+ * metadata row — the metadata row is the completion marker, so a crash
21
+ * mid-ingest leaves no readable half-scope (next access rebuilds).
22
+ * - concurrent first-miss within one process is memoized (single build);
23
+ * cross-process double builds are harmless (pure upserts + idempotent
24
+ * marker).
25
+ * - building a scope DELETES sibling scopes of the same `group` (an opaque
26
+ * `<kind>:<packageId>` prefix key) — version cleanup is one DELETE per
27
+ * stale scope, no blob GC involved (content is inline, rows ARE the data).
28
+ * - `seedDigest` lives in the metadata row, NEVER in the key — dev-mode
29
+ * stale detection compares digests; prod looks up by exact key only.
30
+ */
31
+ var VersionedContentStore = class {
32
+ backend;
33
+ inlineThreshold;
34
+ log;
35
+ /** In-process single-flight per scope (concurrent first-miss → one build). */
36
+ inflight = /* @__PURE__ */ new Map();
37
+ constructor(opts) {
38
+ this.backend = opts.backend;
39
+ this.inlineThreshold = opts.inlineThreshold ?? require_inline_content.DEFAULT_INLINE_THRESHOLD;
40
+ require_inline_content.assertInlineThreshold(this.inlineThreshold);
41
+ this.log = opts.log ?? (() => {});
42
+ }
43
+ /** Materialize a scope if needed. See {@link EnsureScopeOptions}. */
44
+ async ensure(opts) {
45
+ const existing = this.inflight.get(opts.scope);
46
+ if (existing) return existing;
47
+ const run = this.ensureInner(opts).finally(() => {
48
+ this.inflight.delete(opts.scope);
49
+ });
50
+ this.inflight.set(opts.scope, run);
51
+ return run;
52
+ }
53
+ async ensureInner(opts) {
54
+ if (!opts.force) {
55
+ const meta = await this.backend.getScopeMeta(opts.scope);
56
+ if (meta) {
57
+ if (!(opts.verifyDigest && opts.seedDigest !== void 0 && meta.seedDigest !== opts.seedDigest)) return {
58
+ built: false,
59
+ fileCount: meta.fileCount
60
+ };
61
+ this.log(`[versioned-content] stale digest for ${opts.scope} (${meta.seedDigest} → ${opts.seedDigest}) — rebuilding`);
62
+ }
63
+ }
64
+ return this.build(opts);
65
+ }
66
+ async build(opts) {
67
+ const loaded = await opts.load();
68
+ const files = Array.isArray(loaded) ? loaded : loaded.files;
69
+ const loadedDigest = Array.isArray(loaded) ? void 0 : loaded.seedDigest;
70
+ const index = new require_versioned_content_index.VersionedContentIndex({
71
+ index: this.backend.makeIndex(opts.scope),
72
+ inlineThreshold: this.inlineThreshold
73
+ });
74
+ await index.deleteAll();
75
+ if (files.length > 0) await index.putBatch(files);
76
+ await this.backend.putScopeMeta({
77
+ scope: opts.scope,
78
+ scopeGroup: opts.group,
79
+ seedDigest: opts.seedDigest ?? loadedDigest,
80
+ ingestedAt: Date.now(),
81
+ fileCount: files.length
82
+ });
83
+ this.log(`[versioned-content] built scope ${opts.scope} (${files.length} files)`);
84
+ const siblings = await this.backend.listScopeMetaByGroup(opts.group);
85
+ for (const sibling of siblings) {
86
+ if (sibling.scope === opts.scope) continue;
87
+ await this.deleteScope(sibling.scope);
88
+ this.log(`[versioned-content] deleted superseded scope ${sibling.scope}`);
89
+ }
90
+ return {
91
+ built: true,
92
+ fileCount: files.length
93
+ };
94
+ }
95
+ /** Is the scope fully materialized (completion marker present)? */
96
+ async hasScope(scope) {
97
+ return await this.backend.getScopeMeta(scope) !== null;
98
+ }
99
+ /** One-query bulk read of a scope's content. */
100
+ async listEntries(scope, prefix = "") {
101
+ return this.backend.makeIndex(scope).listEntries(prefix, {
102
+ includeContent: true,
103
+ limit: 1e3
104
+ });
105
+ }
106
+ /** Single-file read (null when absent). */
107
+ async get(scope, path) {
108
+ return (await this.backend.makeIndex(scope).get(path))?.content ?? null;
109
+ }
110
+ /** Drop a scope entirely (entries + marker). */
111
+ async deleteScope(scope) {
112
+ await this.backend.deleteScopeMeta(scope);
113
+ await this.backend.makeIndex(scope).deleteByPrefix("", {
114
+ deviceId: require_inline_content.INGEST_DEVICE_ID,
115
+ skipChangelog: true
116
+ });
117
+ }
118
+ };
119
+
120
+ //#endregion
121
+ exports.VersionedContentStore = VersionedContentStore;
@@ -0,0 +1,90 @@
1
+ import { TreeIndex, TreeIndexListEntriesResult } from "./tree-index.cjs";
2
+ import { VersionedContentFile } from "./versioned-content-index.cjs";
3
+
4
+ //#region src/versioned-content-store.d.ts
5
+ /** Metadata row for one materialized scope (the completion marker). */
6
+ interface VersionedScopeMeta {
7
+ scope: string;
8
+ /** Opaque grouping key (same package, any version) for cleanup. */
9
+ scopeGroup: string;
10
+ /** Source-content fingerprint (dev-mode stale detection). */
11
+ seedDigest?: string;
12
+ ingestedAt: number;
13
+ fileCount: number;
14
+ }
15
+ /**
16
+ * Storage backend: binds scopes to TreeIndex instances over the shared DB and
17
+ * owns the scope-metadata table. Implementations are thin (entries machinery
18
+ * is the TreeIndex itself).
19
+ */
20
+ interface VersionedScopeBackend {
21
+ /** A TreeIndex bound to `scope`, constructed with `inlineCache: "on"`. */
22
+ makeIndex(scope: string): TreeIndex;
23
+ getScopeMeta(scope: string): Promise<VersionedScopeMeta | null>;
24
+ putScopeMeta(meta: VersionedScopeMeta): Promise<void>;
25
+ deleteScopeMeta(scope: string): Promise<void>;
26
+ /** Every metadata row in `scopeGroup` (for sibling-version cleanup). */
27
+ listScopeMetaByGroup(scopeGroup: string): Promise<VersionedScopeMeta[]>;
28
+ }
29
+ /** `load()` result — either bare files or files + their freshly-computed digest. */
30
+ type LoadedScopeContent = VersionedContentFile[] | {
31
+ files: VersionedContentFile[];
32
+ seedDigest?: string;
33
+ };
34
+ interface EnsureScopeOptions {
35
+ /** Exact scope key (`versionedScope(kind, id, token)`). */
36
+ scope: string;
37
+ /** Opaque group key — same package across versions (cleanup unit). */
38
+ group: string;
39
+ /**
40
+ * Load the source files (called ONLY on a miss/rebuild). May return a
41
+ * digest computed from the loaded bytes — stored in the metadata row so the
42
+ * prod hot path never recomputes it (`seedDigest` 仅在 ingest 时计算).
43
+ */
44
+ load: () => Promise<LoadedScopeContent>;
45
+ /**
46
+ * Source fingerprint. When supplied WITH `verifyDigest`, a metadata row
47
+ * whose digest differs triggers a rebuild (dev-mode stale defense). Stored
48
+ * in the metadata row either way.
49
+ */
50
+ seedDigest?: string;
51
+ /** Compare digests on hit (dev mode). Default false — prod trusts the key. */
52
+ verifyDigest?: boolean;
53
+ /** Force a rebuild regardless of the marker (dev "don't trust the cache"). */
54
+ force?: boolean;
55
+ }
56
+ interface EnsureScopeResult {
57
+ /** True when this call performed the build (miss/stale/forced). */
58
+ built: boolean;
59
+ fileCount: number;
60
+ }
61
+ interface VersionedContentStoreOptions {
62
+ backend: VersionedScopeBackend;
63
+ /** Per-file inline ceiling — over it ingest HARD-FAILS. Default 4KB. */
64
+ inlineThreshold?: number;
65
+ /** Build/cleanup observability (daemon log). Default: silent. */
66
+ log?: (line: string) => void;
67
+ }
68
+ declare class VersionedContentStore {
69
+ private readonly backend;
70
+ private readonly inlineThreshold;
71
+ private readonly log;
72
+ /** In-process single-flight per scope (concurrent first-miss → one build). */
73
+ private readonly inflight;
74
+ constructor(opts: VersionedContentStoreOptions);
75
+ /** Materialize a scope if needed. See {@link EnsureScopeOptions}. */
76
+ ensure(opts: EnsureScopeOptions): Promise<EnsureScopeResult>;
77
+ private ensureInner;
78
+ private build;
79
+ /** Is the scope fully materialized (completion marker present)? */
80
+ hasScope(scope: string): Promise<boolean>;
81
+ /** One-query bulk read of a scope's content. */
82
+ listEntries(scope: string, prefix?: string): Promise<TreeIndexListEntriesResult>;
83
+ /** Single-file read (null when absent). */
84
+ get(scope: string, path: string): Promise<string | null>;
85
+ /** Drop a scope entirely (entries + marker). */
86
+ deleteScope(scope: string): Promise<void>;
87
+ }
88
+ //#endregion
89
+ export { EnsureScopeOptions, EnsureScopeResult, LoadedScopeContent, VersionedContentStore, VersionedContentStoreOptions, VersionedScopeBackend, VersionedScopeMeta };
90
+ //# sourceMappingURL=versioned-content-store.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versioned-content-store.d.cts","names":[],"sources":["../src/versioned-content-store.ts"],"mappings":";;;;;UAoCiB,kBAAA;EACf,KAAA;EAiBA;EAfA,UAAA;EAe6B;EAb7B,UAAA;EACA,UAAA;EACA,SAAA;AAAA;;;;;;UAQe,qBAAA;EAO2B;EAL1C,SAAA,CAAU,KAAA,WAAgB,SAAA;EAC1B,YAAA,CAAa,KAAA,WAAgB,OAAA,CAAQ,kBAAA;EACrC,YAAA,CAAa,IAAA,EAAM,kBAAA,GAAqB,OAAA;EACxC,eAAA,CAAgB,KAAA,WAAgB,OAAA;EAMJ;EAJ5B,oBAAA,CAAqB,UAAA,WAAqB,OAAA,CAAQ,kBAAA;AAAA;;KAIxC,kBAAA,GACR,oBAAA;EACE,KAAA,EAAO,oBAAA;EAAwB,UAAA;AAAA;AAAA,UAEpB,kBAAA;EAAA;EAEf,KAAA;;EAEA,KAAA;EAFA;;;;;EAQA,IAAA,QAAY,OAAA,CAAQ,kBAAA;EAQpB;;;;AAKF;EAPE,UAAA;;EAEA,YAAA;EAQS;EANT,KAAA;AAAA;AAAA,UAGe,iBAAA;EAOe;EAL9B,KAAA;EACA,SAAA;AAAA;AAAA,UAGe,4BAAA;EACf,OAAA,EAAS,qBAAA;EAIU;EAFnB,eAAA;EAKW;EAHX,GAAA,IAAO,IAAA;AAAA;AAAA,cAGI,qBAAA;EAAA,iBACM,OAAA;EAAA,iBACA,eAAA;EAAA,iBACA,GAAA;EAyEc;EAAA,iBAvEd,QAAA;cAEL,IAAA,EAAM,4BAAA;EAkFsB;EA1ElC,MAAA,CAAO,IAAA,EAAM,kBAAA,GAAqB,OAAA,CAAQ,iBAAA;EAAA,QAWlC,WAAA;EAAA,QAeA,KAAA;EAxCG;EA2EX,QAAA,CAAS,KAAA,WAAgB,OAAA;EAzEd;EA8EX,WAAA,CAAY,KAAA,UAAe,MAAA,YAAc,OAAA,CAAQ,0BAAA;;EAQjD,GAAA,CAAI,KAAA,UAAe,IAAA,WAAe,OAAA;EAlF5B;EAwFN,WAAA,CAAY,KAAA,WAAgB,OAAA;AAAA"}
@@ -0,0 +1,90 @@
1
+ import { TreeIndex, TreeIndexListEntriesResult } from "./tree-index.mjs";
2
+ import { VersionedContentFile } from "./versioned-content-index.mjs";
3
+
4
+ //#region src/versioned-content-store.d.ts
5
+ /** Metadata row for one materialized scope (the completion marker). */
6
+ interface VersionedScopeMeta {
7
+ scope: string;
8
+ /** Opaque grouping key (same package, any version) for cleanup. */
9
+ scopeGroup: string;
10
+ /** Source-content fingerprint (dev-mode stale detection). */
11
+ seedDigest?: string;
12
+ ingestedAt: number;
13
+ fileCount: number;
14
+ }
15
+ /**
16
+ * Storage backend: binds scopes to TreeIndex instances over the shared DB and
17
+ * owns the scope-metadata table. Implementations are thin (entries machinery
18
+ * is the TreeIndex itself).
19
+ */
20
+ interface VersionedScopeBackend {
21
+ /** A TreeIndex bound to `scope`, constructed with `inlineCache: "on"`. */
22
+ makeIndex(scope: string): TreeIndex;
23
+ getScopeMeta(scope: string): Promise<VersionedScopeMeta | null>;
24
+ putScopeMeta(meta: VersionedScopeMeta): Promise<void>;
25
+ deleteScopeMeta(scope: string): Promise<void>;
26
+ /** Every metadata row in `scopeGroup` (for sibling-version cleanup). */
27
+ listScopeMetaByGroup(scopeGroup: string): Promise<VersionedScopeMeta[]>;
28
+ }
29
+ /** `load()` result — either bare files or files + their freshly-computed digest. */
30
+ type LoadedScopeContent = VersionedContentFile[] | {
31
+ files: VersionedContentFile[];
32
+ seedDigest?: string;
33
+ };
34
+ interface EnsureScopeOptions {
35
+ /** Exact scope key (`versionedScope(kind, id, token)`). */
36
+ scope: string;
37
+ /** Opaque group key — same package across versions (cleanup unit). */
38
+ group: string;
39
+ /**
40
+ * Load the source files (called ONLY on a miss/rebuild). May return a
41
+ * digest computed from the loaded bytes — stored in the metadata row so the
42
+ * prod hot path never recomputes it (`seedDigest` 仅在 ingest 时计算).
43
+ */
44
+ load: () => Promise<LoadedScopeContent>;
45
+ /**
46
+ * Source fingerprint. When supplied WITH `verifyDigest`, a metadata row
47
+ * whose digest differs triggers a rebuild (dev-mode stale defense). Stored
48
+ * in the metadata row either way.
49
+ */
50
+ seedDigest?: string;
51
+ /** Compare digests on hit (dev mode). Default false — prod trusts the key. */
52
+ verifyDigest?: boolean;
53
+ /** Force a rebuild regardless of the marker (dev "don't trust the cache"). */
54
+ force?: boolean;
55
+ }
56
+ interface EnsureScopeResult {
57
+ /** True when this call performed the build (miss/stale/forced). */
58
+ built: boolean;
59
+ fileCount: number;
60
+ }
61
+ interface VersionedContentStoreOptions {
62
+ backend: VersionedScopeBackend;
63
+ /** Per-file inline ceiling — over it ingest HARD-FAILS. Default 4KB. */
64
+ inlineThreshold?: number;
65
+ /** Build/cleanup observability (daemon log). Default: silent. */
66
+ log?: (line: string) => void;
67
+ }
68
+ declare class VersionedContentStore {
69
+ private readonly backend;
70
+ private readonly inlineThreshold;
71
+ private readonly log;
72
+ /** In-process single-flight per scope (concurrent first-miss → one build). */
73
+ private readonly inflight;
74
+ constructor(opts: VersionedContentStoreOptions);
75
+ /** Materialize a scope if needed. See {@link EnsureScopeOptions}. */
76
+ ensure(opts: EnsureScopeOptions): Promise<EnsureScopeResult>;
77
+ private ensureInner;
78
+ private build;
79
+ /** Is the scope fully materialized (completion marker present)? */
80
+ hasScope(scope: string): Promise<boolean>;
81
+ /** One-query bulk read of a scope's content. */
82
+ listEntries(scope: string, prefix?: string): Promise<TreeIndexListEntriesResult>;
83
+ /** Single-file read (null when absent). */
84
+ get(scope: string, path: string): Promise<string | null>;
85
+ /** Drop a scope entirely (entries + marker). */
86
+ deleteScope(scope: string): Promise<void>;
87
+ }
88
+ //#endregion
89
+ export { EnsureScopeOptions, EnsureScopeResult, LoadedScopeContent, VersionedContentStore, VersionedContentStoreOptions, VersionedScopeBackend, VersionedScopeMeta };
90
+ //# sourceMappingURL=versioned-content-store.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versioned-content-store.d.mts","names":[],"sources":["../src/versioned-content-store.ts"],"mappings":";;;;;UAoCiB,kBAAA;EACf,KAAA;EAiBA;EAfA,UAAA;EAe6B;EAb7B,UAAA;EACA,UAAA;EACA,SAAA;AAAA;;;;;;UAQe,qBAAA;EAO2B;EAL1C,SAAA,CAAU,KAAA,WAAgB,SAAA;EAC1B,YAAA,CAAa,KAAA,WAAgB,OAAA,CAAQ,kBAAA;EACrC,YAAA,CAAa,IAAA,EAAM,kBAAA,GAAqB,OAAA;EACxC,eAAA,CAAgB,KAAA,WAAgB,OAAA;EAMJ;EAJ5B,oBAAA,CAAqB,UAAA,WAAqB,OAAA,CAAQ,kBAAA;AAAA;;KAIxC,kBAAA,GACR,oBAAA;EACE,KAAA,EAAO,oBAAA;EAAwB,UAAA;AAAA;AAAA,UAEpB,kBAAA;EAAA;EAEf,KAAA;;EAEA,KAAA;EAFA;;;;;EAQA,IAAA,QAAY,OAAA,CAAQ,kBAAA;EAQpB;;;;AAKF;EAPE,UAAA;;EAEA,YAAA;EAQS;EANT,KAAA;AAAA;AAAA,UAGe,iBAAA;EAOe;EAL9B,KAAA;EACA,SAAA;AAAA;AAAA,UAGe,4BAAA;EACf,OAAA,EAAS,qBAAA;EAIU;EAFnB,eAAA;EAKW;EAHX,GAAA,IAAO,IAAA;AAAA;AAAA,cAGI,qBAAA;EAAA,iBACM,OAAA;EAAA,iBACA,eAAA;EAAA,iBACA,GAAA;EAyEc;EAAA,iBAvEd,QAAA;cAEL,IAAA,EAAM,4BAAA;EAkFsB;EA1ElC,MAAA,CAAO,IAAA,EAAM,kBAAA,GAAqB,OAAA,CAAQ,iBAAA;EAAA,QAWlC,WAAA;EAAA,QAeA,KAAA;EAxCG;EA2EX,QAAA,CAAS,KAAA,WAAgB,OAAA;EAzEd;EA8EX,WAAA,CAAY,KAAA,UAAe,MAAA,YAAc,OAAA,CAAQ,0BAAA;;EAQjD,GAAA,CAAI,KAAA,UAAe,IAAA,WAAe,OAAA;EAlF5B;EAwFN,WAAA,CAAY,KAAA,WAAgB,OAAA;AAAA"}
@@ -0,0 +1,122 @@
1
+ import { DEFAULT_INLINE_THRESHOLD, INGEST_DEVICE_ID, assertInlineThreshold } from "./inline-content.mjs";
2
+ import { VersionedContentIndex } from "./versioned-content-index.mjs";
3
+
4
+ //#region src/versioned-content-store.ts
5
+ /**
6
+ * VersionedContentStore — multi-scope manager over {@link VersionedContentIndex}
7
+ * (Layer 1 machinery: versioned package-content materialization).
8
+ *
9
+ * One store instance manages MANY scopes (`<kind>:<packageId>:<versionToken>`,
10
+ * see `versioned-scope.ts`) inside ONE shared database — Node: a daemon-level
11
+ * SQLite file; CF: the shared D1 binding; tests: in-memory. The machinery is
12
+ * KIND-AGNOSTIC: scope and group are opaque keys; consumers map their source
13
+ * content (via AFS) into `VersionedContentFile[]` and never touch the DB.
14
+ *
15
+ * Ingest contract (idempotent, lazily triggered):
16
+ * - `ensure(scope)` is a HIT when the scope's metadata row exists (and, when
17
+ * digest verification is requested, the stored digest matches) — zero
18
+ * source I/O on the hot path.
19
+ * - a MISS loads the source files, upserts every row, THEN writes the
20
+ * metadata row — the metadata row is the completion marker, so a crash
21
+ * mid-ingest leaves no readable half-scope (next access rebuilds).
22
+ * - concurrent first-miss within one process is memoized (single build);
23
+ * cross-process double builds are harmless (pure upserts + idempotent
24
+ * marker).
25
+ * - building a scope DELETES sibling scopes of the same `group` (an opaque
26
+ * `<kind>:<packageId>` prefix key) — version cleanup is one DELETE per
27
+ * stale scope, no blob GC involved (content is inline, rows ARE the data).
28
+ * - `seedDigest` lives in the metadata row, NEVER in the key — dev-mode
29
+ * stale detection compares digests; prod looks up by exact key only.
30
+ */
31
+ var VersionedContentStore = class {
32
+ backend;
33
+ inlineThreshold;
34
+ log;
35
+ /** In-process single-flight per scope (concurrent first-miss → one build). */
36
+ inflight = /* @__PURE__ */ new Map();
37
+ constructor(opts) {
38
+ this.backend = opts.backend;
39
+ this.inlineThreshold = opts.inlineThreshold ?? DEFAULT_INLINE_THRESHOLD;
40
+ assertInlineThreshold(this.inlineThreshold);
41
+ this.log = opts.log ?? (() => {});
42
+ }
43
+ /** Materialize a scope if needed. See {@link EnsureScopeOptions}. */
44
+ async ensure(opts) {
45
+ const existing = this.inflight.get(opts.scope);
46
+ if (existing) return existing;
47
+ const run = this.ensureInner(opts).finally(() => {
48
+ this.inflight.delete(opts.scope);
49
+ });
50
+ this.inflight.set(opts.scope, run);
51
+ return run;
52
+ }
53
+ async ensureInner(opts) {
54
+ if (!opts.force) {
55
+ const meta = await this.backend.getScopeMeta(opts.scope);
56
+ if (meta) {
57
+ if (!(opts.verifyDigest && opts.seedDigest !== void 0 && meta.seedDigest !== opts.seedDigest)) return {
58
+ built: false,
59
+ fileCount: meta.fileCount
60
+ };
61
+ this.log(`[versioned-content] stale digest for ${opts.scope} (${meta.seedDigest} → ${opts.seedDigest}) — rebuilding`);
62
+ }
63
+ }
64
+ return this.build(opts);
65
+ }
66
+ async build(opts) {
67
+ const loaded = await opts.load();
68
+ const files = Array.isArray(loaded) ? loaded : loaded.files;
69
+ const loadedDigest = Array.isArray(loaded) ? void 0 : loaded.seedDigest;
70
+ const index = new VersionedContentIndex({
71
+ index: this.backend.makeIndex(opts.scope),
72
+ inlineThreshold: this.inlineThreshold
73
+ });
74
+ await index.deleteAll();
75
+ if (files.length > 0) await index.putBatch(files);
76
+ await this.backend.putScopeMeta({
77
+ scope: opts.scope,
78
+ scopeGroup: opts.group,
79
+ seedDigest: opts.seedDigest ?? loadedDigest,
80
+ ingestedAt: Date.now(),
81
+ fileCount: files.length
82
+ });
83
+ this.log(`[versioned-content] built scope ${opts.scope} (${files.length} files)`);
84
+ const siblings = await this.backend.listScopeMetaByGroup(opts.group);
85
+ for (const sibling of siblings) {
86
+ if (sibling.scope === opts.scope) continue;
87
+ await this.deleteScope(sibling.scope);
88
+ this.log(`[versioned-content] deleted superseded scope ${sibling.scope}`);
89
+ }
90
+ return {
91
+ built: true,
92
+ fileCount: files.length
93
+ };
94
+ }
95
+ /** Is the scope fully materialized (completion marker present)? */
96
+ async hasScope(scope) {
97
+ return await this.backend.getScopeMeta(scope) !== null;
98
+ }
99
+ /** One-query bulk read of a scope's content. */
100
+ async listEntries(scope, prefix = "") {
101
+ return this.backend.makeIndex(scope).listEntries(prefix, {
102
+ includeContent: true,
103
+ limit: 1e3
104
+ });
105
+ }
106
+ /** Single-file read (null when absent). */
107
+ async get(scope, path) {
108
+ return (await this.backend.makeIndex(scope).get(path))?.content ?? null;
109
+ }
110
+ /** Drop a scope entirely (entries + marker). */
111
+ async deleteScope(scope) {
112
+ await this.backend.deleteScopeMeta(scope);
113
+ await this.backend.makeIndex(scope).deleteByPrefix("", {
114
+ deviceId: INGEST_DEVICE_ID,
115
+ skipChangelog: true
116
+ });
117
+ }
118
+ };
119
+
120
+ //#endregion
121
+ export { VersionedContentStore };
122
+ //# sourceMappingURL=versioned-content-store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versioned-content-store.mjs","names":[],"sources":["../src/versioned-content-store.ts"],"sourcesContent":["/**\n * VersionedContentStore — multi-scope manager over {@link VersionedContentIndex}\n * (Layer 1 machinery: versioned package-content materialization).\n *\n * One store instance manages MANY scopes (`<kind>:<packageId>:<versionToken>`,\n * see `versioned-scope.ts`) inside ONE shared database — Node: a daemon-level\n * SQLite file; CF: the shared D1 binding; tests: in-memory. The machinery is\n * KIND-AGNOSTIC: scope and group are opaque keys; consumers map their source\n * content (via AFS) into `VersionedContentFile[]` and never touch the DB.\n *\n * Ingest contract (idempotent, lazily triggered):\n * - `ensure(scope)` is a HIT when the scope's metadata row exists (and, when\n * digest verification is requested, the stored digest matches) — zero\n * source I/O on the hot path.\n * - a MISS loads the source files, upserts every row, THEN writes the\n * metadata row — the metadata row is the completion marker, so a crash\n * mid-ingest leaves no readable half-scope (next access rebuilds).\n * - concurrent first-miss within one process is memoized (single build);\n * cross-process double builds are harmless (pure upserts + idempotent\n * marker).\n * - building a scope DELETES sibling scopes of the same `group` (an opaque\n * `<kind>:<packageId>` prefix key) — version cleanup is one DELETE per\n * stale scope, no blob GC involved (content is inline, rows ARE the data).\n * - `seedDigest` lives in the metadata row, NEVER in the key — dev-mode\n * stale detection compares digests; prod looks up by exact key only.\n */\n\nimport {\n assertInlineThreshold,\n DEFAULT_INLINE_THRESHOLD,\n INGEST_DEVICE_ID,\n} from \"./inline-content.js\";\nimport type { TreeIndex, TreeIndexListEntriesResult } from \"./tree-index.js\";\nimport { type VersionedContentFile, VersionedContentIndex } from \"./versioned-content-index.js\";\n\n/** Metadata row for one materialized scope (the completion marker). */\nexport interface VersionedScopeMeta {\n scope: string;\n /** Opaque grouping key (same package, any version) for cleanup. */\n scopeGroup: string;\n /** Source-content fingerprint (dev-mode stale detection). */\n seedDigest?: string;\n ingestedAt: number;\n fileCount: number;\n}\n\n/**\n * Storage backend: binds scopes to TreeIndex instances over the shared DB and\n * owns the scope-metadata table. Implementations are thin (entries machinery\n * is the TreeIndex itself).\n */\nexport interface VersionedScopeBackend {\n /** A TreeIndex bound to `scope`, constructed with `inlineCache: \"on\"`. */\n makeIndex(scope: string): TreeIndex;\n getScopeMeta(scope: string): Promise<VersionedScopeMeta | null>;\n putScopeMeta(meta: VersionedScopeMeta): Promise<void>;\n deleteScopeMeta(scope: string): Promise<void>;\n /** Every metadata row in `scopeGroup` (for sibling-version cleanup). */\n listScopeMetaByGroup(scopeGroup: string): Promise<VersionedScopeMeta[]>;\n}\n\n/** `load()` result — either bare files or files + their freshly-computed digest. */\nexport type LoadedScopeContent =\n | VersionedContentFile[]\n | { files: VersionedContentFile[]; seedDigest?: string };\n\nexport interface EnsureScopeOptions {\n /** Exact scope key (`versionedScope(kind, id, token)`). */\n scope: string;\n /** Opaque group key — same package across versions (cleanup unit). */\n group: string;\n /**\n * Load the source files (called ONLY on a miss/rebuild). May return a\n * digest computed from the loaded bytes — stored in the metadata row so the\n * prod hot path never recomputes it (`seedDigest` 仅在 ingest 时计算).\n */\n load: () => Promise<LoadedScopeContent>;\n /**\n * Source fingerprint. When supplied WITH `verifyDigest`, a metadata row\n * whose digest differs triggers a rebuild (dev-mode stale defense). Stored\n * in the metadata row either way.\n */\n seedDigest?: string;\n /** Compare digests on hit (dev mode). Default false — prod trusts the key. */\n verifyDigest?: boolean;\n /** Force a rebuild regardless of the marker (dev \"don't trust the cache\"). */\n force?: boolean;\n}\n\nexport interface EnsureScopeResult {\n /** True when this call performed the build (miss/stale/forced). */\n built: boolean;\n fileCount: number;\n}\n\nexport interface VersionedContentStoreOptions {\n backend: VersionedScopeBackend;\n /** Per-file inline ceiling — over it ingest HARD-FAILS. Default 4KB. */\n inlineThreshold?: number;\n /** Build/cleanup observability (daemon log). Default: silent. */\n log?: (line: string) => void;\n}\n\nexport class VersionedContentStore {\n private readonly backend: VersionedScopeBackend;\n private readonly inlineThreshold: number;\n private readonly log: (line: string) => void;\n /** In-process single-flight per scope (concurrent first-miss → one build). */\n private readonly inflight = new Map<string, Promise<EnsureScopeResult>>();\n\n constructor(opts: VersionedContentStoreOptions) {\n this.backend = opts.backend;\n this.inlineThreshold = opts.inlineThreshold ?? DEFAULT_INLINE_THRESHOLD;\n assertInlineThreshold(this.inlineThreshold);\n this.log = opts.log ?? (() => {});\n }\n\n /** Materialize a scope if needed. See {@link EnsureScopeOptions}. */\n async ensure(opts: EnsureScopeOptions): Promise<EnsureScopeResult> {\n const existing = this.inflight.get(opts.scope);\n if (existing) return existing;\n\n const run = this.ensureInner(opts).finally(() => {\n this.inflight.delete(opts.scope);\n });\n this.inflight.set(opts.scope, run);\n return run;\n }\n\n private async ensureInner(opts: EnsureScopeOptions): Promise<EnsureScopeResult> {\n if (!opts.force) {\n const meta = await this.backend.getScopeMeta(opts.scope);\n if (meta) {\n const stale =\n opts.verifyDigest && opts.seedDigest !== undefined && meta.seedDigest !== opts.seedDigest;\n if (!stale) return { built: false, fileCount: meta.fileCount };\n this.log(\n `[versioned-content] stale digest for ${opts.scope} (${meta.seedDigest} → ${opts.seedDigest}) — rebuilding`,\n );\n }\n }\n return this.build(opts);\n }\n\n private async build(opts: EnsureScopeOptions): Promise<EnsureScopeResult> {\n const loaded = await opts.load();\n const files = Array.isArray(loaded) ? loaded : loaded.files;\n const loadedDigest = Array.isArray(loaded) ? undefined : loaded.seedDigest;\n const index = new VersionedContentIndex({\n index: this.backend.makeIndex(opts.scope),\n inlineThreshold: this.inlineThreshold,\n });\n\n // Rebuild from scratch: drop any partial/stale rows first, then upsert.\n // The metadata row is written LAST — it is the completion marker; until it\n // lands, `ensure` treats the scope as absent (no half-scope is readable).\n await index.deleteAll();\n if (files.length > 0) await index.putBatch(files);\n await this.backend.putScopeMeta({\n scope: opts.scope,\n scopeGroup: opts.group,\n seedDigest: opts.seedDigest ?? loadedDigest,\n ingestedAt: Date.now(),\n fileCount: files.length,\n });\n this.log(`[versioned-content] built scope ${opts.scope} (${files.length} files)`);\n\n // Sibling-version cleanup: every other scope in the group is superseded.\n const siblings = await this.backend.listScopeMetaByGroup(opts.group);\n for (const sibling of siblings) {\n if (sibling.scope === opts.scope) continue;\n await this.deleteScope(sibling.scope);\n this.log(`[versioned-content] deleted superseded scope ${sibling.scope}`);\n }\n\n return { built: true, fileCount: files.length };\n }\n\n /** Is the scope fully materialized (completion marker present)? */\n async hasScope(scope: string): Promise<boolean> {\n return (await this.backend.getScopeMeta(scope)) !== null;\n }\n\n /** One-query bulk read of a scope's content. */\n async listEntries(scope: string, prefix = \"\"): Promise<TreeIndexListEntriesResult> {\n return this.backend.makeIndex(scope).listEntries(prefix, {\n includeContent: true,\n limit: 1000,\n });\n }\n\n /** Single-file read (null when absent). */\n async get(scope: string, path: string): Promise<string | null> {\n const entry = await this.backend.makeIndex(scope).get(path);\n return entry?.content ?? null;\n }\n\n /** Drop a scope entirely (entries + marker). */\n async deleteScope(scope: string): Promise<void> {\n // Marker first: a concurrent reader must not see a marked-but-draining scope.\n await this.backend.deleteScopeMeta(scope);\n await this.backend\n .makeIndex(scope)\n .deleteByPrefix(\"\", { deviceId: INGEST_DEVICE_ID, skipChangelog: true });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuGA,IAAa,wBAAb,MAAmC;CACjC,AAAiB;CACjB,AAAiB;CACjB,AAAiB;;CAEjB,AAAiB,2BAAW,IAAI,KAAyC;CAEzE,YAAY,MAAoC;AAC9C,OAAK,UAAU,KAAK;AACpB,OAAK,kBAAkB,KAAK,mBAAmB;AAC/C,wBAAsB,KAAK,gBAAgB;AAC3C,OAAK,MAAM,KAAK,cAAc;;;CAIhC,MAAM,OAAO,MAAsD;EACjE,MAAM,WAAW,KAAK,SAAS,IAAI,KAAK,MAAM;AAC9C,MAAI,SAAU,QAAO;EAErB,MAAM,MAAM,KAAK,YAAY,KAAK,CAAC,cAAc;AAC/C,QAAK,SAAS,OAAO,KAAK,MAAM;IAChC;AACF,OAAK,SAAS,IAAI,KAAK,OAAO,IAAI;AAClC,SAAO;;CAGT,MAAc,YAAY,MAAsD;AAC9E,MAAI,CAAC,KAAK,OAAO;GACf,MAAM,OAAO,MAAM,KAAK,QAAQ,aAAa,KAAK,MAAM;AACxD,OAAI,MAAM;AAGR,QAAI,EADF,KAAK,gBAAgB,KAAK,eAAe,UAAa,KAAK,eAAe,KAAK,YACrE,QAAO;KAAE,OAAO;KAAO,WAAW,KAAK;KAAW;AAC9D,SAAK,IACH,wCAAwC,KAAK,MAAM,IAAI,KAAK,WAAW,KAAK,KAAK,WAAW,gBAC7F;;;AAGL,SAAO,KAAK,MAAM,KAAK;;CAGzB,MAAc,MAAM,MAAsD;EACxE,MAAM,SAAS,MAAM,KAAK,MAAM;EAChC,MAAM,QAAQ,MAAM,QAAQ,OAAO,GAAG,SAAS,OAAO;EACtD,MAAM,eAAe,MAAM,QAAQ,OAAO,GAAG,SAAY,OAAO;EAChE,MAAM,QAAQ,IAAI,sBAAsB;GACtC,OAAO,KAAK,QAAQ,UAAU,KAAK,MAAM;GACzC,iBAAiB,KAAK;GACvB,CAAC;AAKF,QAAM,MAAM,WAAW;AACvB,MAAI,MAAM,SAAS,EAAG,OAAM,MAAM,SAAS,MAAM;AACjD,QAAM,KAAK,QAAQ,aAAa;GAC9B,OAAO,KAAK;GACZ,YAAY,KAAK;GACjB,YAAY,KAAK,cAAc;GAC/B,YAAY,KAAK,KAAK;GACtB,WAAW,MAAM;GAClB,CAAC;AACF,OAAK,IAAI,mCAAmC,KAAK,MAAM,IAAI,MAAM,OAAO,SAAS;EAGjF,MAAM,WAAW,MAAM,KAAK,QAAQ,qBAAqB,KAAK,MAAM;AACpE,OAAK,MAAM,WAAW,UAAU;AAC9B,OAAI,QAAQ,UAAU,KAAK,MAAO;AAClC,SAAM,KAAK,YAAY,QAAQ,MAAM;AACrC,QAAK,IAAI,gDAAgD,QAAQ,QAAQ;;AAG3E,SAAO;GAAE,OAAO;GAAM,WAAW,MAAM;GAAQ;;;CAIjD,MAAM,SAAS,OAAiC;AAC9C,SAAQ,MAAM,KAAK,QAAQ,aAAa,MAAM,KAAM;;;CAItD,MAAM,YAAY,OAAe,SAAS,IAAyC;AACjF,SAAO,KAAK,QAAQ,UAAU,MAAM,CAAC,YAAY,QAAQ;GACvD,gBAAgB;GAChB,OAAO;GACR,CAAC;;;CAIJ,MAAM,IAAI,OAAe,MAAsC;AAE7D,UADc,MAAM,KAAK,QAAQ,UAAU,MAAM,CAAC,IAAI,KAAK,GAC7C,WAAW;;;CAI3B,MAAM,YAAY,OAA8B;AAE9C,QAAM,KAAK,QAAQ,gBAAgB,MAAM;AACzC,QAAM,KAAK,QACR,UAAU,MAAM,CAChB,eAAe,IAAI;GAAE,UAAU;GAAkB,eAAe;GAAM,CAAC"}
@@ -0,0 +1,39 @@
1
+
2
+ //#region src/versioned-scope.ts
3
+ /** Separator-escape: `%` → `%25`, `:` → `%3A`, `/` → `%2F`. */
4
+ function escapeComponent(raw, name) {
5
+ if (typeof raw !== "string" || raw.length === 0) throw new Error(`versionedScope: ${name} must be a non-empty string`);
6
+ return raw.replaceAll("%", "%25").replaceAll(":", "%3A").replaceAll("/", "%2F");
7
+ }
8
+ /**
9
+ * Build the canonical `<kind>:<packageId>:<versionToken>` scope key.
10
+ * Components are escaped so a separator INSIDE a component can never collide
11
+ * with the separator BETWEEN components. Throws on empty/non-string input.
12
+ */
13
+ function versionedScope(kind, packageId, versionToken) {
14
+ return `${escapeComponent(kind, "kind")}:${escapeComponent(packageId, "packageId")}:${escapeComponent(versionToken, "versionToken")}`;
15
+ }
16
+ /**
17
+ * Build the group key for a (kind, packageId) pair — the same escaped prefix
18
+ * a {@link versionedScope} key starts with, WITHOUT the version token. Used
19
+ * as the opaque cleanup unit: building a new version's scope supersedes every
20
+ * sibling scope sharing this group.
21
+ */
22
+ function versionedScopeGroup(kind, packageId) {
23
+ return `${escapeComponent(kind, "kind")}:${escapeComponent(packageId, "packageId")}`;
24
+ }
25
+ /**
26
+ * Resolve the version token for a package: `manifest.version` when present,
27
+ * else the content digest. Throws when neither exists — an empty or
28
+ * `"undefined"` token would collide every unversioned package into one scope.
29
+ */
30
+ function resolveVersionToken(identity, seedDigest) {
31
+ if (identity.version) return identity.version;
32
+ if (seedDigest) return seedDigest;
33
+ throw new Error(`versionedScope: package "${identity.id}" has neither manifest.version nor a content digest — refusing to build an ambiguous scope key`);
34
+ }
35
+
36
+ //#endregion
37
+ exports.resolveVersionToken = resolveVersionToken;
38
+ exports.versionedScope = versionedScope;
39
+ exports.versionedScopeGroup = versionedScopeGroup;