@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 @@
1
+ {"version":3,"file":"tree-query.mjs","names":["p","src","result"],"sources":["../src/tree-query.ts"],"sourcesContent":["/**\n * TreeQuery — the shared collection-query engine for the did-space TreeIndex\n * family (afs-collection-query Phase 1, design §3.5).\n *\n * One compilation layer, three executors:\n * - SQL builder (`buildTreeQueryStatements`) shared verbatim by\n * SQLiteTreeIndex and (Phase 2) D1TreeIndex — identical dialect\n * (json_extract / json_each / json_type all available on both).\n * - JS executor (`runTreeQueryInMemory`) for MemoryTreeIndex, built on the\n * SAME helpers the SQL path uses for ordering/keyset (`tree-index.ts`) and\n * the canonical core `where` semantics (`whereMatches` from @aigne/afs) —\n * conformance + the lockstep test pin all backends to one behavior.\n *\n * Semantics contract (must match `evaluateCollectionQuery` in @aigne/afs):\n * - string comparisons fold case; numbers/booleans are type-strict\n * - `notIn` includes records where the field is missing/null\n * - `contains` requires an array field; missing/non-array never match\n * - groupBy unnests arrays; missing/null/non-primitive group under `null`;\n * string group keys are folded to lower case\n * - keyset cursor: v2 format shared with list (`v2:<b64>:<b64>`), tie on the\n * child's unique `name`; WHERE applies before pagination so total is the\n * exact filtered count\n */\n\nimport {\n type AFSAggregateGroup,\n type AFSAggregateOp,\n type AFSCollectionQueryGroup,\n type AFSCollectionQuerySpec,\n type AFSTimeGranularity,\n AFSValidationError,\n aggregateGroupKeyNames,\n evaluateCollectionQuery,\n normalizeWhere,\n resolveFieldPath,\n TIME_BUCKET_KEY,\n whereMatchesItem,\n} from \"@aigne/afs\";\nimport {\n buildKeysetCursor,\n buildOrderedQueryParts,\n compareTreeSortValues,\n keysetAfter,\n parseTreeCursor,\n parseTreeEntryMetadata,\n type TreeKeysetCursor,\n} from \"./tree-index.js\";\n\n/** One result row — index-level shape (provider maps to AFSEntry). */\nexport interface TreeQueryRow {\n /** Tree path (dir rows keep their trailing slash, same as listChildren). */\n path: string;\n /** Child basename (the keyset tie column). */\n name: string;\n kind: \"file\" | \"dir\";\n cid: string;\n size: number;\n mtime: number;\n ctime: number;\n mimeType?: string;\n /** Raw record text (doc-joined for dir rows / inline content for file rows). */\n content?: string;\n /** cid of the joined doc sub-file (dir rows only) — caller re-verifies. */\n contentCid?: string;\n /**\n * Application metadata column (entries.metadata, parsed) — surfaced when\n * the row carries it so the in-memory query path can dispatch `meta.*`\n * filter / orderBy against the same data the SQL path reads from\n * `entries.metadata` (planning/agent-hub/metadata-foundation.md).\n */\n metadata?: Record<string, unknown>;\n}\n\nexport interface TreeQueryResult {\n rows: TreeQueryRow[];\n /**\n * Exact post-WHERE total. In aggregate mode this is the number of GROUP\n * rows pre-pagination (mirrors the core reference evaluator).\n */\n total: number;\n cursor?: string;\n groups?: AFSCollectionQueryGroup[];\n /** Aggregate-mode result rows (parallel to {@link AFSCollectionQueryResult.aggregateGroups}). */\n aggregateGroups?: AFSAggregateGroup[];\n /**\n * Present when the spec carried `seekId`. The 0-based index of the sought\n * entry in the sorted+filtered set, or -1 when not found.\n * Mirrors {@link AFSCollectionQueryResult.seekOffset}.\n */\n seekOffset?: number;\n}\n\n/** The spec subset a TreeIndex engine executes (validated + provider-checked). */\nexport interface TreeQuerySpec {\n doc?: string;\n where?: AFSCollectionQuerySpec[\"where\"];\n orderBy?: [string, \"asc\" | \"desc\"][];\n groupBy?: string | string[];\n count?: boolean;\n /**\n * Skip the exact-total count pass (afs-collection-query). When true the\n * engine does NOT build/run the `count` statement and the result carries\n * `total: -1` (\"not computed\" sentinel). See AFSCollectionQuerySpec.skipTotal.\n */\n skipTotal?: boolean;\n limit?: number;\n offset?: number;\n cursor?: string;\n /** Cross-page seek — mirrors {@link AFSCollectionQuerySpec.seekId}. */\n seekId?: string;\n // Aggregate-mode (Phase 2 / G2.D). Presence of `aggregations` switches\n // the engine into the aggregate SQL/JS path.\n aggregations?: AFSAggregateOp[];\n timeGranularity?: AFSTimeGranularity;\n timeBucketField?: string;\n}\n\nconst ENGINE_ORDER_COLUMNS: Record<string, string> = {\n name: \"name\",\n path: \"path\",\n size: \"size\",\n ctime: \"ctime\",\n mtime: \"mtime\",\n mimeType: \"mime_type\",\n};\n\nconst DEFAULT_QUERY_LIMIT = 1000;\n\n/**\n * Provider-level constraints on the (already core-validated) spec that the\n * tree engine enforces uniformly. Strict contract: reject, never ignore.\n *\n * Aggregate-mode orderBy is a totally different field set (group keys +\n * `as` names) — those have already been validated by the core; nothing\n * more for the engine to assert here.\n */\nexport function assertTreeQuerySpec(spec: AFSCollectionQuerySpec): void {\n if (spec.text !== undefined) {\n throw new AFSValidationError(\"Invalid query spec: text: not supported by this provider\");\n }\n if (spec.aggregations) return;\n if (spec.orderBy && spec.orderBy.length > 1) {\n throw new AFSValidationError(\n \"Invalid query spec: orderBy: this provider supports a single sort key\",\n );\n }\n const field = spec.orderBy?.[0]?.[0];\n if (field !== undefined && !ENGINE_ORDER_COLUMNS[field] && !field.startsWith(\"meta.\")) {\n throw new AFSValidationError(`Invalid query spec: orderBy: unsupported field ${field}`);\n }\n}\n\n/** Effective single sort key: requested tuple or the children default (name asc). */\nfunction effectiveOrder(spec: TreeQuerySpec): { field: string; order: \"asc\" | \"desc\" } {\n const tuple = spec.orderBy?.[0];\n return { field: tuple?.[0] ?? \"name\", order: tuple?.[1] ?? \"asc\" };\n}\n\n// ── SQL compilation (sqlite + d1 — identical dialect) ───────────────────────\n\n/** Quoted JSON path for a core-validated dot-path (segments are [\\w-]+). */\nfunction jsonPathOf(field: string): string {\n return `$.${field\n .split(\".\")\n .map((seg) => `\"${seg}\"`)\n .join(\".\")}`;\n}\n\ninterface SqlFragment {\n sql: string;\n args: Array<string | number>;\n}\n\n/**\n * Resolve a where/orderBy field to its source JSON column expression and the\n * `$.path` argument to `json_extract`. `meta.*` paths dispatch to the\n * `entries.metadata` column (planning/agent-hub/metadata-foundation.md);\n * everything else stays on the record (inline content JSON).\n *\n * `doc` (doc-mode = dir-per-record) decides where `meta.*` reads from:\n * application metadata is persisted on the FILE row (`cf.metadata`), not on\n * the parent dir row (`e.metadata`), so a doc-mode meta.* filter MUST read\n * `cf` first and fall back to the dir row — the SAME precedence the orderBy\n * path already uses (`COALESCE(cf.metadata, e.metadata)`). Without this a\n * meta.* WHERE/groupBy/aggregate on a `{uuid}/item.json` collection reads the\n * dir row's (always NULL) metadata and never matches. In file mode `e` IS the\n * record file, so `e.metadata` is correct and `cf` doesn't exist in the FROM.\n */\nfunction fieldSource(field: string, rec: string, doc: boolean): { src: string; path: string } {\n if (field.startsWith(\"meta.\")) {\n return {\n src: doc ? \"COALESCE(cf.metadata, e.metadata)\" : \"e.metadata\",\n path: jsonPathOf(field.slice(\"meta.\".length)),\n };\n }\n return { src: rec, path: jsonPathOf(field) };\n}\n\n/**\n * Compile one where leaf against the record expression. Type-strict by spec\n * value type, case-folded for strings — mirrors `whereMatches`.\n */\nfunction compileLeaf(rec: string, leaf: Record<string, unknown>, doc: boolean): SqlFragment {\n const { src, path: p } = fieldSource(leaf.field as string, rec, doc);\n const eqCond = (value: string | number | boolean): SqlFragment => {\n if (typeof value === \"boolean\") {\n return { sql: `json_type(${src}, ?) = '${value ? \"true\" : \"false\"}'`, args: [p] };\n }\n if (typeof value === \"number\") {\n return {\n sql: `(json_type(${src}, ?) IN ('integer','real') AND json_extract(${src}, ?) = ?)`,\n args: [p, p, value],\n };\n }\n return {\n sql: `(json_type(${src}, ?) = 'text' AND lower(json_extract(${src}, ?)) = lower(?))`,\n args: [p, p, value],\n };\n };\n\n if (\"eq\" in leaf) return eqCond(leaf.eq as string | number | boolean);\n\n if (\"in\" in leaf || \"notIn\" in leaf) {\n const values = (leaf.in ?? leaf.notIn) as Array<string | number | boolean>;\n const parts = values.map((v) => eqCond(v));\n const inSql = `(${parts.map((f) => f.sql).join(\" OR \")})`;\n const inArgs = parts.flatMap((f) => f.args);\n if (\"in\" in leaf) return { sql: inSql, args: inArgs };\n // notIn includes missing/null fields by definition.\n return {\n sql: `(json_type(${src}, ?) IS NULL OR json_type(${src}, ?) = 'null' OR NOT ${inSql})`,\n args: [p, p, ...inArgs],\n };\n }\n\n if (\"contains\" in leaf) {\n const v = leaf.contains as string | number;\n const member =\n typeof v === \"number\"\n ? { sql: `(je.type IN ('integer','real') AND je.value = ?)`, args: [v] as const }\n : { sql: `(je.type = 'text' AND lower(je.value) = lower(?))`, args: [v] as const };\n return {\n sql: `(json_type(${src}, ?) = 'array' AND EXISTS (SELECT 1 FROM json_each(json_extract(${src}, ?)) je WHERE ${member.sql}))`,\n args: [p, p, ...member.args],\n };\n }\n\n const op = \"gte\" in leaf ? \">=\" : \"<=\";\n const bound = (\"gte\" in leaf ? leaf.gte : leaf.lte) as string | number;\n if (typeof bound === \"number\") {\n return {\n sql: `(json_type(${src}, ?) IN ('integer','real') AND json_extract(${src}, ?) ${op} ?)`,\n args: [p, p, bound],\n };\n }\n return {\n sql: `(json_type(${src}, ?) = 'text' AND lower(json_extract(${src}, ?)) ${op} lower(?))`,\n args: [p, p, bound],\n };\n}\n\nexport interface TreeQueryStatements {\n rows: SqlFragment;\n /**\n * Exact-total count statement. Absent when the spec set `skipTotal` — the\n * engine then reports `total: -1`. (Same optional-statement shape as\n * `groups`, so each backend guards on presence identically.)\n */\n count?: SqlFragment;\n groups?: SqlFragment;\n /**\n * Aggregate-mode statements: a single SELECT producing one row per group\n * with `key__<i>` columns + each aggregation `as` value, plus a count of\n * group rows. When present, the engine ignores {@link rows} / {@link count}\n * and reads {@link aggregate.select} for both data + total.\n */\n aggregate?: {\n select: SqlFragment;\n countGroups: SqlFragment;\n /** ordered group-key names (same as {@link aggregateGroupKeyNames}). */\n groupKeys: string[];\n /** aggregation `as` names in the order they appear in the SELECT. */\n aggregations: AFSAggregateOp[];\n };\n /** The effective sort column + order (for keyset cursor construction). */\n order: { field: string; order: \"asc\" | \"desc\"; col: string };\n /**\n * Optional seek: find the 0-based position of the entry with `name = seekId`\n * in the sorted+filtered set. Present only when `spec.seekId` is set.\n * Returns one row `{ rn: number }` (the 0-based index), or zero rows when\n * the sought entry is absent from the filtered set.\n */\n seek?: SqlFragment;\n}\n\n/**\n * Build the three statements (page rows / exact count / optional groups) for\n * a collection query over direct children of `parent` in `scope`.\n *\n * Candidate rows:\n * - doc form: ALL direct children; record = LEFT-JOINed `<dir>/<doc>` inline\n * content (file children have no record — `notIn` still matches them, the\n * same as the reference evaluator's missing-field semantics).\n * - file form: direct FILE children; record = the row's own inline content.\n *\n * Malformed/missing inline JSON collapses to NULL record (json_valid guard),\n * never an engine error.\n */\n/**\n * THE single source of the children-candidates FROM clause — the doc\n * sub-file LEFT JOIN used by the query engine (`listChildren` shares it in\n * the join-free `buildChildrenFrom(undefined)` form post-T4.3), so the\n * dir-per-record join condition can never drift. The `e.kind = 'dir'` join\n * condition is\n * load-bearing: without it a FILE row `items/foo` would join\n * `items/fooitem.json` if such a path happened to exist.\n */\n// Without a hint, SQLite/D1 defaults `WHERE e.scope=? AND e.parent=?` (plus\n// the non-doc `kind='file'` residual, or the LEFT JOIN below) to a `scope=?`\n// PRIMARY KEY scan instead of `idx_entries_children (scope, parent, name)` —\n// measured on production D1 (arc-worker-staging): a 4.7k-row scope scanned\n// down to 0-4 rows once bounded by `parent`. `idx_entries_children` covers\n// both branches since neither adds a WHERE predicate the index can't bound.\nexport const CHILDREN_INDEX = \"idx_entries_children\";\n\n/**\n * Index that serves `ORDER BY <field>` NATIVELY for a `(scope, parent)`\n * children query (E-S-R rule: equality columns then the sort column). ctime /\n * mtime each have a matching `(scope, parent, <col>, name)` index so the sort\n * is index-only and `LIMIT` terminates early instead of reading the whole\n * directory into a temp b-tree. name / size / mimeType / meta.* have no such\n * index → keep `idx_entries_children` (they still sort, but that is unchanged\n * pre-existing behavior, not the aside feed's hot ctime path).\n *\n * Guard: `idx_entries_children` alone forces the (scope,parent,name) index,\n * which cannot order by ctime — so it silently reintroduced the exact\n * `USE TEMP B-TREE FOR ORDER BY` full-directory scan feed-sort-createdat\n * removed (regression caught by e2e-driver-s3-orderby). This routes the ROWS\n * query back onto the ordered index; the count/groups/aggregate FROM keeps the\n * children index (no ORDER BY there — the children hint bounds correctly).\n */\nexport function orderedChildrenIndex(orderField: string): string {\n if (orderField === \"ctime\") return \"idx_entries_parent_ctime\";\n if (orderField === \"mtime\") return \"idx_entries_parent_mtime\";\n return CHILDREN_INDEX;\n}\n\nexport function buildChildrenFrom(\n docFile: string | undefined,\n indexName: string = CHILDREN_INDEX,\n): SqlFragment {\n const hint = ` INDEXED BY ${indexName}`;\n if (!docFile) return { sql: `FROM ds_entries e${hint}`, args: [] };\n return {\n sql: `FROM ds_entries e${hint} LEFT JOIN ds_entries cf ON e.kind = 'dir' AND cf.scope = e.scope AND cf.path = e.path || ?`,\n args: [docFile],\n };\n}\n\n/**\n * SQL expression producing the canonical `time_bucket` string for a unix-ms\n * source expression. Matches the JS `bucketizeTimestamp()` shape exactly so\n * SQL ↔ memory backends stay byte-equal in the lockstep test:\n *\n * - `day` / `month` / `year` map to direct `strftime` formats.\n * - `week` produces ISO 8601 `YYYY-Www` via a Thursday-anchor formula. SQLite\n * has no `%V`, so we shift the date to its ISO Thursday with the\n * `'-3 days', 'weekday 4'` modifier pair, then count weeks from Jan-4's\n * Thursday of that ISO year (which is always in ISO week 1).\n *\n * `srcExpr` MUST be a SQL expression evaluating to a unix-ms integer (caller\n * guarantees — we wrap with `cast(... as integer) / 1000` for the\n * `'unixepoch'` modifier).\n */\nfunction buildTimeBucketExpr(srcExpr: string, granularity: AFSTimeGranularity): string {\n const unix = `(cast(${srcExpr} as integer) / 1000)`;\n switch (granularity) {\n case \"day\":\n return `strftime('%Y-%m-%d', ${unix}, 'unixepoch')`;\n case \"month\":\n return `strftime('%Y-%m', ${unix}, 'unixepoch')`;\n case \"year\":\n return `strftime('%Y', ${unix}, 'unixepoch')`;\n case \"week\": {\n // ISO Thursday of the row's week.\n const thu = `date(${unix}, 'unixepoch', '-3 days', 'weekday 4')`;\n // ISO Thursday of Jan-4 of that same ISO year (= Thursday of ISO week 1).\n const isoYear = `strftime('%Y', ${thu})`;\n const jan4Thu = `date(${isoYear} || '-01-04', '-3 days', 'weekday 4')`;\n const weekNum = `cast(((julianday(${thu}) - julianday(${jan4Thu})) / 7) + 1 as integer)`;\n return `(${isoYear} || '-W' || printf('%02d', ${weekNum}))`;\n }\n }\n}\n\n/**\n * Resolve a group-key dimension name to a SQL expression. `time_bucket` is\n * synthesized from `spec.timeBucketField` (default `meta.timestamp`).\n * Everything else is a field dot-path that goes through `fieldSource()` so\n * `meta.*` lands on `entries.metadata` and content paths stay on the\n * record. Returns the column expression and (for `time_bucket`) any\n * pre-extraction expression with the granularity baked in.\n */\nfunction groupKeyExpr(name: string, spec: TreeQuerySpec, rec: string): string {\n const doc = !!spec.doc;\n if (name === TIME_BUCKET_KEY) {\n const tbField = spec.timeBucketField ?? \"meta.timestamp\";\n const { src, path: p } = fieldSource(tbField, rec, doc);\n return buildTimeBucketExpr(`json_extract(${src}, '${p}')`, spec.timeGranularity!);\n }\n const { src, path: p } = fieldSource(name, rec, doc);\n return `json_extract(${src}, '${p}')`;\n}\n\n/**\n * Compile one aggregation op to its SQL expression. Numeric ops cast to\n * REAL to match the JS reference's `Number(v)` semantics — non-numeric\n * strings/null collapse to 0, BigNumber.toString() roundtrips correctly.\n * `count` is `COUNT(*)` (counts rows, including those with NULL fields).\n */\nfunction aggregateOpExpr(op: AFSAggregateOp, rec: string, doc: boolean): string {\n if (op.op === \"count\") return \"COUNT(*)\";\n const { src, path: p } = fieldSource(op.field!, rec, doc);\n const num = `CAST(json_extract(${src}, '${p}') AS REAL)`;\n switch (op.op) {\n case \"sum\":\n // COALESCE — SUM over no non-null rows is NULL in SQL; reference returns 0.\n return `COALESCE(SUM(${num}), 0)`;\n case \"avg\":\n return `AVG(${num})`; // NULL when all-null — reference returns null too\n case \"max\":\n return `MAX(${num})`;\n case \"min\":\n return `MIN(${num})`;\n }\n}\n\nexport function buildTreeQueryStatements(opts: {\n scope: string;\n parent: string;\n spec: TreeQuerySpec;\n}): TreeQueryStatements {\n const { scope, parent, spec } = opts;\n const doc = spec.doc;\n const { field, order } = effectiveOrder(spec);\n\n const rawRec = doc ? \"cf.content\" : \"e.content\";\n const rec = `(CASE WHEN ${rawRec} IS NOT NULL AND json_valid(${rawRec}) THEN ${rawRec} ELSE NULL END)`;\n\n // Resolve the orderBy field to a SQL column expression.\n // - Built-in fields → e.<column> via ENGINE_ORDER_COLUMNS\n // - meta.* fields → json_extract(e.metadata, '$.path') with the path\n // inlined (already validated against FIELD_PATH_RE, only [\\w-]+ chars\n // per segment, so inline is safe and avoids leaking a bind across\n // ORDER BY which buildOrderedQueryParts can't carry).\n // `col` is used both as the SELECT projection alias (legacy ordering\n // columns) and the ORDER BY expression; `colExpr` carries the actual\n // SQL when the column isn't a plain entries column.\n let col: string;\n let colExpr: string;\n let nullable: boolean;\n if (field.startsWith(\"meta.\")) {\n const metaPath = jsonPathOf(field.slice(\"meta.\".length));\n // jsonPathOf produces `$.\"seg\".\"seg\"`. Single-quote wrap is safe —\n // segments are restricted to [\\w-]+ by FIELD_PATH_RE so they can't\n // contain a single quote.\n // doc mode: prefer the JOIN'd file row's metadata over the dir row's\n // (mirrors the SELECT change above + content's `cf.content` precedence).\n // Without this, dir-per-record collections (`items/{uuid}/item.json`)\n // would orderBy a NULL column even when the file row has the field.\n const metaSrc = doc\n ? `COALESCE(json_extract(cf.metadata, '${metaPath}'), json_extract(e.metadata, '${metaPath}'))`\n : `json_extract(e.metadata, '${metaPath}')`;\n colExpr = metaSrc;\n col = colExpr;\n nullable = true; // meta is always nullable\n } else {\n col = ENGINE_ORDER_COLUMNS[field]!;\n colExpr = `e.${col}`;\n nullable = col === \"mime_type\";\n }\n\n // count / groups / aggregate: no ORDER BY, so the (scope,parent,name)\n // children index bounds them correctly.\n const from = buildChildrenFrom(doc);\n const fromSql = from.sql;\n const fromArgs = from.args;\n // ROWS: pick the index that serves ORDER BY <field> natively (ctime/mtime →\n // idx_entries_parent_(c|m)time) so the sort is index-only and LIMIT stops\n // early — otherwise the children index forces a whole-directory temp-b-tree\n // sort (issue #1104 / feed-sort-createdat regression).\n const rowsFrom = buildChildrenFrom(doc, orderedChildrenIndex(field));\n\n const baseWhere: string[] = [\"e.scope = ?\", \"e.parent = ?\"];\n const baseArgs: Array<string | number> = [scope, parent];\n if (!doc) baseWhere.push(\"e.kind = 'file'\");\n\n const whereLeaves = normalizeWhere(spec.where);\n for (const leaf of whereLeaves) {\n const frag = compileLeaf(rec, leaf as unknown as Record<string, unknown>, !!doc);\n baseWhere.push(frag.sql);\n baseArgs.push(...frag.args);\n }\n\n const whereSql = baseWhere.join(\" AND \");\n\n // Page rows — keyset (v2 cursor) or offset pagination after ordering.\n const parsed = parseTreeCursor(spec.cursor, true);\n const parts = buildOrderedQueryParts({\n col: colExpr,\n nullable,\n order,\n tieCol: \"e.name\",\n cursor: parsed.kind === \"v2\" ? parsed.cursor : undefined,\n });\n const limit = spec.count ? 0 : (spec.limit ?? DEFAULT_QUERY_LIMIT);\n const offset = spec.offset ?? 0;\n // doc mode: pull metadata from the JOIN'd sub-file (`cf`) too, mirroring\n // the way `content` already flows from `cf.content`. Application metadata\n // (e.g. `createdAt` on a `{uuid}/item.json` record) is persisted on the\n // FILE row's `entries.metadata`, not on the parent directory's row —\n // taking only `e.metadata` here would surface a stale NULL even when the\n // doc file has it. The row mapper prefers `cf_metadata` over `e.metadata`\n // when both are present.\n const cfCols = doc\n ? \", cf.content AS cf_content, cf.cid AS cf_cid, cf.metadata AS cf_metadata\"\n : \", e.content AS content\";\n let rowsSql =\n `SELECT e.path, e.name, e.kind, e.cid, e.size, e.mtime, e.ctime, e.mime_type, e.metadata AS metadata${cfCols} ` +\n `${rowsFrom.sql} WHERE ${whereSql}`;\n const rowsArgs = [...rowsFrom.args, ...baseArgs];\n if (parts.cursorWhereSql) {\n rowsSql += ` AND ${parts.cursorWhereSql}`;\n rowsArgs.push(...parts.cursorArgs);\n }\n rowsSql += ` ORDER BY ${parts.orderSql} LIMIT ? OFFSET ?`;\n rowsArgs.push(limit + 1, offset);\n\n // Count-side join-drop (feed-sort §5.4): in doc mode the `cf` LEFT JOIN is\n // 1:1 (each dir row joins at most one `<dir><docFile>` row), so a count with\n // NO record-referencing where equals the count of `e` rows alone — drop the\n // join to halve the rows read (feed All-tab `total`). A where leaf compiles\n // against the joined record (`cf.content` / `cf.metadata`), so a FILTERED\n // count (Articles/Websites/tag/collection) MUST keep the join. Non-doc mode\n // is already join-free, so this only diverges for doc + unfiltered.\n const countFrom = doc && whereLeaves.length === 0 ? buildChildrenFrom(undefined) : from;\n const statements: TreeQueryStatements = {\n rows: { sql: rowsSql, args: rowsArgs },\n // skipTotal (afs-collection-query): drop the count pass entirely — this is\n // the full-directory scan the hot feed read path pays on every load. The\n // engines report total: -1 when this is absent.\n count: spec.skipTotal\n ? undefined\n : {\n sql: `SELECT COUNT(*) AS cnt ${countFrom.sql} WHERE ${whereSql}`,\n args: [...countFrom.args, ...baseArgs],\n },\n order: { field, order, col },\n };\n\n // Aggregate-mode compilation (G2.D). The aggregate SELECT replaces the\n // `rows`/`count`/`groups` SQL paths — see TreeQueryStatements.aggregate.\n if (spec.aggregations) {\n const groupKeys = aggregateGroupKeyNames(spec as AFSCollectionQuerySpec);\n const keySelects: string[] = [];\n for (let i = 0; i < groupKeys.length; i++) {\n keySelects.push(`${groupKeyExpr(groupKeys[i]!, spec, rec)} AS k_${i}`);\n }\n const aggSelects: string[] = [];\n for (const agg of spec.aggregations) {\n // `as` validated as a simple identifier — safe to inline.\n aggSelects.push(`${aggregateOpExpr(agg, rec, !!doc)} AS ${agg.as}`);\n }\n\n // ORDER BY resolves to either a group-key (k_<i>) or an aggregation alias.\n const asSet = new Set(spec.aggregations.map((a) => a.as));\n const groupSet = new Map(groupKeys.map((k, i) => [k, `k_${i}`]));\n const orderParts: string[] = [];\n for (const [orderField, dir] of spec.orderBy ?? []) {\n const expr = groupSet.get(orderField) ?? (asSet.has(orderField) ? orderField : null);\n if (expr) {\n // SQLite NULL ordering: ASC → NULLS LAST via `<col> IS NULL` first.\n orderParts.push(\n `${expr} IS NULL ${dir === \"asc\" ? \"ASC\" : \"DESC\"}, ${expr} ${dir === \"asc\" ? \"ASC\" : \"DESC\"}`,\n );\n }\n }\n // Deterministic tiebreak by joined group key (k_0, k_1, ... in that order).\n for (let i = 0; i < groupKeys.length; i++) orderParts.push(`k_${i} ASC`);\n const orderSql = orderParts.length > 0 ? ` ORDER BY ${orderParts.join(\", \")}` : \"\";\n\n const groupBySql =\n groupKeys.length > 0 ? ` GROUP BY ${groupKeys.map((_, i) => `k_${i}`).join(\", \")}` : \"\";\n const aggLimit = spec.limit ?? DEFAULT_QUERY_LIMIT;\n const aggOffset = spec.offset ?? 0;\n const allSelects = [...keySelects, ...aggSelects].join(\", \");\n const aggregateRowsSql = `SELECT ${allSelects} ${fromSql} WHERE ${whereSql}${groupBySql}${orderSql} LIMIT ? OFFSET ?`;\n\n const countGroupsSql =\n groupKeys.length === 0\n ? `SELECT 1 AS cnt ${fromSql} WHERE ${whereSql}`\n : `SELECT COUNT(*) AS cnt FROM (SELECT ${keySelects.join(\", \")} ${fromSql} WHERE ${whereSql} GROUP BY ${groupKeys.map((_, i) => `k_${i}`).join(\", \")})`;\n\n statements.aggregate = {\n select: { sql: aggregateRowsSql, args: [...fromArgs, ...baseArgs, aggLimit, aggOffset] },\n countGroups: {\n sql: countGroupsSql,\n args: groupKeys.length === 0 ? [...fromArgs, ...baseArgs] : [...fromArgs, ...baseArgs],\n },\n groupKeys,\n aggregations: spec.aggregations,\n };\n return statements;\n }\n\n if (typeof spec.groupBy === \"string\") {\n const gp = jsonPathOf(spec.groupBy);\n // The scalar branch must keep booleans typed: json_extract converts JSON\n // true/false to SQL 1/0, so json_array(json_extract(...)) would group\n // booleans as integers (lockstep divergence vs the JS executor). Literal\n // '[true]'/'[false]' arrays re-enter json_each with the JSON type intact.\n statements.groups = {\n sql:\n `SELECT je.type AS t, CASE WHEN je.type = 'text' THEN lower(je.value) ELSE je.value END AS v, COUNT(*) AS cnt ` +\n `${fromSql}, json_each(CASE json_type(${rec}, ?) ` +\n `WHEN 'array' THEN json_extract(${rec}, ?) ` +\n `WHEN 'true' THEN '[true]' WHEN 'false' THEN '[false]' ` +\n `ELSE json_array(json_extract(${rec}, ?)) END) je ` +\n `WHERE ${whereSql} GROUP BY t, v`,\n args: [...fromArgs, gp, gp, gp, ...baseArgs],\n };\n }\n\n // Seek: find the 0-based position of the entry with name = seekId in the\n // sorted+filtered set. Uses ROW_NUMBER() window function (SQLite 3.25+ /\n // all D1 versions) so the ORDER BY is identical to the page query.\n if (spec.seekId !== undefined) {\n const seekSql =\n `SELECT rn FROM (` +\n `SELECT e.name AS nm, (ROW_NUMBER() OVER (ORDER BY ${parts.orderSql}) - 1) AS rn ` +\n `${fromSql} WHERE ${whereSql}` +\n `) AS _seek WHERE _seek.nm = ?`;\n statements.seek = { sql: seekSql, args: [...fromArgs, ...baseArgs, spec.seekId] };\n }\n\n return statements;\n}\n\n/**\n * Assemble a TreeQueryResult from raw SQL rows — shared by SQLiteTreeIndex\n * and D1TreeIndex so row mapping / hasMore slicing / keyset-cursor\n * construction stay one implementation across SQL backends. `rawRows` is\n * the (limit+1)-sized page from `TreeQueryStatements.rows`.\n */\nexport function buildTreeQueryResultFromSql(opts: {\n rawRows: Array<Record<string, unknown>>;\n total: number;\n groupRows?: Array<{ t: unknown; v: unknown; cnt: unknown }>;\n spec: TreeQuerySpec;\n orderField: string;\n seekOffset?: number;\n}): TreeQueryResult {\n const { rawRows, total, groupRows, spec, orderField, seekOffset } = opts;\n const limit = spec.count ? 0 : (spec.limit ?? DEFAULT_QUERY_LIMIT);\n const num = (v: unknown): number =>\n typeof v === \"bigint\" ? Number(v) : ((v as number | null) ?? 0);\n\n const hasMore = rawRows.length > limit;\n const pageRows = hasMore ? rawRows.slice(0, limit) : rawRows;\n const rows: TreeQueryRow[] = pageRows.map((r) => {\n const row: TreeQueryRow = {\n path: r.path as string,\n name: r.name as string,\n kind: (r.kind as string) === \"dir\" ? \"dir\" : \"file\",\n cid: (r.cid as string | null) ?? \"\",\n size: num(r.size),\n mtime: num(r.mtime),\n ctime: num(r.ctime),\n mimeType: (r.mime_type as string | null) ?? undefined,\n };\n if (r.content != null) row.content = r.content as string;\n if (r.cf_content != null) {\n row.content = r.cf_content as string;\n row.contentCid = (r.cf_cid as string | null) ?? undefined;\n }\n // doc-mode metadata source: the file row (`cf`) wins over the dir row\n // (`e`), matching how `content` is sourced. Falls back to dir-row\n // metadata when the doc file has none (e.g. legacy data written before\n // Phase 1) so behavior degrades to \"no application metadata\" rather\n // than vanishes mid-flight.\n const metaRaw = r.cf_metadata ?? r.metadata;\n const meta = parseTreeEntryMetadata(metaRaw);\n if (meta !== undefined) row.metadata = meta;\n return row;\n });\n\n const result: TreeQueryResult = { rows, total };\n if (groupRows) result.groups = mapGroupRows(groupRows);\n const last = rows[rows.length - 1];\n if (hasMore && last && !spec.count) {\n result.cursor = buildKeysetCursor(rowSortValue(last, orderField), last.name);\n }\n if (seekOffset !== undefined) result.seekOffset = seekOffset;\n return result;\n}\n\n/** Sort-column value of a row (NULL mimeType stays null — sorts last). */\nexport function rowSortValue(row: TreeQueryRow, field: string): string | number | null {\n if (field.startsWith(\"meta.\")) {\n // meta.* dispatch — read from the parsed metadata column. Missing /\n // null sorts last regardless of direction (same as the SQL path's\n // null-handling in buildOrderedQueryParts).\n let cur: unknown = row.metadata;\n for (const seg of field.slice(\"meta.\".length).split(\".\")) {\n if (cur == null || typeof cur !== \"object\" || Array.isArray(cur)) return null;\n cur = (cur as Record<string, unknown>)[seg];\n }\n if (cur == null) return null;\n if (typeof cur === \"string\" || typeof cur === \"number\") return cur;\n return String(cur);\n }\n switch (field) {\n case \"name\":\n return row.name;\n case \"path\":\n return row.path;\n case \"mimeType\":\n return row.mimeType ?? null;\n case \"size\":\n return row.size;\n case \"ctime\":\n return row.ctime;\n case \"mtime\":\n return row.mtime;\n default:\n return null;\n }\n}\n\n/** Canonical group ordering: count desc, then String(value) asc. */\nfunction sortGroups(groups: AFSCollectionQueryGroup[]): AFSCollectionQueryGroup[] {\n return groups.sort((a, b) => b.count - a.count || String(a.value).localeCompare(String(b.value)));\n}\n\n/**\n * Map raw aggregate-mode SQL rows into the canonical `AFSAggregateGroup[]`.\n * Each row has `k_0..k_N` for group dimensions (in `groupKeys` order) plus\n * each aggregation alias. Numeric aggregates that come back as bigint are\n * coerced to Number so callers don't have to worry about wire types.\n */\nexport function mapAggregateRows(\n rows: Array<Record<string, unknown>>,\n groupKeys: string[],\n aggregations: AFSAggregateOp[],\n): AFSAggregateGroup[] {\n const coerce = (v: unknown): unknown => {\n if (typeof v === \"bigint\") return Number(v);\n return v;\n };\n return rows.map((r) => {\n const key: Record<string, string | number | boolean | null> = {};\n for (let i = 0; i < groupKeys.length; i++) {\n const v = coerce(r[`k_${i}`]);\n key[groupKeys[i]!] =\n v === null || v === undefined\n ? null\n : typeof v === \"string\" || typeof v === \"number\" || typeof v === \"boolean\"\n ? (v as string | number | boolean)\n : String(v);\n }\n const out: AFSAggregateGroup = { key };\n for (const agg of aggregations) {\n const raw = coerce(r[agg.as]);\n if (agg.op === \"count\") {\n out[agg.as] = typeof raw === \"number\" ? raw : 0;\n } else if (agg.op === \"sum\") {\n // SUM with COALESCE → number, never null. Cast just in case.\n out[agg.as] = typeof raw === \"number\" ? raw : 0;\n } else {\n // avg / max / min: null when no non-null inputs.\n out[agg.as] = typeof raw === \"number\" ? raw : null;\n }\n }\n return out;\n });\n}\n\n/** Map raw group rows (t, v, cnt) into the canonical groups array. */\nexport function mapGroupRows(\n rows: Array<{ t: unknown; v: unknown; cnt: unknown }>,\n): AFSCollectionQueryGroup[] {\n const merged = new Map<string, AFSCollectionQueryGroup>();\n for (const r of rows) {\n const t = String(r.t);\n let value: AFSCollectionQueryGroup[\"value\"];\n if (t === \"text\") value = String(r.v);\n else if (t === \"integer\" || t === \"real\") value = Number(r.v);\n else if (t === \"true\") value = true;\n else if (t === \"false\") value = false;\n else value = null; // null / object / array members fold into the null group\n const key = `${typeof value}:${String(value)}`;\n const count = typeof r.cnt === \"bigint\" ? Number(r.cnt) : (r.cnt as number);\n const existing = merged.get(key);\n if (existing) existing.count += count;\n else merged.set(key, { value, count });\n }\n return sortGroups([...merged.values()]);\n}\n\n// ── JS executor (MemoryTreeIndex) ───────────────────────────────────────────\n\n/** A candidate row plus its parsed record document (undefined = no record). */\nexport interface TreeQueryCandidate extends TreeQueryRow {\n record?: unknown;\n}\n\n/** Parse a record text into a JSON value; malformed → undefined (no record). */\nexport function parseRecordText(text: string | undefined): unknown {\n if (text === undefined) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction foldPrimitive(value: unknown): AFSCollectionQueryGroup[\"value\"] {\n if (typeof value === \"string\") return value.toLowerCase();\n if (typeof value === \"number\" || typeof value === \"boolean\") return value;\n return null;\n}\n\n/** Group-count with unnest semantics over parsed records (lockstep with SQL). */\nexport function computeGroups(records: Array<unknown>, groupBy: string): AFSCollectionQueryGroup[] {\n const counts = new Map<string, AFSCollectionQueryGroup>();\n const bump = (raw: unknown) => {\n const value = foldPrimitive(raw);\n const key = `${typeof value}:${String(value)}`;\n const existing = counts.get(key);\n if (existing) existing.count += 1;\n else counts.set(key, { value, count: 1 });\n };\n for (const record of records) {\n const value = resolveFieldPath(record, groupBy);\n if (Array.isArray(value)) {\n for (const member of value) bump(member);\n } else {\n bump(value ?? null);\n }\n }\n return sortGroups([...counts.values()]);\n}\n\n/**\n * Project `select` fields out of a parsed record (nested shape for\n * dot-paths) — same projection rule as the core reference evaluator.\n */\nexport function projectRecordFields(record: unknown, select: string[]): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const field of select) {\n const value = resolveFieldPath(record, field);\n if (value === undefined) continue;\n const segments = field.split(\".\");\n let target = out;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!;\n if (typeof target[seg] !== \"object\" || target[seg] === null) target[seg] = {};\n target = target[seg] as Record<string, unknown>;\n }\n target[segments.at(-1)!] = value;\n }\n return out;\n}\n\n/**\n * Execute a tree query over in-memory candidates — semantics locked to the\n * SQL path by the lockstep test. `where` uses `whereMatchesItem` so that\n * `meta.*` paths resolve against the candidate's `metadata` column the same\n * way the SQL path reads `entries.metadata`.\n */\nexport function runTreeQueryInMemory(\n candidates: TreeQueryCandidate[],\n spec: TreeQuerySpec,\n): TreeQueryResult {\n const filtered = spec.where\n ? candidates.filter((c) =>\n whereMatchesItem(\n {\n // Synthesize a minimal AFSEntry shape: `meta` is what the matcher\n // reads for `meta.*` field paths.\n entry: { id: c.path, path: c.path, meta: c.metadata } as never,\n record: c.record,\n },\n spec.where,\n ),\n )\n : [...candidates];\n\n // Aggregate-mode delegates to the core reference evaluator — it IS the\n // authoritative semantics SQL backends lockstep against. We carry over\n // the where-filter we've already done and pass through the rest of the\n // spec verbatim.\n if (spec.aggregations) {\n const items = filtered.map((c) => ({\n entry: { id: c.path, path: c.path, meta: c.metadata } as never,\n record: c.record,\n }));\n const result = evaluateCollectionQuery(items, spec as AFSCollectionQuerySpec);\n return {\n rows: [],\n total: result.total,\n aggregateGroups: result.aggregateGroups,\n };\n }\n\n // skipTotal parity: the memory backend has no scan cost, but it MUST report\n // the same -1 sentinel as the SQL backends so the lockstep test stays green.\n const total = spec.skipTotal ? -1 : filtered.length;\n const groups =\n typeof spec.groupBy === \"string\"\n ? computeGroups(\n filtered.map((c) => c.record),\n spec.groupBy,\n )\n : undefined;\n\n const { field, order } = effectiveOrder(spec);\n filtered.sort((a, b) => {\n const cmp = compareTreeSortValues(rowSortValue(a, field), rowSortValue(b, field), order);\n if (cmp !== 0) return cmp;\n const tie = a.name === b.name ? 0 : a.name < b.name ? -1 : 1;\n return order === \"desc\" ? -tie : tie;\n });\n\n const parsed = parseTreeCursor(spec.cursor, true);\n const afterCursor =\n parsed.kind === \"v2\"\n ? filtered.filter((row) =>\n keysetAfter(rowSortValue(row, field), row.name, parsed.cursor as TreeKeysetCursor, order),\n )\n : filtered;\n\n // seekId: find the 0-based index of the entry with name = seekId in the\n // sorted+filtered set (cursor does not affect the seek position — it finds\n // the absolute index in the full filtered+sorted array).\n let seekOffset: number | undefined;\n if (spec.seekId !== undefined) {\n seekOffset = filtered.findIndex((c) => c.name === spec.seekId);\n }\n\n const offset = spec.offset ?? 0;\n const limit = spec.count ? 0 : (spec.limit ?? DEFAULT_QUERY_LIMIT);\n const page = afterCursor.slice(offset, offset + limit);\n const hasMore = offset + limit < afterCursor.length;\n const last = page[page.length - 1];\n\n const result: TreeQueryResult = {\n rows: page.map(({ record: _record, ...row }) => row),\n total,\n };\n if (groups) result.groups = groups;\n if (hasMore && last && !spec.count) {\n result.cursor = buildKeysetCursor(rowSortValue(last, field), last.name);\n }\n if (seekOffset !== undefined) result.seekOffset = seekOffset;\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAqHA,MAAM,uBAA+C;CACnD,MAAM;CACN,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,UAAU;CACX;AAED,MAAM,sBAAsB;;;;;;;;;AAU5B,SAAgB,oBAAoB,MAAoC;AACtE,KAAI,KAAK,SAAS,OAChB,OAAM,IAAI,mBAAmB,2DAA2D;AAE1F,KAAI,KAAK,aAAc;AACvB,KAAI,KAAK,WAAW,KAAK,QAAQ,SAAS,EACxC,OAAM,IAAI,mBACR,wEACD;CAEH,MAAM,QAAQ,KAAK,UAAU,KAAK;AAClC,KAAI,UAAU,UAAa,CAAC,qBAAqB,UAAU,CAAC,MAAM,WAAW,QAAQ,CACnF,OAAM,IAAI,mBAAmB,kDAAkD,QAAQ;;;AAK3F,SAAS,eAAe,MAA+D;CACrF,MAAM,QAAQ,KAAK,UAAU;AAC7B,QAAO;EAAE,OAAO,QAAQ,MAAM;EAAQ,OAAO,QAAQ,MAAM;EAAO;;;AAMpE,SAAS,WAAW,OAAuB;AACzC,QAAO,KAAK,MACT,MAAM,IAAI,CACV,KAAK,QAAQ,IAAI,IAAI,GAAG,CACxB,KAAK,IAAI;;;;;;;;;;;;;;;;;AAuBd,SAAS,YAAY,OAAe,KAAa,KAA6C;AAC5F,KAAI,MAAM,WAAW,QAAQ,CAC3B,QAAO;EACL,KAAK,MAAM,sCAAsC;EACjD,MAAM,WAAW,MAAM,MAAM,EAAe,CAAC;EAC9C;AAEH,QAAO;EAAE,KAAK;EAAK,MAAM,WAAW,MAAM;EAAE;;;;;;AAO9C,SAAS,YAAY,KAAa,MAA+B,KAA2B;CAC1F,MAAM,EAAE,KAAK,MAAM,MAAM,YAAY,KAAK,OAAiB,KAAK,IAAI;CACpE,MAAM,UAAU,UAAkD;AAChE,MAAI,OAAO,UAAU,UACnB,QAAO;GAAE,KAAK,aAAa,IAAI,UAAU,QAAQ,SAAS,QAAQ;GAAI,MAAM,CAAC,EAAE;GAAE;AAEnF,MAAI,OAAO,UAAU,SACnB,QAAO;GACL,KAAK,cAAc,IAAI,8CAA8C,IAAI;GACzE,MAAM;IAAC;IAAG;IAAG;IAAM;GACpB;AAEH,SAAO;GACL,KAAK,cAAc,IAAI,uCAAuC,IAAI;GAClE,MAAM;IAAC;IAAG;IAAG;IAAM;GACpB;;AAGH,KAAI,QAAQ,KAAM,QAAO,OAAO,KAAK,GAAgC;AAErE,KAAI,QAAQ,QAAQ,WAAW,MAAM;EAEnC,MAAM,SADU,KAAK,MAAM,KAAK,OACX,KAAK,MAAM,OAAO,EAAE,CAAC;EAC1C,MAAM,QAAQ,IAAI,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,KAAK,OAAO,CAAC;EACvD,MAAM,SAAS,MAAM,SAAS,MAAM,EAAE,KAAK;AAC3C,MAAI,QAAQ,KAAM,QAAO;GAAE,KAAK;GAAO,MAAM;GAAQ;AAErD,SAAO;GACL,KAAK,cAAc,IAAI,4BAA4B,IAAI,uBAAuB,MAAM;GACpF,MAAM;IAAC;IAAG;IAAG,GAAG;IAAO;GACxB;;AAGH,KAAI,cAAc,MAAM;EACtB,MAAM,IAAI,KAAK;EACf,MAAM,SACJ,OAAO,MAAM,WACT;GAAE,KAAK;GAAoD,MAAM,CAAC,EAAE;GAAW,GAC/E;GAAE,KAAK;GAAqD,MAAM,CAAC,EAAE;GAAW;AACtF,SAAO;GACL,KAAK,cAAc,IAAI,kEAAkE,IAAI,iBAAiB,OAAO,IAAI;GACzH,MAAM;IAAC;IAAG;IAAG,GAAG,OAAO;IAAK;GAC7B;;CAGH,MAAM,KAAK,SAAS,OAAO,OAAO;CAClC,MAAM,QAAS,SAAS,OAAO,KAAK,MAAM,KAAK;AAC/C,KAAI,OAAO,UAAU,SACnB,QAAO;EACL,KAAK,cAAc,IAAI,8CAA8C,IAAI,OAAO,GAAG;EACnF,MAAM;GAAC;GAAG;GAAG;GAAM;EACpB;AAEH,QAAO;EACL,KAAK,cAAc,IAAI,uCAAuC,IAAI,QAAQ,GAAG;EAC7E,MAAM;GAAC;GAAG;GAAG;GAAM;EACpB;;;;;;;;;;;;;;;;;;;;;;;;AAiEH,MAAa,iBAAiB;;;;;;;;;;;;;;;;;AAkB9B,SAAgB,qBAAqB,YAA4B;AAC/D,KAAI,eAAe,QAAS,QAAO;AACnC,KAAI,eAAe,QAAS,QAAO;AACnC,QAAO;;AAGT,SAAgB,kBACd,SACA,YAAoB,gBACP;CACb,MAAM,OAAO,eAAe;AAC5B,KAAI,CAAC,QAAS,QAAO;EAAE,KAAK,oBAAoB;EAAQ,MAAM,EAAE;EAAE;AAClE,QAAO;EACL,KAAK,oBAAoB,KAAK;EAC9B,MAAM,CAAC,QAAQ;EAChB;;;;;;;;;;;;;;;;;AAkBH,SAAS,oBAAoB,SAAiB,aAAyC;CACrF,MAAM,OAAO,SAAS,QAAQ;AAC9B,SAAQ,aAAR;EACE,KAAK,MACH,QAAO,wBAAwB,KAAK;EACtC,KAAK,QACH,QAAO,qBAAqB,KAAK;EACnC,KAAK,OACH,QAAO,kBAAkB,KAAK;EAChC,KAAK,QAAQ;GAEX,MAAM,MAAM,QAAQ,KAAK;GAEzB,MAAM,UAAU,kBAAkB,IAAI;AAGtC,UAAO,IAAI,QAAQ,6BADH,oBAAoB,IAAI,gBADxB,QAAQ,QAAQ,uCACgC,yBACR;;;;;;;;;;;;AAa9D,SAAS,aAAa,MAAc,MAAqB,KAAqB;CAC5E,MAAM,MAAM,CAAC,CAAC,KAAK;AACnB,KAAI,SAAS,iBAAiB;EAE5B,MAAM,EAAE,YAAK,MAAMA,QAAM,YADT,KAAK,mBAAmB,kBACM,KAAK,IAAI;AACvD,SAAO,oBAAoB,gBAAgBC,MAAI,KAAKD,IAAE,KAAK,KAAK,gBAAiB;;CAEnF,MAAM,EAAE,KAAK,MAAM,MAAM,YAAY,MAAM,KAAK,IAAI;AACpD,QAAO,gBAAgB,IAAI,KAAK,EAAE;;;;;;;;AASpC,SAAS,gBAAgB,IAAoB,KAAa,KAAsB;AAC9E,KAAI,GAAG,OAAO,QAAS,QAAO;CAC9B,MAAM,EAAE,KAAK,MAAM,MAAM,YAAY,GAAG,OAAQ,KAAK,IAAI;CACzD,MAAM,MAAM,qBAAqB,IAAI,KAAK,EAAE;AAC5C,SAAQ,GAAG,IAAX;EACE,KAAK,MAEH,QAAO,gBAAgB,IAAI;EAC7B,KAAK,MACH,QAAO,OAAO,IAAI;EACpB,KAAK,MACH,QAAO,OAAO,IAAI;EACpB,KAAK,MACH,QAAO,OAAO,IAAI;;;AAIxB,SAAgB,yBAAyB,MAIjB;CACtB,MAAM,EAAE,OAAO,QAAQ,SAAS;CAChC,MAAM,MAAM,KAAK;CACjB,MAAM,EAAE,OAAO,UAAU,eAAe,KAAK;CAE7C,MAAM,SAAS,MAAM,eAAe;CACpC,MAAM,MAAM,cAAc,OAAO,8BAA8B,OAAO,SAAS,OAAO;CAWtF,IAAI;CACJ,IAAI;CACJ,IAAI;AACJ,KAAI,MAAM,WAAW,QAAQ,EAAE;EAC7B,MAAM,WAAW,WAAW,MAAM,MAAM,EAAe,CAAC;AAWxD,YAHgB,MACZ,uCAAuC,SAAS,gCAAgC,SAAS,OACzF,6BAA6B,SAAS;AAE1C,QAAM;AACN,aAAW;QACN;AACL,QAAM,qBAAqB;AAC3B,YAAU,KAAK;AACf,aAAW,QAAQ;;CAKrB,MAAM,OAAO,kBAAkB,IAAI;CACnC,MAAM,UAAU,KAAK;CACrB,MAAM,WAAW,KAAK;CAKtB,MAAM,WAAW,kBAAkB,KAAK,qBAAqB,MAAM,CAAC;CAEpE,MAAM,YAAsB,CAAC,eAAe,eAAe;CAC3D,MAAM,WAAmC,CAAC,OAAO,OAAO;AACxD,KAAI,CAAC,IAAK,WAAU,KAAK,kBAAkB;CAE3C,MAAM,cAAc,eAAe,KAAK,MAAM;AAC9C,MAAK,MAAM,QAAQ,aAAa;EAC9B,MAAM,OAAO,YAAY,KAAK,MAA4C,CAAC,CAAC,IAAI;AAChF,YAAU,KAAK,KAAK,IAAI;AACxB,WAAS,KAAK,GAAG,KAAK,KAAK;;CAG7B,MAAM,WAAW,UAAU,KAAK,QAAQ;CAGxC,MAAM,SAAS,gBAAgB,KAAK,QAAQ,KAAK;CACjD,MAAM,QAAQ,uBAAuB;EACnC,KAAK;EACL;EACA;EACA,QAAQ;EACR,QAAQ,OAAO,SAAS,OAAO,OAAO,SAAS;EAChD,CAAC;CACF,MAAM,QAAQ,KAAK,QAAQ,IAAK,KAAK,SAAS;CAC9C,MAAM,SAAS,KAAK,UAAU;CAW9B,IAAI,UACF,sGAJa,MACX,6EACA,yBAE2G,GAC1G,SAAS,IAAI,SAAS;CAC3B,MAAM,WAAW,CAAC,GAAG,SAAS,MAAM,GAAG,SAAS;AAChD,KAAI,MAAM,gBAAgB;AACxB,aAAW,QAAQ,MAAM;AACzB,WAAS,KAAK,GAAG,MAAM,WAAW;;AAEpC,YAAW,aAAa,MAAM,SAAS;AACvC,UAAS,KAAK,QAAQ,GAAG,OAAO;CAShC,MAAM,YAAY,OAAO,YAAY,WAAW,IAAI,kBAAkB,OAAU,GAAG;CACnF,MAAM,aAAkC;EACtC,MAAM;GAAE,KAAK;GAAS,MAAM;GAAU;EAItC,OAAO,KAAK,YACR,SACA;GACE,KAAK,0BAA0B,UAAU,IAAI,SAAS;GACtD,MAAM,CAAC,GAAG,UAAU,MAAM,GAAG,SAAS;GACvC;EACL,OAAO;GAAE;GAAO;GAAO;GAAK;EAC7B;AAID,KAAI,KAAK,cAAc;EACrB,MAAM,YAAY,uBAAuB,KAA+B;EACxE,MAAM,aAAuB,EAAE;AAC/B,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACpC,YAAW,KAAK,GAAG,aAAa,UAAU,IAAK,MAAM,IAAI,CAAC,QAAQ,IAAI;EAExE,MAAM,aAAuB,EAAE;AAC/B,OAAK,MAAM,OAAO,KAAK,aAErB,YAAW,KAAK,GAAG,gBAAgB,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK;EAIrE,MAAM,QAAQ,IAAI,IAAI,KAAK,aAAa,KAAK,MAAM,EAAE,GAAG,CAAC;EACzD,MAAM,WAAW,IAAI,IAAI,UAAU,KAAK,GAAG,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;EAChE,MAAM,aAAuB,EAAE;AAC/B,OAAK,MAAM,CAAC,YAAY,QAAQ,KAAK,WAAW,EAAE,EAAE;GAClD,MAAM,OAAO,SAAS,IAAI,WAAW,KAAK,MAAM,IAAI,WAAW,GAAG,aAAa;AAC/E,OAAI,KAEF,YAAW,KACT,GAAG,KAAK,WAAW,QAAQ,QAAQ,QAAQ,OAAO,IAAI,KAAK,GAAG,QAAQ,QAAQ,QAAQ,SACvF;;AAIL,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IAAK,YAAW,KAAK,KAAK,EAAE,MAAM;EACxE,MAAM,WAAW,WAAW,SAAS,IAAI,aAAa,WAAW,KAAK,KAAK,KAAK;EAEhF,MAAM,aACJ,UAAU,SAAS,IAAI,aAAa,UAAU,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC,KAAK,KAAK,KAAK;EACvF,MAAM,WAAW,KAAK,SAAS;EAC/B,MAAM,YAAY,KAAK,UAAU;EAEjC,MAAM,mBAAmB,UADN,CAAC,GAAG,YAAY,GAAG,WAAW,CAAC,KAAK,KAAK,CACd,GAAG,QAAQ,SAAS,WAAW,aAAa,SAAS;EAEnG,MAAM,iBACJ,UAAU,WAAW,IACjB,mBAAmB,QAAQ,SAAS,aACpC,uCAAuC,WAAW,KAAK,KAAK,CAAC,GAAG,QAAQ,SAAS,SAAS,YAAY,UAAU,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC,KAAK,KAAK,CAAC;AAEzJ,aAAW,YAAY;GACrB,QAAQ;IAAE,KAAK;IAAkB,MAAM;KAAC,GAAG;KAAU,GAAG;KAAU;KAAU;KAAU;IAAE;GACxF,aAAa;IACX,KAAK;IACL,MAAM,UAAU,WAAW,IAAI,CAAC,GAAG,UAAU,GAAG,SAAS,GAAG,CAAC,GAAG,UAAU,GAAG,SAAS;IACvF;GACD;GACA,cAAc,KAAK;GACpB;AACD,SAAO;;AAGT,KAAI,OAAO,KAAK,YAAY,UAAU;EACpC,MAAM,KAAK,WAAW,KAAK,QAAQ;AAKnC,aAAW,SAAS;GAClB,KACE,gHACG,QAAQ,6BAA6B,IAAI,sCACV,IAAI,0FAEN,IAAI,sBAC3B,SAAS;GACpB,MAAM;IAAC,GAAG;IAAU;IAAI;IAAI;IAAI,GAAG;IAAS;GAC7C;;AAMH,KAAI,KAAK,WAAW,OAMlB,YAAW,OAAO;EAAE,KAJlB,qEACqD,MAAM,SAAS,eACjE,QAAQ,SAAS;EAEY,MAAM;GAAC,GAAG;GAAU,GAAG;GAAU,KAAK;GAAO;EAAE;AAGnF,QAAO;;;;;;;;AAST,SAAgB,4BAA4B,MAOxB;CAClB,MAAM,EAAE,SAAS,OAAO,WAAW,MAAM,YAAY,eAAe;CACpE,MAAM,QAAQ,KAAK,QAAQ,IAAK,KAAK,SAAS;CAC9C,MAAM,OAAO,MACX,OAAO,MAAM,WAAW,OAAO,EAAE,GAAK,KAAuB;CAE/D,MAAM,UAAU,QAAQ,SAAS;CAEjC,MAAM,QADW,UAAU,QAAQ,MAAM,GAAG,MAAM,GAAG,SACf,KAAK,MAAM;EAC/C,MAAM,MAAoB;GACxB,MAAM,EAAE;GACR,MAAM,EAAE;GACR,MAAO,EAAE,SAAoB,QAAQ,QAAQ;GAC7C,KAAM,EAAE,OAAyB;GACjC,MAAM,IAAI,EAAE,KAAK;GACjB,OAAO,IAAI,EAAE,MAAM;GACnB,OAAO,IAAI,EAAE,MAAM;GACnB,UAAW,EAAE,aAA+B;GAC7C;AACD,MAAI,EAAE,WAAW,KAAM,KAAI,UAAU,EAAE;AACvC,MAAI,EAAE,cAAc,MAAM;AACxB,OAAI,UAAU,EAAE;AAChB,OAAI,aAAc,EAAE,UAA4B;;EAQlD,MAAM,OAAO,uBADG,EAAE,eAAe,EAAE,SACS;AAC5C,MAAI,SAAS,OAAW,KAAI,WAAW;AACvC,SAAO;GACP;CAEF,MAAM,SAA0B;EAAE;EAAM;EAAO;AAC/C,KAAI,UAAW,QAAO,SAAS,aAAa,UAAU;CACtD,MAAM,OAAO,KAAK,KAAK,SAAS;AAChC,KAAI,WAAW,QAAQ,CAAC,KAAK,MAC3B,QAAO,SAAS,kBAAkB,aAAa,MAAM,WAAW,EAAE,KAAK,KAAK;AAE9E,KAAI,eAAe,OAAW,QAAO,aAAa;AAClD,QAAO;;;AAIT,SAAgB,aAAa,KAAmB,OAAuC;AACrF,KAAI,MAAM,WAAW,QAAQ,EAAE;EAI7B,IAAI,MAAe,IAAI;AACvB,OAAK,MAAM,OAAO,MAAM,MAAM,EAAe,CAAC,MAAM,IAAI,EAAE;AACxD,OAAI,OAAO,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO;AACzE,SAAO,IAAgC;;AAEzC,MAAI,OAAO,KAAM,QAAO;AACxB,MAAI,OAAO,QAAQ,YAAY,OAAO,QAAQ,SAAU,QAAO;AAC/D,SAAO,OAAO,IAAI;;AAEpB,SAAQ,OAAR;EACE,KAAK,OACH,QAAO,IAAI;EACb,KAAK,OACH,QAAO,IAAI;EACb,KAAK,WACH,QAAO,IAAI,YAAY;EACzB,KAAK,OACH,QAAO,IAAI;EACb,KAAK,QACH,QAAO,IAAI;EACb,KAAK,QACH,QAAO,IAAI;EACb,QACE,QAAO;;;;AAKb,SAAS,WAAW,QAA8D;AAChF,QAAO,OAAO,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,MAAM,CAAC,cAAc,OAAO,EAAE,MAAM,CAAC,CAAC;;;;;;;;AASnG,SAAgB,iBACd,MACA,WACA,cACqB;CACrB,MAAM,UAAU,MAAwB;AACtC,MAAI,OAAO,MAAM,SAAU,QAAO,OAAO,EAAE;AAC3C,SAAO;;AAET,QAAO,KAAK,KAAK,MAAM;EACrB,MAAM,MAAwD,EAAE;AAChE,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;GACzC,MAAM,IAAI,OAAO,EAAE,KAAK,KAAK;AAC7B,OAAI,UAAU,MACZ,MAAM,QAAQ,MAAM,SAChB,OACA,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,YAC5D,IACD,OAAO,EAAE;;EAEnB,MAAM,MAAyB,EAAE,KAAK;AACtC,OAAK,MAAM,OAAO,cAAc;GAC9B,MAAM,MAAM,OAAO,EAAE,IAAI,IAAI;AAC7B,OAAI,IAAI,OAAO,QACb,KAAI,IAAI,MAAM,OAAO,QAAQ,WAAW,MAAM;YACrC,IAAI,OAAO,MAEpB,KAAI,IAAI,MAAM,OAAO,QAAQ,WAAW,MAAM;OAG9C,KAAI,IAAI,MAAM,OAAO,QAAQ,WAAW,MAAM;;AAGlD,SAAO;GACP;;;AAIJ,SAAgB,aACd,MAC2B;CAC3B,MAAM,yBAAS,IAAI,KAAsC;AACzD,MAAK,MAAM,KAAK,MAAM;EACpB,MAAM,IAAI,OAAO,EAAE,EAAE;EACrB,IAAI;AACJ,MAAI,MAAM,OAAQ,SAAQ,OAAO,EAAE,EAAE;WAC5B,MAAM,aAAa,MAAM,OAAQ,SAAQ,OAAO,EAAE,EAAE;WACpD,MAAM,OAAQ,SAAQ;WACtB,MAAM,QAAS,SAAQ;MAC3B,SAAQ;EACb,MAAM,MAAM,GAAG,OAAO,MAAM,GAAG,OAAO,MAAM;EAC5C,MAAM,QAAQ,OAAO,EAAE,QAAQ,WAAW,OAAO,EAAE,IAAI,GAAI,EAAE;EAC7D,MAAM,WAAW,OAAO,IAAI,IAAI;AAChC,MAAI,SAAU,UAAS,SAAS;MAC3B,QAAO,IAAI,KAAK;GAAE;GAAO;GAAO,CAAC;;AAExC,QAAO,WAAW,CAAC,GAAG,OAAO,QAAQ,CAAC,CAAC;;;AAWzC,SAAgB,gBAAgB,MAAmC;AACjE,KAAI,SAAS,OAAW,QAAO;AAC/B,KAAI;AACF,SAAO,KAAK,MAAM,KAAK;SACjB;AACN;;;AAIJ,SAAS,cAAc,OAAkD;AACvE,KAAI,OAAO,UAAU,SAAU,QAAO,MAAM,aAAa;AACzD,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAW,QAAO;AACpE,QAAO;;;AAIT,SAAgB,cAAc,SAAyB,SAA4C;CACjG,MAAM,yBAAS,IAAI,KAAsC;CACzD,MAAM,QAAQ,QAAiB;EAC7B,MAAM,QAAQ,cAAc,IAAI;EAChC,MAAM,MAAM,GAAG,OAAO,MAAM,GAAG,OAAO,MAAM;EAC5C,MAAM,WAAW,OAAO,IAAI,IAAI;AAChC,MAAI,SAAU,UAAS,SAAS;MAC3B,QAAO,IAAI,KAAK;GAAE;GAAO,OAAO;GAAG,CAAC;;AAE3C,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;AAC/C,MAAI,MAAM,QAAQ,MAAM,CACtB,MAAK,MAAM,UAAU,MAAO,MAAK,OAAO;MAExC,MAAK,SAAS,KAAK;;AAGvB,QAAO,WAAW,CAAC,GAAG,OAAO,QAAQ,CAAC,CAAC;;;;;;AAOzC,SAAgB,oBAAoB,QAAiB,QAA2C;CAC9F,MAAM,MAA+B,EAAE;AACvC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,iBAAiB,QAAQ,MAAM;AAC7C,MAAI,UAAU,OAAW;EACzB,MAAM,WAAW,MAAM,MAAM,IAAI;EACjC,IAAI,SAAS;AACb,OAAK,IAAI,IAAI,GAAG,IAAI,SAAS,SAAS,GAAG,KAAK;GAC5C,MAAM,MAAM,SAAS;AACrB,OAAI,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,KAAM,QAAO,OAAO,EAAE;AAC7E,YAAS,OAAO;;AAElB,SAAO,SAAS,GAAG,GAAG,IAAK;;AAE7B,QAAO;;;;;;;;AAST,SAAgB,qBACd,YACA,MACiB;CACjB,MAAM,WAAW,KAAK,QAClB,WAAW,QAAQ,MACjB,iBACE;EAGE,OAAO;GAAE,IAAI,EAAE;GAAM,MAAM,EAAE;GAAM,MAAM,EAAE;GAAU;EACrD,QAAQ,EAAE;EACX,EACD,KAAK,MACN,CACF,GACD,CAAC,GAAG,WAAW;AAMnB,KAAI,KAAK,cAAc;EAKrB,MAAME,WAAS,wBAJD,SAAS,KAAK,OAAO;GACjC,OAAO;IAAE,IAAI,EAAE;IAAM,MAAM,EAAE;IAAM,MAAM,EAAE;IAAU;GACrD,QAAQ,EAAE;GACX,EAAE,EAC2C,KAA+B;AAC7E,SAAO;GACL,MAAM,EAAE;GACR,OAAOA,SAAO;GACd,iBAAiBA,SAAO;GACzB;;CAKH,MAAM,QAAQ,KAAK,YAAY,KAAK,SAAS;CAC7C,MAAM,SACJ,OAAO,KAAK,YAAY,WACpB,cACE,SAAS,KAAK,MAAM,EAAE,OAAO,EAC7B,KAAK,QACN,GACD;CAEN,MAAM,EAAE,OAAO,UAAU,eAAe,KAAK;AAC7C,UAAS,MAAM,GAAG,MAAM;EACtB,MAAM,MAAM,sBAAsB,aAAa,GAAG,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,MAAM;AACxF,MAAI,QAAQ,EAAG,QAAO;EACtB,MAAM,MAAM,EAAE,SAAS,EAAE,OAAO,IAAI,EAAE,OAAO,EAAE,OAAO,KAAK;AAC3D,SAAO,UAAU,SAAS,CAAC,MAAM;GACjC;CAEF,MAAM,SAAS,gBAAgB,KAAK,QAAQ,KAAK;CACjD,MAAM,cACJ,OAAO,SAAS,OACZ,SAAS,QAAQ,QACf,YAAY,aAAa,KAAK,MAAM,EAAE,IAAI,MAAM,OAAO,QAA4B,MAAM,CAC1F,GACD;CAKN,IAAI;AACJ,KAAI,KAAK,WAAW,OAClB,cAAa,SAAS,WAAW,MAAM,EAAE,SAAS,KAAK,OAAO;CAGhE,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,QAAQ,KAAK,QAAQ,IAAK,KAAK,SAAS;CAC9C,MAAM,OAAO,YAAY,MAAM,QAAQ,SAAS,MAAM;CACtD,MAAM,UAAU,SAAS,QAAQ,YAAY;CAC7C,MAAM,OAAO,KAAK,KAAK,SAAS;CAEhC,MAAM,SAA0B;EAC9B,MAAM,KAAK,KAAK,EAAE,QAAQ,SAAS,GAAG,UAAU,IAAI;EACpD;EACD;AACD,KAAI,OAAQ,QAAO,SAAS;AAC5B,KAAI,WAAW,QAAQ,CAAC,KAAK,MAC3B,QAAO,SAAS,kBAAkB,aAAa,MAAM,MAAM,EAAE,KAAK,KAAK;AAEzE,KAAI,eAAe,OAAW,QAAO,aAAa;AAClD,QAAO"}
@@ -0,0 +1,97 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ const require_upload_session_store = require('./upload-session-store.cjs');
3
+ let zod = require("zod");
4
+
5
+ //#region src/upload-actions.ts
6
+ /**
7
+ * upload-actions.ts — arg schemas + pure helpers for the DID Space upload-plane
8
+ * provider actions (design §7.2 / §10). The action HANDLERS live on
9
+ * `DIDSpaceProvider` (decorators must register on the class); this module holds
10
+ * the validation/shape logic so the handlers stay thin and the rules are unit-
11
+ * testable in isolation.
12
+ */
13
+ /** Action names (design §15 decision 6). */
14
+ const UPLOAD_ACTIONS = [
15
+ "prepare-upload",
16
+ "commit-upload",
17
+ "abort-upload"
18
+ ];
19
+ /**
20
+ * Sentinel `ifMatch` value meaning "expected-absent precondition" — the
21
+ * browser-upload D3 contract (upload-browser design §4.2a): the ingest facade
22
+ * stamps this on conflict=rename/error entries so a prepare→write window cannot
23
+ * silently clobber a file another writer created in between. Reuses the existing
24
+ * `ifMatch?: string` field (zero schema migration) instead of a new boolean
25
+ * column.
26
+ *
27
+ * INVARIANT (do not change without re-auditing {@link ifMatchPreconditionFailed}):
28
+ * a real CID is base32-lower (`bafk…`); `!` is NOT a legal base32 character, so
29
+ * this sentinel can never collide with a genuine content CID. It is also the
30
+ * on-the-wire value the browser passes verbatim into legacy `batchWrite` entries
31
+ * (browser JS cannot `import` it).
32
+ */
33
+ const AFS_IFMATCH_ABSENT = "!absent";
34
+ /**
35
+ * THE single source of truth for "does this ifMatch precondition fail against
36
+ * the current entry?" — including the D3 expected-absent sentinel (§4.2a).
37
+ *
38
+ * EVERY did-space WRITE-family ifMatch check (single `write`, `batchWrite`
39
+ * recheck, grant `commit` recheck) routes through this one predicate, so the
40
+ * sentinel can never be honored at one write site and silently missed at
41
+ * another (the bug live verification caught: `write()` had been left out).
42
+ * DELETE-family sites intentionally do NOT use this — "expect-absent" is
43
+ * meaningless for delete (you cannot delete what is absent), per design.
44
+ *
45
+ * - ifMatch === undefined → no precondition (last-write-wins)
46
+ * - ifMatch === AFS_IFMATCH_ABSENT → conflict IFF the target EXISTS (inverted)
47
+ * - ifMatch === <cid> → conflict IFF absent OR the cid differs
48
+ */
49
+ function ifMatchPreconditionFailed(ifMatch, existing) {
50
+ if (ifMatch === void 0) return false;
51
+ if (ifMatch === AFS_IFMATCH_ABSENT) return existing != null;
52
+ return !existing || existing.cid !== ifMatch;
53
+ }
54
+ /**
55
+ * Grant validity: the presigned PUT must stay valid for the whole upload window
56
+ * up to commit. Bound to the session TTL (a grant outliving its session is
57
+ * useless) — derived so the two can't drift.
58
+ */
59
+ const GRANT_EXPIRES_SEC = require_upload_session_store.UPLOAD_SESSION_TTL_MS / 1e3;
60
+ /** Fields shared by prepare + commit entries (kept in one place to avoid drift). */
61
+ const baseEntry = zod.z.object({
62
+ path: zod.z.string().min(1),
63
+ cid: zod.z.string().min(1),
64
+ size: zod.z.number().int().nonnegative(),
65
+ mimeType: zod.z.string().optional(),
66
+ ifMatch: zod.z.string().optional(),
67
+ deviceId: zod.z.string().optional(),
68
+ mtime: zod.z.number().int().optional()
69
+ });
70
+ const prepareEntry = baseEntry.extend({ conflict: zod.z.enum(["overwrite", "skip"]).optional() });
71
+ const commitEntry = baseEntry.extend({
72
+ content: zod.z.string().optional(),
73
+ metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
74
+ });
75
+ const PrepareUploadArgsSchema = zod.z.object({
76
+ entries: zod.z.array(prepareEntry).min(1),
77
+ strategy: zod.z.enum(["auto"]).optional()
78
+ });
79
+ const CommitUploadArgsSchema = zod.z.object({
80
+ sessionId: zod.z.string().min(1),
81
+ entries: zod.z.array(commitEntry).min(1)
82
+ });
83
+ const AbortUploadArgsSchema = zod.z.object({ sessionId: zod.z.string().min(1) });
84
+ /** Compact a zod error to a single human message for AFSExecResult.error.message. */
85
+ function zodMessage(err) {
86
+ return err.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
87
+ }
88
+
89
+ //#endregion
90
+ exports.AFS_IFMATCH_ABSENT = AFS_IFMATCH_ABSENT;
91
+ exports.AbortUploadArgsSchema = AbortUploadArgsSchema;
92
+ exports.CommitUploadArgsSchema = CommitUploadArgsSchema;
93
+ exports.GRANT_EXPIRES_SEC = GRANT_EXPIRES_SEC;
94
+ exports.PrepareUploadArgsSchema = PrepareUploadArgsSchema;
95
+ exports.UPLOAD_ACTIONS = UPLOAD_ACTIONS;
96
+ exports.ifMatchPreconditionFailed = ifMatchPreconditionFailed;
97
+ exports.zodMessage = zodMessage;
@@ -0,0 +1,19 @@
1
+ //#region src/upload-actions.d.ts
2
+ /**
3
+ * Sentinel `ifMatch` value meaning "expected-absent precondition" — the
4
+ * browser-upload D3 contract (upload-browser design §4.2a): the ingest facade
5
+ * stamps this on conflict=rename/error entries so a prepare→write window cannot
6
+ * silently clobber a file another writer created in between. Reuses the existing
7
+ * `ifMatch?: string` field (zero schema migration) instead of a new boolean
8
+ * column.
9
+ *
10
+ * INVARIANT (do not change without re-auditing {@link ifMatchPreconditionFailed}):
11
+ * a real CID is base32-lower (`bafk…`); `!` is NOT a legal base32 character, so
12
+ * this sentinel can never collide with a genuine content CID. It is also the
13
+ * on-the-wire value the browser passes verbatim into legacy `batchWrite` entries
14
+ * (browser JS cannot `import` it).
15
+ */
16
+ declare const AFS_IFMATCH_ABSENT = "!absent";
17
+ //#endregion
18
+ export { AFS_IFMATCH_ABSENT };
19
+ //# sourceMappingURL=upload-actions.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-actions.d.cts","names":[],"sources":["../src/upload-actions.ts"],"mappings":";;;;;;;;;;;;;;;cA4Ba,kBAAA"}
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/upload-actions.d.ts
4
+ /**
5
+ * Sentinel `ifMatch` value meaning "expected-absent precondition" — the
6
+ * browser-upload D3 contract (upload-browser design §4.2a): the ingest facade
7
+ * stamps this on conflict=rename/error entries so a prepare→write window cannot
8
+ * silently clobber a file another writer created in between. Reuses the existing
9
+ * `ifMatch?: string` field (zero schema migration) instead of a new boolean
10
+ * column.
11
+ *
12
+ * INVARIANT (do not change without re-auditing {@link ifMatchPreconditionFailed}):
13
+ * a real CID is base32-lower (`bafk…`); `!` is NOT a legal base32 character, so
14
+ * this sentinel can never collide with a genuine content CID. It is also the
15
+ * on-the-wire value the browser passes verbatim into legacy `batchWrite` entries
16
+ * (browser JS cannot `import` it).
17
+ */
18
+ declare const AFS_IFMATCH_ABSENT = "!absent";
19
+ //#endregion
20
+ export { AFS_IFMATCH_ABSENT };
21
+ //# sourceMappingURL=upload-actions.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-actions.d.mts","names":[],"sources":["../src/upload-actions.ts"],"mappings":";;;;;;;;;;;;;;;;;cA4Ba,kBAAA"}
@@ -0,0 +1,90 @@
1
+ import { UPLOAD_SESSION_TTL_MS } from "./upload-session-store.mjs";
2
+ import { z } from "zod";
3
+
4
+ //#region src/upload-actions.ts
5
+ /**
6
+ * upload-actions.ts — arg schemas + pure helpers for the DID Space upload-plane
7
+ * provider actions (design §7.2 / §10). The action HANDLERS live on
8
+ * `DIDSpaceProvider` (decorators must register on the class); this module holds
9
+ * the validation/shape logic so the handlers stay thin and the rules are unit-
10
+ * testable in isolation.
11
+ */
12
+ /** Action names (design §15 decision 6). */
13
+ const UPLOAD_ACTIONS = [
14
+ "prepare-upload",
15
+ "commit-upload",
16
+ "abort-upload"
17
+ ];
18
+ /**
19
+ * Sentinel `ifMatch` value meaning "expected-absent precondition" — the
20
+ * browser-upload D3 contract (upload-browser design §4.2a): the ingest facade
21
+ * stamps this on conflict=rename/error entries so a prepare→write window cannot
22
+ * silently clobber a file another writer created in between. Reuses the existing
23
+ * `ifMatch?: string` field (zero schema migration) instead of a new boolean
24
+ * column.
25
+ *
26
+ * INVARIANT (do not change without re-auditing {@link ifMatchPreconditionFailed}):
27
+ * a real CID is base32-lower (`bafk…`); `!` is NOT a legal base32 character, so
28
+ * this sentinel can never collide with a genuine content CID. It is also the
29
+ * on-the-wire value the browser passes verbatim into legacy `batchWrite` entries
30
+ * (browser JS cannot `import` it).
31
+ */
32
+ const AFS_IFMATCH_ABSENT = "!absent";
33
+ /**
34
+ * THE single source of truth for "does this ifMatch precondition fail against
35
+ * the current entry?" — including the D3 expected-absent sentinel (§4.2a).
36
+ *
37
+ * EVERY did-space WRITE-family ifMatch check (single `write`, `batchWrite`
38
+ * recheck, grant `commit` recheck) routes through this one predicate, so the
39
+ * sentinel can never be honored at one write site and silently missed at
40
+ * another (the bug live verification caught: `write()` had been left out).
41
+ * DELETE-family sites intentionally do NOT use this — "expect-absent" is
42
+ * meaningless for delete (you cannot delete what is absent), per design.
43
+ *
44
+ * - ifMatch === undefined → no precondition (last-write-wins)
45
+ * - ifMatch === AFS_IFMATCH_ABSENT → conflict IFF the target EXISTS (inverted)
46
+ * - ifMatch === <cid> → conflict IFF absent OR the cid differs
47
+ */
48
+ function ifMatchPreconditionFailed(ifMatch, existing) {
49
+ if (ifMatch === void 0) return false;
50
+ if (ifMatch === AFS_IFMATCH_ABSENT) return existing != null;
51
+ return !existing || existing.cid !== ifMatch;
52
+ }
53
+ /**
54
+ * Grant validity: the presigned PUT must stay valid for the whole upload window
55
+ * up to commit. Bound to the session TTL (a grant outliving its session is
56
+ * useless) — derived so the two can't drift.
57
+ */
58
+ const GRANT_EXPIRES_SEC = UPLOAD_SESSION_TTL_MS / 1e3;
59
+ /** Fields shared by prepare + commit entries (kept in one place to avoid drift). */
60
+ const baseEntry = z.object({
61
+ path: z.string().min(1),
62
+ cid: z.string().min(1),
63
+ size: z.number().int().nonnegative(),
64
+ mimeType: z.string().optional(),
65
+ ifMatch: z.string().optional(),
66
+ deviceId: z.string().optional(),
67
+ mtime: z.number().int().optional()
68
+ });
69
+ const prepareEntry = baseEntry.extend({ conflict: z.enum(["overwrite", "skip"]).optional() });
70
+ const commitEntry = baseEntry.extend({
71
+ content: z.string().optional(),
72
+ metadata: z.record(z.string(), z.unknown()).optional()
73
+ });
74
+ const PrepareUploadArgsSchema = z.object({
75
+ entries: z.array(prepareEntry).min(1),
76
+ strategy: z.enum(["auto"]).optional()
77
+ });
78
+ const CommitUploadArgsSchema = z.object({
79
+ sessionId: z.string().min(1),
80
+ entries: z.array(commitEntry).min(1)
81
+ });
82
+ const AbortUploadArgsSchema = z.object({ sessionId: z.string().min(1) });
83
+ /** Compact a zod error to a single human message for AFSExecResult.error.message. */
84
+ function zodMessage(err) {
85
+ return err.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
86
+ }
87
+
88
+ //#endregion
89
+ export { AFS_IFMATCH_ABSENT, AbortUploadArgsSchema, CommitUploadArgsSchema, GRANT_EXPIRES_SEC, PrepareUploadArgsSchema, UPLOAD_ACTIONS, ifMatchPreconditionFailed, zodMessage };
90
+ //# sourceMappingURL=upload-actions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-actions.mjs","names":[],"sources":["../src/upload-actions.ts"],"sourcesContent":["/**\n * upload-actions.ts — arg schemas + pure helpers for the DID Space upload-plane\n * provider actions (design §7.2 / §10). The action HANDLERS live on\n * `DIDSpaceProvider` (decorators must register on the class); this module holds\n * the validation/shape logic so the handlers stay thin and the rules are unit-\n * testable in isolation.\n */\n\nimport { z } from \"zod\";\nimport { UPLOAD_SESSION_TTL_MS } from \"./upload-session-store.js\";\n\n/** Action names (design §15 decision 6). */\nexport const UPLOAD_ACTIONS = [\"prepare-upload\", \"commit-upload\", \"abort-upload\"] as const;\n\n/**\n * Sentinel `ifMatch` value meaning \"expected-absent precondition\" — the\n * browser-upload D3 contract (upload-browser design §4.2a): the ingest facade\n * stamps this on conflict=rename/error entries so a prepare→write window cannot\n * silently clobber a file another writer created in between. Reuses the existing\n * `ifMatch?: string` field (zero schema migration) instead of a new boolean\n * column.\n *\n * INVARIANT (do not change without re-auditing {@link ifMatchPreconditionFailed}):\n * a real CID is base32-lower (`bafk…`); `!` is NOT a legal base32 character, so\n * this sentinel can never collide with a genuine content CID. It is also the\n * on-the-wire value the browser passes verbatim into legacy `batchWrite` entries\n * (browser JS cannot `import` it).\n */\nexport const AFS_IFMATCH_ABSENT = \"!absent\";\n\n/**\n * THE single source of truth for \"does this ifMatch precondition fail against\n * the current entry?\" — including the D3 expected-absent sentinel (§4.2a).\n *\n * EVERY did-space WRITE-family ifMatch check (single `write`, `batchWrite`\n * recheck, grant `commit` recheck) routes through this one predicate, so the\n * sentinel can never be honored at one write site and silently missed at\n * another (the bug live verification caught: `write()` had been left out).\n * DELETE-family sites intentionally do NOT use this — \"expect-absent\" is\n * meaningless for delete (you cannot delete what is absent), per design.\n *\n * - ifMatch === undefined → no precondition (last-write-wins)\n * - ifMatch === AFS_IFMATCH_ABSENT → conflict IFF the target EXISTS (inverted)\n * - ifMatch === <cid> → conflict IFF absent OR the cid differs\n */\nexport function ifMatchPreconditionFailed(\n ifMatch: string | undefined,\n existing: { cid: string } | null | undefined,\n): boolean {\n if (ifMatch === undefined) return false;\n if (ifMatch === AFS_IFMATCH_ABSENT) return existing != null;\n return !existing || existing.cid !== ifMatch;\n}\n\n/**\n * Grant validity: the presigned PUT must stay valid for the whole upload window\n * up to commit. Bound to the session TTL (a grant outliving its session is\n * useless) — derived so the two can't drift.\n */\nexport const GRANT_EXPIRES_SEC = UPLOAD_SESSION_TTL_MS / 1000;\n\n/** Fields shared by prepare + commit entries (kept in one place to avoid drift). */\nconst baseEntry = z.object({\n path: z.string().min(1),\n cid: z.string().min(1),\n size: z.number().int().nonnegative(),\n mimeType: z.string().optional(),\n ifMatch: z.string().optional(),\n deviceId: z.string().optional(),\n mtime: z.number().int().optional(),\n});\n\n// `conflict` is accepted for forward-compat (design §7.2 example) but v1 always\n// overwrites (strict trust mode); it is not yet acted on.\nconst prepareEntry = baseEntry.extend({ conflict: z.enum([\"overwrite\", \"skip\"]).optional() });\n\n// A commit entry MAY carry a small inline text copy of the bytes the client\n// just PUT (inline-content-commit §5.1): the upload plane is metadata-only, so\n// without this the `content` column stays NULL and collection-query\n// (json_extract over that column) loses upload-plane-synced small records. The\n// server digest-pre-verifies + text-mime-gates this copy before storing it; an\n// unverifiable/non-text copy is dropped (the object itself is still\n// checksum-proven in R2). `prepare` stays on `baseEntry` — content is\n// irrelevant to grant signing.\n// `metadata` (did-space-metadata G2): application fields (e.g. Aside's\n// `meta.createdAt` that feeds orderBy) that the upload-plane commit must\n// persist to the `entries.metadata` column. Unlike the batchWrite/write legs\n// (where afs-rpc `wireMeta` lifts a top-level `meta`), commit-upload is an exec\n// action whose args are forwarded verbatim — so the metadata MUST ride an\n// explicit field here. `z.record` imposes no size bound; the handler strips\n// provider-reserved keys + enforces the 64KB cap (design §5).\nconst commitEntry = baseEntry.extend({\n content: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const PrepareUploadArgsSchema = z.object({\n entries: z.array(prepareEntry).min(1),\n strategy: z.enum([\"auto\"]).optional(),\n});\n\nexport const CommitUploadArgsSchema = z.object({\n sessionId: z.string().min(1),\n entries: z.array(commitEntry).min(1),\n});\n\nexport const AbortUploadArgsSchema = z.object({\n sessionId: z.string().min(1),\n});\n\nexport type PrepareUploadArgs = z.infer<typeof PrepareUploadArgsSchema>;\nexport type CommitUploadArgs = z.infer<typeof CommitUploadArgsSchema>;\n\n/** Compact a zod error to a single human message for AFSExecResult.error.message. */\nexport function zodMessage(err: z.ZodError): string {\n return err.issues.map((i) => `${i.path.join(\".\") || \"(root)\"}: ${i.message}`).join(\"; \");\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,iBAAiB;CAAC;CAAkB;CAAiB;CAAe;;;;;;;;;;;;;;;AAgBjF,MAAa,qBAAqB;;;;;;;;;;;;;;;;AAiBlC,SAAgB,0BACd,SACA,UACS;AACT,KAAI,YAAY,OAAW,QAAO;AAClC,KAAI,YAAY,mBAAoB,QAAO,YAAY;AACvD,QAAO,CAAC,YAAY,SAAS,QAAQ;;;;;;;AAQvC,MAAa,oBAAoB,wBAAwB;;AAGzD,MAAM,YAAY,EAAE,OAAO;CACzB,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE;CACvB,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE;CACtB,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa;CACpC,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACnC,CAAC;AAIF,MAAM,eAAe,UAAU,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,aAAa,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;AAiB7F,MAAM,cAAc,UAAU,OAAO;CACnC,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;CACvD,CAAC;AAEF,MAAa,0BAA0B,EAAE,OAAO;CAC9C,SAAS,EAAE,MAAM,aAAa,CAAC,IAAI,EAAE;CACrC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU;CACtC,CAAC;AAEF,MAAa,yBAAyB,EAAE,OAAO;CAC7C,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC5B,SAAS,EAAE,MAAM,YAAY,CAAC,IAAI,EAAE;CACrC,CAAC;AAEF,MAAa,wBAAwB,EAAE,OAAO,EAC5C,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE,EAC7B,CAAC;;AAMF,SAAgB,WAAW,KAAyB;AAClD,QAAO,IAAI,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK"}
@@ -0,0 +1,185 @@
1
+ const require_d1_schema = require('./d1-schema.cjs');
2
+
3
+ //#region src/upload-session-store.ts
4
+ /**
5
+ * Prepared-session TTL (design §9 / §15). Hours-scale and DELIBERATELY far
6
+ * shorter than `DEFAULT_GC_GRACE_MS` (7 days): in-flight protection cannot lean
7
+ * on the GC grace window (prepared TTL ≪ grace), so the prepared-session UNION
8
+ * in reference-check.ts is the real protection. 2h is ample for a large
9
+ * first-sync batch's uploads to land + commit.
10
+ */
11
+ const UPLOAD_SESSION_TTL_MS = 7200 * 1e3;
12
+ function assertNonEmpty(input) {
13
+ if (!input.entries || input.entries.length === 0) throw new Error("createSession: entries must be non-empty");
14
+ }
15
+ /**
16
+ * Statement strings shared by BOTH backends (D1 binds positionally, libsql via
17
+ * `args`) — hoisted so the two impls can never drift on column lists / WHERE
18
+ * clauses. Mirrors reference-check.ts's hoisted-SQL style.
19
+ */
20
+ const SQL = {
21
+ insertSession: "INSERT INTO ds_upload_sessions (id, scope, caller, expires_at, status, created_at) VALUES (?, ?, ?, ?, 'prepared', ?)",
22
+ insertEntry: "INSERT INTO ds_upload_session_entries (session_id, path, cid, object_key, checksum_sha256, size, mime_type, if_match, device_id, mtime, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
23
+ selectSession: "SELECT * FROM ds_upload_sessions WHERE id = ?",
24
+ selectEntries: "SELECT * FROM ds_upload_session_entries WHERE session_id = ?",
25
+ updateEntryStatus: "UPDATE ds_upload_session_entries SET status = ? WHERE session_id = ? AND path = ?",
26
+ markCommitted: "UPDATE ds_upload_sessions SET status = 'committed' WHERE id = ?",
27
+ markAborted: "UPDATE ds_upload_sessions SET status = 'aborted' WHERE id = ?",
28
+ sweepEntries: "DELETE FROM ds_upload_session_entries WHERE session_id IN (SELECT id FROM ds_upload_sessions WHERE expires_at < ?)",
29
+ sweepSessions: "DELETE FROM ds_upload_sessions WHERE expires_at < ?"
30
+ };
31
+ /** Positional bind args for `insertEntry` (shared by both backends). */
32
+ function entryArgs(sessionId, e) {
33
+ return [
34
+ sessionId,
35
+ e.path,
36
+ e.cid,
37
+ e.objectKey,
38
+ e.checksumSha256,
39
+ e.size,
40
+ e.mimeType ?? null,
41
+ e.ifMatch ?? null,
42
+ e.deviceId ?? null,
43
+ e.mtime ?? null,
44
+ e.status
45
+ ];
46
+ }
47
+ var D1UploadSessionStore = class {
48
+ constructor(db) {
49
+ this.db = db;
50
+ }
51
+ async ensureTable() {
52
+ const stmts = [
53
+ this.db.prepare(require_d1_schema.UPLOAD_SESSIONS_TABLE_SQL),
54
+ this.db.prepare(require_d1_schema.UPLOAD_SESSION_ENTRIES_TABLE_SQL),
55
+ ...require_d1_schema.UPLOAD_SESSIONS_INDEX_SQL.map((sql) => this.db.prepare(sql))
56
+ ];
57
+ await this.db.batch(stmts);
58
+ }
59
+ async createSession(input) {
60
+ assertNonEmpty(input);
61
+ const createdAt = input.createdAt ?? Date.now();
62
+ const stmts = [this.db.prepare(SQL.insertSession).bind(input.id, input.scope, input.caller ?? null, input.expiresAt, createdAt), ...input.entries.map((e) => this.db.prepare(SQL.insertEntry).bind(...entryArgs(input.id, e)))];
63
+ await this.db.batch(stmts);
64
+ }
65
+ async getSession(id) {
66
+ const row = await this.db.prepare(SQL.selectSession).bind(id).first();
67
+ if (!row) return null;
68
+ return rowToSession(row, (await this.db.prepare(SQL.selectEntries).bind(id).all()).results);
69
+ }
70
+ async markEntries(id, updates) {
71
+ if (updates.length === 0) return;
72
+ const stmts = updates.map((u) => this.db.prepare(SQL.updateEntryStatus).bind(u.status, id, u.path));
73
+ await this.db.batch(stmts);
74
+ }
75
+ async markCommitted(id) {
76
+ await this.db.prepare(SQL.markCommitted).bind(id).run();
77
+ }
78
+ async markAborted(id) {
79
+ await this.db.prepare(SQL.markAborted).bind(id).run();
80
+ }
81
+ async sweepExpired(now) {
82
+ await this.db.prepare(SQL.sweepEntries).bind(now).run();
83
+ return (await this.db.prepare(SQL.sweepSessions).bind(now).run()).meta?.changes ?? 0;
84
+ }
85
+ };
86
+ var SqliteUploadSessionStore = class {
87
+ constructor(client) {
88
+ this.client = client;
89
+ }
90
+ async ensureTable() {
91
+ await this.client.execute(require_d1_schema.UPLOAD_SESSIONS_TABLE_SQL);
92
+ await this.client.execute(require_d1_schema.UPLOAD_SESSION_ENTRIES_TABLE_SQL);
93
+ for (const sql of require_d1_schema.UPLOAD_SESSIONS_INDEX_SQL) await this.client.execute(sql);
94
+ }
95
+ async createSession(input) {
96
+ assertNonEmpty(input);
97
+ const createdAt = input.createdAt ?? Date.now();
98
+ const stmts = [{
99
+ sql: SQL.insertSession,
100
+ args: [
101
+ input.id,
102
+ input.scope,
103
+ input.caller ?? null,
104
+ input.expiresAt,
105
+ createdAt
106
+ ]
107
+ }, ...input.entries.map((e) => ({
108
+ sql: SQL.insertEntry,
109
+ args: entryArgs(input.id, e)
110
+ }))];
111
+ await this.client.batch(stmts, "write");
112
+ }
113
+ async getSession(id) {
114
+ const sessionRes = await this.client.execute({
115
+ sql: SQL.selectSession,
116
+ args: [id]
117
+ });
118
+ if (sessionRes.rows.length === 0) return null;
119
+ const entriesRes = await this.client.execute({
120
+ sql: SQL.selectEntries,
121
+ args: [id]
122
+ });
123
+ return rowToSession(sessionRes.rows[0], entriesRes.rows);
124
+ }
125
+ async markEntries(id, updates) {
126
+ if (updates.length === 0) return;
127
+ await this.client.batch(updates.map((u) => ({
128
+ sql: SQL.updateEntryStatus,
129
+ args: [
130
+ u.status,
131
+ id,
132
+ u.path
133
+ ]
134
+ })), "write");
135
+ }
136
+ async markCommitted(id) {
137
+ await this.client.execute({
138
+ sql: SQL.markCommitted,
139
+ args: [id]
140
+ });
141
+ }
142
+ async markAborted(id) {
143
+ await this.client.execute({
144
+ sql: SQL.markAborted,
145
+ args: [id]
146
+ });
147
+ }
148
+ async sweepExpired(now) {
149
+ await this.client.execute({
150
+ sql: SQL.sweepEntries,
151
+ args: [now]
152
+ });
153
+ return (await this.client.execute({
154
+ sql: SQL.sweepSessions,
155
+ args: [now]
156
+ })).rowsAffected ?? 0;
157
+ }
158
+ };
159
+ function rowToSession(s, entryRows) {
160
+ return {
161
+ id: String(s.id),
162
+ scope: String(s.scope),
163
+ caller: s.caller == null ? null : String(s.caller),
164
+ expiresAt: Number(s.expires_at),
165
+ status: String(s.status),
166
+ createdAt: Number(s.created_at),
167
+ entries: entryRows.map((e) => ({
168
+ path: String(e.path),
169
+ cid: String(e.cid),
170
+ objectKey: String(e.object_key),
171
+ checksumSha256: String(e.checksum_sha256),
172
+ size: Number(e.size),
173
+ mimeType: e.mime_type == null ? null : String(e.mime_type),
174
+ ifMatch: e.if_match == null ? null : String(e.if_match),
175
+ deviceId: e.device_id == null ? null : String(e.device_id),
176
+ mtime: e.mtime == null ? null : Number(e.mtime),
177
+ status: String(e.status)
178
+ }))
179
+ };
180
+ }
181
+
182
+ //#endregion
183
+ exports.D1UploadSessionStore = D1UploadSessionStore;
184
+ exports.SqliteUploadSessionStore = SqliteUploadSessionStore;
185
+ exports.UPLOAD_SESSION_TTL_MS = UPLOAD_SESSION_TTL_MS;