@dotdo/postgres 0.1.0

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 (1129) hide show
  1. package/README.md +868 -0
  2. package/dist/cdc/change-stream.d.ts +44 -0
  3. package/dist/cdc/change-stream.d.ts.map +1 -0
  4. package/dist/cdc/change-stream.js +95 -0
  5. package/dist/cdc/change-stream.js.map +1 -0
  6. package/dist/cdc/filter.d.ts +58 -0
  7. package/dist/cdc/filter.d.ts.map +1 -0
  8. package/dist/cdc/filter.js +520 -0
  9. package/dist/cdc/filter.js.map +1 -0
  10. package/dist/cdc/index.d.ts +47 -0
  11. package/dist/cdc/index.d.ts.map +1 -0
  12. package/dist/cdc/index.js +50 -0
  13. package/dist/cdc/index.js.map +1 -0
  14. package/dist/cdc/resume-token.d.ts +60 -0
  15. package/dist/cdc/resume-token.d.ts.map +1 -0
  16. package/dist/cdc/resume-token.js +228 -0
  17. package/dist/cdc/resume-token.js.map +1 -0
  18. package/dist/cdc/transport/index.d.ts +7 -0
  19. package/dist/cdc/transport/index.d.ts.map +1 -0
  20. package/dist/cdc/transport/index.js +7 -0
  21. package/dist/cdc/transport/index.js.map +1 -0
  22. package/dist/cdc/transport/sse.d.ts +120 -0
  23. package/dist/cdc/transport/sse.d.ts.map +1 -0
  24. package/dist/cdc/transport/sse.js +590 -0
  25. package/dist/cdc/transport/sse.js.map +1 -0
  26. package/dist/cdc/transport/websocket.d.ts +130 -0
  27. package/dist/cdc/transport/websocket.d.ts.map +1 -0
  28. package/dist/cdc/transport/websocket.js +688 -0
  29. package/dist/cdc/transport/websocket.js.map +1 -0
  30. package/dist/cdc/types.d.ts +306 -0
  31. package/dist/cdc/types.d.ts.map +1 -0
  32. package/dist/cdc/types.js +8 -0
  33. package/dist/cdc/types.js.map +1 -0
  34. package/dist/config/index.d.ts +25 -0
  35. package/dist/config/index.d.ts.map +1 -0
  36. package/dist/config/index.js +25 -0
  37. package/dist/config/index.js.map +1 -0
  38. package/dist/config/memory.d.ts +139 -0
  39. package/dist/config/memory.d.ts.map +1 -0
  40. package/dist/config/memory.js +157 -0
  41. package/dist/config/memory.js.map +1 -0
  42. package/dist/config/storage.d.ts +157 -0
  43. package/dist/config/storage.d.ts.map +1 -0
  44. package/dist/config/storage.js +178 -0
  45. package/dist/config/storage.js.map +1 -0
  46. package/dist/config/streaming.d.ts +117 -0
  47. package/dist/config/streaming.d.ts.map +1 -0
  48. package/dist/config/streaming.js +132 -0
  49. package/dist/config/streaming.js.map +1 -0
  50. package/dist/config/timeouts.d.ts +168 -0
  51. package/dist/config/timeouts.d.ts.map +1 -0
  52. package/dist/config/timeouts.js +192 -0
  53. package/dist/config/timeouts.js.map +1 -0
  54. package/dist/extensions/config.d.ts +89 -0
  55. package/dist/extensions/config.d.ts.map +1 -0
  56. package/dist/extensions/config.js +216 -0
  57. package/dist/extensions/config.js.map +1 -0
  58. package/dist/extensions/geo.d.ts +452 -0
  59. package/dist/extensions/geo.d.ts.map +1 -0
  60. package/dist/extensions/geo.js +583 -0
  61. package/dist/extensions/geo.js.map +1 -0
  62. package/dist/extensions/index.d.ts +167 -0
  63. package/dist/extensions/index.d.ts.map +1 -0
  64. package/dist/extensions/index.js +99 -0
  65. package/dist/extensions/index.js.map +1 -0
  66. package/dist/extensions/loader.d.ts +226 -0
  67. package/dist/extensions/loader.d.ts.map +1 -0
  68. package/dist/extensions/loader.js +456 -0
  69. package/dist/extensions/loader.js.map +1 -0
  70. package/dist/extensions/pgmq-lite.d.ts +330 -0
  71. package/dist/extensions/pgmq-lite.d.ts.map +1 -0
  72. package/dist/extensions/pgmq-lite.js +648 -0
  73. package/dist/extensions/pgmq-lite.js.map +1 -0
  74. package/dist/extensions/plugins.d.ts +260 -0
  75. package/dist/extensions/plugins.d.ts.map +1 -0
  76. package/dist/extensions/plugins.js +535 -0
  77. package/dist/extensions/plugins.js.map +1 -0
  78. package/dist/extensions/registry.d.ts +93 -0
  79. package/dist/extensions/registry.d.ts.map +1 -0
  80. package/dist/extensions/registry.js +182 -0
  81. package/dist/extensions/registry.js.map +1 -0
  82. package/dist/extensions/vector.d.ts +106 -0
  83. package/dist/extensions/vector.d.ts.map +1 -0
  84. package/dist/extensions/vector.js +129 -0
  85. package/dist/extensions/vector.js.map +1 -0
  86. package/dist/iceberg/analytics.d.ts +279 -0
  87. package/dist/iceberg/analytics.d.ts.map +1 -0
  88. package/dist/iceberg/analytics.js +448 -0
  89. package/dist/iceberg/analytics.js.map +1 -0
  90. package/dist/iceberg/catalog-api.d.ts +39 -0
  91. package/dist/iceberg/catalog-api.d.ts.map +1 -0
  92. package/dist/iceberg/catalog-api.js +388 -0
  93. package/dist/iceberg/catalog-api.js.map +1 -0
  94. package/dist/iceberg/catalog.d.ts +401 -0
  95. package/dist/iceberg/catalog.d.ts.map +1 -0
  96. package/dist/iceberg/catalog.js +677 -0
  97. package/dist/iceberg/catalog.js.map +1 -0
  98. package/dist/iceberg/duckdb-wasm.d.ts +447 -0
  99. package/dist/iceberg/duckdb-wasm.d.ts.map +1 -0
  100. package/dist/iceberg/duckdb-wasm.js +600 -0
  101. package/dist/iceberg/duckdb-wasm.js.map +1 -0
  102. package/dist/iceberg/index.d.ts +92 -0
  103. package/dist/iceberg/index.d.ts.map +1 -0
  104. package/dist/iceberg/index.js +119 -0
  105. package/dist/iceberg/index.js.map +1 -0
  106. package/dist/iceberg/metadata.d.ts +214 -0
  107. package/dist/iceberg/metadata.d.ts.map +1 -0
  108. package/dist/iceberg/metadata.js +535 -0
  109. package/dist/iceberg/metadata.js.map +1 -0
  110. package/dist/iceberg/optimizer.d.ts +296 -0
  111. package/dist/iceberg/optimizer.d.ts.map +1 -0
  112. package/dist/iceberg/optimizer.js +889 -0
  113. package/dist/iceberg/optimizer.js.map +1 -0
  114. package/dist/iceberg/parquet.d.ts +447 -0
  115. package/dist/iceberg/parquet.d.ts.map +1 -0
  116. package/dist/iceberg/parquet.js +1225 -0
  117. package/dist/iceberg/parquet.js.map +1 -0
  118. package/dist/iceberg/r2-organization.d.ts +422 -0
  119. package/dist/iceberg/r2-organization.d.ts.map +1 -0
  120. package/dist/iceberg/r2-organization.js +672 -0
  121. package/dist/iceberg/r2-organization.js.map +1 -0
  122. package/dist/iceberg/scheduler-do-example.d.ts +158 -0
  123. package/dist/iceberg/scheduler-do-example.d.ts.map +1 -0
  124. package/dist/iceberg/scheduler-do-example.js +261 -0
  125. package/dist/iceberg/scheduler-do-example.js.map +1 -0
  126. package/dist/iceberg/scheduler.d.ts +434 -0
  127. package/dist/iceberg/scheduler.d.ts.map +1 -0
  128. package/dist/iceberg/scheduler.js +818 -0
  129. package/dist/iceberg/scheduler.js.map +1 -0
  130. package/dist/iceberg/schema.d.ts +149 -0
  131. package/dist/iceberg/schema.d.ts.map +1 -0
  132. package/dist/iceberg/schema.js +525 -0
  133. package/dist/iceberg/schema.js.map +1 -0
  134. package/dist/iceberg/snapshot-manager.d.ts +406 -0
  135. package/dist/iceberg/snapshot-manager.d.ts.map +1 -0
  136. package/dist/iceberg/snapshot-manager.js +934 -0
  137. package/dist/iceberg/snapshot-manager.js.map +1 -0
  138. package/dist/iceberg/sql-router.d.ts +194 -0
  139. package/dist/iceberg/sql-router.d.ts.map +1 -0
  140. package/dist/iceberg/sql-router.js +180 -0
  141. package/dist/iceberg/sql-router.js.map +1 -0
  142. package/dist/iceberg/test-fixtures.d.ts +151 -0
  143. package/dist/iceberg/test-fixtures.d.ts.map +1 -0
  144. package/dist/iceberg/test-fixtures.js +446 -0
  145. package/dist/iceberg/test-fixtures.js.map +1 -0
  146. package/dist/iceberg/time-travel-api.d.ts +102 -0
  147. package/dist/iceberg/time-travel-api.d.ts.map +1 -0
  148. package/dist/iceberg/time-travel-api.js +437 -0
  149. package/dist/iceberg/time-travel-api.js.map +1 -0
  150. package/dist/iceberg/time-travel.d.ts +293 -0
  151. package/dist/iceberg/time-travel.d.ts.map +1 -0
  152. package/dist/iceberg/time-travel.js +689 -0
  153. package/dist/iceberg/time-travel.js.map +1 -0
  154. package/dist/iceberg/transformer.d.ts +356 -0
  155. package/dist/iceberg/transformer.d.ts.map +1 -0
  156. package/dist/iceberg/transformer.js +770 -0
  157. package/dist/iceberg/transformer.js.map +1 -0
  158. package/dist/iceberg/types.d.ts +318 -0
  159. package/dist/iceberg/types.d.ts.map +1 -0
  160. package/dist/iceberg/types.js +9 -0
  161. package/dist/iceberg/types.js.map +1 -0
  162. package/dist/iceberg/writer.d.ts +144 -0
  163. package/dist/iceberg/writer.d.ts.map +1 -0
  164. package/dist/iceberg/writer.js +452 -0
  165. package/dist/iceberg/writer.js.map +1 -0
  166. package/dist/index.d.ts +50 -0
  167. package/dist/index.d.ts.map +1 -0
  168. package/dist/index.js +69 -0
  169. package/dist/index.js.map +1 -0
  170. package/dist/lineage/index.d.ts +11 -0
  171. package/dist/lineage/index.d.ts.map +1 -0
  172. package/dist/lineage/index.js +11 -0
  173. package/dist/lineage/index.js.map +1 -0
  174. package/dist/lineage/integration.d.ts +134 -0
  175. package/dist/lineage/integration.d.ts.map +1 -0
  176. package/dist/lineage/integration.js +258 -0
  177. package/dist/lineage/integration.js.map +1 -0
  178. package/dist/lineage/tracker.d.ts +189 -0
  179. package/dist/lineage/tracker.d.ts.map +1 -0
  180. package/dist/lineage/tracker.js +1352 -0
  181. package/dist/lineage/tracker.js.map +1 -0
  182. package/dist/lineage/types.d.ts +318 -0
  183. package/dist/lineage/types.d.ts.map +1 -0
  184. package/dist/lineage/types.js +9 -0
  185. package/dist/lineage/types.js.map +1 -0
  186. package/dist/middleware/index.d.ts +11 -0
  187. package/dist/middleware/index.d.ts.map +1 -0
  188. package/dist/middleware/index.js +16 -0
  189. package/dist/middleware/index.js.map +1 -0
  190. package/dist/middleware/rate-limit.d.ts +397 -0
  191. package/dist/middleware/rate-limit.d.ts.map +1 -0
  192. package/dist/middleware/rate-limit.js +507 -0
  193. package/dist/middleware/rate-limit.js.map +1 -0
  194. package/dist/migration-tooling/external-migration.d.ts +601 -0
  195. package/dist/migration-tooling/external-migration.d.ts.map +1 -0
  196. package/dist/migration-tooling/external-migration.js +1612 -0
  197. package/dist/migration-tooling/external-migration.js.map +1 -0
  198. package/dist/migration-tooling/index.d.ts +19 -0
  199. package/dist/migration-tooling/index.d.ts.map +1 -0
  200. package/dist/migration-tooling/index.js +19 -0
  201. package/dist/migration-tooling/index.js.map +1 -0
  202. package/dist/migrations/auto-migrator.d.ts +289 -0
  203. package/dist/migrations/auto-migrator.d.ts.map +1 -0
  204. package/dist/migrations/auto-migrator.js +396 -0
  205. package/dist/migrations/auto-migrator.js.map +1 -0
  206. package/dist/migrations/bulk-orchestrator.d.ts +403 -0
  207. package/dist/migrations/bulk-orchestrator.d.ts.map +1 -0
  208. package/dist/migrations/bulk-orchestrator.js +646 -0
  209. package/dist/migrations/bulk-orchestrator.js.map +1 -0
  210. package/dist/migrations/compatibility.d.ts +216 -0
  211. package/dist/migrations/compatibility.d.ts.map +1 -0
  212. package/dist/migrations/compatibility.js +651 -0
  213. package/dist/migrations/compatibility.js.map +1 -0
  214. package/dist/migrations/do-migrations.d.ts +101 -0
  215. package/dist/migrations/do-migrations.d.ts.map +1 -0
  216. package/dist/migrations/do-migrations.js +1060 -0
  217. package/dist/migrations/do-migrations.js.map +1 -0
  218. package/dist/migrations/do-migrations.types.d.ts +550 -0
  219. package/dist/migrations/do-migrations.types.d.ts.map +1 -0
  220. package/dist/migrations/do-migrations.types.js +15 -0
  221. package/dist/migrations/do-migrations.types.js.map +1 -0
  222. package/dist/migrations/drizzle-compat.d.ts +163 -0
  223. package/dist/migrations/drizzle-compat.d.ts.map +1 -0
  224. package/dist/migrations/drizzle-compat.js +273 -0
  225. package/dist/migrations/drizzle-compat.js.map +1 -0
  226. package/dist/migrations/index.d.ts +109 -0
  227. package/dist/migrations/index.d.ts.map +1 -0
  228. package/dist/migrations/index.js +127 -0
  229. package/dist/migrations/index.js.map +1 -0
  230. package/dist/migrations/migration-api.d.ts +161 -0
  231. package/dist/migrations/migration-api.d.ts.map +1 -0
  232. package/dist/migrations/migration-api.js +499 -0
  233. package/dist/migrations/migration-api.js.map +1 -0
  234. package/dist/migrations/progress-tracker-do.d.ts +195 -0
  235. package/dist/migrations/progress-tracker-do.d.ts.map +1 -0
  236. package/dist/migrations/progress-tracker-do.js +339 -0
  237. package/dist/migrations/progress-tracker-do.js.map +1 -0
  238. package/dist/migrations/progress-tracker-kv.d.ts +103 -0
  239. package/dist/migrations/progress-tracker-kv.d.ts.map +1 -0
  240. package/dist/migrations/progress-tracker-kv.js +231 -0
  241. package/dist/migrations/progress-tracker-kv.js.map +1 -0
  242. package/dist/migrations/progress-tracker.d.ts +320 -0
  243. package/dist/migrations/progress-tracker.d.ts.map +1 -0
  244. package/dist/migrations/progress-tracker.js +443 -0
  245. package/dist/migrations/progress-tracker.js.map +1 -0
  246. package/dist/migrations/registry.d.ts +231 -0
  247. package/dist/migrations/registry.d.ts.map +1 -0
  248. package/dist/migrations/registry.js +376 -0
  249. package/dist/migrations/registry.js.map +1 -0
  250. package/dist/migrations/runner.d.ts +197 -0
  251. package/dist/migrations/runner.d.ts.map +1 -0
  252. package/dist/migrations/runner.js +1167 -0
  253. package/dist/migrations/runner.js.map +1 -0
  254. package/dist/migrations/schema-generator.d.ts +111 -0
  255. package/dist/migrations/schema-generator.d.ts.map +1 -0
  256. package/dist/migrations/schema-generator.js +335 -0
  257. package/dist/migrations/schema-generator.js.map +1 -0
  258. package/dist/migrations/testing.d.ts +321 -0
  259. package/dist/migrations/testing.d.ts.map +1 -0
  260. package/dist/migrations/testing.js +645 -0
  261. package/dist/migrations/testing.js.map +1 -0
  262. package/dist/migrations/types.d.ts +503 -0
  263. package/dist/migrations/types.d.ts.map +1 -0
  264. package/dist/migrations/types.js +11 -0
  265. package/dist/migrations/types.js.map +1 -0
  266. package/dist/migrations/validator.d.ts +215 -0
  267. package/dist/migrations/validator.d.ts.map +1 -0
  268. package/dist/migrations/validator.js +494 -0
  269. package/dist/migrations/validator.js.map +1 -0
  270. package/dist/observability/alerting.d.ts +116 -0
  271. package/dist/observability/alerting.d.ts.map +1 -0
  272. package/dist/observability/alerting.js +353 -0
  273. package/dist/observability/alerting.js.map +1 -0
  274. package/dist/observability/analytics-engine.d.ts +357 -0
  275. package/dist/observability/analytics-engine.d.ts.map +1 -0
  276. package/dist/observability/analytics-engine.js +430 -0
  277. package/dist/observability/analytics-engine.js.map +1 -0
  278. package/dist/observability/cost-metrics.d.ts +269 -0
  279. package/dist/observability/cost-metrics.d.ts.map +1 -0
  280. package/dist/observability/cost-metrics.js +560 -0
  281. package/dist/observability/cost-metrics.js.map +1 -0
  282. package/dist/observability/cross-do-tracing.d.ts +305 -0
  283. package/dist/observability/cross-do-tracing.d.ts.map +1 -0
  284. package/dist/observability/cross-do-tracing.js +431 -0
  285. package/dist/observability/cross-do-tracing.js.map +1 -0
  286. package/dist/observability/error-rate-collector.d.ts +163 -0
  287. package/dist/observability/error-rate-collector.d.ts.map +1 -0
  288. package/dist/observability/error-rate-collector.js +306 -0
  289. package/dist/observability/error-rate-collector.js.map +1 -0
  290. package/dist/observability/exporters.d.ts +231 -0
  291. package/dist/observability/exporters.d.ts.map +1 -0
  292. package/dist/observability/exporters.js +479 -0
  293. package/dist/observability/exporters.js.map +1 -0
  294. package/dist/observability/health-check.d.ts +106 -0
  295. package/dist/observability/health-check.d.ts.map +1 -0
  296. package/dist/observability/health-check.js +243 -0
  297. package/dist/observability/health-check.js.map +1 -0
  298. package/dist/observability/index.d.ts +297 -0
  299. package/dist/observability/index.d.ts.map +1 -0
  300. package/dist/observability/index.js +455 -0
  301. package/dist/observability/index.js.map +1 -0
  302. package/dist/observability/instrumentation.d.ts +222 -0
  303. package/dist/observability/instrumentation.d.ts.map +1 -0
  304. package/dist/observability/instrumentation.js +532 -0
  305. package/dist/observability/instrumentation.js.map +1 -0
  306. package/dist/observability/memory-metrics.d.ts +227 -0
  307. package/dist/observability/memory-metrics.d.ts.map +1 -0
  308. package/dist/observability/memory-metrics.js +688 -0
  309. package/dist/observability/memory-metrics.js.map +1 -0
  310. package/dist/observability/metrics-endpoint.d.ts +91 -0
  311. package/dist/observability/metrics-endpoint.d.ts.map +1 -0
  312. package/dist/observability/metrics-endpoint.js +246 -0
  313. package/dist/observability/metrics-endpoint.js.map +1 -0
  314. package/dist/observability/metrics.d.ts +88 -0
  315. package/dist/observability/metrics.d.ts.map +1 -0
  316. package/dist/observability/metrics.js +253 -0
  317. package/dist/observability/metrics.js.map +1 -0
  318. package/dist/observability/observability-features.d.ts +488 -0
  319. package/dist/observability/observability-features.d.ts.map +1 -0
  320. package/dist/observability/observability-features.js +773 -0
  321. package/dist/observability/observability-features.js.map +1 -0
  322. package/dist/observability/prometheus.d.ts +39 -0
  323. package/dist/observability/prometheus.d.ts.map +1 -0
  324. package/dist/observability/prometheus.js +120 -0
  325. package/dist/observability/prometheus.js.map +1 -0
  326. package/dist/observability/propagation.d.ts +126 -0
  327. package/dist/observability/propagation.d.ts.map +1 -0
  328. package/dist/observability/propagation.js +234 -0
  329. package/dist/observability/propagation.js.map +1 -0
  330. package/dist/observability/query-latency.d.ts +243 -0
  331. package/dist/observability/query-latency.d.ts.map +1 -0
  332. package/dist/observability/query-latency.js +292 -0
  333. package/dist/observability/query-latency.js.map +1 -0
  334. package/dist/observability/query-performance.d.ts +169 -0
  335. package/dist/observability/query-performance.d.ts.map +1 -0
  336. package/dist/observability/query-performance.js +290 -0
  337. package/dist/observability/query-performance.js.map +1 -0
  338. package/dist/observability/storage-tier-metrics.d.ts +174 -0
  339. package/dist/observability/storage-tier-metrics.d.ts.map +1 -0
  340. package/dist/observability/storage-tier-metrics.js +306 -0
  341. package/dist/observability/storage-tier-metrics.js.map +1 -0
  342. package/dist/observability/tier-cost-optimizer.d.ts +155 -0
  343. package/dist/observability/tier-cost-optimizer.d.ts.map +1 -0
  344. package/dist/observability/tier-cost-optimizer.js +536 -0
  345. package/dist/observability/tier-cost-optimizer.js.map +1 -0
  346. package/dist/observability/tracer.d.ts +149 -0
  347. package/dist/observability/tracer.d.ts.map +1 -0
  348. package/dist/observability/tracer.js +435 -0
  349. package/dist/observability/tracer.js.map +1 -0
  350. package/dist/observability/types.d.ts +402 -0
  351. package/dist/observability/types.d.ts.map +1 -0
  352. package/dist/observability/types.js +103 -0
  353. package/dist/observability/types.js.map +1 -0
  354. package/dist/pglite/workers-pglite.d.ts +138 -0
  355. package/dist/pglite/workers-pglite.d.ts.map +1 -0
  356. package/dist/pglite/workers-pglite.js +143 -0
  357. package/dist/pglite/workers-pglite.js.map +1 -0
  358. package/dist/pglite-assets/pglite.data +0 -0
  359. package/dist/pglite-assets/pglite.wasm +0 -0
  360. package/dist/playground/index.d.ts +52 -0
  361. package/dist/playground/index.d.ts.map +1 -0
  362. package/dist/playground/index.js +55 -0
  363. package/dist/playground/index.js.map +1 -0
  364. package/dist/playground/keyboard-shortcuts.d.ts +116 -0
  365. package/dist/playground/keyboard-shortcuts.d.ts.map +1 -0
  366. package/dist/playground/keyboard-shortcuts.js +588 -0
  367. package/dist/playground/keyboard-shortcuts.js.map +1 -0
  368. package/dist/playground/playground.d.ts +82 -0
  369. package/dist/playground/playground.d.ts.map +1 -0
  370. package/dist/playground/playground.js +271 -0
  371. package/dist/playground/playground.js.map +1 -0
  372. package/dist/playground/query-executor.d.ts +115 -0
  373. package/dist/playground/query-executor.d.ts.map +1 -0
  374. package/dist/playground/query-executor.js +558 -0
  375. package/dist/playground/query-executor.js.map +1 -0
  376. package/dist/playground/query-history.d.ts +92 -0
  377. package/dist/playground/query-history.d.ts.map +1 -0
  378. package/dist/playground/query-history.js +259 -0
  379. package/dist/playground/query-history.js.map +1 -0
  380. package/dist/playground/result-formatter.d.ts +59 -0
  381. package/dist/playground/result-formatter.d.ts.map +1 -0
  382. package/dist/playground/result-formatter.js +341 -0
  383. package/dist/playground/result-formatter.js.map +1 -0
  384. package/dist/playground/sample-datasets.d.ts +77 -0
  385. package/dist/playground/sample-datasets.d.ts.map +1 -0
  386. package/dist/playground/sample-datasets.js +641 -0
  387. package/dist/playground/sample-datasets.js.map +1 -0
  388. package/dist/playground/sample-queries.d.ts +73 -0
  389. package/dist/playground/sample-queries.d.ts.map +1 -0
  390. package/dist/playground/sample-queries.js +1095 -0
  391. package/dist/playground/sample-queries.js.map +1 -0
  392. package/dist/playground/schema-explorer.d.ts +55 -0
  393. package/dist/playground/schema-explorer.d.ts.map +1 -0
  394. package/dist/playground/schema-explorer.js +473 -0
  395. package/dist/playground/schema-explorer.js.map +1 -0
  396. package/dist/playground/types.d.ts +430 -0
  397. package/dist/playground/types.d.ts.map +1 -0
  398. package/dist/playground/types.js +10 -0
  399. package/dist/playground/types.js.map +1 -0
  400. package/dist/readonly/cache-reader.d.ts +145 -0
  401. package/dist/readonly/cache-reader.d.ts.map +1 -0
  402. package/dist/readonly/cache-reader.js +198 -0
  403. package/dist/readonly/cache-reader.js.map +1 -0
  404. package/dist/readonly/config.d.ts +74 -0
  405. package/dist/readonly/config.d.ts.map +1 -0
  406. package/dist/readonly/config.js +67 -0
  407. package/dist/readonly/config.js.map +1 -0
  408. package/dist/readonly/index.d.ts +22 -0
  409. package/dist/readonly/index.d.ts.map +1 -0
  410. package/dist/readonly/index.js +17 -0
  411. package/dist/readonly/index.js.map +1 -0
  412. package/dist/readonly/pglite-wrapper.d.ts +82 -0
  413. package/dist/readonly/pglite-wrapper.d.ts.map +1 -0
  414. package/dist/readonly/pglite-wrapper.js +123 -0
  415. package/dist/readonly/pglite-wrapper.js.map +1 -0
  416. package/dist/readonly/worker.d.ts +142 -0
  417. package/dist/readonly/worker.d.ts.map +1 -0
  418. package/dist/readonly/worker.js +187 -0
  419. package/dist/readonly/worker.js.map +1 -0
  420. package/dist/readonly/write-blocker.d.ts +47 -0
  421. package/dist/readonly/write-blocker.d.ts.map +1 -0
  422. package/dist/readonly/write-blocker.js +136 -0
  423. package/dist/readonly/write-blocker.js.map +1 -0
  424. package/dist/recovery/disaster-recovery.d.ts +326 -0
  425. package/dist/recovery/disaster-recovery.d.ts.map +1 -0
  426. package/dist/recovery/disaster-recovery.js +799 -0
  427. package/dist/recovery/disaster-recovery.js.map +1 -0
  428. package/dist/recovery/index.d.ts +12 -0
  429. package/dist/recovery/index.d.ts.map +1 -0
  430. package/dist/recovery/index.js +12 -0
  431. package/dist/recovery/index.js.map +1 -0
  432. package/dist/recovery/parquet-parser.d.ts +321 -0
  433. package/dist/recovery/parquet-parser.d.ts.map +1 -0
  434. package/dist/recovery/parquet-parser.js +797 -0
  435. package/dist/recovery/parquet-parser.js.map +1 -0
  436. package/dist/retention/index.d.ts +50 -0
  437. package/dist/retention/index.d.ts.map +1 -0
  438. package/dist/retention/index.js +50 -0
  439. package/dist/retention/index.js.map +1 -0
  440. package/dist/retention/policy.d.ts +344 -0
  441. package/dist/retention/policy.d.ts.map +1 -0
  442. package/dist/retention/policy.js +472 -0
  443. package/dist/retention/policy.js.map +1 -0
  444. package/dist/retention/purger.d.ts +187 -0
  445. package/dist/retention/purger.d.ts.map +1 -0
  446. package/dist/retention/purger.js +411 -0
  447. package/dist/retention/purger.js.map +1 -0
  448. package/dist/rls/auth-integration.d.ts +280 -0
  449. package/dist/rls/auth-integration.d.ts.map +1 -0
  450. package/dist/rls/auth-integration.js +399 -0
  451. package/dist/rls/auth-integration.js.map +1 -0
  452. package/dist/rls/generator.d.ts +249 -0
  453. package/dist/rls/generator.d.ts.map +1 -0
  454. package/dist/rls/generator.js +495 -0
  455. package/dist/rls/generator.js.map +1 -0
  456. package/dist/rls/index.d.ts +26 -0
  457. package/dist/rls/index.d.ts.map +1 -0
  458. package/dist/rls/index.js +58 -0
  459. package/dist/rls/index.js.map +1 -0
  460. package/dist/rls/policy.d.ts +116 -0
  461. package/dist/rls/policy.d.ts.map +1 -0
  462. package/dist/rls/policy.js +77 -0
  463. package/dist/rls/policy.js.map +1 -0
  464. package/dist/rls/validator.d.ts +155 -0
  465. package/dist/rls/validator.d.ts.map +1 -0
  466. package/dist/rls/validator.js +792 -0
  467. package/dist/rls/validator.js.map +1 -0
  468. package/dist/routing/adaptive-router.d.ts +317 -0
  469. package/dist/routing/adaptive-router.d.ts.map +1 -0
  470. package/dist/routing/adaptive-router.js +554 -0
  471. package/dist/routing/adaptive-router.js.map +1 -0
  472. package/dist/routing/circuit-breaker.d.ts +339 -0
  473. package/dist/routing/circuit-breaker.d.ts.map +1 -0
  474. package/dist/routing/circuit-breaker.js +620 -0
  475. package/dist/routing/circuit-breaker.js.map +1 -0
  476. package/dist/routing/cost-metrics.d.ts +133 -0
  477. package/dist/routing/cost-metrics.d.ts.map +1 -0
  478. package/dist/routing/cost-metrics.js +259 -0
  479. package/dist/routing/cost-metrics.js.map +1 -0
  480. package/dist/routing/do-connection-pool.d.ts +243 -0
  481. package/dist/routing/do-connection-pool.d.ts.map +1 -0
  482. package/dist/routing/do-connection-pool.js +572 -0
  483. package/dist/routing/do-connection-pool.js.map +1 -0
  484. package/dist/routing/index.d.ts +59 -0
  485. package/dist/routing/index.d.ts.map +1 -0
  486. package/dist/routing/index.js +59 -0
  487. package/dist/routing/index.js.map +1 -0
  488. package/dist/routing/query-complexity-estimator.d.ts +73 -0
  489. package/dist/routing/query-complexity-estimator.d.ts.map +1 -0
  490. package/dist/routing/query-complexity-estimator.js +327 -0
  491. package/dist/routing/query-complexity-estimator.js.map +1 -0
  492. package/dist/routing/request-coalescing.d.ts +178 -0
  493. package/dist/routing/request-coalescing.d.ts.map +1 -0
  494. package/dist/routing/request-coalescing.js +325 -0
  495. package/dist/routing/request-coalescing.js.map +1 -0
  496. package/dist/routing/runtime-router.d.ts +107 -0
  497. package/dist/routing/runtime-router.d.ts.map +1 -0
  498. package/dist/routing/runtime-router.js +246 -0
  499. package/dist/routing/runtime-router.js.map +1 -0
  500. package/dist/routing/tenant-router.d.ts +848 -0
  501. package/dist/routing/tenant-router.d.ts.map +1 -0
  502. package/dist/routing/tenant-router.js +1056 -0
  503. package/dist/routing/tenant-router.js.map +1 -0
  504. package/dist/routing/websocket-pool.d.ts +119 -0
  505. package/dist/routing/websocket-pool.d.ts.map +1 -0
  506. package/dist/routing/websocket-pool.js +436 -0
  507. package/dist/routing/websocket-pool.js.map +1 -0
  508. package/dist/storage/cache-layer.d.ts +159 -0
  509. package/dist/storage/cache-layer.d.ts.map +1 -0
  510. package/dist/storage/cache-layer.js +245 -0
  511. package/dist/storage/cache-layer.js.map +1 -0
  512. package/dist/storage/cost-aware-tiering.d.ts +258 -0
  513. package/dist/storage/cost-aware-tiering.d.ts.map +1 -0
  514. package/dist/storage/cost-aware-tiering.js +526 -0
  515. package/dist/storage/cost-aware-tiering.js.map +1 -0
  516. package/dist/storage/index.d.ts +87 -0
  517. package/dist/storage/index.d.ts.map +1 -0
  518. package/dist/storage/index.js +78 -0
  519. package/dist/storage/index.js.map +1 -0
  520. package/dist/storage/interfaces.d.ts +856 -0
  521. package/dist/storage/interfaces.d.ts.map +1 -0
  522. package/dist/storage/interfaces.js +69 -0
  523. package/dist/storage/interfaces.js.map +1 -0
  524. package/dist/storage/r2-layer.d.ts +226 -0
  525. package/dist/storage/r2-layer.d.ts.map +1 -0
  526. package/dist/storage/r2-layer.js +307 -0
  527. package/dist/storage/r2-layer.js.map +1 -0
  528. package/dist/storage/r2-overflow.d.ts +344 -0
  529. package/dist/storage/r2-overflow.d.ts.map +1 -0
  530. package/dist/storage/r2-overflow.js +730 -0
  531. package/dist/storage/r2-overflow.js.map +1 -0
  532. package/dist/storage/r2-page-vfs.d.ts +374 -0
  533. package/dist/storage/r2-page-vfs.d.ts.map +1 -0
  534. package/dist/storage/r2-page-vfs.js +754 -0
  535. package/dist/storage/r2-page-vfs.js.map +1 -0
  536. package/dist/storage/swr-cache.d.ts +181 -0
  537. package/dist/storage/swr-cache.d.ts.map +1 -0
  538. package/dist/storage/swr-cache.js +295 -0
  539. package/dist/storage/swr-cache.js.map +1 -0
  540. package/dist/storage/tiered-orchestrator.d.ts +951 -0
  541. package/dist/storage/tiered-orchestrator.d.ts.map +1 -0
  542. package/dist/storage/tiered-orchestrator.js +1731 -0
  543. package/dist/storage/tiered-orchestrator.js.map +1 -0
  544. package/dist/storage/tiered-vfs-swr.d.ts +279 -0
  545. package/dist/storage/tiered-vfs-swr.d.ts.map +1 -0
  546. package/dist/storage/tiered-vfs-swr.js +584 -0
  547. package/dist/storage/tiered-vfs-swr.js.map +1 -0
  548. package/dist/storage/tiered-vfs.d.ts +405 -0
  549. package/dist/storage/tiered-vfs.d.ts.map +1 -0
  550. package/dist/storage/tiered-vfs.js +833 -0
  551. package/dist/storage/tiered-vfs.js.map +1 -0
  552. package/dist/streaming/backpressure-controller.d.ts +173 -0
  553. package/dist/streaming/backpressure-controller.d.ts.map +1 -0
  554. package/dist/streaming/backpressure-controller.js +344 -0
  555. package/dist/streaming/backpressure-controller.js.map +1 -0
  556. package/dist/streaming/buffer-pool.d.ts +241 -0
  557. package/dist/streaming/buffer-pool.d.ts.map +1 -0
  558. package/dist/streaming/buffer-pool.js +381 -0
  559. package/dist/streaming/buffer-pool.js.map +1 -0
  560. package/dist/streaming/cdc-iceberg-connector.d.ts +272 -0
  561. package/dist/streaming/cdc-iceberg-connector.d.ts.map +1 -0
  562. package/dist/streaming/cdc-iceberg-connector.js +408 -0
  563. package/dist/streaming/cdc-iceberg-connector.js.map +1 -0
  564. package/dist/streaming/index.d.ts +111 -0
  565. package/dist/streaming/index.d.ts.map +1 -0
  566. package/dist/streaming/index.js +128 -0
  567. package/dist/streaming/index.js.map +1 -0
  568. package/dist/streaming/live-cdc-stream.d.ts +400 -0
  569. package/dist/streaming/live-cdc-stream.d.ts.map +1 -0
  570. package/dist/streaming/live-cdc-stream.js +703 -0
  571. package/dist/streaming/live-cdc-stream.js.map +1 -0
  572. package/dist/streaming/memory-bounded-stream.d.ts +207 -0
  573. package/dist/streaming/memory-bounded-stream.d.ts.map +1 -0
  574. package/dist/streaming/memory-bounded-stream.js +340 -0
  575. package/dist/streaming/memory-bounded-stream.js.map +1 -0
  576. package/dist/streaming/query-streamer.d.ts +379 -0
  577. package/dist/streaming/query-streamer.d.ts.map +1 -0
  578. package/dist/streaming/query-streamer.js +495 -0
  579. package/dist/streaming/query-streamer.js.map +1 -0
  580. package/dist/streaming/response-streaming.d.ts +203 -0
  581. package/dist/streaming/response-streaming.d.ts.map +1 -0
  582. package/dist/streaming/response-streaming.js +449 -0
  583. package/dist/streaming/response-streaming.js.map +1 -0
  584. package/dist/types/branded.d.ts +859 -0
  585. package/dist/types/branded.d.ts.map +1 -0
  586. package/dist/types/branded.js +891 -0
  587. package/dist/types/branded.js.map +1 -0
  588. package/dist/types/utilities.d.ts +757 -0
  589. package/dist/types/utilities.d.ts.map +1 -0
  590. package/dist/types/utilities.js +447 -0
  591. package/dist/types/utilities.js.map +1 -0
  592. package/dist/wal/replay-engine.d.ts +344 -0
  593. package/dist/wal/replay-engine.d.ts.map +1 -0
  594. package/dist/wal/replay-engine.js +975 -0
  595. package/dist/wal/replay-engine.js.map +1 -0
  596. package/dist/worker/__mocks__/capnweb.d.ts +13 -0
  597. package/dist/worker/__mocks__/capnweb.d.ts.map +1 -0
  598. package/dist/worker/__mocks__/capnweb.js +15 -0
  599. package/dist/worker/__mocks__/capnweb.js.map +1 -0
  600. package/dist/worker/__mocks__/cloudflare-workers.d.ts +31 -0
  601. package/dist/worker/__mocks__/cloudflare-workers.d.ts.map +1 -0
  602. package/dist/worker/__mocks__/cloudflare-workers.js +33 -0
  603. package/dist/worker/__mocks__/cloudflare-workers.js.map +1 -0
  604. package/dist/worker/__mocks__/pglite.data.d.ts +3 -0
  605. package/dist/worker/__mocks__/pglite.data.d.ts.map +1 -0
  606. package/dist/worker/__mocks__/pglite.data.js +20 -0
  607. package/dist/worker/__mocks__/pglite.data.js.map +1 -0
  608. package/dist/worker/__mocks__/pglite.wasm.d.ts +3 -0
  609. package/dist/worker/__mocks__/pglite.wasm.d.ts.map +1 -0
  610. package/dist/worker/__mocks__/pglite.wasm.js +30 -0
  611. package/dist/worker/__mocks__/pglite.wasm.js.map +1 -0
  612. package/dist/worker/auth-rate-limiter.d.ts +270 -0
  613. package/dist/worker/auth-rate-limiter.d.ts.map +1 -0
  614. package/dist/worker/auth-rate-limiter.js +332 -0
  615. package/dist/worker/auth-rate-limiter.js.map +1 -0
  616. package/dist/worker/auth.d.ts +345 -0
  617. package/dist/worker/auth.d.ts.map +1 -0
  618. package/dist/worker/auth.js +837 -0
  619. package/dist/worker/auth.js.map +1 -0
  620. package/dist/worker/cdc-backpressure.d.ts +338 -0
  621. package/dist/worker/cdc-backpressure.d.ts.map +1 -0
  622. package/dist/worker/cdc-backpressure.js +619 -0
  623. package/dist/worker/cdc-backpressure.js.map +1 -0
  624. package/dist/worker/cdc-sse.d.ts +277 -0
  625. package/dist/worker/cdc-sse.d.ts.map +1 -0
  626. package/dist/worker/cdc-sse.js +528 -0
  627. package/dist/worker/cdc-sse.js.map +1 -0
  628. package/dist/worker/cdc-websocket.d.ts +252 -0
  629. package/dist/worker/cdc-websocket.d.ts.map +1 -0
  630. package/dist/worker/cdc-websocket.js +940 -0
  631. package/dist/worker/cdc-websocket.js.map +1 -0
  632. package/dist/worker/cdc.d.ts +95 -0
  633. package/dist/worker/cdc.d.ts.map +1 -0
  634. package/dist/worker/cdc.js +211 -0
  635. package/dist/worker/cdc.js.map +1 -0
  636. package/dist/worker/concerns/auth-concern.d.ts +50 -0
  637. package/dist/worker/concerns/auth-concern.d.ts.map +1 -0
  638. package/dist/worker/concerns/auth-concern.js +131 -0
  639. package/dist/worker/concerns/auth-concern.js.map +1 -0
  640. package/dist/worker/concerns/cdc-concern.d.ts +99 -0
  641. package/dist/worker/concerns/cdc-concern.d.ts.map +1 -0
  642. package/dist/worker/concerns/cdc-concern.js +137 -0
  643. package/dist/worker/concerns/cdc-concern.js.map +1 -0
  644. package/dist/worker/concerns/index.d.ts +22 -0
  645. package/dist/worker/concerns/index.d.ts.map +1 -0
  646. package/dist/worker/concerns/index.js +13 -0
  647. package/dist/worker/concerns/index.js.map +1 -0
  648. package/dist/worker/concerns/query-execution-concern.d.ts +104 -0
  649. package/dist/worker/concerns/query-execution-concern.d.ts.map +1 -0
  650. package/dist/worker/concerns/query-execution-concern.js +95 -0
  651. package/dist/worker/concerns/query-execution-concern.js.map +1 -0
  652. package/dist/worker/concerns/storage-orchestration-concern.d.ts +78 -0
  653. package/dist/worker/concerns/storage-orchestration-concern.d.ts.map +1 -0
  654. package/dist/worker/concerns/storage-orchestration-concern.js +240 -0
  655. package/dist/worker/concerns/storage-orchestration-concern.js.map +1 -0
  656. package/dist/worker/do-auth-manager.d.ts +108 -0
  657. package/dist/worker/do-auth-manager.d.ts.map +1 -0
  658. package/dist/worker/do-auth-manager.js +212 -0
  659. package/dist/worker/do-auth-manager.js.map +1 -0
  660. package/dist/worker/do-pglite-manager.d.ts +137 -0
  661. package/dist/worker/do-pglite-manager.d.ts.map +1 -0
  662. package/dist/worker/do-pglite-manager.js +228 -0
  663. package/dist/worker/do-pglite-manager.js.map +1 -0
  664. package/dist/worker/do.d.ts +556 -0
  665. package/dist/worker/do.d.ts.map +1 -0
  666. package/dist/worker/do.js +1441 -0
  667. package/dist/worker/do.js.map +1 -0
  668. package/dist/worker/entry.d.ts +23 -0
  669. package/dist/worker/entry.d.ts.map +1 -0
  670. package/dist/worker/entry.js +362 -0
  671. package/dist/worker/entry.js.map +1 -0
  672. package/dist/worker/errors.d.ts +106 -0
  673. package/dist/worker/errors.d.ts.map +1 -0
  674. package/dist/worker/errors.js +178 -0
  675. package/dist/worker/errors.js.map +1 -0
  676. package/dist/worker/health-check-manager.d.ts +141 -0
  677. package/dist/worker/health-check-manager.d.ts.map +1 -0
  678. package/dist/worker/health-check-manager.js +145 -0
  679. package/dist/worker/health-check-manager.js.map +1 -0
  680. package/dist/worker/index.d.ts +60 -0
  681. package/dist/worker/index.d.ts.map +1 -0
  682. package/dist/worker/index.js +67 -0
  683. package/dist/worker/index.js.map +1 -0
  684. package/dist/worker/memory-pressure.d.ts +892 -0
  685. package/dist/worker/memory-pressure.d.ts.map +1 -0
  686. package/dist/worker/memory-pressure.js +1990 -0
  687. package/dist/worker/memory-pressure.js.map +1 -0
  688. package/dist/worker/migration-manager.d.ts +153 -0
  689. package/dist/worker/migration-manager.d.ts.map +1 -0
  690. package/dist/worker/migration-manager.js +461 -0
  691. package/dist/worker/migration-manager.js.map +1 -0
  692. package/dist/worker/plugin-manager.d.ts +147 -0
  693. package/dist/worker/plugin-manager.d.ts.map +1 -0
  694. package/dist/worker/plugin-manager.js +408 -0
  695. package/dist/worker/plugin-manager.js.map +1 -0
  696. package/dist/worker/proxy.d.ts +330 -0
  697. package/dist/worker/proxy.d.ts.map +1 -0
  698. package/dist/worker/proxy.js +504 -0
  699. package/dist/worker/proxy.js.map +1 -0
  700. package/dist/worker/query-execution-manager.d.ts +107 -0
  701. package/dist/worker/query-execution-manager.d.ts.map +1 -0
  702. package/dist/worker/query-execution-manager.js +155 -0
  703. package/dist/worker/query-execution-manager.js.map +1 -0
  704. package/dist/worker/query-executor.d.ts +163 -0
  705. package/dist/worker/query-executor.d.ts.map +1 -0
  706. package/dist/worker/query-executor.js +413 -0
  707. package/dist/worker/query-executor.js.map +1 -0
  708. package/dist/worker/query-stats-manager.d.ts +117 -0
  709. package/dist/worker/query-stats-manager.d.ts.map +1 -0
  710. package/dist/worker/query-stats-manager.js +162 -0
  711. package/dist/worker/query-stats-manager.js.map +1 -0
  712. package/dist/worker/result-handler.d.ts +192 -0
  713. package/dist/worker/result-handler.d.ts.map +1 -0
  714. package/dist/worker/result-handler.js +346 -0
  715. package/dist/worker/result-handler.js.map +1 -0
  716. package/dist/worker/routes.d.ts +135 -0
  717. package/dist/worker/routes.d.ts.map +1 -0
  718. package/dist/worker/routes.js +460 -0
  719. package/dist/worker/routes.js.map +1 -0
  720. package/dist/worker/rpc-methods-manager.d.ts +142 -0
  721. package/dist/worker/rpc-methods-manager.d.ts.map +1 -0
  722. package/dist/worker/rpc-methods-manager.js +195 -0
  723. package/dist/worker/rpc-methods-manager.js.map +1 -0
  724. package/dist/worker/rpc.d.ts +259 -0
  725. package/dist/worker/rpc.d.ts.map +1 -0
  726. package/dist/worker/rpc.js +398 -0
  727. package/dist/worker/rpc.js.map +1 -0
  728. package/dist/worker/schema-version.d.ts +209 -0
  729. package/dist/worker/schema-version.d.ts.map +1 -0
  730. package/dist/worker/schema-version.js +450 -0
  731. package/dist/worker/schema-version.js.map +1 -0
  732. package/dist/worker/session-manager.d.ts +282 -0
  733. package/dist/worker/session-manager.d.ts.map +1 -0
  734. package/dist/worker/session-manager.js +523 -0
  735. package/dist/worker/session-manager.js.map +1 -0
  736. package/dist/worker/shutdown-manager.d.ts +188 -0
  737. package/dist/worker/shutdown-manager.d.ts.map +1 -0
  738. package/dist/worker/shutdown-manager.js +347 -0
  739. package/dist/worker/shutdown-manager.js.map +1 -0
  740. package/dist/worker/sql-transform.d.ts +61 -0
  741. package/dist/worker/sql-transform.d.ts.map +1 -0
  742. package/dist/worker/sql-transform.js +312 -0
  743. package/dist/worker/sql-transform.js.map +1 -0
  744. package/dist/worker/types.d.ts +738 -0
  745. package/dist/worker/types.d.ts.map +1 -0
  746. package/dist/worker/types.js +6 -0
  747. package/dist/worker/types.js.map +1 -0
  748. package/dist/worker/user-routes.d.ts +76 -0
  749. package/dist/worker/user-routes.d.ts.map +1 -0
  750. package/dist/worker/user-routes.js +188 -0
  751. package/dist/worker/user-routes.js.map +1 -0
  752. package/dist/worker/wal-facade.d.ts +138 -0
  753. package/dist/worker/wal-facade.d.ts.map +1 -0
  754. package/dist/worker/wal-facade.js +184 -0
  755. package/dist/worker/wal-facade.js.map +1 -0
  756. package/dist/worker/wal-r2.d.ts +271 -0
  757. package/dist/worker/wal-r2.d.ts.map +1 -0
  758. package/dist/worker/wal-r2.js +689 -0
  759. package/dist/worker/wal-r2.js.map +1 -0
  760. package/dist/worker/wal-replay.d.ts +361 -0
  761. package/dist/worker/wal-replay.d.ts.map +1 -0
  762. package/dist/worker/wal-replay.js +628 -0
  763. package/dist/worker/wal-replay.js.map +1 -0
  764. package/dist/worker/wal-retention.d.ts +389 -0
  765. package/dist/worker/wal-retention.d.ts.map +1 -0
  766. package/dist/worker/wal-retention.js +763 -0
  767. package/dist/worker/wal-retention.js.map +1 -0
  768. package/dist/worker/wal.d.ts +278 -0
  769. package/dist/worker/wal.d.ts.map +1 -0
  770. package/dist/worker/wal.js +467 -0
  771. package/dist/worker/wal.js.map +1 -0
  772. package/dist/worker/websocket.d.ts +85 -0
  773. package/dist/worker/websocket.d.ts.map +1 -0
  774. package/dist/worker/websocket.js +227 -0
  775. package/dist/worker/websocket.js.map +1 -0
  776. package/package.json +108 -0
  777. package/src/cdc/change-stream.ts +137 -0
  778. package/src/cdc/filter.ts +646 -0
  779. package/src/cdc/index.ts +112 -0
  780. package/src/cdc/resume-token.ts +280 -0
  781. package/src/cdc/transport/index.ts +7 -0
  782. package/src/cdc/transport/sse.ts +723 -0
  783. package/src/cdc/transport/websocket.ts +873 -0
  784. package/src/cdc/types.ts +346 -0
  785. package/src/config/index.ts +25 -0
  786. package/src/config/memory.ts +177 -0
  787. package/src/config/storage.ts +204 -0
  788. package/src/config/streaming.ts +147 -0
  789. package/src/config/timeouts.ts +221 -0
  790. package/src/extensions/config.test.ts +187 -0
  791. package/src/extensions/config.ts +278 -0
  792. package/src/extensions/geo.test.ts +455 -0
  793. package/src/extensions/geo.ts +858 -0
  794. package/src/extensions/index.test.ts +259 -0
  795. package/src/extensions/index.ts +227 -0
  796. package/src/extensions/loader.test.ts +555 -0
  797. package/src/extensions/loader.ts +588 -0
  798. package/src/extensions/pgmq-lite.test.ts +727 -0
  799. package/src/extensions/pgmq-lite.ts +770 -0
  800. package/src/extensions/plugins.test.ts +528 -0
  801. package/src/extensions/plugins.ts +718 -0
  802. package/src/extensions/registry.test.ts +202 -0
  803. package/src/extensions/registry.ts +267 -0
  804. package/src/extensions/vector.test.ts +195 -0
  805. package/src/extensions/vector.ts +217 -0
  806. package/src/iceberg/SCHEDULER.md +580 -0
  807. package/src/iceberg/analytics.test.ts +703 -0
  808. package/src/iceberg/analytics.ts +727 -0
  809. package/src/iceberg/catalog-api.test.ts +838 -0
  810. package/src/iceberg/catalog-api.ts +520 -0
  811. package/src/iceberg/catalog.test.ts +680 -0
  812. package/src/iceberg/catalog.ts +1007 -0
  813. package/src/iceberg/iceberg.test.ts +705 -0
  814. package/src/iceberg/index.ts +406 -0
  815. package/src/iceberg/metadata.test.ts +632 -0
  816. package/src/iceberg/metadata.ts +649 -0
  817. package/src/iceberg/optimizer.test.ts +868 -0
  818. package/src/iceberg/optimizer.ts +1287 -0
  819. package/src/iceberg/parquet.test.ts +899 -0
  820. package/src/iceberg/parquet.ts +1640 -0
  821. package/src/iceberg/r2-organization.test.ts +615 -0
  822. package/src/iceberg/r2-organization.ts +951 -0
  823. package/src/iceberg/scheduler-do-example.ts +364 -0
  824. package/src/iceberg/scheduler.test.ts +861 -0
  825. package/src/iceberg/scheduler.ts +1201 -0
  826. package/src/iceberg/schema.test.ts +547 -0
  827. package/src/iceberg/schema.ts +616 -0
  828. package/src/iceberg/snapshot-manager.test.ts +919 -0
  829. package/src/iceberg/snapshot-manager.ts +1369 -0
  830. package/src/iceberg/sql-router.test.ts +334 -0
  831. package/src/iceberg/sql-router.ts +337 -0
  832. package/src/iceberg/test-fixtures.ts +605 -0
  833. package/src/iceberg/time-travel-api.test.ts +1029 -0
  834. package/src/iceberg/time-travel-api.ts +731 -0
  835. package/src/iceberg/time-travel.test.ts +1218 -0
  836. package/src/iceberg/time-travel.ts +1052 -0
  837. package/src/iceberg/transformer.test.ts +689 -0
  838. package/src/iceberg/transformer.ts +1029 -0
  839. package/src/iceberg/types.ts +373 -0
  840. package/src/iceberg/writer.test.ts +716 -0
  841. package/src/iceberg/writer.ts +590 -0
  842. package/src/index.ts +212 -0
  843. package/src/lineage/index.ts +42 -0
  844. package/src/lineage/integration.ts +334 -0
  845. package/src/lineage/tracker.ts +1618 -0
  846. package/src/lineage/types.ts +354 -0
  847. package/src/middleware/index.ts +36 -0
  848. package/src/middleware/rate-limit-concurrent.test.ts +794 -0
  849. package/src/middleware/rate-limit.test.ts +1568 -0
  850. package/src/middleware/rate-limit.ts +840 -0
  851. package/src/migration-tooling/external-migration.test.ts +1864 -0
  852. package/src/migration-tooling/external-migration.ts +2355 -0
  853. package/src/migration-tooling/index.ts +19 -0
  854. package/src/migrations/ARCHITECTURE.md +474 -0
  855. package/src/migrations/PROGRESS_TRACKING.md +485 -0
  856. package/src/migrations/auto-migrator.test.ts +732 -0
  857. package/src/migrations/auto-migrator.ts +531 -0
  858. package/src/migrations/bulk-orchestrator.test.ts +801 -0
  859. package/src/migrations/bulk-orchestrator.ts +1039 -0
  860. package/src/migrations/compatibility.test.ts +958 -0
  861. package/src/migrations/compatibility.ts +902 -0
  862. package/src/migrations/do-migrations.test.ts +2620 -0
  863. package/src/migrations/do-migrations.ts +1289 -0
  864. package/src/migrations/do-migrations.types.ts +715 -0
  865. package/src/migrations/drizzle-compat.test.ts +210 -0
  866. package/src/migrations/drizzle-compat.ts +337 -0
  867. package/src/migrations/index.ts +334 -0
  868. package/src/migrations/migration-api.test.ts +438 -0
  869. package/src/migrations/migration-api.ts +704 -0
  870. package/src/migrations/progress-tracker-do.ts +518 -0
  871. package/src/migrations/progress-tracker-kv.ts +305 -0
  872. package/src/migrations/progress-tracker.test.ts +937 -0
  873. package/src/migrations/progress-tracker.ts +665 -0
  874. package/src/migrations/registry.test.ts +331 -0
  875. package/src/migrations/registry.ts +468 -0
  876. package/src/migrations/rollback.test.ts +644 -0
  877. package/src/migrations/runner.test.ts +807 -0
  878. package/src/migrations/runner.test.ts.backup +759 -0
  879. package/src/migrations/runner.ts +1459 -0
  880. package/src/migrations/schema-generator.test.ts +649 -0
  881. package/src/migrations/schema-generator.ts +513 -0
  882. package/src/migrations/testing.ts +1037 -0
  883. package/src/migrations/types.ts +573 -0
  884. package/src/migrations/validator.test.ts +660 -0
  885. package/src/migrations/validator.ts +741 -0
  886. package/src/observability/alerting.test.ts +1133 -0
  887. package/src/observability/alerting.ts +455 -0
  888. package/src/observability/analytics-engine.ts +733 -0
  889. package/src/observability/cost-metrics.ts +804 -0
  890. package/src/observability/cross-do-tracing.test.ts +516 -0
  891. package/src/observability/cross-do-tracing.ts +588 -0
  892. package/src/observability/dashboards/postgres-do-overview.json +1656 -0
  893. package/src/observability/error-rate-collector.test.ts +977 -0
  894. package/src/observability/error-rate-collector.ts +518 -0
  895. package/src/observability/exporters.test.ts +365 -0
  896. package/src/observability/exporters.ts +650 -0
  897. package/src/observability/health-check.test.ts +353 -0
  898. package/src/observability/health-check.ts +341 -0
  899. package/src/observability/index.test.ts +298 -0
  900. package/src/observability/index.ts +885 -0
  901. package/src/observability/instrumentation.test.ts +428 -0
  902. package/src/observability/instrumentation.ts +788 -0
  903. package/src/observability/memory-metrics.test.ts +355 -0
  904. package/src/observability/memory-metrics.ts +990 -0
  905. package/src/observability/metrics-endpoint.test.ts +402 -0
  906. package/src/observability/metrics-endpoint.ts +374 -0
  907. package/src/observability/metrics.test.ts +291 -0
  908. package/src/observability/metrics.ts +315 -0
  909. package/src/observability/observability-features.ts +1296 -0
  910. package/src/observability/prometheus.test.ts +292 -0
  911. package/src/observability/prometheus.ts +170 -0
  912. package/src/observability/propagation.test.ts +417 -0
  913. package/src/observability/propagation.ts +294 -0
  914. package/src/observability/query-latency.ts +586 -0
  915. package/src/observability/query-performance.test.ts +406 -0
  916. package/src/observability/query-performance.ts +491 -0
  917. package/src/observability/storage-tier-metrics.test.ts +633 -0
  918. package/src/observability/storage-tier-metrics.ts +570 -0
  919. package/src/observability/tier-cost-optimizer.ts +740 -0
  920. package/src/observability/tracer.test.ts +346 -0
  921. package/src/observability/tracer.ts +585 -0
  922. package/src/observability/types.test.ts +726 -0
  923. package/src/observability/types.ts +434 -0
  924. package/src/pglite/auto-demotion.test.ts +477 -0
  925. package/src/pglite/auto-demotion.ts +385 -0
  926. package/src/pglite/auto-promotion.test.ts +824 -0
  927. package/src/pglite/auto-promotion.ts +547 -0
  928. package/src/pglite/cache-layer.test.ts +469 -0
  929. package/src/pglite/cache-layer.ts +271 -0
  930. package/src/pglite/cold-start-manager.ts +1260 -0
  931. package/src/pglite/cold-start-optimizer.test.ts +937 -0
  932. package/src/pglite/cold-start-optimizer.ts +1895 -0
  933. package/src/pglite/dovfs-adapter.ts +1122 -0
  934. package/src/pglite/dovfs.ts +1258 -0
  935. package/src/pglite/etag-cache.test.ts +844 -0
  936. package/src/pglite/etag-cache.ts +526 -0
  937. package/src/pglite/index.ts +442 -0
  938. package/src/pglite/init.test.ts +455 -0
  939. package/src/pglite/init.ts +574 -0
  940. package/src/pglite/lifecycle.test.ts +599 -0
  941. package/src/pglite/lifecycle.ts +704 -0
  942. package/src/pglite/parallel-loader.test.ts +586 -0
  943. package/src/pglite/parallel-loader.ts +481 -0
  944. package/src/pglite/production-pglite.test.ts +666 -0
  945. package/src/pglite/production-pglite.ts +537 -0
  946. package/src/pglite/query-executor.ts +614 -0
  947. package/src/pglite/r2-layer.test.ts +501 -0
  948. package/src/pglite/r2-layer.ts +322 -0
  949. package/src/pglite/tiered-init.test.ts +725 -0
  950. package/src/pglite/tiered-init.ts +556 -0
  951. package/src/pglite/tiered-vfs.test.ts +726 -0
  952. package/src/pglite/tiered-vfs.ts +33 -0
  953. package/src/pglite/tiering-stats.test.ts +531 -0
  954. package/src/pglite/tiering-stats.ts +407 -0
  955. package/src/pglite/transaction-hooks.ts +343 -0
  956. package/src/pglite/warm-loader.test.ts +1701 -0
  957. package/src/pglite/warm-loader.ts +528 -0
  958. package/src/pglite/workers-pglite.ts +224 -0
  959. package/src/pglite-assets/pglite.data +0 -0
  960. package/src/pglite-assets/pglite.wasm +0 -0
  961. package/src/pglite.d.ts +47 -0
  962. package/src/playground/index.ts +137 -0
  963. package/src/playground/keyboard-shortcuts.ts +677 -0
  964. package/src/playground/playground.ts +323 -0
  965. package/src/playground/query-executor.ts +669 -0
  966. package/src/playground/query-history.ts +328 -0
  967. package/src/playground/result-formatter.ts +420 -0
  968. package/src/playground/sample-datasets.ts +674 -0
  969. package/src/playground/sample-queries.ts +1168 -0
  970. package/src/playground/schema-explorer.ts +558 -0
  971. package/src/playground/types.ts +518 -0
  972. package/src/readonly/cache-reader.test.ts +460 -0
  973. package/src/readonly/cache-reader.ts +313 -0
  974. package/src/readonly/config.test.ts +187 -0
  975. package/src/readonly/config.ts +128 -0
  976. package/src/readonly/index.ts +50 -0
  977. package/src/readonly/pglite-wrapper.test.ts +278 -0
  978. package/src/readonly/pglite-wrapper.ts +184 -0
  979. package/src/readonly/worker.test.ts +533 -0
  980. package/src/readonly/worker.ts +341 -0
  981. package/src/readonly/write-blocker.test.ts +459 -0
  982. package/src/readonly/write-blocker.ts +175 -0
  983. package/src/recovery/disaster-recovery.test.ts +618 -0
  984. package/src/recovery/disaster-recovery.ts +1181 -0
  985. package/src/recovery/index.ts +43 -0
  986. package/src/recovery/parquet-parser.ts +974 -0
  987. package/src/retention/index.ts +74 -0
  988. package/src/retention/policy.test.ts +571 -0
  989. package/src/retention/policy.ts +774 -0
  990. package/src/retention/purger.test.ts +465 -0
  991. package/src/retention/purger.ts +558 -0
  992. package/src/rls/auth-integration.test.ts +752 -0
  993. package/src/rls/auth-integration.ts +533 -0
  994. package/src/rls/generator.test.ts +829 -0
  995. package/src/rls/generator.ts +573 -0
  996. package/src/rls/index.ts +128 -0
  997. package/src/rls/policy.ts +208 -0
  998. package/src/rls/rls.test.ts +1071 -0
  999. package/src/rls/validator.test.ts +930 -0
  1000. package/src/rls/validator.ts +895 -0
  1001. package/src/routing/adaptive-router.test.ts +884 -0
  1002. package/src/routing/adaptive-router.ts +845 -0
  1003. package/src/routing/circuit-breaker.test.ts +1505 -0
  1004. package/src/routing/circuit-breaker.ts +852 -0
  1005. package/src/routing/cost-metrics.test.ts +565 -0
  1006. package/src/routing/cost-metrics.ts +408 -0
  1007. package/src/routing/do-connection-pool.test.ts +1109 -0
  1008. package/src/routing/do-connection-pool.ts +828 -0
  1009. package/src/routing/index.ts +158 -0
  1010. package/src/routing/query-complexity-estimator.test.ts +356 -0
  1011. package/src/routing/query-complexity-estimator.ts +444 -0
  1012. package/src/routing/request-coalescing.test.ts +738 -0
  1013. package/src/routing/request-coalescing.ts +475 -0
  1014. package/src/routing/runtime-router.test.ts +436 -0
  1015. package/src/routing/runtime-router.ts +357 -0
  1016. package/src/routing/tenant-router.test.ts +2493 -0
  1017. package/src/routing/tenant-router.ts +1908 -0
  1018. package/src/routing/websocket-pool.test.ts +551 -0
  1019. package/src/routing/websocket-pool.ts +577 -0
  1020. package/src/storage/access-pattern-tracker.test.ts +874 -0
  1021. package/src/storage/cache-layer.test.ts +560 -0
  1022. package/src/storage/cache-layer.ts +328 -0
  1023. package/src/storage/cost-aware-tiering.test.ts +652 -0
  1024. package/src/storage/cost-aware-tiering.ts +794 -0
  1025. package/src/storage/do-sqlite-blobs.test.ts +937 -0
  1026. package/src/storage/index.ts +272 -0
  1027. package/src/storage/interfaces.ts +974 -0
  1028. package/src/storage/r2-layer.test.ts +653 -0
  1029. package/src/storage/r2-layer.ts +434 -0
  1030. package/src/storage/r2-overflow.ts +920 -0
  1031. package/src/storage/r2-page-vfs.test.ts +2348 -0
  1032. package/src/storage/r2-page-vfs.ts +1054 -0
  1033. package/src/storage/swr-cache.test.ts +832 -0
  1034. package/src/storage/swr-cache.ts +398 -0
  1035. package/src/storage/swr-tiered-integration.test.ts +617 -0
  1036. package/src/storage/tiered-orchestrator.test.ts +2441 -0
  1037. package/src/storage/tiered-orchestrator.ts +2081 -0
  1038. package/src/storage/tiered-vfs-swr.test.ts +736 -0
  1039. package/src/storage/tiered-vfs-swr.ts +735 -0
  1040. package/src/storage/tiered-vfs.test.ts +793 -0
  1041. package/src/storage/tiered-vfs.ts +1082 -0
  1042. package/src/streaming/backpressure-controller.ts +452 -0
  1043. package/src/streaming/buffer-pool.ts +484 -0
  1044. package/src/streaming/cdc-iceberg-connector.ts +605 -0
  1045. package/src/streaming/index.ts +225 -0
  1046. package/src/streaming/live-cdc-stream.ts +985 -0
  1047. package/src/streaming/memory-bounded-stream.ts +443 -0
  1048. package/src/streaming/query-streamer.ts +662 -0
  1049. package/src/streaming/response-streaming.ts +557 -0
  1050. package/src/types/branded.ts +1075 -0
  1051. package/src/types/branded.ts.backup +273 -0
  1052. package/src/types/utilities.ts +1023 -0
  1053. package/src/types/wasm.d.ts +30 -0
  1054. package/src/validation/typed-errors.test.ts +420 -0
  1055. package/src/wal/replay-engine.ts +1264 -0
  1056. package/src/worker/__mocks__/capnweb.ts +15 -0
  1057. package/src/worker/__mocks__/pglite.data.ts +22 -0
  1058. package/src/worker/__mocks__/pglite.wasm.ts +33 -0
  1059. package/src/worker/auth-rate-limiter.test.ts +272 -0
  1060. package/src/worker/auth-rate-limiter.ts +448 -0
  1061. package/src/worker/auth.security-red.test.ts +1236 -0
  1062. package/src/worker/auth.security.test.ts +822 -0
  1063. package/src/worker/auth.test.ts +469 -0
  1064. package/src/worker/auth.ts +1104 -0
  1065. package/src/worker/cdc-backpressure.test.ts +726 -0
  1066. package/src/worker/cdc-backpressure.ts +866 -0
  1067. package/src/worker/cdc-sse.test.ts +780 -0
  1068. package/src/worker/cdc-sse.ts +728 -0
  1069. package/src/worker/cdc-websocket.ts +1229 -0
  1070. package/src/worker/cdc-ws.test.ts +1009 -0
  1071. package/src/worker/cdc.test.ts +327 -0
  1072. package/src/worker/cdc.ts +289 -0
  1073. package/src/worker/concerns/auth-concern.ts +179 -0
  1074. package/src/worker/concerns/cdc-concern.ts +247 -0
  1075. package/src/worker/concerns/index.ts +58 -0
  1076. package/src/worker/concerns/query-execution-concern.ts +194 -0
  1077. package/src/worker/concerns/storage-orchestration-concern.ts +373 -0
  1078. package/src/worker/discriminated-types.test.ts +280 -0
  1079. package/src/worker/do-auth-manager.ts +257 -0
  1080. package/src/worker/do-decomposition.test.ts +1236 -0
  1081. package/src/worker/do-pglite-manager.ts +302 -0
  1082. package/src/worker/do.test.ts +2254 -0
  1083. package/src/worker/do.ts +1878 -0
  1084. package/src/worker/entry.ts +417 -0
  1085. package/src/worker/errors.ts +285 -0
  1086. package/src/worker/health-check-manager.test.ts +261 -0
  1087. package/src/worker/health-check-manager.ts +231 -0
  1088. package/src/worker/index.ts +389 -0
  1089. package/src/worker/memory-pressure.test.ts +1460 -0
  1090. package/src/worker/memory-pressure.ts +2650 -0
  1091. package/src/worker/migration-manager.ts +582 -0
  1092. package/src/worker/neon-compat.test.ts +332 -0
  1093. package/src/worker/plugin-manager.ts +485 -0
  1094. package/src/worker/postgres.do-rpc.d.ts +76 -0
  1095. package/src/worker/proxy.ts +694 -0
  1096. package/src/worker/query-execution-manager.test.ts +303 -0
  1097. package/src/worker/query-execution-manager.ts +219 -0
  1098. package/src/worker/query-executor.test.ts +282 -0
  1099. package/src/worker/query-executor.ts +560 -0
  1100. package/src/worker/query-stats-manager.ts +229 -0
  1101. package/src/worker/result-handler.test.ts +364 -0
  1102. package/src/worker/result-handler.ts +510 -0
  1103. package/src/worker/routes.test.ts +795 -0
  1104. package/src/worker/routes.ts +650 -0
  1105. package/src/worker/rpc-methods-manager.test.ts +326 -0
  1106. package/src/worker/rpc-methods-manager.ts +276 -0
  1107. package/src/worker/rpc.ts +524 -0
  1108. package/src/worker/schema-version.ts +605 -0
  1109. package/src/worker/session-manager.test.ts +506 -0
  1110. package/src/worker/session-manager.ts +732 -0
  1111. package/src/worker/shutdown-manager.ts +469 -0
  1112. package/src/worker/sql-transform.test.ts +286 -0
  1113. package/src/worker/sql-transform.ts +368 -0
  1114. package/src/worker/supabase-compat.test.ts +621 -0
  1115. package/src/worker/types.test.ts +292 -0
  1116. package/src/worker/types.ts +873 -0
  1117. package/src/worker/user-routes.test.ts +703 -0
  1118. package/src/worker/user-routes.ts +303 -0
  1119. package/src/worker/wal-facade.ts +235 -0
  1120. package/src/worker/wal-r2.test.ts +570 -0
  1121. package/src/worker/wal-r2.ts +930 -0
  1122. package/src/worker/wal-replay.test.ts +845 -0
  1123. package/src/worker/wal-replay.ts +897 -0
  1124. package/src/worker/wal-retention.test.ts +758 -0
  1125. package/src/worker/wal-retention.ts +1075 -0
  1126. package/src/worker/wal.test.ts +618 -0
  1127. package/src/worker/wal.ts +697 -0
  1128. package/src/worker/websocket.test.ts +296 -0
  1129. package/src/worker/websocket.ts +284 -0
