@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,104 @@
1
+ import { AFSRoot } from "@aigne/afs";
2
+
3
+ //#region src/dirty-path-queue.d.ts
4
+ /** Latest op merged for a dirty path. Mirrors `ds_changelog.op` / the CHECK. */
5
+ type DirtyPathOp = "put" | "delete";
6
+ /**
7
+ * A dirty row as handed to the follower. The follower uses `op/cid/latestSeq`
8
+ * for scheduling + the ack race-guard ONLY — it re-reads current live state
9
+ * before writing the index (design §4.3), so `cid` here is never trusted as the
10
+ * final content. `path` is the form the queue returns: full DID-space path from
11
+ * a raw backend, or the prefix-STRIPPED `originalPath` from
12
+ * {@link PrefixedTreeIndex} (e.g. `items/x.json`).
13
+ */
14
+ interface DirtyPathRow {
15
+ path: string;
16
+ latestSeq: number;
17
+ op: DirtyPathOp;
18
+ cid?: string | null;
19
+ mtime: number;
20
+ attempts: number;
21
+ }
22
+ /**
23
+ * Full stored shape of a `ds_dirty_path` row (all columns). SQL backends store
24
+ * it as a table row; the Memory backend keeps a `Map<\`${scope}\0${path}\`,
25
+ * DirtyPathStoredRow>` and mutates it through {@link applyDirtyUpsert} so its
26
+ * merge/guard semantics match the D1/SQLite trigger byte-for-byte.
27
+ */
28
+ interface DirtyPathStoredRow {
29
+ scope: string;
30
+ path: string;
31
+ latestSeq: number;
32
+ op: DirtyPathOp;
33
+ cid: string | null;
34
+ mtime: number;
35
+ updatedAt: number;
36
+ attempts: number;
37
+ nextAttemptAt: number;
38
+ lastError: string | null;
39
+ }
40
+ interface ListDirtyPathOptions {
41
+ /** Full DID-space path prefix to scan (e.g. `blocklets/{instanceDid}/user/`). */
42
+ prefix: string;
43
+ /** Max dirty rows to return this tick. */
44
+ limit: number;
45
+ /** Clock override (ms) for the `next_attempt_at <= now` ready filter; defaults to the wall clock. */
46
+ now?: number;
47
+ }
48
+ /**
49
+ * Source-side dirty queue — list ready rows, ack a processed row, or record a
50
+ * failed attempt. All three carry the `latestSeq` race guard (design §8): an
51
+ * ack/mark that targets a `latest_seq` a concurrent write has already superseded
52
+ * affects 0 rows and returns `false`, leaving the newer dirty row for the next
53
+ * tick.
54
+ */
55
+ interface DirtyPathQueue {
56
+ /** Ready dirty rows under `prefix` with `next_attempt_at <= now`, capped at `limit`. */
57
+ listDirtyPaths(options: ListDirtyPathOptions): Promise<DirtyPathRow[]>;
58
+ /**
59
+ * Delete the dirty row for `path` iff its `latest_seq` still equals
60
+ * `latestSeq` (nothing newer arrived). Returns `true` when a row was removed,
61
+ * `false` when the guard lost (a newer write bumped latest_seq — leave it).
62
+ */
63
+ ackDirtyPath(path: string, latestSeq: number): Promise<boolean>;
64
+ /**
65
+ * Record a failed attempt: `attempts+1`, set `last_error`/`next_attempt_at`,
66
+ * guarded by the SAME `latest_seq` equality so a fresh write's reset is never
67
+ * clobbered. Returns `true` when the guarded row was updated.
68
+ */
69
+ markDirtyPathError(path: string, latestSeq: number, error: string, nextAttemptAt: number): Promise<boolean>;
70
+ }
71
+ /**
72
+ * The follower's two source planes, wired explicitly so the AFS read plane and
73
+ * the dirty ack plane never get conflated (design §7):
74
+ * - `contentAfs`: read/stat CURRENT live content only.
75
+ * - `dirtyQueue`: list/ack/fail dirty rows only.
76
+ * They may share one underlying source-side TreeIndex, but ack/fail is a
77
+ * runtime-internal capability — it is never exposed as a `/user` AFS action.
78
+ */
79
+ interface DirtyPathFollowerSource {
80
+ contentAfs: AFSRoot;
81
+ dirtyQueue: DirtyPathQueue;
82
+ }
83
+ /**
84
+ * Apply one changelog-derived write to a Memory dirty map entry, reproducing the
85
+ * `ds_changelog_to_ds_dirty_path` trigger's merge/guard EXACTLY:
86
+ * - no existing row → the incoming row (attempts/backoff/error zeroed).
87
+ * - incoming.latestSeq > existing.latestSeq → overwrite + reset attempts/
88
+ * next_attempt_at/last_error.
89
+ * - stale incoming (seq <= existing) → keep existing unchanged (no rollback).
90
+ *
91
+ * Returned value is what the map should store for `(scope, path)`.
92
+ */
93
+ declare function applyDirtyUpsert(existing: DirtyPathStoredRow | undefined, incoming: {
94
+ scope: string;
95
+ path: string;
96
+ latestSeq: number;
97
+ op: DirtyPathOp;
98
+ cid: string | null;
99
+ mtime: number;
100
+ updatedAt: number;
101
+ }): DirtyPathStoredRow;
102
+ //#endregion
103
+ export { DirtyPathFollowerSource, DirtyPathOp, DirtyPathQueue, DirtyPathRow, DirtyPathStoredRow, ListDirtyPathOptions, applyDirtyUpsert };
104
+ //# sourceMappingURL=dirty-path-queue.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirty-path-queue.d.mts","names":[],"sources":["../src/dirty-path-queue.ts"],"mappings":";;;;KAwCY,WAAA;;;;;;;;;UAUK,YAAA;EACf,IAAA;EACA,SAAA;EACA,EAAA,EAAI,WAAA;EACJ,GAAA;EACA,KAAA;EACA,QAAA;AAAA;;;;;;;UASe,kBAAA;EACf,KAAA;EACA,IAAA;EACA,SAAA;EACA,EAAA,EAAI,WAAA;EACJ,GAAA;EACA,KAAA;EACA,SAAA;EACA,QAAA;EACA,aAAA;EACA,SAAA;AAAA;AAAA,UAGe,oBAAA;EA8CA;EA5Cf,MAAA;;EAEA,KAAA;EA2CA;EAzCA,GAAA;AAAA;;;;AAuDF;;;;UA7CiB,cAAA;EAwDd;EAtDD,cAAA,CAAe,OAAA,EAAS,oBAAA,GAAuB,OAAA,CAAQ,YAAA;EAsDpC;;;;;EAhDnB,YAAA,CAAa,IAAA,UAAc,SAAA,WAAoB,OAAA;EA2C7C;;;;;EArCF,kBAAA,CACE,IAAA,UACA,SAAA,UACA,KAAA,UACA,aAAA,WACC,OAAA;AAAA;;;;;;;;;UAWY,uBAAA;EACf,UAAA,EAAY,OAAA;EACZ,UAAA,EAAY,cAAA;AAAA;;;;;;;;;;;iBAaE,gBAAA,CACd,QAAA,EAAU,kBAAA,cACV,QAAA;EACE,KAAA;EACA,IAAA;EACA,SAAA;EACA,EAAA,EAAI,WAAA;EACJ,GAAA;EACA,KAAA;EACA,SAAA;AAAA,IAED,kBAAA"}
@@ -0,0 +1,31 @@
1
+ //#region src/dirty-path-queue.ts
2
+ /**
3
+ * Apply one changelog-derived write to a Memory dirty map entry, reproducing the
4
+ * `ds_changelog_to_ds_dirty_path` trigger's merge/guard EXACTLY:
5
+ * - no existing row → the incoming row (attempts/backoff/error zeroed).
6
+ * - incoming.latestSeq > existing.latestSeq → overwrite + reset attempts/
7
+ * next_attempt_at/last_error.
8
+ * - stale incoming (seq <= existing) → keep existing unchanged (no rollback).
9
+ *
10
+ * Returned value is what the map should store for `(scope, path)`.
11
+ */
12
+ function applyDirtyUpsert(existing, incoming) {
13
+ const next = {
14
+ scope: incoming.scope,
15
+ path: incoming.path,
16
+ latestSeq: incoming.latestSeq,
17
+ op: incoming.op,
18
+ cid: incoming.cid,
19
+ mtime: incoming.mtime,
20
+ updatedAt: incoming.updatedAt,
21
+ attempts: 0,
22
+ nextAttemptAt: 0,
23
+ lastError: null
24
+ };
25
+ if (!existing) return next;
26
+ return incoming.latestSeq > existing.latestSeq ? next : existing;
27
+ }
28
+
29
+ //#endregion
30
+ export { applyDirtyUpsert };
31
+ //# sourceMappingURL=dirty-path-queue.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirty-path-queue.mjs","names":[],"sources":["../src/dirty-path-queue.ts"],"sourcesContent":["/**\n * Dirty-path queue contract — the internal source-side interface the index\n * follower drains instead of replaying `/.changelog`\n * (planning/index-provider/dirty-path-follower, design §7).\n *\n * This is deliberately NOT a public AFS action. `ackDirtyPath` /\n * `markDirtyPathError` MUTATE source-side scheduling state and must never be\n * reachable by an ordinary `/user` client (design §7 security boundary). The\n * runtime wires it inside the worker/daemon (Phase 3); the follower holds a\n * {@link DirtyPathQueue} handle, the same source-side TreeIndex that owns the\n * `ds_dirty_path` rows.\n *\n * ── Cursor semantics after cutover (design §4.2, defined here, enforced in\n * Phase 2/3) ──\n * `idx_index_cursor` still registers/schedules followers, but once the changelog\n * replay drain is deleted (Phase 4) NOTHING advances `idx_index_cursor.last_seq`\n * — it becomes a DEAD COLUMN kept only for schema/log compatibility. Freshness\n * is \"are there ready dirty rows under my prefix?\", NOT a `last_seq` probe.\n * Fairness rides `served_at`, which dirty mode updates via `markCursorServed`\n * UNCONDITIONALLY every drain (not the changelog era's `!advanced` gate — an\n * always-active scope would otherwise freeze at the front of the queue).\n *\n * ── `IndexFollowerBatchResult` in dirty mode (design §8, defined here,\n * consumed by Phase 2) ──\n * - `scanned` : dirty rows inspected this tick (NOT changelog rows).\n * - `processed` : put dirty rows that indexed current live content.\n * - `noopProcessed`: delete / live-missing / non-indexable rows acked as noop.\n * - `advanced` : ≥1 dirty row acked (or an ack guard-lost to a confirmed\n * newer latest_seq); mark-error does NOT count as advanced.\n * - `hasMore` : ready dirty rows remain under the prefix, or the tick\n * stopped on budget.\n * - `fromSeq/toSeq`: log-compat only; dirty mode never writes `last_seq`.\n * Round-robin scheduling keys off `advanced/hasMore/scanned` + dirty count, so a\n * poison row (mark-error, `advanced=false`, `hasMore=true`) does not busy-loop a\n * single tick, and a scope with ready rows still gets requeued.\n */\n\nimport type { AFSRoot } from \"@aigne/afs\";\n\n/** Latest op merged for a dirty path. Mirrors `ds_changelog.op` / the CHECK. */\nexport type DirtyPathOp = \"put\" | \"delete\";\n\n/**\n * A dirty row as handed to the follower. The follower uses `op/cid/latestSeq`\n * for scheduling + the ack race-guard ONLY — it re-reads current live state\n * before writing the index (design §4.3), so `cid` here is never trusted as the\n * final content. `path` is the form the queue returns: full DID-space path from\n * a raw backend, or the prefix-STRIPPED `originalPath` from\n * {@link PrefixedTreeIndex} (e.g. `items/x.json`).\n */\nexport interface DirtyPathRow {\n path: string;\n latestSeq: number;\n op: DirtyPathOp;\n cid?: string | null;\n mtime: number;\n attempts: number;\n}\n\n/**\n * Full stored shape of a `ds_dirty_path` row (all columns). SQL backends store\n * it as a table row; the Memory backend keeps a `Map<\\`${scope}\\0${path}\\`,\n * DirtyPathStoredRow>` and mutates it through {@link applyDirtyUpsert} so its\n * merge/guard semantics match the D1/SQLite trigger byte-for-byte.\n */\nexport interface DirtyPathStoredRow {\n scope: string;\n path: string;\n latestSeq: number;\n op: DirtyPathOp;\n cid: string | null;\n mtime: number;\n updatedAt: number;\n attempts: number;\n nextAttemptAt: number;\n lastError: string | null;\n}\n\nexport interface ListDirtyPathOptions {\n /** Full DID-space path prefix to scan (e.g. `blocklets/{instanceDid}/user/`). */\n prefix: string;\n /** Max dirty rows to return this tick. */\n limit: number;\n /** Clock override (ms) for the `next_attempt_at <= now` ready filter; defaults to the wall clock. */\n now?: number;\n}\n\n/**\n * Source-side dirty queue — list ready rows, ack a processed row, or record a\n * failed attempt. All three carry the `latestSeq` race guard (design §8): an\n * ack/mark that targets a `latest_seq` a concurrent write has already superseded\n * affects 0 rows and returns `false`, leaving the newer dirty row for the next\n * tick.\n */\nexport interface DirtyPathQueue {\n /** Ready dirty rows under `prefix` with `next_attempt_at <= now`, capped at `limit`. */\n listDirtyPaths(options: ListDirtyPathOptions): Promise<DirtyPathRow[]>;\n /**\n * Delete the dirty row for `path` iff its `latest_seq` still equals\n * `latestSeq` (nothing newer arrived). Returns `true` when a row was removed,\n * `false` when the guard lost (a newer write bumped latest_seq — leave it).\n */\n ackDirtyPath(path: string, latestSeq: number): Promise<boolean>;\n /**\n * Record a failed attempt: `attempts+1`, set `last_error`/`next_attempt_at`,\n * guarded by the SAME `latest_seq` equality so a fresh write's reset is never\n * clobbered. Returns `true` when the guarded row was updated.\n */\n markDirtyPathError(\n path: string,\n latestSeq: number,\n error: string,\n nextAttemptAt: number,\n ): Promise<boolean>;\n}\n\n/**\n * The follower's two source planes, wired explicitly so the AFS read plane and\n * the dirty ack plane never get conflated (design §7):\n * - `contentAfs`: read/stat CURRENT live content only.\n * - `dirtyQueue`: list/ack/fail dirty rows only.\n * They may share one underlying source-side TreeIndex, but ack/fail is a\n * runtime-internal capability — it is never exposed as a `/user` AFS action.\n */\nexport interface DirtyPathFollowerSource {\n contentAfs: AFSRoot;\n dirtyQueue: DirtyPathQueue;\n}\n\n/**\n * Apply one changelog-derived write to a Memory dirty map entry, reproducing the\n * `ds_changelog_to_ds_dirty_path` trigger's merge/guard EXACTLY:\n * - no existing row → the incoming row (attempts/backoff/error zeroed).\n * - incoming.latestSeq > existing.latestSeq → overwrite + reset attempts/\n * next_attempt_at/last_error.\n * - stale incoming (seq <= existing) → keep existing unchanged (no rollback).\n *\n * Returned value is what the map should store for `(scope, path)`.\n */\nexport function applyDirtyUpsert(\n existing: DirtyPathStoredRow | undefined,\n incoming: {\n scope: string;\n path: string;\n latestSeq: number;\n op: DirtyPathOp;\n cid: string | null;\n mtime: number;\n updatedAt: number;\n },\n): DirtyPathStoredRow {\n const next: DirtyPathStoredRow = {\n scope: incoming.scope,\n path: incoming.path,\n latestSeq: incoming.latestSeq,\n op: incoming.op,\n cid: incoming.cid,\n mtime: incoming.mtime,\n updatedAt: incoming.updatedAt,\n attempts: 0,\n nextAttemptAt: 0,\n lastError: null,\n };\n if (!existing) return next;\n // Trigger guard: WHERE excluded.latest_seq > ds_dirty_path.latest_seq.\n return incoming.latestSeq > existing.latestSeq ? next : existing;\n}\n"],"mappings":";;;;;;;;;;;AA2IA,SAAgB,iBACd,UACA,UASoB;CACpB,MAAM,OAA2B;EAC/B,OAAO,SAAS;EAChB,MAAM,SAAS;EACf,WAAW,SAAS;EACpB,IAAI,SAAS;EACb,KAAK,SAAS;EACd,OAAO,SAAS;EAChB,WAAW,SAAS;EACpB,UAAU;EACV,eAAe;EACf,WAAW;EACZ;AACD,KAAI,CAAC,SAAU,QAAO;AAEtB,QAAO,SAAS,YAAY,SAAS,YAAY,OAAO"}
@@ -0,0 +1,142 @@
1
+
2
+ //#region src/dirty-path-schema.ts
3
+ /**
4
+ * ds_dirty_path schema — single source of truth for the index-follower dirty
5
+ * queue (planning/index-provider/dirty-path-follower).
6
+ *
7
+ * `ds_dirty_path` is a SOURCE-SIDE derived projection of `ds_changelog`: one row
8
+ * per `(scope, path)` recording the CURRENT op/latest_seq that still needs
9
+ * materialising into the index. It replaces the follower's `O(changelog rows)`
10
+ * replay with an `O(unique dirty paths)` drain (design §1-§4).
11
+ *
12
+ * Physical placement follows the CLAUDE.md D1-table-naming rule: it lives beside
13
+ * `ds_changelog` in the DID Space metadata DB (CF: D1; Node: each user's
14
+ * `_metadata.db`) — the ONLY place it can stay atomically consistent with the
15
+ * changelog via an `AFTER INSERT` trigger — so it carries the `ds_` (DID Space)
16
+ * prefix even though it logically serves the index provider.
17
+ *
18
+ * This module is the ONE definition of the DDL. It is materialised by:
19
+ * - CF : a numbered D1 migration (Phase 4) that runs these statements.
20
+ * - Node : `metadata-db.ts` ensureSchema (Phase 1), which has no migration
21
+ * runner and CREATEs tables on DB open.
22
+ * - tests : every phase's D1 (miniflare / mock-d1) + libsql setup runs these
23
+ * statements directly (raw DDL, NOT via a numbered migration) so
24
+ * Phase 0-3 can exercise the trigger before Phase 4 exists.
25
+ * All four import THESE constants — never re-spell the SQL — so the trigger
26
+ * guard, CHECK, and column list can never drift across runtimes.
27
+ */
28
+ /**
29
+ * CREATE TABLE statement. Idempotent (`IF NOT EXISTS`).
30
+ *
31
+ * Columns (design §5):
32
+ * - scope : DID Space scope, mirrors `ds_changelog.scope`.
33
+ * - path : full DID-space path, e.g. `blocklets/{did}/user/items/x.json`.
34
+ * - latest_seq : max `ds_changelog.seq` this path has merged to (race guard).
35
+ * - op : latest op, `put` | `delete` (CHECK-constrained).
36
+ * - cid : latest cid; NULL on delete.
37
+ * - mtime : latest changelog mtime.
38
+ * - updated_at : last upsert/reset time (ms) — scheduling + observability.
39
+ * - attempts : consecutive failures (poison-row backoff).
40
+ * - next_attempt_at : retry-backoff watermark; a row with next_attempt_at > now
41
+ * is skipped so a poison row never fills a tick's budget.
42
+ * - last_error : most recent failure summary.
43
+ *
44
+ * PRIMARY KEY(scope, path) is what makes the trigger's `ON CONFLICT(scope, path)`
45
+ * merge N changelog rows for one path into a single dirty row.
46
+ */
47
+ const DIRTY_PATH_TABLE_SQL = `CREATE TABLE IF NOT EXISTS ds_dirty_path (
48
+ scope TEXT NOT NULL,
49
+ path TEXT NOT NULL,
50
+ latest_seq INTEGER NOT NULL,
51
+ op TEXT NOT NULL CHECK (op IN ('put', 'delete')),
52
+ cid TEXT,
53
+ mtime INTEGER NOT NULL,
54
+ updated_at INTEGER NOT NULL,
55
+ attempts INTEGER NOT NULL DEFAULT 0,
56
+ next_attempt_at INTEGER NOT NULL DEFAULT 0,
57
+ last_error TEXT,
58
+ PRIMARY KEY(scope, path)
59
+ )`;
60
+ /**
61
+ * CREATE INDEX statements. The `(scope, next_attempt_at, updated_at)` index
62
+ * serves the follower's ready-row scan: scope equality, `next_attempt_at <= now`
63
+ * range, `updated_at` tiebreak — an index-only ordered seek instead of a scope
64
+ * full-scan on every tick.
65
+ */
66
+ const DIRTY_PATH_INDEX_SQL = ["CREATE INDEX IF NOT EXISTS ds_dirty_path_scope_retry ON ds_dirty_path(scope, next_attempt_at, updated_at)"];
67
+ /**
68
+ * The `AFTER INSERT ON ds_changelog` trigger that derives dirty rows.
69
+ *
70
+ * Because it hangs off the changelog INSERT, it covers EVERY changelog write
71
+ * face by construction (design §6) — the 6 TreeIndex methods AND the metadata-db
72
+ * direct writes — with no need to enumerate write sites (that enumeration is the
73
+ * §6 fallback, only for a runtime whose trigger support is inadequate).
74
+ *
75
+ * Merge/guard semantics (mirrored in-process by {@link applyDirtyUpsert} for the
76
+ * Memory backend, so all three backends stay in lockstep):
77
+ * - first write for a path → INSERT the dirty row.
78
+ * - later write with a GREATER seq → overwrite op/cid/mtime/latest_seq and
79
+ * RESET attempts/next_attempt_at/last_error (a fresh write clears prior
80
+ * failure/backoff state).
81
+ * - a stale write (seq <= current latest_seq) → the
82
+ * `WHERE excluded.latest_seq > ds_dirty_path.latest_seq` guard makes it a
83
+ * no-op; latest_seq NEVER rolls backwards.
84
+ *
85
+ * NOTE: the body contains inner `;` (required by the trigger grammar). Any SQL
86
+ * splitter feeding this to D1 one statement at a time MUST keep the whole
87
+ * `CREATE TRIGGER … BEGIN … END` block intact — same rule as the existing
88
+ * `SCOPE_META_FILE_COUNT_TRIGGER_SQL` (see apply-d1-migrations.mjs
89
+ * splitSqlStatements).
90
+ *
91
+ * `strftime('%s','now')` is second-granularity → *1000 for a millis `updated_at`
92
+ * that reads uniformly with the app-clock millis the SQL/Memory backends write.
93
+ */
94
+ const DIRTY_PATH_TRIGGER_SQL = [`CREATE TRIGGER IF NOT EXISTS ds_changelog_to_ds_dirty_path
95
+ AFTER INSERT ON ds_changelog
96
+ BEGIN
97
+ INSERT INTO ds_dirty_path (
98
+ scope, path, latest_seq, op, cid, mtime, updated_at, attempts, next_attempt_at, last_error
99
+ )
100
+ VALUES (
101
+ NEW.scope, NEW.path, NEW.seq, NEW.op, NEW.cid, NEW.mtime,
102
+ CAST(strftime('%s', 'now') AS INTEGER) * 1000, 0, 0, NULL
103
+ )
104
+ ON CONFLICT(scope, path) DO UPDATE SET
105
+ latest_seq = excluded.latest_seq,
106
+ op = excluded.op,
107
+ cid = excluded.cid,
108
+ mtime = excluded.mtime,
109
+ updated_at = excluded.updated_at,
110
+ attempts = 0,
111
+ next_attempt_at = 0,
112
+ last_error = NULL
113
+ WHERE excluded.latest_seq > ds_dirty_path.latest_seq;
114
+ END`];
115
+ /**
116
+ * Full DDL sequence (table → index → trigger) in dependency order. Test setups
117
+ * and the Node ensureSchema loop run this after the `ds_changelog` DDL (the
118
+ * trigger references `ds_changelog`, so the changelog table must exist first).
119
+ */
120
+ const DIRTY_PATH_DDL = [
121
+ DIRTY_PATH_TABLE_SQL,
122
+ ...DIRTY_PATH_INDEX_SQL,
123
+ ...DIRTY_PATH_TRIGGER_SQL
124
+ ];
125
+ /** Ready dirty rows under a prefix. Binds: `(scope, lo, hi, now, limit)`. */
126
+ const DIRTY_PATH_LIST_SQL = `SELECT path, latest_seq, op, cid, mtime, attempts FROM ds_dirty_path INDEXED BY ds_dirty_path_scope_retry
127
+ WHERE scope = ? AND path >= ? AND path < ? AND next_attempt_at <= ?
128
+ ORDER BY next_attempt_at ASC, updated_at ASC LIMIT ?`;
129
+ /** Guarded ack (delete iff latest_seq unchanged). Binds: `(scope, path, latestSeq)`. */
130
+ const DIRTY_PATH_ACK_SQL = "DELETE FROM ds_dirty_path WHERE scope = ? AND path = ? AND latest_seq = ?";
131
+ /** Guarded failure record. Binds: `(error, nextAttemptAt, scope, path, latestSeq)`. */
132
+ const DIRTY_PATH_MARK_ERROR_SQL = `UPDATE ds_dirty_path SET attempts = attempts + 1, last_error = ?, next_attempt_at = ?
133
+ WHERE scope = ? AND path = ? AND latest_seq = ?`;
134
+
135
+ //#endregion
136
+ exports.DIRTY_PATH_ACK_SQL = DIRTY_PATH_ACK_SQL;
137
+ exports.DIRTY_PATH_DDL = DIRTY_PATH_DDL;
138
+ exports.DIRTY_PATH_INDEX_SQL = DIRTY_PATH_INDEX_SQL;
139
+ exports.DIRTY_PATH_LIST_SQL = DIRTY_PATH_LIST_SQL;
140
+ exports.DIRTY_PATH_MARK_ERROR_SQL = DIRTY_PATH_MARK_ERROR_SQL;
141
+ exports.DIRTY_PATH_TABLE_SQL = DIRTY_PATH_TABLE_SQL;
142
+ exports.DIRTY_PATH_TRIGGER_SQL = DIRTY_PATH_TRIGGER_SQL;
@@ -0,0 +1,90 @@
1
+ //#region src/dirty-path-schema.d.ts
2
+ /**
3
+ * ds_dirty_path schema — single source of truth for the index-follower dirty
4
+ * queue (planning/index-provider/dirty-path-follower).
5
+ *
6
+ * `ds_dirty_path` is a SOURCE-SIDE derived projection of `ds_changelog`: one row
7
+ * per `(scope, path)` recording the CURRENT op/latest_seq that still needs
8
+ * materialising into the index. It replaces the follower's `O(changelog rows)`
9
+ * replay with an `O(unique dirty paths)` drain (design §1-§4).
10
+ *
11
+ * Physical placement follows the CLAUDE.md D1-table-naming rule: it lives beside
12
+ * `ds_changelog` in the DID Space metadata DB (CF: D1; Node: each user's
13
+ * `_metadata.db`) — the ONLY place it can stay atomically consistent with the
14
+ * changelog via an `AFTER INSERT` trigger — so it carries the `ds_` (DID Space)
15
+ * prefix even though it logically serves the index provider.
16
+ *
17
+ * This module is the ONE definition of the DDL. It is materialised by:
18
+ * - CF : a numbered D1 migration (Phase 4) that runs these statements.
19
+ * - Node : `metadata-db.ts` ensureSchema (Phase 1), which has no migration
20
+ * runner and CREATEs tables on DB open.
21
+ * - tests : every phase's D1 (miniflare / mock-d1) + libsql setup runs these
22
+ * statements directly (raw DDL, NOT via a numbered migration) so
23
+ * Phase 0-3 can exercise the trigger before Phase 4 exists.
24
+ * All four import THESE constants — never re-spell the SQL — so the trigger
25
+ * guard, CHECK, and column list can never drift across runtimes.
26
+ */
27
+ /**
28
+ * CREATE TABLE statement. Idempotent (`IF NOT EXISTS`).
29
+ *
30
+ * Columns (design §5):
31
+ * - scope : DID Space scope, mirrors `ds_changelog.scope`.
32
+ * - path : full DID-space path, e.g. `blocklets/{did}/user/items/x.json`.
33
+ * - latest_seq : max `ds_changelog.seq` this path has merged to (race guard).
34
+ * - op : latest op, `put` | `delete` (CHECK-constrained).
35
+ * - cid : latest cid; NULL on delete.
36
+ * - mtime : latest changelog mtime.
37
+ * - updated_at : last upsert/reset time (ms) — scheduling + observability.
38
+ * - attempts : consecutive failures (poison-row backoff).
39
+ * - next_attempt_at : retry-backoff watermark; a row with next_attempt_at > now
40
+ * is skipped so a poison row never fills a tick's budget.
41
+ * - last_error : most recent failure summary.
42
+ *
43
+ * PRIMARY KEY(scope, path) is what makes the trigger's `ON CONFLICT(scope, path)`
44
+ * merge N changelog rows for one path into a single dirty row.
45
+ */
46
+ declare const DIRTY_PATH_TABLE_SQL = "CREATE TABLE IF NOT EXISTS ds_dirty_path (\n scope TEXT NOT NULL,\n path TEXT NOT NULL,\n latest_seq INTEGER NOT NULL,\n op TEXT NOT NULL CHECK (op IN ('put', 'delete')),\n cid TEXT,\n mtime INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n next_attempt_at INTEGER NOT NULL DEFAULT 0,\n last_error TEXT,\n PRIMARY KEY(scope, path)\n)";
47
+ /**
48
+ * CREATE INDEX statements. The `(scope, next_attempt_at, updated_at)` index
49
+ * serves the follower's ready-row scan: scope equality, `next_attempt_at <= now`
50
+ * range, `updated_at` tiebreak — an index-only ordered seek instead of a scope
51
+ * full-scan on every tick.
52
+ */
53
+ declare const DIRTY_PATH_INDEX_SQL: readonly string[];
54
+ /**
55
+ * The `AFTER INSERT ON ds_changelog` trigger that derives dirty rows.
56
+ *
57
+ * Because it hangs off the changelog INSERT, it covers EVERY changelog write
58
+ * face by construction (design §6) — the 6 TreeIndex methods AND the metadata-db
59
+ * direct writes — with no need to enumerate write sites (that enumeration is the
60
+ * §6 fallback, only for a runtime whose trigger support is inadequate).
61
+ *
62
+ * Merge/guard semantics (mirrored in-process by {@link applyDirtyUpsert} for the
63
+ * Memory backend, so all three backends stay in lockstep):
64
+ * - first write for a path → INSERT the dirty row.
65
+ * - later write with a GREATER seq → overwrite op/cid/mtime/latest_seq and
66
+ * RESET attempts/next_attempt_at/last_error (a fresh write clears prior
67
+ * failure/backoff state).
68
+ * - a stale write (seq <= current latest_seq) → the
69
+ * `WHERE excluded.latest_seq > ds_dirty_path.latest_seq` guard makes it a
70
+ * no-op; latest_seq NEVER rolls backwards.
71
+ *
72
+ * NOTE: the body contains inner `;` (required by the trigger grammar). Any SQL
73
+ * splitter feeding this to D1 one statement at a time MUST keep the whole
74
+ * `CREATE TRIGGER … BEGIN … END` block intact — same rule as the existing
75
+ * `SCOPE_META_FILE_COUNT_TRIGGER_SQL` (see apply-d1-migrations.mjs
76
+ * splitSqlStatements).
77
+ *
78
+ * `strftime('%s','now')` is second-granularity → *1000 for a millis `updated_at`
79
+ * that reads uniformly with the app-clock millis the SQL/Memory backends write.
80
+ */
81
+ declare const DIRTY_PATH_TRIGGER_SQL: readonly string[];
82
+ /**
83
+ * Full DDL sequence (table → index → trigger) in dependency order. Test setups
84
+ * and the Node ensureSchema loop run this after the `ds_changelog` DDL (the
85
+ * trigger references `ds_changelog`, so the changelog table must exist first).
86
+ */
87
+ declare const DIRTY_PATH_DDL: readonly string[];
88
+ //#endregion
89
+ export { DIRTY_PATH_DDL, DIRTY_PATH_INDEX_SQL, DIRTY_PATH_TABLE_SQL, DIRTY_PATH_TRIGGER_SQL };
90
+ //# sourceMappingURL=dirty-path-schema.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirty-path-schema.d.cts","names":[],"sources":["../src/dirty-path-schema.ts"],"mappings":";;AA6CA;;;;;AAoBA;;;;;AA+BA;;;;;AA6BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAhFa,oBAAA;;;;;;;cAoBA,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+BA,sBAAA;;;;;;cA6BA,cAAA"}
@@ -0,0 +1,90 @@
1
+ //#region src/dirty-path-schema.d.ts
2
+ /**
3
+ * ds_dirty_path schema — single source of truth for the index-follower dirty
4
+ * queue (planning/index-provider/dirty-path-follower).
5
+ *
6
+ * `ds_dirty_path` is a SOURCE-SIDE derived projection of `ds_changelog`: one row
7
+ * per `(scope, path)` recording the CURRENT op/latest_seq that still needs
8
+ * materialising into the index. It replaces the follower's `O(changelog rows)`
9
+ * replay with an `O(unique dirty paths)` drain (design §1-§4).
10
+ *
11
+ * Physical placement follows the CLAUDE.md D1-table-naming rule: it lives beside
12
+ * `ds_changelog` in the DID Space metadata DB (CF: D1; Node: each user's
13
+ * `_metadata.db`) — the ONLY place it can stay atomically consistent with the
14
+ * changelog via an `AFTER INSERT` trigger — so it carries the `ds_` (DID Space)
15
+ * prefix even though it logically serves the index provider.
16
+ *
17
+ * This module is the ONE definition of the DDL. It is materialised by:
18
+ * - CF : a numbered D1 migration (Phase 4) that runs these statements.
19
+ * - Node : `metadata-db.ts` ensureSchema (Phase 1), which has no migration
20
+ * runner and CREATEs tables on DB open.
21
+ * - tests : every phase's D1 (miniflare / mock-d1) + libsql setup runs these
22
+ * statements directly (raw DDL, NOT via a numbered migration) so
23
+ * Phase 0-3 can exercise the trigger before Phase 4 exists.
24
+ * All four import THESE constants — never re-spell the SQL — so the trigger
25
+ * guard, CHECK, and column list can never drift across runtimes.
26
+ */
27
+ /**
28
+ * CREATE TABLE statement. Idempotent (`IF NOT EXISTS`).
29
+ *
30
+ * Columns (design §5):
31
+ * - scope : DID Space scope, mirrors `ds_changelog.scope`.
32
+ * - path : full DID-space path, e.g. `blocklets/{did}/user/items/x.json`.
33
+ * - latest_seq : max `ds_changelog.seq` this path has merged to (race guard).
34
+ * - op : latest op, `put` | `delete` (CHECK-constrained).
35
+ * - cid : latest cid; NULL on delete.
36
+ * - mtime : latest changelog mtime.
37
+ * - updated_at : last upsert/reset time (ms) — scheduling + observability.
38
+ * - attempts : consecutive failures (poison-row backoff).
39
+ * - next_attempt_at : retry-backoff watermark; a row with next_attempt_at > now
40
+ * is skipped so a poison row never fills a tick's budget.
41
+ * - last_error : most recent failure summary.
42
+ *
43
+ * PRIMARY KEY(scope, path) is what makes the trigger's `ON CONFLICT(scope, path)`
44
+ * merge N changelog rows for one path into a single dirty row.
45
+ */
46
+ declare const DIRTY_PATH_TABLE_SQL = "CREATE TABLE IF NOT EXISTS ds_dirty_path (\n scope TEXT NOT NULL,\n path TEXT NOT NULL,\n latest_seq INTEGER NOT NULL,\n op TEXT NOT NULL CHECK (op IN ('put', 'delete')),\n cid TEXT,\n mtime INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n next_attempt_at INTEGER NOT NULL DEFAULT 0,\n last_error TEXT,\n PRIMARY KEY(scope, path)\n)";
47
+ /**
48
+ * CREATE INDEX statements. The `(scope, next_attempt_at, updated_at)` index
49
+ * serves the follower's ready-row scan: scope equality, `next_attempt_at <= now`
50
+ * range, `updated_at` tiebreak — an index-only ordered seek instead of a scope
51
+ * full-scan on every tick.
52
+ */
53
+ declare const DIRTY_PATH_INDEX_SQL: readonly string[];
54
+ /**
55
+ * The `AFTER INSERT ON ds_changelog` trigger that derives dirty rows.
56
+ *
57
+ * Because it hangs off the changelog INSERT, it covers EVERY changelog write
58
+ * face by construction (design §6) — the 6 TreeIndex methods AND the metadata-db
59
+ * direct writes — with no need to enumerate write sites (that enumeration is the
60
+ * §6 fallback, only for a runtime whose trigger support is inadequate).
61
+ *
62
+ * Merge/guard semantics (mirrored in-process by {@link applyDirtyUpsert} for the
63
+ * Memory backend, so all three backends stay in lockstep):
64
+ * - first write for a path → INSERT the dirty row.
65
+ * - later write with a GREATER seq → overwrite op/cid/mtime/latest_seq and
66
+ * RESET attempts/next_attempt_at/last_error (a fresh write clears prior
67
+ * failure/backoff state).
68
+ * - a stale write (seq <= current latest_seq) → the
69
+ * `WHERE excluded.latest_seq > ds_dirty_path.latest_seq` guard makes it a
70
+ * no-op; latest_seq NEVER rolls backwards.
71
+ *
72
+ * NOTE: the body contains inner `;` (required by the trigger grammar). Any SQL
73
+ * splitter feeding this to D1 one statement at a time MUST keep the whole
74
+ * `CREATE TRIGGER … BEGIN … END` block intact — same rule as the existing
75
+ * `SCOPE_META_FILE_COUNT_TRIGGER_SQL` (see apply-d1-migrations.mjs
76
+ * splitSqlStatements).
77
+ *
78
+ * `strftime('%s','now')` is second-granularity → *1000 for a millis `updated_at`
79
+ * that reads uniformly with the app-clock millis the SQL/Memory backends write.
80
+ */
81
+ declare const DIRTY_PATH_TRIGGER_SQL: readonly string[];
82
+ /**
83
+ * Full DDL sequence (table → index → trigger) in dependency order. Test setups
84
+ * and the Node ensureSchema loop run this after the `ds_changelog` DDL (the
85
+ * trigger references `ds_changelog`, so the changelog table must exist first).
86
+ */
87
+ declare const DIRTY_PATH_DDL: readonly string[];
88
+ //#endregion
89
+ export { DIRTY_PATH_DDL, DIRTY_PATH_INDEX_SQL, DIRTY_PATH_TABLE_SQL, DIRTY_PATH_TRIGGER_SQL };
90
+ //# sourceMappingURL=dirty-path-schema.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirty-path-schema.d.mts","names":[],"sources":["../src/dirty-path-schema.ts"],"mappings":";;AA6CA;;;;;AAoBA;;;;;AA+BA;;;;;AA6BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAhFa,oBAAA;;;;;;;cAoBA,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+BA,sBAAA;;;;;;cA6BA,cAAA"}
@@ -0,0 +1,136 @@
1
+ //#region src/dirty-path-schema.ts
2
+ /**
3
+ * ds_dirty_path schema — single source of truth for the index-follower dirty
4
+ * queue (planning/index-provider/dirty-path-follower).
5
+ *
6
+ * `ds_dirty_path` is a SOURCE-SIDE derived projection of `ds_changelog`: one row
7
+ * per `(scope, path)` recording the CURRENT op/latest_seq that still needs
8
+ * materialising into the index. It replaces the follower's `O(changelog rows)`
9
+ * replay with an `O(unique dirty paths)` drain (design §1-§4).
10
+ *
11
+ * Physical placement follows the CLAUDE.md D1-table-naming rule: it lives beside
12
+ * `ds_changelog` in the DID Space metadata DB (CF: D1; Node: each user's
13
+ * `_metadata.db`) — the ONLY place it can stay atomically consistent with the
14
+ * changelog via an `AFTER INSERT` trigger — so it carries the `ds_` (DID Space)
15
+ * prefix even though it logically serves the index provider.
16
+ *
17
+ * This module is the ONE definition of the DDL. It is materialised by:
18
+ * - CF : a numbered D1 migration (Phase 4) that runs these statements.
19
+ * - Node : `metadata-db.ts` ensureSchema (Phase 1), which has no migration
20
+ * runner and CREATEs tables on DB open.
21
+ * - tests : every phase's D1 (miniflare / mock-d1) + libsql setup runs these
22
+ * statements directly (raw DDL, NOT via a numbered migration) so
23
+ * Phase 0-3 can exercise the trigger before Phase 4 exists.
24
+ * All four import THESE constants — never re-spell the SQL — so the trigger
25
+ * guard, CHECK, and column list can never drift across runtimes.
26
+ */
27
+ /**
28
+ * CREATE TABLE statement. Idempotent (`IF NOT EXISTS`).
29
+ *
30
+ * Columns (design §5):
31
+ * - scope : DID Space scope, mirrors `ds_changelog.scope`.
32
+ * - path : full DID-space path, e.g. `blocklets/{did}/user/items/x.json`.
33
+ * - latest_seq : max `ds_changelog.seq` this path has merged to (race guard).
34
+ * - op : latest op, `put` | `delete` (CHECK-constrained).
35
+ * - cid : latest cid; NULL on delete.
36
+ * - mtime : latest changelog mtime.
37
+ * - updated_at : last upsert/reset time (ms) — scheduling + observability.
38
+ * - attempts : consecutive failures (poison-row backoff).
39
+ * - next_attempt_at : retry-backoff watermark; a row with next_attempt_at > now
40
+ * is skipped so a poison row never fills a tick's budget.
41
+ * - last_error : most recent failure summary.
42
+ *
43
+ * PRIMARY KEY(scope, path) is what makes the trigger's `ON CONFLICT(scope, path)`
44
+ * merge N changelog rows for one path into a single dirty row.
45
+ */
46
+ const DIRTY_PATH_TABLE_SQL = `CREATE TABLE IF NOT EXISTS ds_dirty_path (
47
+ scope TEXT NOT NULL,
48
+ path TEXT NOT NULL,
49
+ latest_seq INTEGER NOT NULL,
50
+ op TEXT NOT NULL CHECK (op IN ('put', 'delete')),
51
+ cid TEXT,
52
+ mtime INTEGER NOT NULL,
53
+ updated_at INTEGER NOT NULL,
54
+ attempts INTEGER NOT NULL DEFAULT 0,
55
+ next_attempt_at INTEGER NOT NULL DEFAULT 0,
56
+ last_error TEXT,
57
+ PRIMARY KEY(scope, path)
58
+ )`;
59
+ /**
60
+ * CREATE INDEX statements. The `(scope, next_attempt_at, updated_at)` index
61
+ * serves the follower's ready-row scan: scope equality, `next_attempt_at <= now`
62
+ * range, `updated_at` tiebreak — an index-only ordered seek instead of a scope
63
+ * full-scan on every tick.
64
+ */
65
+ const DIRTY_PATH_INDEX_SQL = ["CREATE INDEX IF NOT EXISTS ds_dirty_path_scope_retry ON ds_dirty_path(scope, next_attempt_at, updated_at)"];
66
+ /**
67
+ * The `AFTER INSERT ON ds_changelog` trigger that derives dirty rows.
68
+ *
69
+ * Because it hangs off the changelog INSERT, it covers EVERY changelog write
70
+ * face by construction (design §6) — the 6 TreeIndex methods AND the metadata-db
71
+ * direct writes — with no need to enumerate write sites (that enumeration is the
72
+ * §6 fallback, only for a runtime whose trigger support is inadequate).
73
+ *
74
+ * Merge/guard semantics (mirrored in-process by {@link applyDirtyUpsert} for the
75
+ * Memory backend, so all three backends stay in lockstep):
76
+ * - first write for a path → INSERT the dirty row.
77
+ * - later write with a GREATER seq → overwrite op/cid/mtime/latest_seq and
78
+ * RESET attempts/next_attempt_at/last_error (a fresh write clears prior
79
+ * failure/backoff state).
80
+ * - a stale write (seq <= current latest_seq) → the
81
+ * `WHERE excluded.latest_seq > ds_dirty_path.latest_seq` guard makes it a
82
+ * no-op; latest_seq NEVER rolls backwards.
83
+ *
84
+ * NOTE: the body contains inner `;` (required by the trigger grammar). Any SQL
85
+ * splitter feeding this to D1 one statement at a time MUST keep the whole
86
+ * `CREATE TRIGGER … BEGIN … END` block intact — same rule as the existing
87
+ * `SCOPE_META_FILE_COUNT_TRIGGER_SQL` (see apply-d1-migrations.mjs
88
+ * splitSqlStatements).
89
+ *
90
+ * `strftime('%s','now')` is second-granularity → *1000 for a millis `updated_at`
91
+ * that reads uniformly with the app-clock millis the SQL/Memory backends write.
92
+ */
93
+ const DIRTY_PATH_TRIGGER_SQL = [`CREATE TRIGGER IF NOT EXISTS ds_changelog_to_ds_dirty_path
94
+ AFTER INSERT ON ds_changelog
95
+ BEGIN
96
+ INSERT INTO ds_dirty_path (
97
+ scope, path, latest_seq, op, cid, mtime, updated_at, attempts, next_attempt_at, last_error
98
+ )
99
+ VALUES (
100
+ NEW.scope, NEW.path, NEW.seq, NEW.op, NEW.cid, NEW.mtime,
101
+ CAST(strftime('%s', 'now') AS INTEGER) * 1000, 0, 0, NULL
102
+ )
103
+ ON CONFLICT(scope, path) DO UPDATE SET
104
+ latest_seq = excluded.latest_seq,
105
+ op = excluded.op,
106
+ cid = excluded.cid,
107
+ mtime = excluded.mtime,
108
+ updated_at = excluded.updated_at,
109
+ attempts = 0,
110
+ next_attempt_at = 0,
111
+ last_error = NULL
112
+ WHERE excluded.latest_seq > ds_dirty_path.latest_seq;
113
+ END`];
114
+ /**
115
+ * Full DDL sequence (table → index → trigger) in dependency order. Test setups
116
+ * and the Node ensureSchema loop run this after the `ds_changelog` DDL (the
117
+ * trigger references `ds_changelog`, so the changelog table must exist first).
118
+ */
119
+ const DIRTY_PATH_DDL = [
120
+ DIRTY_PATH_TABLE_SQL,
121
+ ...DIRTY_PATH_INDEX_SQL,
122
+ ...DIRTY_PATH_TRIGGER_SQL
123
+ ];
124
+ /** Ready dirty rows under a prefix. Binds: `(scope, lo, hi, now, limit)`. */
125
+ const DIRTY_PATH_LIST_SQL = `SELECT path, latest_seq, op, cid, mtime, attempts FROM ds_dirty_path INDEXED BY ds_dirty_path_scope_retry
126
+ WHERE scope = ? AND path >= ? AND path < ? AND next_attempt_at <= ?
127
+ ORDER BY next_attempt_at ASC, updated_at ASC LIMIT ?`;
128
+ /** Guarded ack (delete iff latest_seq unchanged). Binds: `(scope, path, latestSeq)`. */
129
+ const DIRTY_PATH_ACK_SQL = "DELETE FROM ds_dirty_path WHERE scope = ? AND path = ? AND latest_seq = ?";
130
+ /** Guarded failure record. Binds: `(error, nextAttemptAt, scope, path, latestSeq)`. */
131
+ const DIRTY_PATH_MARK_ERROR_SQL = `UPDATE ds_dirty_path SET attempts = attempts + 1, last_error = ?, next_attempt_at = ?
132
+ WHERE scope = ? AND path = ? AND latest_seq = ?`;
133
+
134
+ //#endregion
135
+ export { DIRTY_PATH_ACK_SQL, DIRTY_PATH_DDL, DIRTY_PATH_INDEX_SQL, DIRTY_PATH_LIST_SQL, DIRTY_PATH_MARK_ERROR_SQL, DIRTY_PATH_TABLE_SQL, DIRTY_PATH_TRIGGER_SQL };
136
+ //# sourceMappingURL=dirty-path-schema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirty-path-schema.mjs","names":[],"sources":["../src/dirty-path-schema.ts"],"sourcesContent":["/**\n * ds_dirty_path schema — single source of truth for the index-follower dirty\n * queue (planning/index-provider/dirty-path-follower).\n *\n * `ds_dirty_path` is a SOURCE-SIDE derived projection of `ds_changelog`: one row\n * per `(scope, path)` recording the CURRENT op/latest_seq that still needs\n * materialising into the index. It replaces the follower's `O(changelog rows)`\n * replay with an `O(unique dirty paths)` drain (design §1-§4).\n *\n * Physical placement follows the CLAUDE.md D1-table-naming rule: it lives beside\n * `ds_changelog` in the DID Space metadata DB (CF: D1; Node: each user's\n * `_metadata.db`) — the ONLY place it can stay atomically consistent with the\n * changelog via an `AFTER INSERT` trigger — so it carries the `ds_` (DID Space)\n * prefix even though it logically serves the index provider.\n *\n * This module is the ONE definition of the DDL. It is materialised by:\n * - CF : a numbered D1 migration (Phase 4) that runs these statements.\n * - Node : `metadata-db.ts` ensureSchema (Phase 1), which has no migration\n * runner and CREATEs tables on DB open.\n * - tests : every phase's D1 (miniflare / mock-d1) + libsql setup runs these\n * statements directly (raw DDL, NOT via a numbered migration) so\n * Phase 0-3 can exercise the trigger before Phase 4 exists.\n * All four import THESE constants — never re-spell the SQL — so the trigger\n * guard, CHECK, and column list can never drift across runtimes.\n */\n\n/**\n * CREATE TABLE statement. Idempotent (`IF NOT EXISTS`).\n *\n * Columns (design §5):\n * - scope : DID Space scope, mirrors `ds_changelog.scope`.\n * - path : full DID-space path, e.g. `blocklets/{did}/user/items/x.json`.\n * - latest_seq : max `ds_changelog.seq` this path has merged to (race guard).\n * - op : latest op, `put` | `delete` (CHECK-constrained).\n * - cid : latest cid; NULL on delete.\n * - mtime : latest changelog mtime.\n * - updated_at : last upsert/reset time (ms) — scheduling + observability.\n * - attempts : consecutive failures (poison-row backoff).\n * - next_attempt_at : retry-backoff watermark; a row with next_attempt_at > now\n * is skipped so a poison row never fills a tick's budget.\n * - last_error : most recent failure summary.\n *\n * PRIMARY KEY(scope, path) is what makes the trigger's `ON CONFLICT(scope, path)`\n * merge N changelog rows for one path into a single dirty row.\n */\nexport const DIRTY_PATH_TABLE_SQL = `CREATE TABLE IF NOT EXISTS ds_dirty_path (\n scope TEXT NOT NULL,\n path TEXT NOT NULL,\n latest_seq INTEGER NOT NULL,\n op TEXT NOT NULL CHECK (op IN ('put', 'delete')),\n cid TEXT,\n mtime INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n next_attempt_at INTEGER NOT NULL DEFAULT 0,\n last_error TEXT,\n PRIMARY KEY(scope, path)\n)`;\n\n/**\n * CREATE INDEX statements. The `(scope, next_attempt_at, updated_at)` index\n * serves the follower's ready-row scan: scope equality, `next_attempt_at <= now`\n * range, `updated_at` tiebreak — an index-only ordered seek instead of a scope\n * full-scan on every tick.\n */\nexport const DIRTY_PATH_INDEX_SQL: readonly string[] = [\n \"CREATE INDEX IF NOT EXISTS ds_dirty_path_scope_retry ON ds_dirty_path(scope, next_attempt_at, updated_at)\",\n];\n\n/**\n * The `AFTER INSERT ON ds_changelog` trigger that derives dirty rows.\n *\n * Because it hangs off the changelog INSERT, it covers EVERY changelog write\n * face by construction (design §6) — the 6 TreeIndex methods AND the metadata-db\n * direct writes — with no need to enumerate write sites (that enumeration is the\n * §6 fallback, only for a runtime whose trigger support is inadequate).\n *\n * Merge/guard semantics (mirrored in-process by {@link applyDirtyUpsert} for the\n * Memory backend, so all three backends stay in lockstep):\n * - first write for a path → INSERT the dirty row.\n * - later write with a GREATER seq → overwrite op/cid/mtime/latest_seq and\n * RESET attempts/next_attempt_at/last_error (a fresh write clears prior\n * failure/backoff state).\n * - a stale write (seq <= current latest_seq) → the\n * `WHERE excluded.latest_seq > ds_dirty_path.latest_seq` guard makes it a\n * no-op; latest_seq NEVER rolls backwards.\n *\n * NOTE: the body contains inner `;` (required by the trigger grammar). Any SQL\n * splitter feeding this to D1 one statement at a time MUST keep the whole\n * `CREATE TRIGGER … BEGIN … END` block intact — same rule as the existing\n * `SCOPE_META_FILE_COUNT_TRIGGER_SQL` (see apply-d1-migrations.mjs\n * splitSqlStatements).\n *\n * `strftime('%s','now')` is second-granularity → *1000 for a millis `updated_at`\n * that reads uniformly with the app-clock millis the SQL/Memory backends write.\n */\nexport const DIRTY_PATH_TRIGGER_SQL: readonly string[] = [\n `CREATE TRIGGER IF NOT EXISTS ds_changelog_to_ds_dirty_path\n AFTER INSERT ON ds_changelog\n BEGIN\n INSERT INTO ds_dirty_path (\n scope, path, latest_seq, op, cid, mtime, updated_at, attempts, next_attempt_at, last_error\n )\n VALUES (\n NEW.scope, NEW.path, NEW.seq, NEW.op, NEW.cid, NEW.mtime,\n CAST(strftime('%s', 'now') AS INTEGER) * 1000, 0, 0, NULL\n )\n ON CONFLICT(scope, path) DO UPDATE SET\n latest_seq = excluded.latest_seq,\n op = excluded.op,\n cid = excluded.cid,\n mtime = excluded.mtime,\n updated_at = excluded.updated_at,\n attempts = 0,\n next_attempt_at = 0,\n last_error = NULL\n WHERE excluded.latest_seq > ds_dirty_path.latest_seq;\n END`,\n];\n\n/**\n * Full DDL sequence (table → index → trigger) in dependency order. Test setups\n * and the Node ensureSchema loop run this after the `ds_changelog` DDL (the\n * trigger references `ds_changelog`, so the changelog table must exist first).\n */\nexport const DIRTY_PATH_DDL: readonly string[] = [\n DIRTY_PATH_TABLE_SQL,\n ...DIRTY_PATH_INDEX_SQL,\n ...DIRTY_PATH_TRIGGER_SQL,\n];\n\n// ── DirtyPathQueue query SQL (single source, D1 + SQLite) ──\n//\n// D1TreeIndex and SQLiteTreeIndex run the SAME SQLite dialect, so — like\n// `CHANGELOG_INSERT_SQL` — the statements live here once and both backends bind\n// against them (only the client API differs). Keeping the bind ORDER documented\n// beside each string is what stops the two runtimes drifting.\n\n// `INDEXED BY ds_dirty_path_scope_retry` is NOT cosmetic — it PINS the plan.\n// This query has two viable indexes: the PK auto-index `(scope, path)` serves\n// the `path` prefix range, while `ds_dirty_path_scope_retry (scope,\n// next_attempt_at, updated_at)` serves the `next_attempt_at<=now` filter AND the\n// ORDER BY for free. WITHOUT stats the planner picks the retry index (an ordered\n// index-only seek, no sort). But once D1 gains ANALYZE cardinality stats it FLIPS\n// to the PK index + `USE TEMP B-TREE FOR ORDER BY`, which scans this instance's\n// WHOLE path range and sorts it every tick — the exact rows_read cliff that bit\n// staging in issue #1120 (measured here: ~3.3× slower on an 8k-row table). The\n// hint forces the ordered-seek plan unconditionally, mirroring markStale's\n// `INDEXED BY idx_ad_scope_anchor_type` fix in d1-index-backend.ts.\n//\n// Tradeoff (accepted): the retry index orders by time, not path, so in a scope\n// SHARED by several instances a quiet instance walks past siblings' ready rows\n// (residual-filtered by `path`) before filling its LIMIT. That is bounded by the\n// scope's ready set and never worse than the ANALYZE'd temp-b-tree it replaces.\n// If multi-blocklet-per-space with concurrent backlogs ever becomes hot, switch\n// to `ORDER BY path` on the PK index (a true per-instance bounded seek) — see the\n// dirty-path-follower design review (finding #4).\n/** Ready dirty rows under a prefix. Binds: `(scope, lo, hi, now, limit)`. */\nexport const DIRTY_PATH_LIST_SQL = `SELECT path, latest_seq, op, cid, mtime, attempts FROM ds_dirty_path INDEXED BY ds_dirty_path_scope_retry\n WHERE scope = ? AND path >= ? AND path < ? AND next_attempt_at <= ?\n ORDER BY next_attempt_at ASC, updated_at ASC LIMIT ?`;\n\n/** Guarded ack (delete iff latest_seq unchanged). Binds: `(scope, path, latestSeq)`. */\nexport const DIRTY_PATH_ACK_SQL =\n \"DELETE FROM ds_dirty_path WHERE scope = ? AND path = ? AND latest_seq = ?\";\n\n/** Guarded failure record. Binds: `(error, nextAttemptAt, scope, path, latestSeq)`. */\nexport const DIRTY_PATH_MARK_ERROR_SQL = `UPDATE ds_dirty_path SET attempts = attempts + 1, last_error = ?, next_attempt_at = ?\n WHERE scope = ? AND path = ? AND latest_seq = ?`;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAa,uBAAuB;;;;;;;;;;;;;;;;;;;AAoBpC,MAAa,uBAA0C,CACrD,4GACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BD,MAAa,yBAA4C,CACvD;;;;;;;;;;;;;;;;;;;;QAqBD;;;;;;AAOD,MAAa,iBAAoC;CAC/C;CACA,GAAG;CACH,GAAG;CACJ;;AA6BD,MAAa,sBAAsB;;;;AAKnC,MAAa,qBACX;;AAGF,MAAa,4BAA4B"}