@@ -0,0 +1,2441 @@
1
+ /**
2
+ * Tests for TieredStorageOrchestrator
3
+ *
4
+ * Tests the unified tiered storage system:
5
+ * - HOT tier: Cloudflare Cache API
6
+ * - WARM tier: Durable Object SQLite blobs
7
+ * - COLD tier: R2 object storage
8
+ */
9
+
10
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
11
+ import {
12
+ TieredStorageOrchestrator,
13
+ createTieredStorageOrchestrator,
14
+ TieredOrchestratorConfig,
15
+ StorageTier,
16
+ TierMigrationEvent,
17
+ } from './tiered-orchestrator'
18
+ import type { CacheLayer } from './cache-layer'
19
+ import type { R2StorageLayer } from './r2-layer'
20
+ import type { SWRCacheLayer } from './swr-cache'
21
+
22
+ // Mock CacheLayer
23
+ const mockCacheLayer = {
24
+ get: vi.fn(),
25
+ put: vi.fn(),
26
+ delete: vi.fn(),
27
+ has: vi.fn(),
28
+ getWithMetadata: vi.fn(),
29
+ getStats: vi.fn().mockReturnValue({
30
+ hits: 0,
31
+ misses: 0,
32
+ writes: 0,
33
+ deletes: 0,
34
+ bytesRead: 0,
35
+ bytesWritten: 0,
36
+ errors: 0,
37
+ hitRatio: 0,
38
+ }),
39
+ }
40
+
41
+ // Mock SWRCacheLayer
42
+ const mockSWRCacheLayer = {
43
+ get: vi.fn().mockResolvedValue({ data: null, hit: false, stale: false, revalidating: false }),
44
+ put: vi.fn().mockResolvedValue(undefined),
45
+ invalidate: vi.fn().mockResolvedValue(undefined),
46
+ has: vi.fn().mockResolvedValue(false),
47
+ setOriginFetcher: vi.fn(),
48
+ getStats: vi.fn().mockReturnValue({
49
+ primaryHits: 0,
50
+ staleHits: 0,
51
+ misses: 0,
52
+ revalidations: 0,
53
+ writes: 0,
54
+ invalidations: 0,
55
+ bytesRead: 0,
56
+ bytesWritten: 0,
57
+ errors: 0,
58
+ primaryHitRatio: 0,
59
+ combinedHitRatio: 0,
60
+ }),
61
+ }
62
+
63
+ // Mock DOStorage
64
+ const mockDOStorage = {
65
+ get: vi.fn(),
66
+ put: vi.fn().mockResolvedValue(undefined),
67
+ delete: vi.fn().mockResolvedValue(undefined),
68
+ list: vi.fn(),
69
+ }
70
+
71
+ // Mock R2StorageLayer
72
+ const mockR2Layer = {
73
+ get: vi.fn(),
74
+ getRange: vi.fn(),
75
+ put: vi.fn().mockResolvedValue(undefined),
76
+ delete: vi.fn().mockResolvedValue(undefined),
77
+ deleteMany: vi.fn(),
78
+ head: vi.fn(),
79
+ has: vi.fn(),
80
+ list: vi.fn(),
81
+ getStats: vi.fn().mockReturnValue({
82
+ reads: 0,
83
+ rangeReads: 0,
84
+ writes: 0,
85
+ deletes: 0,
86
+ bytesRead: 0,
87
+ bytesWritten: 0,
88
+ errors: 0,
89
+ headRequests: 0,
90
+ listRequests: 0,
91
+ }),
92
+ }
93
+
94
+ describe('TieredStorageOrchestrator', () => {
95
+ beforeEach(() => {
96
+ vi.clearAllMocks()
97
+ mockSWRCacheLayer.get.mockResolvedValue({ data: null, hit: false, stale: false, revalidating: false })
98
+ })
99
+
100
+ describe('createTieredStorageOrchestrator()', () => {
101
+ it('should create an orchestrator instance', () => {
102
+ const orchestrator = createTieredStorageOrchestrator({
103
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
104
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
105
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
106
+ })
107
+ expect(orchestrator).toBeInstanceOf(TieredStorageOrchestrator)
108
+ })
109
+
110
+ it('should create an orchestrator with SWR cache layer', () => {
111
+ const orchestrator = createTieredStorageOrchestrator({
112
+ swrCacheLayer: mockSWRCacheLayer as unknown as SWRCacheLayer,
113
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
114
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
115
+ })
116
+ expect(orchestrator).toBeInstanceOf(TieredStorageOrchestrator)
117
+ })
118
+
119
+ it('should accept all configuration options', () => {
120
+ const config: TieredOrchestratorConfig = {
121
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
122
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
123
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
124
+ pageSize: 16384,
125
+ coldToWarmThreshold: 5,
126
+ warmToHotThreshold: 10,
127
+ promotionWindowMs: 120000,
128
+ hotTtlMs: 600000,
129
+ warmTtlMs: 7200000,
130
+ maxHotPages: 500,
131
+ autoPromote: true,
132
+ autoDemote: true,
133
+ writeThrough: true,
134
+ doPrefix: 'data:',
135
+ }
136
+ const orchestrator = createTieredStorageOrchestrator(config)
137
+ expect(orchestrator.getConfig().pageSize).toBe(16384)
138
+ expect(orchestrator.getConfig().coldToWarmThreshold).toBe(5)
139
+ expect(orchestrator.getConfig().writeThrough).toBe(true)
140
+ })
141
+ })
142
+
143
+ describe('read() - tiered lookup', () => {
144
+ it('should read from hot tier first (Cache)', async () => {
145
+ const data = new Uint8Array([1, 2, 3, 4, 5])
146
+ mockCacheLayer.get.mockResolvedValue(data)
147
+
148
+ const orchestrator = createTieredStorageOrchestrator({
149
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
150
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
151
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
152
+ })
153
+
154
+ const result = await orchestrator.read('page-123')
155
+ expect(result.data).toEqual(data)
156
+ expect(result.tier).toBe('hot')
157
+ expect(mockCacheLayer.get).toHaveBeenCalledWith('page-123')
158
+ expect(mockDOStorage.get).not.toHaveBeenCalled()
159
+ expect(mockR2Layer.get).not.toHaveBeenCalled()
160
+ })
161
+
162
+ it('should read from hot tier first (SWR Cache)', async () => {
163
+ const data = new Uint8Array([1, 2, 3, 4, 5])
164
+ mockSWRCacheLayer.get.mockResolvedValue({ data, hit: true, stale: false, revalidating: false })
165
+
166
+ const orchestrator = createTieredStorageOrchestrator({
167
+ swrCacheLayer: mockSWRCacheLayer as unknown as SWRCacheLayer,
168
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
169
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
170
+ })
171
+
172
+ const result = await orchestrator.read('page-123')
173
+ expect(result.data).toEqual(data)
174
+ expect(result.tier).toBe('hot')
175
+ expect(result.stale).toBe(false)
176
+ expect(mockDOStorage.get).not.toHaveBeenCalled()
177
+ })
178
+
179
+ it('should return stale data with revalidation flag from SWR', async () => {
180
+ const data = new Uint8Array([1, 2, 3])
181
+ mockSWRCacheLayer.get.mockResolvedValue({ data, hit: true, stale: true, revalidating: true })
182
+
183
+ const orchestrator = createTieredStorageOrchestrator({
184
+ swrCacheLayer: mockSWRCacheLayer as unknown as SWRCacheLayer,
185
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
186
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
187
+ })
188
+
189
+ const result = await orchestrator.read('page-123')
190
+ expect(result.data).toEqual(data)
191
+ expect(result.tier).toBe('hot')
192
+ expect(result.stale).toBe(true)
193
+ expect(result.revalidating).toBe(true)
194
+ })
195
+
196
+ it('should read from warm tier on hot miss', async () => {
197
+ const data = new Uint8Array([10, 20, 30])
198
+ mockCacheLayer.get.mockResolvedValue(null)
199
+ mockDOStorage.get.mockResolvedValue(data)
200
+
201
+ const orchestrator = createTieredStorageOrchestrator({
202
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
203
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
204
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
205
+ })
206
+
207
+ const result = await orchestrator.read('page-123')
208
+ expect(result.data).toEqual(data)
209
+ expect(result.tier).toBe('warm')
210
+ expect(mockCacheLayer.get).toHaveBeenCalled()
211
+ expect(mockDOStorage.get).toHaveBeenCalledWith('page:page-123')
212
+ })
213
+
214
+ it('should read from cold tier on warm miss', async () => {
215
+ const data = new Uint8Array([100, 200])
216
+ mockCacheLayer.get.mockResolvedValue(null)
217
+ mockDOStorage.get.mockResolvedValue(undefined)
218
+ mockR2Layer.get.mockResolvedValue(data)
219
+
220
+ const orchestrator = createTieredStorageOrchestrator({
221
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
222
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
223
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
224
+ })
225
+
226
+ const result = await orchestrator.read('page-123')
227
+ expect(result.data).toEqual(data)
228
+ expect(result.tier).toBe('cold')
229
+ expect(mockR2Layer.get).toHaveBeenCalledWith('page-123')
230
+ })
231
+
232
+ it('should return null when not found in any tier', async () => {
233
+ mockCacheLayer.get.mockResolvedValue(null)
234
+ mockDOStorage.get.mockResolvedValue(undefined)
235
+ mockR2Layer.get.mockResolvedValue(null)
236
+
237
+ const orchestrator = createTieredStorageOrchestrator({
238
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
239
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
240
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
241
+ })
242
+
243
+ const result = await orchestrator.read('nonexistent')
244
+ expect(result.data).toBeNull()
245
+ expect(result.tier).toBeUndefined()
246
+ })
247
+
248
+ it('should track access in storage index', async () => {
249
+ const data = new Uint8Array([1, 2, 3])
250
+ mockCacheLayer.get.mockResolvedValue(null)
251
+ mockDOStorage.get.mockResolvedValue(data)
252
+
253
+ const orchestrator = createTieredStorageOrchestrator({
254
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
255
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
256
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
257
+ })
258
+
259
+ await orchestrator.read('page-123')
260
+ const entry = orchestrator.getIndexEntry('page-123')
261
+ expect(entry).toBeDefined()
262
+ expect(entry?.tier).toBe('warm')
263
+ expect(entry?.accessCount).toBe(1)
264
+
265
+ await orchestrator.read('page-123')
266
+ const entry2 = orchestrator.getIndexEntry('page-123')
267
+ expect(entry2?.accessCount).toBe(2)
268
+ })
269
+
270
+ it('should skip tracking when skipTracking is true', async () => {
271
+ const data = new Uint8Array([1, 2, 3])
272
+ mockCacheLayer.get.mockResolvedValue(null)
273
+ mockDOStorage.get.mockResolvedValue(data)
274
+
275
+ const orchestrator = createTieredStorageOrchestrator({
276
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
277
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
278
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
279
+ })
280
+
281
+ await orchestrator.read('page-123', { skipTracking: true })
282
+ expect(orchestrator.getIndexEntry('page-123')).toBeUndefined()
283
+ })
284
+ })
285
+
286
+ describe('write()', () => {
287
+ it('should write to warm tier by default', async () => {
288
+ const data = new Uint8Array([1, 2, 3])
289
+
290
+ const orchestrator = createTieredStorageOrchestrator({
291
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
292
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
293
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
294
+ })
295
+
296
+ await orchestrator.write('page-123', data)
297
+ expect(mockDOStorage.put).toHaveBeenCalledWith('page:page-123', data)
298
+ expect(mockCacheLayer.put).not.toHaveBeenCalled()
299
+ expect(mockR2Layer.put).not.toHaveBeenCalled()
300
+ })
301
+
302
+ it('should write to hot tier when specified', async () => {
303
+ const data = new Uint8Array([1, 2, 3])
304
+
305
+ const orchestrator = createTieredStorageOrchestrator({
306
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
307
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
308
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
309
+ })
310
+
311
+ await orchestrator.write('page-123', data, { tier: 'hot' })
312
+ expect(mockDOStorage.put).toHaveBeenCalledWith('page:page-123', data)
313
+ expect(mockCacheLayer.put).toHaveBeenCalledWith('page-123', data, expect.any(Object))
314
+ })
315
+
316
+ it('should write to cold tier when specified', async () => {
317
+ const data = new Uint8Array([1, 2, 3])
318
+
319
+ const orchestrator = createTieredStorageOrchestrator({
320
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
321
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
322
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
323
+ })
324
+
325
+ await orchestrator.write('page-123', data, { tier: 'cold' })
326
+ expect(mockR2Layer.put).toHaveBeenCalledWith('page-123', data, expect.any(Object))
327
+ expect(mockDOStorage.put).not.toHaveBeenCalled()
328
+ })
329
+
330
+ it('should write-through to cold tier when enabled', async () => {
331
+ const data = new Uint8Array([1, 2, 3])
332
+
333
+ const orchestrator = createTieredStorageOrchestrator({
334
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
335
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
336
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
337
+ })
338
+
339
+ await orchestrator.write('page-123', data, { tier: 'warm', writeThrough: true })
340
+ expect(mockDOStorage.put).toHaveBeenCalled()
341
+ expect(mockR2Layer.put).toHaveBeenCalled()
342
+ })
343
+
344
+ it('should update storage index on write', async () => {
345
+ const data = new Uint8Array([1, 2, 3])
346
+
347
+ const orchestrator = createTieredStorageOrchestrator({
348
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
349
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
350
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
351
+ })
352
+
353
+ await orchestrator.write('page-123', data)
354
+ const entry = orchestrator.getIndexEntry('page-123')
355
+ expect(entry).toBeDefined()
356
+ expect(entry?.tier).toBe('warm')
357
+ expect(entry?.size).toBe(3)
358
+ })
359
+ })
360
+
361
+ describe('delete()', () => {
362
+ it('should delete from all tiers', async () => {
363
+ mockCacheLayer.delete.mockResolvedValue(true)
364
+
365
+ const orchestrator = createTieredStorageOrchestrator({
366
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
367
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
368
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
369
+ })
370
+
371
+ // First write to create index entry
372
+ await orchestrator.write('page-123', new Uint8Array([1, 2, 3]))
373
+ expect(orchestrator.hasInIndex('page-123')).toBe(true)
374
+
375
+ // Then delete
376
+ await orchestrator.delete('page-123')
377
+ expect(mockCacheLayer.delete).toHaveBeenCalledWith('page-123')
378
+ expect(mockDOStorage.delete).toHaveBeenCalledWith('page:page-123')
379
+ expect(mockR2Layer.delete).toHaveBeenCalledWith('page-123')
380
+ expect(orchestrator.hasInIndex('page-123')).toBe(false)
381
+ })
382
+ })
383
+
384
+ describe('promoteToWarm()', () => {
385
+ it('should promote data from cold to warm tier', async () => {
386
+ const data = new Uint8Array([1, 2, 3])
387
+ mockR2Layer.get.mockResolvedValue(data)
388
+
389
+ const orchestrator = createTieredStorageOrchestrator({
390
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
391
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
392
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
393
+ })
394
+
395
+ const event = await orchestrator.promoteToWarm('page-123')
396
+ expect(event.success).toBe(true)
397
+ expect(event.fromTier).toBe('cold')
398
+ expect(event.toTier).toBe('warm')
399
+ expect(mockDOStorage.put).toHaveBeenCalledWith('page:page-123', data)
400
+ })
401
+
402
+ it('should use provided data instead of fetching', async () => {
403
+ const data = new Uint8Array([1, 2, 3])
404
+
405
+ const orchestrator = createTieredStorageOrchestrator({
406
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
407
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
408
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
409
+ })
410
+
411
+ const event = await orchestrator.promoteToWarm('page-123', data)
412
+ expect(event.success).toBe(true)
413
+ expect(mockR2Layer.get).not.toHaveBeenCalled()
414
+ expect(mockDOStorage.put).toHaveBeenCalledWith('page:page-123', data)
415
+ })
416
+
417
+ it('should fail if data not found in cold tier', async () => {
418
+ mockR2Layer.get.mockResolvedValue(null)
419
+
420
+ const orchestrator = createTieredStorageOrchestrator({
421
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
422
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
423
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
424
+ })
425
+
426
+ const event = await orchestrator.promoteToWarm('nonexistent')
427
+ expect(event.success).toBe(false)
428
+ expect(event.error).toContain('not found')
429
+ })
430
+
431
+ it('should notify migration callbacks', async () => {
432
+ const data = new Uint8Array([1, 2, 3])
433
+ mockR2Layer.get.mockResolvedValue(data)
434
+ const callback = vi.fn()
435
+
436
+ const orchestrator = createTieredStorageOrchestrator({
437
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
438
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
439
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
440
+ })
441
+ orchestrator.onMigration(callback)
442
+
443
+ await orchestrator.promoteToWarm('page-123')
444
+ expect(callback).toHaveBeenCalledWith(expect.objectContaining({
445
+ key: 'page-123',
446
+ fromTier: 'cold',
447
+ toTier: 'warm',
448
+ success: true,
449
+ }))
450
+ })
451
+ })
452
+
453
+ describe('promoteToHot()', () => {
454
+ it('should promote data from warm to hot tier', async () => {
455
+ const data = new Uint8Array([1, 2, 3])
456
+ mockDOStorage.get.mockResolvedValue(data)
457
+
458
+ const orchestrator = createTieredStorageOrchestrator({
459
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
460
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
461
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
462
+ })
463
+
464
+ const event = await orchestrator.promoteToHot('page-123')
465
+ expect(event.success).toBe(true)
466
+ expect(event.fromTier).toBe('warm')
467
+ expect(event.toTier).toBe('hot')
468
+ expect(mockCacheLayer.put).toHaveBeenCalledWith('page-123', data, expect.any(Object))
469
+ })
470
+
471
+ it('should promote to SWR cache when configured', async () => {
472
+ const data = new Uint8Array([1, 2, 3])
473
+ mockDOStorage.get.mockResolvedValue(data)
474
+
475
+ const orchestrator = createTieredStorageOrchestrator({
476
+ swrCacheLayer: mockSWRCacheLayer as unknown as SWRCacheLayer,
477
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
478
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
479
+ })
480
+
481
+ const event = await orchestrator.promoteToHot('page-123')
482
+ expect(event.success).toBe(true)
483
+ expect(mockSWRCacheLayer.put).toHaveBeenCalledWith('page-123', data)
484
+ })
485
+ })
486
+
487
+ describe('demoteFromHot()', () => {
488
+ it('should demote data from hot to warm tier', async () => {
489
+ mockCacheLayer.delete.mockResolvedValue(true)
490
+
491
+ const orchestrator = createTieredStorageOrchestrator({
492
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
493
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
494
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
495
+ })
496
+
497
+ // First add entry to index
498
+ await orchestrator.write('page-123', new Uint8Array([1, 2, 3]), { tier: 'hot' })
499
+
500
+ const event = await orchestrator.demoteFromHot('page-123')
501
+ expect(event.success).toBe(true)
502
+ expect(event.fromTier).toBe('hot')
503
+ expect(event.toTier).toBe('warm')
504
+ expect(mockCacheLayer.delete).toHaveBeenCalledWith('page-123')
505
+ })
506
+
507
+ it('should invalidate SWR cache when configured', async () => {
508
+ const orchestrator = createTieredStorageOrchestrator({
509
+ swrCacheLayer: mockSWRCacheLayer as unknown as SWRCacheLayer,
510
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
511
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
512
+ })
513
+
514
+ const event = await orchestrator.demoteFromHot('page-123')
515
+ expect(event.success).toBe(true)
516
+ expect(mockSWRCacheLayer.invalidate).toHaveBeenCalledWith('page-123')
517
+ })
518
+ })
519
+
520
+ describe('demoteFromWarm()', () => {
521
+ it('should demote data from warm to cold tier', async () => {
522
+ const data = new Uint8Array([1, 2, 3])
523
+ mockDOStorage.get.mockResolvedValue(data)
524
+
525
+ const orchestrator = createTieredStorageOrchestrator({
526
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
527
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
528
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
529
+ })
530
+
531
+ const event = await orchestrator.demoteFromWarm('page-123')
532
+ expect(event.success).toBe(true)
533
+ expect(event.fromTier).toBe('warm')
534
+ expect(event.toTier).toBe('cold')
535
+ expect(mockR2Layer.put).toHaveBeenCalledWith('page-123', data, expect.any(Object))
536
+ expect(mockDOStorage.delete).toHaveBeenCalledWith('page:page-123')
537
+ })
538
+
539
+ it('should fail if data not found in warm tier', async () => {
540
+ mockDOStorage.get.mockResolvedValue(undefined)
541
+
542
+ const orchestrator = createTieredStorageOrchestrator({
543
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
544
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
545
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
546
+ })
547
+
548
+ const event = await orchestrator.demoteFromWarm('nonexistent')
549
+ expect(event.success).toBe(false)
550
+ expect(event.error).toContain('not found')
551
+ })
552
+ })
553
+
554
+ describe('runDemotionCycle()', () => {
555
+ it('should demote expired hot tier entries', async () => {
556
+ mockCacheLayer.get.mockResolvedValue(null)
557
+ mockDOStorage.get.mockResolvedValue(new Uint8Array([1, 2, 3]))
558
+ mockCacheLayer.delete.mockResolvedValue(true)
559
+
560
+ const orchestrator = createTieredStorageOrchestrator({
561
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
562
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
563
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
564
+ hotTtlMs: 0, // Immediate expiry for testing
565
+ autoDemote: true,
566
+ })
567
+
568
+ // Add a hot tier entry
569
+ await orchestrator.write('page-123', new Uint8Array([1, 2, 3]), { tier: 'hot' })
570
+
571
+ // Run demotion cycle
572
+ const events = await orchestrator.runDemotionCycle()
573
+ expect(events.length).toBeGreaterThan(0)
574
+ expect(events[0].fromTier).toBe('hot')
575
+ expect(events[0].toTier).toBe('warm')
576
+ })
577
+
578
+ it('should not demote when autoDemote is false', async () => {
579
+ const orchestrator = createTieredStorageOrchestrator({
580
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
581
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
582
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
583
+ autoDemote: false,
584
+ })
585
+
586
+ const events = await orchestrator.runDemotionCycle()
587
+ expect(events).toHaveLength(0)
588
+ })
589
+ })
590
+
591
+ describe('evictLRUFromHot()', () => {
592
+ it('should evict least recently used entries from hot tier', async () => {
593
+ mockCacheLayer.delete.mockResolvedValue(true)
594
+
595
+ const orchestrator = createTieredStorageOrchestrator({
596
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
597
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
598
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
599
+ })
600
+
601
+ // Add multiple hot tier entries
602
+ await orchestrator.write('page-1', new Uint8Array([1]), { tier: 'hot' })
603
+ await orchestrator.write('page-2', new Uint8Array([2]), { tier: 'hot' })
604
+ await orchestrator.write('page-3', new Uint8Array([3]), { tier: 'hot' })
605
+
606
+ const events = await orchestrator.evictLRUFromHot(2)
607
+ expect(events.length).toBe(2)
608
+ expect(events[0].reason).toBe('lru_eviction')
609
+ })
610
+ })
611
+
612
+ describe('syncDirtyToCold()', () => {
613
+ it('should sync dirty entries to cold tier', async () => {
614
+ const data = new Uint8Array([1, 2, 3])
615
+ mockDOStorage.get.mockResolvedValue(data)
616
+
617
+ const orchestrator = createTieredStorageOrchestrator({
618
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
619
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
620
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
621
+ })
622
+
623
+ // Write without write-through (creates dirty entry)
624
+ await orchestrator.write('page-123', data, { tier: 'warm' })
625
+ expect(orchestrator.getIndexEntry('page-123')?.dirty).toBe(true)
626
+
627
+ // Sync dirty entries
628
+ const synced = await orchestrator.syncDirtyToCold()
629
+ expect(synced).toBe(1)
630
+ expect(mockR2Layer.put).toHaveBeenCalled()
631
+ expect(orchestrator.getIndexEntry('page-123')?.dirty).toBe(false)
632
+ })
633
+ })
634
+
635
+ describe('getStats()', () => {
636
+ it('should return comprehensive statistics', async () => {
637
+ const orchestrator = createTieredStorageOrchestrator({
638
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
639
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
640
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
641
+ })
642
+
643
+ // Generate some activity
644
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]))
645
+ await orchestrator.write('page-2', new Uint8Array([4, 5, 6]))
646
+
647
+ const stats = orchestrator.getStats()
648
+ expect(stats.index.totalEntries).toBe(2)
649
+ expect(stats.index.byTier.warm).toBe(2)
650
+ expect(stats.warm.writes).toBe(2)
651
+ expect(stats.health.hot).toBe('healthy')
652
+ expect(stats.health.warm).toBe('healthy')
653
+ expect(stats.health.cold).toBe('healthy')
654
+ })
655
+ })
656
+
657
+ describe('storage index', () => {
658
+ it('should track tier for keys', async () => {
659
+ const orchestrator = createTieredStorageOrchestrator({
660
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
661
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
662
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
663
+ })
664
+
665
+ await orchestrator.write('page-warm', new Uint8Array([1]), { tier: 'warm' })
666
+ await orchestrator.write('page-cold', new Uint8Array([2]), { tier: 'cold' })
667
+ await orchestrator.write('page-hot', new Uint8Array([3]), { tier: 'hot' })
668
+
669
+ expect(orchestrator.getTier('page-warm')).toBe('warm')
670
+ expect(orchestrator.getTier('page-cold')).toBe('cold')
671
+ expect(orchestrator.getTier('page-hot')).toBe('hot')
672
+ })
673
+
674
+ it('should clear index', async () => {
675
+ const orchestrator = createTieredStorageOrchestrator({
676
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
677
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
678
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
679
+ })
680
+
681
+ await orchestrator.write('page-1', new Uint8Array([1]))
682
+ expect(orchestrator.hasInIndex('page-1')).toBe(true)
683
+
684
+ orchestrator.clearIndex()
685
+ expect(orchestrator.hasInIndex('page-1')).toBe(false)
686
+ })
687
+ })
688
+
689
+ describe('resetStats()', () => {
690
+ it('should reset all statistics', async () => {
691
+ const orchestrator = createTieredStorageOrchestrator({
692
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
693
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
694
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
695
+ })
696
+
697
+ // Generate some activity
698
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]))
699
+
700
+ let stats = orchestrator.getStats()
701
+ expect(stats.warm.writes).toBe(1)
702
+
703
+ orchestrator.resetStats()
704
+ stats = orchestrator.getStats()
705
+ expect(stats.warm.writes).toBe(0)
706
+ })
707
+ })
708
+
709
+ describe('resetHealth()', () => {
710
+ it('should reset tier health', () => {
711
+ const orchestrator = createTieredStorageOrchestrator({
712
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
713
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
714
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
715
+ })
716
+
717
+ orchestrator.resetHealth('hot')
718
+ expect(orchestrator.getStats().health.hot).toBe('healthy')
719
+ })
720
+
721
+ it('should reset all tier health', () => {
722
+ const orchestrator = createTieredStorageOrchestrator({
723
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
724
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
725
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
726
+ })
727
+
728
+ orchestrator.resetHealth()
729
+ const stats = orchestrator.getStats()
730
+ expect(stats.health.hot).toBe('healthy')
731
+ expect(stats.health.warm).toBe('healthy')
732
+ expect(stats.health.cold).toBe('healthy')
733
+ })
734
+ })
735
+
736
+ describe('auto-promotion', () => {
737
+ it('should auto-promote from cold to warm on threshold', async () => {
738
+ const data = new Uint8Array([1, 2, 3])
739
+ mockCacheLayer.get.mockResolvedValue(null)
740
+ mockDOStorage.get.mockResolvedValue(undefined)
741
+ mockR2Layer.get.mockResolvedValue(data)
742
+
743
+ const orchestrator = createTieredStorageOrchestrator({
744
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
745
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
746
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
747
+ coldToWarmThreshold: 2,
748
+ autoPromote: true,
749
+ })
750
+
751
+ // First read
752
+ await orchestrator.read('page-123')
753
+ expect(mockDOStorage.put).not.toHaveBeenCalled()
754
+
755
+ // Second read - should trigger promotion
756
+ await orchestrator.read('page-123')
757
+ // Promotion happens in background, so we need to wait a tick
758
+ await new Promise(resolve => setTimeout(resolve, 0))
759
+ expect(mockDOStorage.put).toHaveBeenCalled()
760
+ })
761
+
762
+ it('should skip auto-promotion when disabled', async () => {
763
+ const data = new Uint8Array([1, 2, 3])
764
+ mockCacheLayer.get.mockResolvedValue(null)
765
+ mockDOStorage.get.mockResolvedValue(undefined)
766
+ mockR2Layer.get.mockResolvedValue(data)
767
+
768
+ const orchestrator = createTieredStorageOrchestrator({
769
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
770
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
771
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
772
+ coldToWarmThreshold: 1,
773
+ autoPromote: false,
774
+ })
775
+
776
+ await orchestrator.read('page-123')
777
+ expect(mockDOStorage.put).not.toHaveBeenCalled()
778
+ })
779
+
780
+ it('should skip promotion when skipPromotion is true', async () => {
781
+ const data = new Uint8Array([1, 2, 3])
782
+ mockCacheLayer.get.mockResolvedValue(null)
783
+ mockDOStorage.get.mockResolvedValue(undefined)
784
+ mockR2Layer.get.mockResolvedValue(data)
785
+
786
+ const orchestrator = createTieredStorageOrchestrator({
787
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
788
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
789
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
790
+ coldToWarmThreshold: 1,
791
+ autoPromote: true,
792
+ })
793
+
794
+ await orchestrator.read('page-123', { skipPromotion: true })
795
+ expect(mockDOStorage.put).not.toHaveBeenCalled()
796
+ })
797
+ })
798
+ })
799
+
800
+ /**
801
+ * ========================================================================
802
+ * RED TESTS: Unified Tiering Orchestrator Features
803
+ * Issue: postgres-h91k [TIER-RED]
804
+ *
805
+ * These tests define the expected behavior for features that don't exist yet:
806
+ * 1. DO lifecycle hooks (persist hot on hibernate, restore on wake)
807
+ * 2. Background demotion cycles via DO alarm
808
+ * 3. Circuit breaker for tier failures
809
+ * 4. Integrated metrics aggregation
810
+ * 5. Advanced promotion/demotion strategies
811
+ * 6. Concurrent operation handling
812
+ * ========================================================================
813
+ */
814
+ describe('TieredStorageOrchestrator - Unified Tiering Features [RED]', () => {
815
+ beforeEach(() => {
816
+ vi.clearAllMocks()
817
+ mockSWRCacheLayer.get.mockResolvedValue({ data: null, hit: false, stale: false, revalidating: false })
818
+ })
819
+
820
+ describe('DO Lifecycle Hooks', () => {
821
+ describe('onHibernate()', () => {
822
+ it('should persist all hot tier entries to warm tier on hibernate', async () => {
823
+ const data1 = new Uint8Array([1, 2, 3])
824
+ const data2 = new Uint8Array([4, 5, 6])
825
+
826
+ // Setup: SWR cache has data
827
+ mockSWRCacheLayer.get
828
+ .mockImplementation(async (key: string) => {
829
+ if (key === 'page-1') return { data: data1, hit: true, stale: false, revalidating: false }
830
+ if (key === 'page-2') return { data: data2, hit: true, stale: false, revalidating: false }
831
+ return { data: null, hit: false, stale: false, revalidating: false }
832
+ })
833
+
834
+ const orchestrator = createTieredStorageOrchestrator({
835
+ swrCacheLayer: mockSWRCacheLayer as unknown as SWRCacheLayer,
836
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
837
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
838
+ })
839
+
840
+ // Add entries to hot tier tracking
841
+ await orchestrator.write('page-1', data1, { tier: 'hot' })
842
+ await orchestrator.write('page-2', data2, { tier: 'hot' })
843
+
844
+ // This method should exist and persist hot entries
845
+ // @ts-expect-error - Method doesn't exist yet (RED test)
846
+ const result = await orchestrator.onHibernate()
847
+
848
+ expect(result).toBeDefined()
849
+ expect(result.persistedCount).toBe(2)
850
+ expect(result.success).toBe(true)
851
+ })
852
+
853
+ it('should sync all dirty entries to cold tier on hibernate', async () => {
854
+ const data = new Uint8Array([1, 2, 3])
855
+ mockDOStorage.get.mockResolvedValue(data)
856
+
857
+ const orchestrator = createTieredStorageOrchestrator({
858
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
859
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
860
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
861
+ })
862
+
863
+ // Write without write-through (creates dirty entry)
864
+ await orchestrator.write('page-1', data, { tier: 'warm' })
865
+
866
+ // @ts-expect-error - Method doesn't exist yet (RED test)
867
+ const result = await orchestrator.onHibernate()
868
+
869
+ expect(result.syncedToCold).toBeGreaterThan(0)
870
+ expect(mockR2Layer.put).toHaveBeenCalled()
871
+ })
872
+
873
+ it('should return hibernate state for later restoration', async () => {
874
+ const data = new Uint8Array([1, 2, 3])
875
+
876
+ const orchestrator = createTieredStorageOrchestrator({
877
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
878
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
879
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
880
+ })
881
+
882
+ await orchestrator.write('page-1', data, { tier: 'warm' })
883
+
884
+ // @ts-expect-error - Method doesn't exist yet (RED test)
885
+ const state = await orchestrator.onHibernate()
886
+
887
+ expect(state.hotKeys).toBeDefined()
888
+ expect(state.indexSnapshot).toBeDefined()
889
+ expect(state.timestamp).toBeDefined()
890
+ })
891
+ })
892
+
893
+ describe('onWake()', () => {
894
+ it('should restore hot tier entries from hibernate state', async () => {
895
+ const data = new Uint8Array([1, 2, 3])
896
+ mockDOStorage.get.mockResolvedValue(data)
897
+
898
+ const orchestrator = createTieredStorageOrchestrator({
899
+ swrCacheLayer: mockSWRCacheLayer as unknown as SWRCacheLayer,
900
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
901
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
902
+ })
903
+
904
+ const hibernateState = {
905
+ hotKeys: ['page-1', 'page-2'],
906
+ indexSnapshot: new Map(),
907
+ timestamp: Date.now() - 1000,
908
+ }
909
+
910
+ // @ts-expect-error - Method doesn't exist yet (RED test)
911
+ const result = await orchestrator.onWake(hibernateState)
912
+
913
+ expect(result.restoredCount).toBe(2)
914
+ expect(mockSWRCacheLayer.put).toHaveBeenCalledTimes(2)
915
+ })
916
+
917
+ it('should prefetch frequently accessed pages on wake', async () => {
918
+ const data = new Uint8Array([1, 2, 3])
919
+ mockDOStorage.get.mockResolvedValue(data)
920
+
921
+ const orchestrator = createTieredStorageOrchestrator({
922
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
923
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
924
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
925
+ })
926
+
927
+ const hibernateState = {
928
+ hotKeys: [],
929
+ indexSnapshot: new Map([
930
+ ['page-high-access', { key: 'page-high-access', tier: 'warm' as StorageTier, accessCount: 100, size: 8192, lastAccess: Date.now() - 5000, created: Date.now() - 100000, modified: Date.now() - 5000, dirty: false }],
931
+ ]),
932
+ timestamp: Date.now() - 1000,
933
+ }
934
+
935
+ // @ts-expect-error - Method doesn't exist yet (RED test)
936
+ const result = await orchestrator.onWake(hibernateState, { prefetchThreshold: 50 })
937
+
938
+ expect(result.prefetchedCount).toBeGreaterThan(0)
939
+ })
940
+
941
+ it('should validate and repair storage index on wake', async () => {
942
+ const orchestrator = createTieredStorageOrchestrator({
943
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
944
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
945
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
946
+ })
947
+
948
+ // @ts-expect-error - Method doesn't exist yet (RED test)
949
+ const result = await orchestrator.onWake(null, { validateIndex: true })
950
+
951
+ expect(result.indexValidated).toBe(true)
952
+ expect(result.indexRepairs).toBeDefined()
953
+ })
954
+ })
955
+ })
956
+
957
+ describe('Background Task Scheduling (DO Alarm)', () => {
958
+ describe('scheduleAlarm()', () => {
959
+ it('should schedule a background demotion cycle', async () => {
960
+ const mockAlarmStorage = {
961
+ ...mockDOStorage,
962
+ setAlarm: vi.fn().mockResolvedValue(undefined),
963
+ getAlarm: vi.fn().mockResolvedValue(null),
964
+ deleteAlarm: vi.fn().mockResolvedValue(undefined),
965
+ }
966
+
967
+ const orchestrator = createTieredStorageOrchestrator({
968
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
969
+ doStorage: mockAlarmStorage as unknown as DurableObjectStorage,
970
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
971
+ })
972
+
973
+ // @ts-expect-error - Method doesn't exist yet (RED test)
974
+ await orchestrator.scheduleAlarm({ type: 'demotion', delayMs: 60000 })
975
+
976
+ expect(mockAlarmStorage.setAlarm).toHaveBeenCalled()
977
+ })
978
+
979
+ it('should schedule a background sync cycle', async () => {
980
+ const mockAlarmStorage = {
981
+ ...mockDOStorage,
982
+ setAlarm: vi.fn().mockResolvedValue(undefined),
983
+ getAlarm: vi.fn().mockResolvedValue(null),
984
+ }
985
+
986
+ const orchestrator = createTieredStorageOrchestrator({
987
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
988
+ doStorage: mockAlarmStorage as unknown as DurableObjectStorage,
989
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
990
+ })
991
+
992
+ // @ts-expect-error - Method doesn't exist yet (RED test)
993
+ await orchestrator.scheduleAlarm({ type: 'sync', delayMs: 300000 })
994
+
995
+ expect(mockAlarmStorage.setAlarm).toHaveBeenCalled()
996
+ })
997
+
998
+ it('should not schedule duplicate alarms', async () => {
999
+ const existingAlarmTime = Date.now() + 30000
1000
+ const mockAlarmStorage = {
1001
+ ...mockDOStorage,
1002
+ setAlarm: vi.fn().mockResolvedValue(undefined),
1003
+ getAlarm: vi.fn().mockResolvedValue(existingAlarmTime),
1004
+ }
1005
+
1006
+ const orchestrator = createTieredStorageOrchestrator({
1007
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1008
+ doStorage: mockAlarmStorage as unknown as DurableObjectStorage,
1009
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1010
+ })
1011
+
1012
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1013
+ await orchestrator.scheduleAlarm({ type: 'demotion', delayMs: 60000, skipIfExists: true })
1014
+
1015
+ expect(mockAlarmStorage.setAlarm).not.toHaveBeenCalled()
1016
+ })
1017
+ })
1018
+
1019
+ describe('handleAlarm()', () => {
1020
+ it('should run demotion cycle on alarm', async () => {
1021
+ const orchestrator = createTieredStorageOrchestrator({
1022
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1023
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1024
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1025
+ hotTtlMs: 0, // Immediate expiry
1026
+ autoDemote: true,
1027
+ })
1028
+
1029
+ // Add hot tier entry
1030
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]), { tier: 'hot' })
1031
+
1032
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1033
+ const result = await orchestrator.handleAlarm({ type: 'demotion' })
1034
+
1035
+ expect(result.demotionCount).toBeGreaterThanOrEqual(0)
1036
+ expect(result.nextAlarmMs).toBeDefined()
1037
+ })
1038
+
1039
+ it('should run sync cycle on alarm', async () => {
1040
+ const data = new Uint8Array([1, 2, 3])
1041
+ mockDOStorage.get.mockResolvedValue(data)
1042
+
1043
+ const orchestrator = createTieredStorageOrchestrator({
1044
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1045
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1046
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1047
+ })
1048
+
1049
+ // Create dirty entry
1050
+ await orchestrator.write('page-1', data, { tier: 'warm' })
1051
+
1052
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1053
+ const result = await orchestrator.handleAlarm({ type: 'sync' })
1054
+
1055
+ expect(result.syncedCount).toBeGreaterThanOrEqual(0)
1056
+ })
1057
+
1058
+ it('should reschedule alarm after completion', async () => {
1059
+ const mockAlarmStorage = {
1060
+ ...mockDOStorage,
1061
+ setAlarm: vi.fn().mockResolvedValue(undefined),
1062
+ getAlarm: vi.fn().mockResolvedValue(null),
1063
+ }
1064
+
1065
+ const orchestrator = createTieredStorageOrchestrator({
1066
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1067
+ doStorage: mockAlarmStorage as unknown as DurableObjectStorage,
1068
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1069
+ })
1070
+
1071
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1072
+ await orchestrator.handleAlarm({ type: 'demotion', rescheduleMs: 60000 })
1073
+
1074
+ expect(mockAlarmStorage.setAlarm).toHaveBeenCalled()
1075
+ })
1076
+ })
1077
+ })
1078
+
1079
+ describe('Circuit Breaker Pattern', () => {
1080
+ describe('tier failure handling', () => {
1081
+ it('should open circuit breaker after consecutive failures', async () => {
1082
+ mockR2Layer.get.mockRejectedValue(new Error('R2 unavailable'))
1083
+
1084
+ const orchestrator = createTieredStorageOrchestrator({
1085
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1086
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1087
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1088
+ // @ts-expect-error - Config option doesn't exist yet
1089
+ circuitBreaker: {
1090
+ enabled: true,
1091
+ failureThreshold: 3,
1092
+ resetTimeoutMs: 30000,
1093
+ },
1094
+ })
1095
+
1096
+ // Trigger multiple failures
1097
+ mockCacheLayer.get.mockResolvedValue(null)
1098
+ mockDOStorage.get.mockResolvedValue(undefined)
1099
+
1100
+ for (let i = 0; i < 5; i++) {
1101
+ try {
1102
+ await orchestrator.read(`page-${i}`)
1103
+ } catch {
1104
+ // Expected to fail
1105
+ }
1106
+ }
1107
+
1108
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1109
+ const circuitState = orchestrator.getCircuitBreakerState('cold')
1110
+
1111
+ expect(circuitState.state).toBe('open')
1112
+ expect(circuitState.failures).toBeGreaterThanOrEqual(3)
1113
+ })
1114
+
1115
+ it('should skip unavailable tier when circuit is open', async () => {
1116
+ mockR2Layer.get.mockRejectedValue(new Error('R2 unavailable'))
1117
+
1118
+ const orchestrator = createTieredStorageOrchestrator({
1119
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1120
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1121
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1122
+ // @ts-expect-error - Config option doesn't exist yet
1123
+ circuitBreaker: {
1124
+ enabled: true,
1125
+ failureThreshold: 1,
1126
+ },
1127
+ })
1128
+
1129
+ // Open the circuit
1130
+ mockCacheLayer.get.mockResolvedValue(null)
1131
+ mockDOStorage.get.mockResolvedValue(undefined)
1132
+
1133
+ try {
1134
+ await orchestrator.read('page-1')
1135
+ } catch {
1136
+ // Expected
1137
+ }
1138
+
1139
+ // Circuit is now open, R2 should be skipped
1140
+ vi.clearAllMocks()
1141
+ mockCacheLayer.get.mockResolvedValue(null)
1142
+ mockDOStorage.get.mockResolvedValue(undefined)
1143
+ await orchestrator.read('page-2')
1144
+
1145
+ // R2 should not have been called because circuit is open
1146
+ expect(mockR2Layer.get).not.toHaveBeenCalled()
1147
+ })
1148
+
1149
+ it('should transition to half-open after reset timeout', async () => {
1150
+ vi.useFakeTimers()
1151
+
1152
+ const orchestrator = createTieredStorageOrchestrator({
1153
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1154
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1155
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1156
+ // @ts-expect-error - Config option doesn't exist yet
1157
+ circuitBreaker: {
1158
+ enabled: true,
1159
+ failureThreshold: 1,
1160
+ resetTimeoutMs: 5000,
1161
+ },
1162
+ })
1163
+
1164
+ // Open the circuit
1165
+ mockCacheLayer.get.mockResolvedValue(null)
1166
+ mockDOStorage.get.mockResolvedValue(undefined)
1167
+ mockR2Layer.get.mockRejectedValue(new Error('R2 unavailable'))
1168
+
1169
+ try {
1170
+ await orchestrator.read('page-1')
1171
+ } catch {
1172
+ // Expected
1173
+ }
1174
+
1175
+ // Advance time past reset timeout
1176
+ vi.advanceTimersByTime(6000)
1177
+
1178
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1179
+ const circuitState = orchestrator.getCircuitBreakerState('cold')
1180
+
1181
+ expect(circuitState.state).toBe('half-open')
1182
+
1183
+ vi.useRealTimers()
1184
+ })
1185
+
1186
+ it('should close circuit after successful operation in half-open state', async () => {
1187
+ vi.useFakeTimers()
1188
+
1189
+ const orchestrator = createTieredStorageOrchestrator({
1190
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1191
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1192
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1193
+ // @ts-expect-error - Config option doesn't exist yet
1194
+ circuitBreaker: {
1195
+ enabled: true,
1196
+ failureThreshold: 1,
1197
+ resetTimeoutMs: 5000,
1198
+ },
1199
+ })
1200
+
1201
+ // Open the circuit
1202
+ mockCacheLayer.get.mockResolvedValue(null)
1203
+ mockDOStorage.get.mockResolvedValue(undefined)
1204
+ mockR2Layer.get.mockRejectedValueOnce(new Error('R2 unavailable'))
1205
+
1206
+ try {
1207
+ await orchestrator.read('page-1')
1208
+ } catch {
1209
+ // Expected
1210
+ }
1211
+
1212
+ // Advance to half-open
1213
+ vi.advanceTimersByTime(6000)
1214
+
1215
+ // Now R2 succeeds
1216
+ mockR2Layer.get.mockResolvedValue(new Uint8Array([1, 2, 3]))
1217
+
1218
+ await orchestrator.read('page-2')
1219
+
1220
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1221
+ const circuitState = orchestrator.getCircuitBreakerState('cold')
1222
+
1223
+ expect(circuitState.state).toBe('closed')
1224
+
1225
+ vi.useRealTimers()
1226
+ })
1227
+ })
1228
+
1229
+ describe('fallback behavior', () => {
1230
+ it('should use fallback tier when primary tier circuit is open', async () => {
1231
+ const data = new Uint8Array([1, 2, 3])
1232
+
1233
+ const orchestrator = createTieredStorageOrchestrator({
1234
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1235
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1236
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1237
+ // @ts-expect-error - Config option doesn't exist yet
1238
+ circuitBreaker: {
1239
+ enabled: true,
1240
+ failureThreshold: 1,
1241
+ fallbackEnabled: true,
1242
+ },
1243
+ })
1244
+
1245
+ // Configure circuit to be open for cold tier
1246
+ // @ts-expect-error - Method doesn't exist yet
1247
+ orchestrator.setCircuitBreakerState('cold', 'open')
1248
+
1249
+ // Data should come from warm tier fallback
1250
+ mockCacheLayer.get.mockResolvedValue(null)
1251
+ mockDOStorage.get.mockResolvedValue(data)
1252
+
1253
+ const result = await orchestrator.read('page-1')
1254
+
1255
+ expect(result.data).toEqual(data)
1256
+ expect(result.tier).toBe('warm')
1257
+ // @ts-expect-error - Property doesn't exist yet
1258
+ expect(result.fallback).toBe(true)
1259
+ })
1260
+ })
1261
+ })
1262
+
1263
+ describe('Integrated Metrics Aggregation', () => {
1264
+ describe('getAggregatedMetrics()', () => {
1265
+ it('should aggregate metrics from all components', async () => {
1266
+ const orchestrator = createTieredStorageOrchestrator({
1267
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1268
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1269
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1270
+ })
1271
+
1272
+ // Generate some activity
1273
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]))
1274
+ await orchestrator.write('page-2', new Uint8Array([4, 5, 6]))
1275
+
1276
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1277
+ const metrics = orchestrator.getAggregatedMetrics()
1278
+
1279
+ expect(metrics).toMatchObject({
1280
+ timestamp: expect.any(Number),
1281
+ period: expect.any(String),
1282
+ tiers: {
1283
+ hot: expect.objectContaining({
1284
+ reads: expect.any(Number),
1285
+ writes: expect.any(Number),
1286
+ hitRatio: expect.any(Number),
1287
+ }),
1288
+ warm: expect.objectContaining({
1289
+ reads: expect.any(Number),
1290
+ writes: expect.any(Number),
1291
+ bytesStored: expect.any(Number),
1292
+ }),
1293
+ cold: expect.objectContaining({
1294
+ reads: expect.any(Number),
1295
+ writes: expect.any(Number),
1296
+ bytesStored: expect.any(Number),
1297
+ }),
1298
+ },
1299
+ migrations: expect.objectContaining({
1300
+ promotions: expect.any(Number),
1301
+ demotions: expect.any(Number),
1302
+ failures: expect.any(Number),
1303
+ }),
1304
+ circuitBreakers: expect.any(Object),
1305
+ })
1306
+ })
1307
+
1308
+ it('should track latency percentiles', async () => {
1309
+ const orchestrator = createTieredStorageOrchestrator({
1310
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1311
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1312
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1313
+ // @ts-expect-error - Config option doesn't exist yet
1314
+ metrics: {
1315
+ trackLatency: true,
1316
+ },
1317
+ })
1318
+
1319
+ mockCacheLayer.get.mockResolvedValue(null)
1320
+ mockDOStorage.get.mockResolvedValue(new Uint8Array([1, 2, 3]))
1321
+
1322
+ // Generate some reads
1323
+ for (let i = 0; i < 10; i++) {
1324
+ await orchestrator.read(`page-${i}`)
1325
+ }
1326
+
1327
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1328
+ const metrics = orchestrator.getAggregatedMetrics()
1329
+
1330
+ expect(metrics.latency).toBeDefined()
1331
+ expect(metrics.latency.p50).toBeDefined()
1332
+ expect(metrics.latency.p95).toBeDefined()
1333
+ expect(metrics.latency.p99).toBeDefined()
1334
+ })
1335
+
1336
+ it('should calculate cost estimates', async () => {
1337
+ const orchestrator = createTieredStorageOrchestrator({
1338
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1339
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1340
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1341
+ // @ts-expect-error - Config option doesn't exist yet
1342
+ metrics: {
1343
+ trackCost: true,
1344
+ costConfig: {
1345
+ r2StoragePerGBMonth: 0.015,
1346
+ r2ClassAOperations: 0.0000045,
1347
+ r2ClassBOperations: 0.00000036,
1348
+ doStoragePerGBMonth: 0.20,
1349
+ cacheOperations: 0, // FREE
1350
+ },
1351
+ },
1352
+ })
1353
+
1354
+ await orchestrator.write('page-1', new Uint8Array(1024 * 1024), { tier: 'cold' })
1355
+
1356
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1357
+ const metrics = orchestrator.getAggregatedMetrics()
1358
+
1359
+ expect(metrics.estimatedCost).toBeDefined()
1360
+ expect(metrics.estimatedCost.r2).toBeGreaterThan(0)
1361
+ expect(metrics.estimatedCost.total).toBeGreaterThan(0)
1362
+ })
1363
+ })
1364
+
1365
+ describe('exportMetrics()', () => {
1366
+ it('should export metrics in Prometheus format', async () => {
1367
+ const orchestrator = createTieredStorageOrchestrator({
1368
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1369
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1370
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1371
+ })
1372
+
1373
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]))
1374
+
1375
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1376
+ const prometheus = orchestrator.exportMetrics('prometheus')
1377
+
1378
+ expect(prometheus).toContain('tiered_storage_writes_total')
1379
+ expect(prometheus).toContain('tiered_storage_bytes_written')
1380
+ })
1381
+
1382
+ it('should export metrics in JSON format', async () => {
1383
+ const orchestrator = createTieredStorageOrchestrator({
1384
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1385
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1386
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1387
+ })
1388
+
1389
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1390
+ const json = orchestrator.exportMetrics('json')
1391
+
1392
+ expect(typeof json).toBe('object')
1393
+ expect(json.timestamp).toBeDefined()
1394
+ })
1395
+ })
1396
+ })
1397
+
1398
+ describe('Component Integration', () => {
1399
+ describe('with TieredVFS', () => {
1400
+ it('should integrate with TieredVFS for PGLite page operations', async () => {
1401
+ const orchestrator = createTieredStorageOrchestrator({
1402
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1403
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1404
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1405
+ })
1406
+
1407
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1408
+ const vfs = orchestrator.createVFSInterface({
1409
+ pageSize: 8192,
1410
+ promotionThreshold: 3,
1411
+ })
1412
+
1413
+ expect(vfs.readPage).toBeDefined()
1414
+ expect(vfs.writePage).toBeDefined()
1415
+ expect(vfs.deletePage).toBeDefined()
1416
+ })
1417
+ })
1418
+
1419
+ describe('with AutoPromoter', () => {
1420
+ it('should configure auto-promoter with custom policies', async () => {
1421
+ const orchestrator = createTieredStorageOrchestrator({
1422
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1423
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1424
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1425
+ })
1426
+
1427
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1428
+ orchestrator.configureAutoPromoter({
1429
+ coldToWarm: {
1430
+ accessThreshold: 5,
1431
+ timeWindowMs: 60000,
1432
+ },
1433
+ warmToHot: {
1434
+ accessThreshold: 10,
1435
+ timeWindowMs: 30000,
1436
+ },
1437
+ })
1438
+
1439
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1440
+ const config = orchestrator.getAutoPromoterConfig()
1441
+
1442
+ expect(config.coldToWarm.accessThreshold).toBe(5)
1443
+ expect(config.warmToHot.accessThreshold).toBe(10)
1444
+ })
1445
+ })
1446
+
1447
+ describe('with AutoDemoter', () => {
1448
+ it('should configure auto-demoter with custom policies', async () => {
1449
+ const orchestrator = createTieredStorageOrchestrator({
1450
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1451
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1452
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1453
+ })
1454
+
1455
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1456
+ orchestrator.configureAutoDemoter({
1457
+ hotToWarm: {
1458
+ ttlMs: 300000, // 5 minutes
1459
+ maxEntries: 1000,
1460
+ },
1461
+ warmToCold: {
1462
+ ttlMs: 3600000, // 1 hour
1463
+ maxEntries: 10000,
1464
+ },
1465
+ })
1466
+
1467
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1468
+ const config = orchestrator.getAutoDemoterConfig()
1469
+
1470
+ expect(config.hotToWarm.ttlMs).toBe(300000)
1471
+ expect(config.warmToCold.maxEntries).toBe(10000)
1472
+ })
1473
+
1474
+ it('should support custom demotion strategies', async () => {
1475
+ const orchestrator = createTieredStorageOrchestrator({
1476
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1477
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1478
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1479
+ })
1480
+
1481
+ const customStrategy = vi.fn().mockReturnValue(['page-1', 'page-2'])
1482
+
1483
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1484
+ orchestrator.setDemotionStrategy('hot', customStrategy)
1485
+
1486
+ await orchestrator.write('page-1', new Uint8Array([1]), { tier: 'hot' })
1487
+ await orchestrator.write('page-2', new Uint8Array([2]), { tier: 'hot' })
1488
+ await orchestrator.write('page-3', new Uint8Array([3]), { tier: 'hot' })
1489
+
1490
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1491
+ const events = await orchestrator.runDemotionWithStrategy('hot', 2)
1492
+
1493
+ expect(customStrategy).toHaveBeenCalled()
1494
+ expect(events.length).toBe(2)
1495
+ })
1496
+ })
1497
+ })
1498
+
1499
+ describe('Error Recovery', () => {
1500
+ describe('atomic operations', () => {
1501
+ it('should rollback promotion on partial failure', async () => {
1502
+ const data = new Uint8Array([1, 2, 3])
1503
+ mockR2Layer.get.mockResolvedValue(data)
1504
+ mockDOStorage.put.mockRejectedValue(new Error('DO write failed'))
1505
+
1506
+ const orchestrator = createTieredStorageOrchestrator({
1507
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1508
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1509
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1510
+ })
1511
+
1512
+ const event = await orchestrator.promoteToWarm('page-1')
1513
+
1514
+ expect(event.success).toBe(false)
1515
+ // Data should still be accessible from cold tier
1516
+ expect(orchestrator.getTier('page-1')).not.toBe('warm')
1517
+ })
1518
+
1519
+ it('should handle demotion failure gracefully', async () => {
1520
+ const data = new Uint8Array([1, 2, 3])
1521
+ mockDOStorage.get.mockResolvedValue(data)
1522
+ mockDOStorage.put.mockResolvedValue(undefined)
1523
+ mockR2Layer.put.mockRejectedValue(new Error('R2 write failed'))
1524
+
1525
+ const orchestrator = createTieredStorageOrchestrator({
1526
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1527
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1528
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1529
+ })
1530
+
1531
+ // Write to warm tier first
1532
+ await orchestrator.write('page-1', data, { tier: 'warm' })
1533
+
1534
+ const event = await orchestrator.demoteFromWarm('page-1')
1535
+
1536
+ expect(event.success).toBe(false)
1537
+ // Data should still be in warm tier
1538
+ expect(orchestrator.getTier('page-1')).toBe('warm')
1539
+ })
1540
+ })
1541
+
1542
+ describe('consistency checks', () => {
1543
+ it('should detect and repair tier inconsistencies', async () => {
1544
+ const orchestrator = createTieredStorageOrchestrator({
1545
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1546
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1547
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1548
+ })
1549
+
1550
+ // Simulate inconsistency: index says warm, but data is only in cold
1551
+ // @ts-expect-error - Accessing private method for test setup
1552
+ orchestrator.storageIndex?.set('page-1', {
1553
+ key: 'page-1',
1554
+ tier: 'warm',
1555
+ size: 100,
1556
+ lastAccess: Date.now(),
1557
+ accessCount: 1,
1558
+ created: Date.now(),
1559
+ modified: Date.now(),
1560
+ dirty: false,
1561
+ })
1562
+
1563
+ mockDOStorage.get.mockResolvedValue(undefined) // Not in warm
1564
+ mockR2Layer.get.mockResolvedValue(new Uint8Array([1, 2, 3])) // But in cold
1565
+
1566
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1567
+ const result = await orchestrator.runConsistencyCheck()
1568
+
1569
+ expect(result.inconsistencies).toBeGreaterThan(0)
1570
+ expect(result.repairs).toBeDefined()
1571
+ })
1572
+ })
1573
+ })
1574
+
1575
+ describe('Advanced Tier Promotion Logic', () => {
1576
+ describe('time-window based promotion', () => {
1577
+ it('should not promote if access count met but time window exceeded', async () => {
1578
+ const data = new Uint8Array([1, 2, 3])
1579
+ mockCacheLayer.get.mockResolvedValue(null)
1580
+ mockDOStorage.get.mockResolvedValue(undefined)
1581
+ mockR2Layer.get.mockResolvedValue(data)
1582
+
1583
+ const orchestrator = createTieredStorageOrchestrator({
1584
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1585
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1586
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1587
+ coldToWarmThreshold: 2,
1588
+ promotionWindowMs: 1000, // Very short window
1589
+ autoPromote: true,
1590
+ })
1591
+
1592
+ // First access
1593
+ await orchestrator.read('page-123')
1594
+
1595
+ // Simulate time passing beyond window
1596
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1597
+ orchestrator.setIndexEntryTimestamp('page-123', 'created', Date.now() - 5000)
1598
+
1599
+ // Second access - should NOT trigger promotion (time window exceeded)
1600
+ vi.clearAllMocks()
1601
+ await orchestrator.read('page-123')
1602
+ await new Promise(resolve => setTimeout(resolve, 0))
1603
+
1604
+ expect(mockDOStorage.put).not.toHaveBeenCalled()
1605
+ })
1606
+ })
1607
+
1608
+ describe('burst access pattern detection', () => {
1609
+ it('should detect burst access patterns and prioritize promotion', async () => {
1610
+ const data = new Uint8Array([1, 2, 3])
1611
+ mockCacheLayer.get.mockResolvedValue(null)
1612
+ mockDOStorage.get.mockResolvedValue(undefined)
1613
+ mockR2Layer.get.mockResolvedValue(data)
1614
+
1615
+ const orchestrator = createTieredStorageOrchestrator({
1616
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1617
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1618
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1619
+ // @ts-expect-error - Config option doesn't exist yet
1620
+ burstDetection: {
1621
+ enabled: true,
1622
+ windowMs: 1000,
1623
+ accessThreshold: 5,
1624
+ },
1625
+ })
1626
+
1627
+ // Simulate burst: 10 accesses in rapid succession
1628
+ for (let i = 0; i < 10; i++) {
1629
+ await orchestrator.read('page-burst')
1630
+ }
1631
+
1632
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1633
+ const metrics = orchestrator.getAccessPatternMetrics('page-burst')
1634
+
1635
+ expect(metrics.isBurst).toBe(true)
1636
+ expect(metrics.accessRate).toBeGreaterThan(5)
1637
+ })
1638
+ })
1639
+ })
1640
+
1641
+ describe('Advanced Tier Demotion Logic', () => {
1642
+ describe('LRU with size-based eviction', () => {
1643
+ it('should evict large entries first when memory pressure is high', async () => {
1644
+ mockCacheLayer.delete.mockResolvedValue(true)
1645
+
1646
+ const orchestrator = createTieredStorageOrchestrator({
1647
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1648
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1649
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1650
+ // @ts-expect-error - Config option doesn't exist yet
1651
+ evictionStrategy: 'size-weighted-lru',
1652
+ })
1653
+
1654
+ // Add entries of different sizes
1655
+ await orchestrator.write('small', new Uint8Array(100), { tier: 'hot' })
1656
+ await orchestrator.write('medium', new Uint8Array(1000), { tier: 'hot' })
1657
+ await orchestrator.write('large', new Uint8Array(10000), { tier: 'hot' })
1658
+
1659
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1660
+ const events = await orchestrator.evictByMemoryPressure(5000) // Need 5KB
1661
+
1662
+ // Large entry should be evicted first to free most space
1663
+ expect(events.some((e: TierMigrationEvent) => e.key === 'large')).toBe(true)
1664
+ })
1665
+ })
1666
+
1667
+ describe('priority-based demotion', () => {
1668
+ it('should respect entry priority when demoting', async () => {
1669
+ mockCacheLayer.delete.mockResolvedValue(true)
1670
+
1671
+ const orchestrator = createTieredStorageOrchestrator({
1672
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1673
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1674
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1675
+ hotTtlMs: 0, // Immediate expiry
1676
+ autoDemote: true,
1677
+ })
1678
+
1679
+ // Add entries with different priorities
1680
+ await orchestrator.write('low-priority', new Uint8Array([1]), { tier: 'hot' })
1681
+ await orchestrator.write('high-priority', new Uint8Array([2]), { tier: 'hot' })
1682
+
1683
+ // Set priority
1684
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1685
+ orchestrator.setEntryPriority('high-priority', 'high')
1686
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1687
+ orchestrator.setEntryPriority('low-priority', 'low')
1688
+
1689
+ // Run demotion cycle with limit of 1
1690
+ const events = await orchestrator.runDemotionCycle(1)
1691
+
1692
+ // Low priority entry should be demoted first
1693
+ expect(events[0].key).toBe('low-priority')
1694
+ })
1695
+ })
1696
+ })
1697
+
1698
+ describe('Concurrent Operations', () => {
1699
+ describe('batch operations', () => {
1700
+ it('should support batch promotion', async () => {
1701
+ const data = new Uint8Array([1, 2, 3])
1702
+ mockR2Layer.get.mockResolvedValue(data)
1703
+
1704
+ const orchestrator = createTieredStorageOrchestrator({
1705
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1706
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1707
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1708
+ })
1709
+
1710
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1711
+ const results = await orchestrator.batchPromoteToWarm(['page-1', 'page-2', 'page-3'])
1712
+
1713
+ expect(results.length).toBe(3)
1714
+ expect(results.filter((r: TierMigrationEvent) => r.success).length).toBeGreaterThan(0)
1715
+ })
1716
+
1717
+ it('should support batch demotion', async () => {
1718
+ const data = new Uint8Array([1, 2, 3])
1719
+ mockDOStorage.get.mockResolvedValue(data)
1720
+
1721
+ const orchestrator = createTieredStorageOrchestrator({
1722
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1723
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1724
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1725
+ })
1726
+
1727
+ // Add entries to warm tier
1728
+ await orchestrator.write('page-1', data, { tier: 'warm' })
1729
+ await orchestrator.write('page-2', data, { tier: 'warm' })
1730
+ await orchestrator.write('page-3', data, { tier: 'warm' })
1731
+
1732
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1733
+ const results = await orchestrator.batchDemoteFromWarm(['page-1', 'page-2', 'page-3'])
1734
+
1735
+ expect(results.length).toBe(3)
1736
+ expect(results.filter((r: TierMigrationEvent) => r.success).length).toBeGreaterThan(0)
1737
+ })
1738
+ })
1739
+ })
1740
+
1741
+ describe('Telemetry and Observability', () => {
1742
+ describe('health checks', () => {
1743
+ it('should perform comprehensive health check across all tiers', async () => {
1744
+ const orchestrator = createTieredStorageOrchestrator({
1745
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1746
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1747
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1748
+ })
1749
+
1750
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1751
+ const health = await orchestrator.healthCheck()
1752
+
1753
+ expect(health).toMatchObject({
1754
+ overall: expect.stringMatching(/healthy|degraded|unhealthy/),
1755
+ tiers: {
1756
+ hot: expect.objectContaining({ status: expect.any(String) }),
1757
+ warm: expect.objectContaining({ status: expect.any(String) }),
1758
+ cold: expect.objectContaining({ status: expect.any(String) }),
1759
+ },
1760
+ latency: expect.objectContaining({
1761
+ hot: expect.any(Number),
1762
+ warm: expect.any(Number),
1763
+ cold: expect.any(Number),
1764
+ }),
1765
+ })
1766
+ })
1767
+ })
1768
+ })
1769
+
1770
+ /**
1771
+ * ========================================================================
1772
+ * Additional RED Tests: Unified Tiering Orchestrator
1773
+ * Issue: postgres-h91k [TIER-RED]
1774
+ *
1775
+ * These tests cover additional edge cases and advanced features.
1776
+ * ========================================================================
1777
+ */
1778
+
1779
+ describe('Memory Pressure Management', () => {
1780
+ describe('getMemoryUsage()', () => {
1781
+ it('should calculate total memory usage across all tiers', async () => {
1782
+ const orchestrator = createTieredStorageOrchestrator({
1783
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1784
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1785
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1786
+ })
1787
+
1788
+ await orchestrator.write('page-1', new Uint8Array(1000), { tier: 'hot' })
1789
+ await orchestrator.write('page-2', new Uint8Array(2000), { tier: 'warm' })
1790
+
1791
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1792
+ const usage = orchestrator.getMemoryUsage()
1793
+
1794
+ expect(usage).toMatchObject({
1795
+ hot: expect.any(Number),
1796
+ warm: expect.any(Number),
1797
+ cold: expect.any(Number),
1798
+ total: expect.any(Number),
1799
+ limit: expect.any(Number),
1800
+ utilizationPercent: expect.any(Number),
1801
+ })
1802
+ expect(usage.hot).toBe(1000)
1803
+ expect(usage.warm).toBe(2000)
1804
+ })
1805
+ })
1806
+
1807
+ describe('setMemoryLimit()', () => {
1808
+ it('should configure memory limits for each tier', async () => {
1809
+ const orchestrator = createTieredStorageOrchestrator({
1810
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1811
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1812
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1813
+ })
1814
+
1815
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1816
+ orchestrator.setMemoryLimit({
1817
+ hot: 10 * 1024 * 1024, // 10MB
1818
+ warm: 50 * 1024 * 1024, // 50MB
1819
+ })
1820
+
1821
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1822
+ const limits = orchestrator.getMemoryLimits()
1823
+
1824
+ expect(limits.hot).toBe(10 * 1024 * 1024)
1825
+ expect(limits.warm).toBe(50 * 1024 * 1024)
1826
+ })
1827
+
1828
+ it('should trigger eviction when memory limit exceeded', async () => {
1829
+ mockCacheLayer.delete.mockResolvedValue(true)
1830
+
1831
+ const orchestrator = createTieredStorageOrchestrator({
1832
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1833
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1834
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1835
+ })
1836
+
1837
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1838
+ orchestrator.setMemoryLimit({ hot: 100 }) // Very small limit
1839
+
1840
+ // Write more than the limit
1841
+ await orchestrator.write('page-1', new Uint8Array(50), { tier: 'hot' })
1842
+ await orchestrator.write('page-2', new Uint8Array(60), { tier: 'hot' })
1843
+
1844
+ // Should have triggered auto-eviction
1845
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1846
+ const usage = orchestrator.getMemoryUsage()
1847
+ expect(usage.hot).toBeLessThanOrEqual(100)
1848
+ })
1849
+ })
1850
+ })
1851
+
1852
+ describe('Read Coalescing', () => {
1853
+ describe('coalesced reads for concurrent requests', () => {
1854
+ it('should coalesce multiple concurrent reads for the same key', async () => {
1855
+ const data = new Uint8Array([1, 2, 3])
1856
+ let fetchCount = 0
1857
+ mockCacheLayer.get.mockResolvedValue(null)
1858
+ mockDOStorage.get.mockImplementation(async () => {
1859
+ fetchCount++
1860
+ await new Promise(resolve => setTimeout(resolve, 10))
1861
+ return data
1862
+ })
1863
+
1864
+ const orchestrator = createTieredStorageOrchestrator({
1865
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1866
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1867
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1868
+ // @ts-expect-error - Config option doesn't exist yet (RED test)
1869
+ enableReadCoalescing: true,
1870
+ })
1871
+
1872
+ // Trigger 5 concurrent reads for the same key
1873
+ const results = await Promise.all([
1874
+ orchestrator.read('page-123'),
1875
+ orchestrator.read('page-123'),
1876
+ orchestrator.read('page-123'),
1877
+ orchestrator.read('page-123'),
1878
+ orchestrator.read('page-123'),
1879
+ ])
1880
+
1881
+ // All should return the same data
1882
+ for (const result of results) {
1883
+ expect(result.data).toEqual(data)
1884
+ }
1885
+
1886
+ // But only ONE fetch should have been made
1887
+ expect(fetchCount).toBe(1)
1888
+ })
1889
+ })
1890
+ })
1891
+
1892
+ describe('Prefetching', () => {
1893
+ describe('prefetch()', () => {
1894
+ it('should prefetch data into hot tier', async () => {
1895
+ const data = new Uint8Array([1, 2, 3])
1896
+ mockR2Layer.get.mockResolvedValue(data)
1897
+ mockDOStorage.get.mockResolvedValue(undefined)
1898
+ mockCacheLayer.get.mockResolvedValue(null)
1899
+
1900
+ const orchestrator = createTieredStorageOrchestrator({
1901
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1902
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1903
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1904
+ })
1905
+
1906
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1907
+ const result = await orchestrator.prefetch(['page-1', 'page-2', 'page-3'], { targetTier: 'hot' })
1908
+
1909
+ expect(result.prefetchedCount).toBe(3)
1910
+ expect(result.failedKeys).toHaveLength(0)
1911
+ })
1912
+
1913
+ it('should respect concurrent prefetch limit', async () => {
1914
+ const data = new Uint8Array([1, 2, 3])
1915
+ let concurrentOps = 0
1916
+ let maxConcurrent = 0
1917
+
1918
+ mockR2Layer.get.mockImplementation(async () => {
1919
+ concurrentOps++
1920
+ maxConcurrent = Math.max(maxConcurrent, concurrentOps)
1921
+ await new Promise(resolve => setTimeout(resolve, 10))
1922
+ concurrentOps--
1923
+ return data
1924
+ })
1925
+
1926
+ const orchestrator = createTieredStorageOrchestrator({
1927
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1928
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1929
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1930
+ })
1931
+
1932
+ const keys = Array.from({ length: 20 }, (_, i) => `page-${i}`)
1933
+
1934
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1935
+ await orchestrator.prefetch(keys, { targetTier: 'warm', maxConcurrency: 5 })
1936
+
1937
+ expect(maxConcurrent).toBeLessThanOrEqual(5)
1938
+ })
1939
+ })
1940
+ })
1941
+
1942
+ describe('Event Streaming', () => {
1943
+ describe('subscribeToEvents()', () => {
1944
+ it('should stream tier migration events', async () => {
1945
+ const data = new Uint8Array([1, 2, 3])
1946
+ mockR2Layer.get.mockResolvedValue(data)
1947
+
1948
+ const orchestrator = createTieredStorageOrchestrator({
1949
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1950
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1951
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1952
+ })
1953
+
1954
+ const events: TierMigrationEvent[] = []
1955
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1956
+ const unsubscribe = orchestrator.subscribeToEvents((event: TierMigrationEvent) => {
1957
+ events.push(event)
1958
+ })
1959
+
1960
+ await orchestrator.promoteToWarm('page-1')
1961
+ await orchestrator.promoteToWarm('page-2')
1962
+
1963
+ expect(events.length).toBe(2)
1964
+ expect(events[0].key).toBe('page-1')
1965
+ expect(events[1].key).toBe('page-2')
1966
+
1967
+ unsubscribe()
1968
+
1969
+ // After unsubscribe, should not receive more events
1970
+ await orchestrator.promoteToWarm('page-3')
1971
+ expect(events.length).toBe(2)
1972
+ })
1973
+
1974
+ it('should filter events by type', async () => {
1975
+ mockCacheLayer.delete.mockResolvedValue(true)
1976
+
1977
+ const orchestrator = createTieredStorageOrchestrator({
1978
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
1979
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
1980
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
1981
+ })
1982
+
1983
+ const promotionEvents: TierMigrationEvent[] = []
1984
+ // @ts-expect-error - Method doesn't exist yet (RED test)
1985
+ orchestrator.subscribeToEvents(
1986
+ (event: TierMigrationEvent) => promotionEvents.push(event),
1987
+ { filter: { type: 'promotion' } }
1988
+ )
1989
+
1990
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]), { tier: 'hot' })
1991
+ await orchestrator.demoteFromHot('page-1')
1992
+
1993
+ // Should not have captured demotion events
1994
+ expect(promotionEvents.every(e => e.toTier !== 'warm' || e.fromTier !== 'hot')).toBe(true)
1995
+ })
1996
+ })
1997
+ })
1998
+
1999
+ describe('Snapshot and Restore', () => {
2000
+ describe('createSnapshot()', () => {
2001
+ it('should create a serializable snapshot of storage state', async () => {
2002
+ const orchestrator = createTieredStorageOrchestrator({
2003
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2004
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2005
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2006
+ })
2007
+
2008
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]), { tier: 'warm' })
2009
+ await orchestrator.write('page-2', new Uint8Array([4, 5, 6]), { tier: 'warm' })
2010
+
2011
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2012
+ const snapshot = await orchestrator.createSnapshot()
2013
+
2014
+ expect(snapshot).toMatchObject({
2015
+ version: expect.any(Number),
2016
+ timestamp: expect.any(Number),
2017
+ index: expect.any(Array),
2018
+ stats: expect.any(Object),
2019
+ })
2020
+ expect(snapshot.index.length).toBe(2)
2021
+ })
2022
+ })
2023
+
2024
+ describe('restoreFromSnapshot()', () => {
2025
+ it('should restore storage state from snapshot', async () => {
2026
+ const orchestrator = createTieredStorageOrchestrator({
2027
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2028
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2029
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2030
+ })
2031
+
2032
+ const snapshot = {
2033
+ version: 1,
2034
+ timestamp: Date.now(),
2035
+ index: [
2036
+ { key: 'page-1', tier: 'warm' as StorageTier, size: 100, lastAccess: Date.now(), accessCount: 5, created: Date.now(), modified: Date.now(), dirty: false },
2037
+ { key: 'page-2', tier: 'cold' as StorageTier, size: 200, lastAccess: Date.now(), accessCount: 2, created: Date.now(), modified: Date.now(), dirty: false },
2038
+ ],
2039
+ stats: {},
2040
+ }
2041
+
2042
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2043
+ await orchestrator.restoreFromSnapshot(snapshot)
2044
+
2045
+ expect(orchestrator.hasInIndex('page-1')).toBe(true)
2046
+ expect(orchestrator.hasInIndex('page-2')).toBe(true)
2047
+ expect(orchestrator.getTier('page-1')).toBe('warm')
2048
+ expect(orchestrator.getTier('page-2')).toBe('cold')
2049
+ })
2050
+ })
2051
+ })
2052
+
2053
+ describe('Tier Warmup Strategies', () => {
2054
+ describe('warmup()', () => {
2055
+ it('should warm up cold data to warm tier based on strategy', async () => {
2056
+ const data = new Uint8Array([1, 2, 3])
2057
+ mockR2Layer.get.mockResolvedValue(data)
2058
+ mockR2Layer.list.mockResolvedValue({
2059
+ objects: [
2060
+ { key: 'page-1', size: 100 },
2061
+ { key: 'page-2', size: 200 },
2062
+ { key: 'page-3', size: 300 },
2063
+ ],
2064
+ truncated: false,
2065
+ })
2066
+
2067
+ const orchestrator = createTieredStorageOrchestrator({
2068
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2069
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2070
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2071
+ })
2072
+
2073
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2074
+ const result = await orchestrator.warmup({
2075
+ strategy: 'all',
2076
+ targetTier: 'warm',
2077
+ maxBytes: 1000,
2078
+ })
2079
+
2080
+ expect(result.warmedUp).toBeGreaterThan(0)
2081
+ expect(result.bytesTransferred).toBeLessThanOrEqual(1000)
2082
+ })
2083
+
2084
+ it('should warm up based on recent access patterns', async () => {
2085
+ const data = new Uint8Array([1, 2, 3])
2086
+ mockR2Layer.get.mockResolvedValue(data)
2087
+
2088
+ const orchestrator = createTieredStorageOrchestrator({
2089
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2090
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2091
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2092
+ })
2093
+
2094
+ // Setup: add some entries to index with varying access counts
2095
+ // @ts-expect-error - Accessing private for test setup
2096
+ orchestrator.storageIndex?.set('page-high', {
2097
+ key: 'page-high', tier: 'cold', size: 100, accessCount: 100,
2098
+ lastAccess: Date.now(), created: Date.now(), modified: Date.now(), dirty: false
2099
+ })
2100
+ // @ts-expect-error - Accessing private for test setup
2101
+ orchestrator.storageIndex?.set('page-low', {
2102
+ key: 'page-low', tier: 'cold', size: 100, accessCount: 1,
2103
+ lastAccess: Date.now(), created: Date.now(), modified: Date.now(), dirty: false
2104
+ })
2105
+
2106
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2107
+ const result = await orchestrator.warmup({
2108
+ strategy: 'most-accessed',
2109
+ targetTier: 'warm',
2110
+ limit: 1,
2111
+ })
2112
+
2113
+ expect(result.keys).toContain('page-high')
2114
+ expect(result.keys).not.toContain('page-low')
2115
+ })
2116
+ })
2117
+ })
2118
+
2119
+ describe('Garbage Collection', () => {
2120
+ describe('garbageCollect()', () => {
2121
+ it('should remove orphaned entries from storage index', async () => {
2122
+ mockDOStorage.get.mockResolvedValue(undefined) // Nothing in warm tier
2123
+ mockR2Layer.get.mockResolvedValue(null) // Nothing in cold tier
2124
+
2125
+ const orchestrator = createTieredStorageOrchestrator({
2126
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2127
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2128
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2129
+ })
2130
+
2131
+ // Manually add orphaned entry to index
2132
+ // @ts-expect-error - Accessing private for test setup
2133
+ orchestrator.storageIndex?.set('orphaned-page', {
2134
+ key: 'orphaned-page', tier: 'warm', size: 100, accessCount: 1,
2135
+ lastAccess: Date.now(), created: Date.now(), modified: Date.now(), dirty: false
2136
+ })
2137
+
2138
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2139
+ const result = await orchestrator.garbageCollect()
2140
+
2141
+ expect(result.removedEntries).toBe(1)
2142
+ expect(orchestrator.hasInIndex('orphaned-page')).toBe(false)
2143
+ })
2144
+
2145
+ it('should compact cold tier by removing duplicates', async () => {
2146
+ const orchestrator = createTieredStorageOrchestrator({
2147
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2148
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2149
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2150
+ })
2151
+
2152
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2153
+ const result = await orchestrator.garbageCollect({ compactCold: true })
2154
+
2155
+ expect(result.compactionStats).toBeDefined()
2156
+ expect(result.compactionStats.bytesReclaimed).toBeGreaterThanOrEqual(0)
2157
+ })
2158
+ })
2159
+ })
2160
+
2161
+ describe('Rate Limiting', () => {
2162
+ describe('tier operation rate limiting', () => {
2163
+ it('should respect rate limits for R2 operations', async () => {
2164
+ const data = new Uint8Array([1, 2, 3])
2165
+ mockR2Layer.get.mockResolvedValue(data)
2166
+
2167
+ const orchestrator = createTieredStorageOrchestrator({
2168
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2169
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2170
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2171
+ // @ts-expect-error - Config option doesn't exist yet (RED test)
2172
+ rateLimits: {
2173
+ cold: {
2174
+ readsPerSecond: 10,
2175
+ writesPerSecond: 5,
2176
+ },
2177
+ },
2178
+ })
2179
+
2180
+ const startTime = Date.now()
2181
+
2182
+ // Attempt 20 reads rapidly
2183
+ const promises = []
2184
+ for (let i = 0; i < 20; i++) {
2185
+ promises.push(orchestrator.read(`page-${i}`))
2186
+ }
2187
+
2188
+ await Promise.all(promises)
2189
+ const endTime = Date.now()
2190
+
2191
+ // Should have taken at least 1 second due to rate limiting (20 reads at 10/sec)
2192
+ expect(endTime - startTime).toBeGreaterThanOrEqual(1000)
2193
+ })
2194
+ })
2195
+ })
2196
+
2197
+ describe('Compression', () => {
2198
+ describe('compressed storage', () => {
2199
+ it('should compress data before storing in cold tier', async () => {
2200
+ const orchestrator = createTieredStorageOrchestrator({
2201
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2202
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2203
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2204
+ // @ts-expect-error - Config option doesn't exist yet (RED test)
2205
+ compression: {
2206
+ enabled: true,
2207
+ algorithm: 'gzip',
2208
+ minSizeBytes: 100,
2209
+ },
2210
+ })
2211
+
2212
+ const largeData = new Uint8Array(1000).fill(65) // Repetitive data compresses well
2213
+
2214
+ await orchestrator.write('page-compressible', largeData, { tier: 'cold' })
2215
+
2216
+ // The data stored in R2 should be compressed
2217
+ expect(mockR2Layer.put).toHaveBeenCalledWith(
2218
+ 'page-compressible',
2219
+ expect.any(Uint8Array),
2220
+ expect.objectContaining({
2221
+ customMetadata: expect.objectContaining({
2222
+ compressed: 'true',
2223
+ originalSize: '1000',
2224
+ }),
2225
+ })
2226
+ )
2227
+
2228
+ // The stored data should be smaller than original
2229
+ const storedData = mockR2Layer.put.mock.calls[0][1] as Uint8Array
2230
+ expect(storedData.length).toBeLessThan(largeData.length)
2231
+ })
2232
+
2233
+ it('should decompress data when reading from cold tier', async () => {
2234
+ // Mock compressed data
2235
+ const originalData = new Uint8Array(1000).fill(65)
2236
+ const compressedData = new Uint8Array([/* simulated compressed data */])
2237
+
2238
+ mockCacheLayer.get.mockResolvedValue(null)
2239
+ mockDOStorage.get.mockResolvedValue(undefined)
2240
+ mockR2Layer.get.mockResolvedValue(compressedData)
2241
+ // @ts-expect-error - Mock method doesn't have full typing
2242
+ mockR2Layer.head = vi.fn().mockResolvedValue({
2243
+ customMetadata: { compressed: 'true', originalSize: '1000' },
2244
+ })
2245
+
2246
+ const orchestrator = createTieredStorageOrchestrator({
2247
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2248
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2249
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2250
+ // @ts-expect-error - Config option doesn't exist yet (RED test)
2251
+ compression: {
2252
+ enabled: true,
2253
+ algorithm: 'gzip',
2254
+ },
2255
+ })
2256
+
2257
+ // @ts-expect-error - Method should auto-decompress
2258
+ const result = await orchestrator.read('page-compressed', { decompress: true })
2259
+
2260
+ expect(result.data?.length).toBe(1000)
2261
+ })
2262
+ })
2263
+ })
2264
+
2265
+ describe('Tier Pinning', () => {
2266
+ describe('pinToTier()', () => {
2267
+ it('should prevent data from being demoted', async () => {
2268
+ mockCacheLayer.delete.mockResolvedValue(true)
2269
+
2270
+ const orchestrator = createTieredStorageOrchestrator({
2271
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2272
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2273
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2274
+ hotTtlMs: 0, // Immediate expiry
2275
+ autoDemote: true,
2276
+ })
2277
+
2278
+ await orchestrator.write('pinned-page', new Uint8Array([1, 2, 3]), { tier: 'hot' })
2279
+
2280
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2281
+ orchestrator.pinToTier('pinned-page', 'hot')
2282
+
2283
+ // Run demotion cycle
2284
+ const events = await orchestrator.runDemotionCycle()
2285
+
2286
+ // Pinned page should NOT have been demoted
2287
+ expect(events.find(e => e.key === 'pinned-page')).toBeUndefined()
2288
+ expect(orchestrator.getTier('pinned-page')).toBe('hot')
2289
+ })
2290
+ })
2291
+
2292
+ describe('unpinFromTier()', () => {
2293
+ it('should allow data to be demoted again', async () => {
2294
+ mockCacheLayer.delete.mockResolvedValue(true)
2295
+
2296
+ const orchestrator = createTieredStorageOrchestrator({
2297
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2298
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2299
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2300
+ hotTtlMs: 0,
2301
+ autoDemote: true,
2302
+ })
2303
+
2304
+ await orchestrator.write('page-1', new Uint8Array([1, 2, 3]), { tier: 'hot' })
2305
+
2306
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2307
+ orchestrator.pinToTier('page-1', 'hot')
2308
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2309
+ orchestrator.unpinFromTier('page-1')
2310
+
2311
+ const events = await orchestrator.runDemotionCycle()
2312
+
2313
+ // Should now be demoted
2314
+ expect(events.find(e => e.key === 'page-1')).toBeDefined()
2315
+ })
2316
+ })
2317
+ })
2318
+
2319
+ describe('Cost Optimization', () => {
2320
+ describe('getCostBreakdown()', () => {
2321
+ it('should provide detailed cost breakdown by operation type', async () => {
2322
+ const orchestrator = createTieredStorageOrchestrator({
2323
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2324
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2325
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2326
+ // @ts-expect-error - Config doesn't exist yet
2327
+ metrics: {
2328
+ trackCost: true,
2329
+ costConfig: {
2330
+ r2StoragePerGBMonth: 0.015,
2331
+ r2ClassAOperations: 0.0000045,
2332
+ r2ClassBOperations: 0.00000036,
2333
+ doStoragePerGBMonth: 0.20,
2334
+ cacheOperations: 0,
2335
+ },
2336
+ },
2337
+ })
2338
+
2339
+ // Simulate some operations
2340
+ await orchestrator.write('page-1', new Uint8Array(1024 * 1024), { tier: 'cold' })
2341
+ await orchestrator.write('page-2', new Uint8Array(1024 * 1024), { tier: 'warm' })
2342
+
2343
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2344
+ const costBreakdown = orchestrator.getCostBreakdown()
2345
+
2346
+ expect(costBreakdown).toMatchObject({
2347
+ storage: {
2348
+ r2: expect.any(Number),
2349
+ do: expect.any(Number),
2350
+ cache: expect.any(Number),
2351
+ },
2352
+ operations: {
2353
+ r2Writes: expect.any(Number),
2354
+ r2Reads: expect.any(Number),
2355
+ doWrites: expect.any(Number),
2356
+ doReads: expect.any(Number),
2357
+ },
2358
+ total: expect.any(Number),
2359
+ projectedMonthly: expect.any(Number),
2360
+ })
2361
+ })
2362
+ })
2363
+
2364
+ describe('optimizeTierPlacement()', () => {
2365
+ it('should suggest optimal tier placement based on access patterns', async () => {
2366
+ const orchestrator = createTieredStorageOrchestrator({
2367
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2368
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2369
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2370
+ })
2371
+
2372
+ // Setup: add entries with varying access patterns
2373
+ // @ts-expect-error - Accessing private for test
2374
+ orchestrator.storageIndex?.set('hot-candidate', {
2375
+ key: 'hot-candidate', tier: 'cold', size: 1000, accessCount: 100,
2376
+ lastAccess: Date.now(), created: Date.now() - 3600000, modified: Date.now(), dirty: false
2377
+ })
2378
+ // @ts-expect-error - Accessing private for test
2379
+ orchestrator.storageIndex?.set('cold-candidate', {
2380
+ key: 'cold-candidate', tier: 'warm', size: 10000, accessCount: 1,
2381
+ lastAccess: Date.now() - 86400000, created: Date.now() - 86400000 * 7, modified: Date.now() - 86400000, dirty: false
2382
+ })
2383
+
2384
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2385
+ const suggestions = await orchestrator.optimizeTierPlacement()
2386
+
2387
+ expect(suggestions.promotions).toContainEqual(
2388
+ expect.objectContaining({ key: 'hot-candidate', suggestedTier: 'warm' })
2389
+ )
2390
+ expect(suggestions.demotions).toContainEqual(
2391
+ expect.objectContaining({ key: 'cold-candidate', suggestedTier: 'cold' })
2392
+ )
2393
+ expect(suggestions.estimatedSavings).toBeGreaterThanOrEqual(0)
2394
+ })
2395
+ })
2396
+ })
2397
+
2398
+ describe('Durability and Replication', () => {
2399
+ describe('ensureDurability()', () => {
2400
+ it('should verify data exists in cold tier', async () => {
2401
+ const data = new Uint8Array([1, 2, 3])
2402
+ mockDOStorage.get.mockResolvedValue(data)
2403
+ mockR2Layer.has.mockResolvedValue(true)
2404
+
2405
+ const orchestrator = createTieredStorageOrchestrator({
2406
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2407
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2408
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2409
+ })
2410
+
2411
+ await orchestrator.write('page-1', data, { tier: 'warm' })
2412
+
2413
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2414
+ const result = await orchestrator.ensureDurability('page-1')
2415
+
2416
+ expect(result.durable).toBe(true)
2417
+ expect(result.locations).toContain('cold')
2418
+ })
2419
+
2420
+ it('should copy data to cold tier if not present', async () => {
2421
+ const data = new Uint8Array([1, 2, 3])
2422
+ mockDOStorage.get.mockResolvedValue(data)
2423
+ mockR2Layer.has.mockResolvedValue(false)
2424
+
2425
+ const orchestrator = createTieredStorageOrchestrator({
2426
+ cacheLayer: mockCacheLayer as unknown as CacheLayer,
2427
+ doStorage: mockDOStorage as unknown as DurableObjectStorage,
2428
+ r2Layer: mockR2Layer as unknown as R2StorageLayer,
2429
+ })
2430
+
2431
+ await orchestrator.write('page-1', data, { tier: 'warm' })
2432
+
2433
+ // @ts-expect-error - Method doesn't exist yet (RED test)
2434
+ const result = await orchestrator.ensureDurability('page-1', { copyToCold: true })
2435
+
2436
+ expect(result.durable).toBe(true)
2437
+ expect(mockR2Layer.put).toHaveBeenCalledWith('page-1', data, expect.any(Object))
2438
+ })
2439
+ })
2440
+ })
2441
+ })