@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,2081 @@
1
+ /**
2
+ * TieredStorageOrchestrator - Unified Tiering System
3
+ *
4
+ * Orchestrates the complete tiered storage system with:
5
+ * - HOT tier: Cloudflare Cache API (FREE, sync-fast)
6
+ * - WARM tier: Durable Object SQLite blobs (persistent, sync)
7
+ * - COLD tier: R2 object storage (cheap, async)
8
+ *
9
+ * ## Features
10
+ * - Unified read/write interface across all tiers
11
+ * - Automatic promotion based on access patterns
12
+ * - Automatic demotion based on TTL/LRU policies
13
+ * - Cost-optimized tier selection
14
+ * - Comprehensive statistics and monitoring
15
+ * - Storage index for fast tier location lookups
16
+ * - DO lifecycle hooks (hibernate/wake)
17
+ * - Circuit breaker pattern for tier failures
18
+ * - Integrated metrics aggregation
19
+ * - Bounded storage index with LRU/LFU eviction
20
+ * - Detailed performance metrics and memory tracking
21
+ *
22
+ * ## Performance Characteristics
23
+ *
24
+ * ### Storage Index Operations
25
+ *
26
+ * | Operation | Time Complexity | Typical Latency |
27
+ * |---------------|-----------------|-----------------|
28
+ * | Lookup (get) | O(1) average | 50-100ns |
29
+ * | Update (set) | O(1) amortized | 100-200ns |
30
+ * | Delete | O(1) | 50-100ns |
31
+ * | Eviction | O(n*k) | 1-10ms/batch |
32
+ * | Maintenance | O(n * storage) | 100ms-10s |
33
+ *
34
+ * Where:
35
+ * - n = total index entries
36
+ * - k = eviction batch size (typically 10-20)
37
+ *
38
+ * ### Memory Usage
39
+ *
40
+ * The storage index uses approximately:
41
+ * - 120 bytes per entry (base overhead)
42
+ * - 2 bytes per character in key (JS UTF-16 strings)
43
+ * - ~10% additional for Map internal structures
44
+ *
45
+ * **Example Memory Usage:**
46
+ * | Entries | Avg Key Length | Estimated Memory |
47
+ * |---------|----------------|------------------|
48
+ * | 1,000 | 20 chars | ~160 KB |
49
+ * | 10,000 | 20 chars | ~1.6 MB |
50
+ * | 100,000 | 20 chars | ~16 MB |
51
+ * | 10,000 | 100 chars | ~3.2 MB |
52
+ *
53
+ * ### Eviction Policies
54
+ *
55
+ * - **LRU (default)**: Evicts least recently accessed entries first.
56
+ * Best for: Most workloads with temporal locality.
57
+ *
58
+ * - **LFU**: Evicts least frequently accessed entries first.
59
+ * Best for: Workloads with stable hot sets.
60
+ *
61
+ * - **FIFO**: Evicts oldest entries by creation time.
62
+ * Best for: Predictable eviction patterns, debugging.
63
+ *
64
+ * ### Hot Path Optimizations
65
+ *
66
+ * 1. **Single-entry lookup cache**: Avoids redundant Map lookups when
67
+ * the same key is accessed multiple times in quick succession
68
+ * (e.g., read -> check promotion -> update index).
69
+ *
70
+ * 2. **Partial selection for eviction**: Uses O(n*k) partial selection
71
+ * instead of O(n log n) full sort when evicting k entries from n.
72
+ *
73
+ * 3. **Batch eviction**: Amortizes eviction overhead across multiple
74
+ * entries (configurable via indexEvictionBatchSize).
75
+ *
76
+ * ### Configuration Guidelines
77
+ *
78
+ * For Cloudflare Workers (128MB limit):
79
+ * ```typescript
80
+ * const orchestrator = createTieredStorageOrchestrator({
81
+ * // ... other config
82
+ * maxIndexEntries: 50000, // ~8MB for 20-char keys
83
+ * maxIndexMemoryBytes: 8 * 1024 * 1024, // 8MB hard limit
84
+ * indexEvictionPolicy: 'lru',
85
+ * indexEvictionBatchSize: 100, // Evict 100 at a time
86
+ * indexMemoryWarningThreshold: 0.8, // Warn at 80%
87
+ * })
88
+ * ```
89
+ *
90
+ * @module storage/tiered-orchestrator
91
+ */
92
+
93
+ import type { CacheLayer, CacheLayerStats } from './cache-layer'
94
+ import type { R2StorageLayer, R2StorageLayerStats } from './r2-layer'
95
+ import type { SWRCacheLayer, SWRCacheStats, ExecutionContext } from './swr-cache'
96
+
97
+ // =============================================================================
98
+ // Imported Configuration Constants (from centralized config)
99
+ // =============================================================================
100
+
101
+ import {
102
+ DEFAULT_PAGE_SIZE,
103
+ DEFAULT_COLD_TO_WARM_THRESHOLD,
104
+ DEFAULT_WARM_TO_HOT_THRESHOLD,
105
+ DEFAULT_PROMOTION_WINDOW_MS,
106
+ DEFAULT_HOT_TTL_MS,
107
+ DEFAULT_WARM_TTL_MS,
108
+ DEFAULT_MAX_HOT_PAGES,
109
+ DEFAULT_DO_PREFIX,
110
+ DEFAULT_INDEX_EVICTION_BATCH_SIZE,
111
+ DEFAULT_CIRCUIT_BREAKER_FAILURE_THRESHOLD,
112
+ DEFAULT_CIRCUIT_BREAKER_RESET_TIMEOUT_MS,
113
+ } from '../config/storage'
114
+
115
+ import {
116
+ INDEX_ENTRY_MEMORY_OVERHEAD_BYTES,
117
+ MAP_OVERHEAD_ESTIMATE_PERCENT,
118
+ DEFAULT_INDEX_MEMORY_WARNING_THRESHOLD,
119
+ INDEX_MEMORY_EVICTION_TARGET,
120
+ DEFAULT_AVG_KEY_LENGTH_CHARS,
121
+ } from '../config/memory'
122
+
123
+ import { assertNever } from '../types/utilities'
124
+
125
+ // =============================================================================
126
+ // Local Constant Aliases (for internal use)
127
+ // =============================================================================
128
+
129
+ /** Estimated bytes per index entry */
130
+ const ENTRY_MEMORY_OVERHEAD_BYTES = INDEX_ENTRY_MEMORY_OVERHEAD_BYTES
131
+
132
+ // =============================================================================
133
+ // Timing Constants (local)
134
+ // =============================================================================
135
+
136
+ /**
137
+ * Nanoseconds per millisecond for performance timing conversions.
138
+ */
139
+ const NS_PER_MS = 1e6
140
+
141
+ // =============================================================================
142
+ // Types
143
+ // =============================================================================
144
+ /**
145
+ * Storage tier identifiers
146
+ * - hot: Cloudflare Cache API (FREE, fastest, ephemeral)
147
+ * - warm: Durable Object Storage (fast, persistent, limited)
148
+ * - cold: R2 (durable, unlimited, slower)
149
+ */
150
+ export type StorageTier = 'hot' | 'warm' | 'cold'
151
+
152
+ /**
153
+ * Tier health status
154
+ */
155
+ export type TierHealth = 'healthy' | 'degraded' | 'unavailable'
156
+
157
+ /**
158
+ * Circuit breaker state
159
+ */
160
+ export type CircuitBreakerState = 'closed' | 'open' | 'half-open'
161
+
162
+ /**
163
+ * Circuit breaker configuration
164
+ */
165
+ export interface CircuitBreakerConfig {
166
+ enabled: boolean
167
+ failureThreshold: number
168
+ resetTimeoutMs?: number
169
+ fallbackEnabled?: boolean
170
+ }
171
+
172
+ /**
173
+ * Circuit breaker state info
174
+ */
175
+ export interface CircuitBreakerStateInfo {
176
+ state: CircuitBreakerState
177
+ failures: number
178
+ lastFailure?: number
179
+ lastSuccess?: number
180
+ openedAt?: number
181
+ }
182
+
183
+ /**
184
+ * Entry in the storage index tracking data locations
185
+ */
186
+ export interface StorageIndexEntry {
187
+ key: string
188
+ tier: StorageTier
189
+ size: number
190
+ lastAccess: number
191
+ accessCount: number
192
+ created: number
193
+ modified: number
194
+ dirty: boolean
195
+ }
196
+
197
+ /**
198
+ * Hibernate state for DO lifecycle
199
+ */
200
+ export interface HibernateState {
201
+ hotKeys: string[]
202
+ indexSnapshot: Map<string, StorageIndexEntry>
203
+ timestamp: number
204
+ }
205
+
206
+ /**
207
+ * Result of hibernate operation
208
+ */
209
+ export interface HibernateResult {
210
+ success: boolean
211
+ persistedCount: number
212
+ syncedToCold: number
213
+ hotKeys: string[]
214
+ indexSnapshot: Map<string, StorageIndexEntry>
215
+ timestamp: number
216
+ }
217
+
218
+ /**
219
+ * Options for wake operation
220
+ */
221
+ export interface WakeOptions {
222
+ prefetchThreshold?: number
223
+ validateIndex?: boolean
224
+ }
225
+
226
+ /**
227
+ * Result of wake operation
228
+ */
229
+ export interface WakeResult {
230
+ restoredCount: number
231
+ prefetchedCount: number
232
+ indexValidated: boolean
233
+ indexRepairs: number
234
+ }
235
+
236
+ /**
237
+ * Alarm task type
238
+ */
239
+ export type AlarmType = 'demotion' | 'sync'
240
+
241
+ /**
242
+ * Alarm configuration
243
+ */
244
+ export interface AlarmConfig {
245
+ type: AlarmType
246
+ delayMs: number
247
+ skipIfExists?: boolean
248
+ rescheduleMs?: number
249
+ }
250
+
251
+ /**
252
+ * Result of alarm handling
253
+ */
254
+ export interface AlarmResult {
255
+ demotionCount?: number
256
+ syncedCount?: number
257
+ nextAlarmMs?: number
258
+ }
259
+
260
+ /**
261
+ * Aggregated metrics from all components
262
+ */
263
+ export interface AggregatedMetrics {
264
+ timestamp: number
265
+ period: string
266
+ tiers: {
267
+ hot: { reads: number; writes: number; hitRatio: number; bytesRead: number; bytesWritten: number }
268
+ warm: { reads: number; writes: number; bytesStored: number; bytesRead: number; bytesWritten: number }
269
+ cold: { reads: number; writes: number; bytesStored: number; bytesRead: number; bytesWritten: number }
270
+ }
271
+ migrations: { promotions: number; demotions: number; failures: number }
272
+ circuitBreakers: Record<StorageTier, CircuitBreakerStateInfo>
273
+ latency?: { p50: number; p95: number; p99: number }
274
+ estimatedCost?: { r2: number; do: number; cache: number; total: number }
275
+ errors: { totalErrors: number; byTier: Record<StorageTier, number> }
276
+ }
277
+
278
+ /**
279
+ * Auto-promoter configuration
280
+ */
281
+ export interface AutoPromoterConfig {
282
+ coldToWarm: { accessThreshold: number; timeWindowMs?: number }
283
+ warmToHot: { accessThreshold: number; timeWindowMs?: number }
284
+ }
285
+
286
+ /**
287
+ * Auto-demoter configuration
288
+ */
289
+ export interface AutoDemoterConfig {
290
+ hotToWarm: { ttlMs: number; maxEntries?: number }
291
+ warmToCold: { ttlMs: number; maxEntries?: number }
292
+ }
293
+
294
+ /**
295
+ * VFS interface for PGLite integration
296
+ */
297
+ export interface VFSInterface {
298
+ readPage: (pageId: string) => Promise<Uint8Array | null>
299
+ writePage: (pageId: string, data: Uint8Array) => Promise<void>
300
+ deletePage: (pageId: string) => Promise<void>
301
+ }
302
+
303
+ /**
304
+ * Metrics configuration
305
+ */
306
+ export interface MetricsConfig {
307
+ trackLatency?: boolean
308
+ trackCost?: boolean
309
+ costConfig?: {
310
+ r2StoragePerGBMonth: number
311
+ r2ClassAOperations: number
312
+ r2ClassBOperations: number
313
+ doStoragePerGBMonth: number
314
+ cacheOperations: number
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Observable error with context
320
+ */
321
+ export interface ObservableError {
322
+ context: string
323
+ error: Error
324
+ tier?: StorageTier
325
+ key?: string
326
+ timestamp: number
327
+ retryCount?: number
328
+ correlationId?: string
329
+ }
330
+
331
+ /**
332
+ * Error observer callback type
333
+ */
334
+ export type ErrorObserver = (error: ObservableError) => void
335
+
336
+ /**
337
+ * Circuit breaker change event
338
+ */
339
+ export interface CircuitBreakerChangeEvent {
340
+ tier: StorageTier
341
+ state: CircuitBreakerState
342
+ failures: number
343
+ error?: Error | undefined
344
+ }
345
+
346
+ /**
347
+ * Circuit breaker change observer callback type
348
+ */
349
+ export type CircuitBreakerChangeObserver = (event: CircuitBreakerChangeEvent) => void
350
+
351
+ /**
352
+ * Logger interface for debug logging
353
+ */
354
+ export interface Logger {
355
+ debug: (msg: string) => void
356
+ info: (msg: string) => void
357
+ warn: (msg: string) => void
358
+ error: (msg: string) => void
359
+ }
360
+
361
+ /**
362
+ * Demotion strategy function
363
+ */
364
+ export type DemotionStrategy = (entries: StorageIndexEntry[], count: number) => string[]
365
+
366
+ /**
367
+ * Index eviction policy
368
+ */
369
+ export type IndexEvictionPolicy = 'lru' | 'lfu' | 'fifo'
370
+
371
+ /**
372
+ * Callback for index eviction events
373
+ */
374
+ export type IndexEvictionCallback = (evictedKeys: string[]) => void
375
+
376
+ /**
377
+ * Configuration for the TieredStorageOrchestrator
378
+ */
379
+ export interface TieredOrchestratorConfig {
380
+ cacheLayer?: CacheLayer
381
+ swrCacheLayer?: SWRCacheLayer
382
+ doStorage: DurableObjectStorage
383
+ r2Layer: R2StorageLayer
384
+ pageSize?: number
385
+ coldToWarmThreshold?: number
386
+ warmToHotThreshold?: number
387
+ promotionWindowMs?: number
388
+ hotTtlMs?: number
389
+ warmTtlMs?: number
390
+ maxHotPages?: number
391
+ autoPromote?: boolean
392
+ autoDemote?: boolean
393
+ writeThrough?: boolean
394
+ doPrefix?: string
395
+ circuitBreaker?: CircuitBreakerConfig
396
+ metrics?: MetricsConfig
397
+ logger?: Logger
398
+ // Index bounds configuration
399
+ maxIndexEntries?: number
400
+ maxIndexMemoryBytes?: number
401
+ indexEvictionPolicy?: IndexEvictionPolicy
402
+ indexEvictionBatchSize?: number
403
+ indexMemoryWarningThreshold?: number
404
+ indexMaintenanceIntervalMs?: number
405
+ onIndexEviction?: IndexEvictionCallback
406
+ }
407
+
408
+ /**
409
+ * Options for read operations
410
+ */
411
+ export interface ReadOptions {
412
+ skipPromotion?: boolean
413
+ skipTracking?: boolean
414
+ ctx?: ExecutionContext
415
+ }
416
+
417
+ /**
418
+ * Options for write operations
419
+ */
420
+ export interface WriteOptions {
421
+ tier?: StorageTier
422
+ metadata?: Record<string, string>
423
+ writeThrough?: boolean
424
+ skipIndex?: boolean
425
+ }
426
+
427
+ /**
428
+ * Result of a read operation with tier information
429
+ */
430
+ export interface ReadResult {
431
+ data: Uint8Array | null
432
+ tier?: StorageTier | undefined
433
+ stale?: boolean | undefined
434
+ revalidating?: boolean | undefined
435
+ fallback?: boolean | undefined
436
+ }
437
+
438
+ /**
439
+ * Promotion/demotion event
440
+ */
441
+ export interface TierMigrationEvent {
442
+ key: string
443
+ fromTier: StorageTier
444
+ toTier: StorageTier
445
+ success: boolean
446
+ timestamp: number
447
+ error?: string
448
+ reason: 'access_pattern' | 'ttl_expired' | 'lru_eviction' | 'manual' | 'write_through'
449
+ }
450
+
451
+ export type TierMigrationCallback = (event: TierMigrationEvent) => void
452
+
453
+ /**
454
+ * Performance metrics for index operations
455
+ *
456
+ * Tracks timing and operation counts for performance analysis:
457
+ * - lookupCount: Total number of index lookups (get operations)
458
+ * - updateCount: Total number of index updates (set/modify operations)
459
+ * - evictionCount: Total number of entries evicted
460
+ * - avgLookupTimeNs: Average lookup time in nanoseconds
461
+ * - avgUpdateTimeNs: Average update time in nanoseconds
462
+ * - evictionTimeNs: Total time spent on eviction in nanoseconds
463
+ * - maintenanceTimeNs: Total time spent on maintenance in nanoseconds
464
+ * - peakEntries: Peak number of entries observed
465
+ * - peakMemoryBytes: Peak memory usage observed
466
+ */
467
+ export interface IndexPerformanceMetrics {
468
+ // Operation counts
469
+ lookupCount: number
470
+ updateCount: number
471
+ evictionCount: number
472
+ // Timing metrics (nanoseconds for precision)
473
+ totalLookupTimeNs: number
474
+ totalUpdateTimeNs: number
475
+ totalEvictionTimeNs: number
476
+ totalMaintenanceTimeNs: number
477
+ // Derived metrics (computed on request)
478
+ avgLookupTimeNs: number
479
+ avgUpdateTimeNs: number
480
+ // Peak tracking
481
+ peakEntries: number
482
+ peakMemoryBytes: number
483
+ // Efficiency metrics
484
+ evictionBatches: number
485
+ entriesPerEvictionBatch: number
486
+ // Hot path metrics
487
+ cacheHitCount: number
488
+ cacheMissCount: number
489
+ }
490
+
491
+ /**
492
+ * Statistics for the orchestrator
493
+ */
494
+ export interface OrchestratorStats {
495
+ index: {
496
+ totalEntries: number
497
+ byTier: Record<StorageTier, number>
498
+ totalSize: number
499
+ dirtyEntries: number
500
+ memoryBytes: number
501
+ /** Performance metrics for index operations */
502
+ performance: IndexPerformanceMetrics
503
+ }
504
+ hot: CacheLayerStats | SWRCacheStats
505
+ warm: {
506
+ reads: number
507
+ writes: number
508
+ deletes: number
509
+ bytesRead: number
510
+ bytesWritten: number
511
+ }
512
+ cold: R2StorageLayerStats
513
+ promotions: {
514
+ coldToWarm: number
515
+ warmToHot: number
516
+ failed: number
517
+ }
518
+ demotions: {
519
+ hotToWarm: number
520
+ warmToCold: number
521
+ failed: number
522
+ }
523
+ health: Record<StorageTier, TierHealth>
524
+ errors: Record<StorageTier, number>
525
+ }
526
+
527
+ // Internal config type with all required fields
528
+ interface InternalConfig {
529
+ pageSize: number
530
+ coldToWarmThreshold: number
531
+ warmToHotThreshold: number
532
+ promotionWindowMs: number
533
+ hotTtlMs: number
534
+ warmTtlMs: number
535
+ maxHotPages: number
536
+ autoPromote: boolean
537
+ autoDemote: boolean
538
+ writeThrough: boolean
539
+ doPrefix: string
540
+ circuitBreaker?: CircuitBreakerConfig | undefined
541
+ metrics?: MetricsConfig | undefined
542
+ logger?: Logger | undefined
543
+ maxIndexEntries?: number | undefined
544
+ maxIndexMemoryBytes?: number | undefined
545
+ indexEvictionPolicy: IndexEvictionPolicy
546
+ indexEvictionBatchSize: number
547
+ indexMemoryWarningThreshold: number
548
+ indexMaintenanceIntervalMs?: number | undefined
549
+ onIndexEviction?: IndexEvictionCallback | undefined
550
+ }
551
+
552
+ /**
553
+ * TieredStorageOrchestrator - Unified Tiering System
554
+ *
555
+ * ## Performance Characteristics
556
+ *
557
+ * ### Index Operations
558
+ * - **Lookup (get)**: O(1) average via Map, ~50-100ns typical
559
+ * - **Update (set/modify)**: O(1) average for existing keys, O(1) amortized for new keys
560
+ * - **Eviction**: O(n*k) where n=index size, k=batch size; optimized for k << n
561
+ *
562
+ * ### Memory Usage
563
+ * - **Per-entry overhead**: ~120 bytes base + 2 bytes per character in key
564
+ * - **Example**: 10,000 entries with 20-char keys = ~1.6MB
565
+ * - **Warning threshold**: Configurable, default 80% of maxIndexMemoryBytes
566
+ *
567
+ * ### Eviction Policies
568
+ * - **LRU (default)**: Evicts least recently accessed entries
569
+ * - **LFU**: Evicts least frequently accessed entries
570
+ * - **FIFO**: Evicts oldest entries by creation time
571
+ *
572
+ * ### Hot Path Optimizations
573
+ * - Index entry caching for repeated lookups within promotion checks
574
+ * - Batch eviction to amortize overhead across multiple entries
575
+ * - Partial selection algorithm for eviction (avoids full sort when k << n)
576
+ *
577
+ * @class TieredStorageOrchestrator
578
+ */
579
+ export class TieredStorageOrchestrator {
580
+ private cacheLayer?: CacheLayer | undefined
581
+ private swrCacheLayer?: SWRCacheLayer | undefined
582
+ private doStorage: DurableObjectStorage
583
+ private r2Layer: R2StorageLayer
584
+ private config: InternalConfig
585
+
586
+ private storageIndex: Map<string, StorageIndexEntry> = new Map()
587
+ private indexMemoryBytes: number = 0
588
+ private maintenanceTimer?: ReturnType<typeof setInterval> | undefined
589
+ private memoryWarningEmitted: boolean = false
590
+
591
+ /**
592
+ * Estimated memory per index entry (key string avg ~20 chars + StorageIndexEntry object)
593
+ *
594
+ * Breakdown:
595
+ * - Map entry overhead: ~40 bytes (key reference + value reference + internal structure)
596
+ * - StorageIndexEntry object: ~80 bytes (8 bytes per field * 9 fields + object overhead)
597
+ * - Total base: ~120 bytes per entry
598
+ * - Key string: 2 bytes per character (JS strings are UTF-16)
599
+ */
600
+ private static readonly ENTRY_MEMORY_OVERHEAD = ENTRY_MEMORY_OVERHEAD_BYTES
601
+
602
+ // Performance metrics tracking
603
+ private performanceMetrics = {
604
+ lookupCount: 0,
605
+ updateCount: 0,
606
+ evictionCount: 0,
607
+ totalLookupTimeNs: 0,
608
+ totalUpdateTimeNs: 0,
609
+ totalEvictionTimeNs: 0,
610
+ totalMaintenanceTimeNs: 0,
611
+ peakEntries: 0,
612
+ peakMemoryBytes: 0,
613
+ evictionBatches: 0,
614
+ cacheHitCount: 0,
615
+ cacheMissCount: 0,
616
+ }
617
+
618
+ // Cache for hot path optimization - stores recently looked up entry for promotion check
619
+ private lastLookupKey: string | null = null
620
+ private lastLookupEntry: StorageIndexEntry | null = null
621
+
622
+ private warmStats = { reads: 0, writes: 0, deletes: 0, bytesRead: 0, bytesWritten: 0 }
623
+ private promotionStats = { coldToWarm: 0, warmToHot: 0, failed: 0 }
624
+ private demotionStats = { hotToWarm: 0, warmToCold: 0, failed: 0 }
625
+ private tierHealth: Record<StorageTier, TierHealth> = { hot: 'healthy', warm: 'healthy', cold: 'healthy' }
626
+
627
+ private circuitBreakers: Record<StorageTier, CircuitBreakerStateInfo> = {
628
+ hot: { state: 'closed', failures: 0 },
629
+ warm: { state: 'closed', failures: 0 },
630
+ cold: { state: 'closed', failures: 0 },
631
+ }
632
+
633
+ private latencyMeasurements: number[] = []
634
+ private demotionStrategies: Partial<Record<StorageTier, DemotionStrategy>> = {}
635
+ private autoPromoterConfig: AutoPromoterConfig = { coldToWarm: { accessThreshold: DEFAULT_COLD_TO_WARM_THRESHOLD }, warmToHot: { accessThreshold: DEFAULT_WARM_TO_HOT_THRESHOLD } }
636
+ private autoDemoterConfig: AutoDemoterConfig = { hotToWarm: { ttlMs: DEFAULT_HOT_TTL_MS }, warmToCold: { ttlMs: DEFAULT_WARM_TTL_MS } }
637
+ private migrationCallbacks: TierMigrationCallback[] = []
638
+ private errorObservers: ErrorObserver[] = []
639
+ private circuitBreakerObservers: CircuitBreakerChangeObserver[] = []
640
+ private errorCounts: Record<StorageTier, number> = { hot: 0, warm: 0, cold: 0 }
641
+ private keyRetryCounters: Map<string, number> = new Map()
642
+ private correlationCounter = 0
643
+ private eventSubscribers: Array<{ callback: TierMigrationCallback; filter?: { type?: 'promotion' | 'demotion' } | undefined }> = []
644
+ private entryPriorities: Map<string, 'low' | 'normal' | 'high'> = new Map()
645
+ private pinnedEntries: Set<string> = new Set()
646
+ private memoryLimits: Partial<Record<StorageTier, number>> = {}
647
+
648
+ constructor(config: TieredOrchestratorConfig) {
649
+ this.cacheLayer = config.cacheLayer
650
+ this.swrCacheLayer = config.swrCacheLayer
651
+ this.doStorage = config.doStorage
652
+ this.r2Layer = config.r2Layer
653
+ this.config = {
654
+ pageSize: config.pageSize ?? DEFAULT_PAGE_SIZE,
655
+ coldToWarmThreshold: config.coldToWarmThreshold ?? DEFAULT_COLD_TO_WARM_THRESHOLD,
656
+ warmToHotThreshold: config.warmToHotThreshold ?? DEFAULT_WARM_TO_HOT_THRESHOLD,
657
+ promotionWindowMs: config.promotionWindowMs ?? DEFAULT_PROMOTION_WINDOW_MS,
658
+ hotTtlMs: config.hotTtlMs ?? DEFAULT_HOT_TTL_MS,
659
+ warmTtlMs: config.warmTtlMs ?? DEFAULT_WARM_TTL_MS,
660
+ maxHotPages: config.maxHotPages ?? DEFAULT_MAX_HOT_PAGES,
661
+ autoPromote: config.autoPromote ?? true,
662
+ autoDemote: config.autoDemote ?? true,
663
+ writeThrough: config.writeThrough ?? false,
664
+ doPrefix: config.doPrefix ?? DEFAULT_DO_PREFIX,
665
+ circuitBreaker: config.circuitBreaker,
666
+ metrics: config.metrics,
667
+ logger: config.logger,
668
+ // Index bounds configuration
669
+ maxIndexEntries: config.maxIndexEntries,
670
+ maxIndexMemoryBytes: config.maxIndexMemoryBytes,
671
+ indexEvictionPolicy: config.indexEvictionPolicy ?? 'lru',
672
+ indexEvictionBatchSize: config.indexEvictionBatchSize ?? DEFAULT_INDEX_EVICTION_BATCH_SIZE,
673
+ indexMemoryWarningThreshold: config.indexMemoryWarningThreshold ?? DEFAULT_INDEX_MEMORY_WARNING_THRESHOLD,
674
+ indexMaintenanceIntervalMs: config.indexMaintenanceIntervalMs,
675
+ onIndexEviction: config.onIndexEviction,
676
+ }
677
+ this.autoPromoterConfig = {
678
+ coldToWarm: { accessThreshold: this.config.coldToWarmThreshold },
679
+ warmToHot: { accessThreshold: this.config.warmToHotThreshold, timeWindowMs: this.config.promotionWindowMs },
680
+ }
681
+ this.autoDemoterConfig = {
682
+ hotToWarm: { ttlMs: this.config.hotTtlMs },
683
+ warmToCold: { ttlMs: this.config.warmTtlMs },
684
+ }
685
+ // Start maintenance timer if configured
686
+ if (this.config.indexMaintenanceIntervalMs) {
687
+ this.startIndexMaintenance()
688
+ }
689
+ }
690
+
691
+ onMigration(callback: TierMigrationCallback): void { this.migrationCallbacks.push(callback) }
692
+ onError(callback: ErrorObserver): void { this.errorObservers.push(callback) }
693
+ onCircuitBreakerChange(callback: CircuitBreakerChangeObserver): void { this.circuitBreakerObservers.push(callback) }
694
+
695
+ private notifyError(error: ObservableError): void {
696
+ if (error.tier) this.errorCounts[error.tier]++
697
+ if (this.config.logger) {
698
+ if (error.context === 'tier_read_failure' && error.tier) {
699
+ this.config.logger.warn(`${error.tier} tier read failed: ${error.error.message}${error.key ? ` (key: ${error.key})` : ''}`)
700
+ } else {
701
+ this.config.logger.warn(`${error.context}: ${error.error.message}${error.key ? ` (key: ${error.key})` : ''}${error.tier ? ` (tier: ${error.tier})` : ''}`)
702
+ }
703
+ }
704
+ for (const callback of this.errorObservers) {
705
+ try { callback(error) } catch { /* ignore observer errors */ }
706
+ }
707
+ }
708
+
709
+ private notifyCircuitBreakerChange(event: CircuitBreakerChangeEvent): void {
710
+ this.config.logger?.warn(`Circuit breaker ${event.tier} changed to ${event.state} (failures: ${event.failures})`)
711
+ for (const callback of this.circuitBreakerObservers) {
712
+ try { callback(event) } catch { /* ignore observer errors */ }
713
+ }
714
+ }
715
+
716
+ private notifyMigration(event: TierMigrationEvent): void {
717
+ for (const callback of this.migrationCallbacks) {
718
+ try { callback(event) } catch (e) {
719
+ const error = e instanceof Error ? e : new Error(String(e))
720
+ this.notifyError({
721
+ context: 'migration_callback',
722
+ error,
723
+ key: event.key,
724
+ timestamp: Date.now(),
725
+ })
726
+ }
727
+ }
728
+ // Also notify event subscribers
729
+ if (this.eventSubscribers && this.eventSubscribers.length > 0) {
730
+ const isPromotion = event.toTier === 'hot' || (event.fromTier === 'cold' && event.toTier === 'warm')
731
+ for (const { callback, filter } of this.eventSubscribers) {
732
+ if (filter?.type === 'promotion' && !isPromotion) continue
733
+ if (filter?.type === 'demotion' && isPromotion) continue
734
+ try { callback(event) } catch { /* ignore */ }
735
+ }
736
+ }
737
+ }
738
+
739
+ private doKey(key: string): string { return `${this.config.doPrefix}${key}` }
740
+
741
+ /**
742
+ * High-resolution timer for performance measurement
743
+ * Uses performance.now() when available, falls back to Date.now() * 1e6
744
+ */
745
+ private getNowNs(): number {
746
+ if (typeof performance !== 'undefined' && performance.now) {
747
+ return performance.now() * NS_PER_MS // Convert ms to ns
748
+ }
749
+ return Date.now() * NS_PER_MS
750
+ }
751
+
752
+ /**
753
+ * Update the storage index with entry information
754
+ *
755
+ * Performance characteristics:
756
+ * - O(1) for existing keys (direct Map update)
757
+ * - O(1) amortized for new keys (may trigger eviction)
758
+ * - Tracks timing for performance metrics
759
+ *
760
+ * @param key - The storage key
761
+ * @param tier - The storage tier where data resides
762
+ * @param size - Size of the data in bytes
763
+ * @param dirty - Whether the entry has uncommitted changes
764
+ */
765
+ private updateIndex(key: string, tier: StorageTier, size: number, dirty = false): void {
766
+ const startNs = this.getNowNs()
767
+ this.performanceMetrics.updateCount++
768
+
769
+ const existing = this.storageIndex.get(key)
770
+ const now = Date.now()
771
+ if (existing) {
772
+ existing.tier = tier
773
+ existing.size = size
774
+ existing.lastAccess = now
775
+ existing.accessCount++
776
+ existing.modified = now
777
+ existing.dirty = dirty
778
+ // Invalidate lookup cache if this was the cached entry
779
+ if (this.lastLookupKey === key) {
780
+ this.lastLookupEntry = existing
781
+ }
782
+ } else {
783
+ // Check if we need to evict before adding
784
+ this.enforceIndexBounds()
785
+ const newEntry: StorageIndexEntry = { key, tier, size, lastAccess: now, accessCount: 1, created: now, modified: now, dirty }
786
+ this.storageIndex.set(key, newEntry)
787
+ // Update memory tracking
788
+ this.indexMemoryBytes += TieredStorageOrchestrator.ENTRY_MEMORY_OVERHEAD + key.length * 2
789
+ // Track peak values
790
+ if (this.storageIndex.size > this.performanceMetrics.peakEntries) {
791
+ this.performanceMetrics.peakEntries = this.storageIndex.size
792
+ }
793
+ if (this.indexMemoryBytes > this.performanceMetrics.peakMemoryBytes) {
794
+ this.performanceMetrics.peakMemoryBytes = this.indexMemoryBytes
795
+ }
796
+ // Check memory warning threshold
797
+ this.checkMemoryWarning()
798
+ }
799
+
800
+ this.performanceMetrics.totalUpdateTimeNs += this.getNowNs() - startNs
801
+ }
802
+
803
+ /**
804
+ * Enforce index bounds by evicting entries if necessary
805
+ */
806
+ private enforceIndexBounds(): void {
807
+ // Check entry count limit
808
+ if (this.config.maxIndexEntries && this.storageIndex.size >= this.config.maxIndexEntries) {
809
+ this.evictIndexEntries(this.config.indexEvictionBatchSize)
810
+ }
811
+ // Check memory limit
812
+ if (this.config.maxIndexMemoryBytes && this.indexMemoryBytes >= this.config.maxIndexMemoryBytes) {
813
+ // Estimate how many entries to evict based on average entry size
814
+ const avgEntrySize = this.indexMemoryBytes / Math.max(1, this.storageIndex.size)
815
+ const targetBytes = this.config.maxIndexMemoryBytes * INDEX_MEMORY_EVICTION_TARGET // Target 80% of limit
816
+ const bytesToFree = this.indexMemoryBytes - targetBytes
817
+ const entriesToEvict = Math.max(1, Math.ceil(bytesToFree / avgEntrySize))
818
+ this.evictIndexEntries(entriesToEvict)
819
+ }
820
+ }
821
+
822
+ /**
823
+ * Select the k entries with the smallest scores (partial selection)
824
+ *
825
+ * Uses a simple O(n*k) algorithm which is efficient when k << n.
826
+ * For large k values, this degrades gracefully but still avoids
827
+ * the memory overhead of creating and sorting a full array copy.
828
+ */
829
+ private selectSmallestEntries(k: number, getScore: (entry: StorageIndexEntry) => number): StorageIndexEntry[] {
830
+ const result: StorageIndexEntry[] = []
831
+ const resultScores: number[] = []
832
+
833
+ for (const entry of this.storageIndex.values()) {
834
+ const score = getScore(entry)
835
+
836
+ if (result.length < k) {
837
+ // Still building up the result set
838
+ result.push(entry)
839
+ resultScores.push(score)
840
+ } else {
841
+ // Find the entry with the highest score in our result (the one to potentially replace)
842
+ let maxIdx = 0
843
+ let maxScore = resultScores[0] ?? 0
844
+ for (let i = 1; i < resultScores.length; i++) {
845
+ const currentScore = resultScores[i] ?? 0
846
+ if (currentScore > maxScore) {
847
+ maxIdx = i
848
+ maxScore = currentScore
849
+ }
850
+ }
851
+ // If this entry has a lower score, replace the max
852
+ if (score < maxScore) {
853
+ result[maxIdx] = entry
854
+ resultScores[maxIdx] = score
855
+ }
856
+ }
857
+ }
858
+
859
+ return result
860
+ }
861
+
862
+ /**
863
+ * Evict entries from the index based on configured policy
864
+ *
865
+ * ## Performance Characteristics
866
+ *
867
+ * **Time Complexity**: O(n*k) where n=index size, k=entries to evict
868
+ * - Uses partial selection instead of full sort O(n log n)
869
+ * - Efficient when k << n (typical case: k=10-20, n=10000+)
870
+ *
871
+ * **Space Complexity**: O(k) for the result buffer
872
+ * - Does not create intermediate arrays of size n
873
+ *
874
+ * **Memory optimization**: Uses partial selection instead of full sort.
875
+ * This is more efficient when evicting a small batch from a large index.
876
+ *
877
+ * @param count - Number of entries to evict
878
+ */
879
+ private evictIndexEntries(count: number): void {
880
+ if (this.storageIndex.size === 0 || count <= 0) return
881
+
882
+ const startNs = this.getNowNs()
883
+
884
+ // Select comparison function based on policy
885
+ let getScore: (entry: StorageIndexEntry) => number
886
+ switch (this.config.indexEvictionPolicy) {
887
+ case 'lfu':
888
+ // Least Frequently Used - lower accessCount = evict first
889
+ getScore = (e) => e.accessCount
890
+ break
891
+ case 'fifo':
892
+ // First In First Out - older created = evict first
893
+ getScore = (e) => e.created
894
+ break
895
+ case 'lru':
896
+ default:
897
+ // Least Recently Used - older lastAccess = evict first
898
+ getScore = (e) => e.lastAccess
899
+ break
900
+ }
901
+
902
+ // Use partial selection: find the k entries with smallest scores
903
+ // This avoids full sort when count << size
904
+ const toEvict = this.selectSmallestEntries(count, getScore)
905
+ const evictedKeys: string[] = []
906
+
907
+ for (const entry of toEvict) {
908
+ this.storageIndex.delete(entry.key)
909
+ this.indexMemoryBytes -= TieredStorageOrchestrator.ENTRY_MEMORY_OVERHEAD + entry.key.length * 2
910
+ evictedKeys.push(entry.key)
911
+ // Invalidate lookup cache if evicted
912
+ if (this.lastLookupKey === entry.key) {
913
+ this.lastLookupKey = null
914
+ this.lastLookupEntry = null
915
+ }
916
+ }
917
+
918
+ // Update performance metrics
919
+ this.performanceMetrics.evictionCount += evictedKeys.length
920
+ this.performanceMetrics.evictionBatches++
921
+ this.performanceMetrics.totalEvictionTimeNs += this.getNowNs() - startNs
922
+
923
+ // Ensure memory tracking doesn't go negative
924
+ if (this.indexMemoryBytes < 0) this.indexMemoryBytes = 0
925
+
926
+ // Notify callback if configured
927
+ if (this.config.onIndexEviction && evictedKeys.length > 0) {
928
+ try {
929
+ this.config.onIndexEviction(evictedKeys)
930
+ } catch (e) {
931
+ // Ignore callback errors
932
+ this.config.logger?.warn(`Index eviction callback error: ${e instanceof Error ? e.message : String(e)}`)
933
+ }
934
+ }
935
+ }
936
+
937
+ /**
938
+ * Check and emit memory warning if threshold exceeded
939
+ */
940
+ private checkMemoryWarning(): void {
941
+ if (!this.config.maxIndexMemoryBytes || this.memoryWarningEmitted) return
942
+
943
+ const threshold = this.config.maxIndexMemoryBytes * this.config.indexMemoryWarningThreshold
944
+ if (this.indexMemoryBytes >= threshold) {
945
+ this.memoryWarningEmitted = true
946
+ this.config.logger?.warn(`Index memory approaching limit: ${this.indexMemoryBytes} bytes (${Math.round(this.indexMemoryBytes / this.config.maxIndexMemoryBytes * 100)}% of ${this.config.maxIndexMemoryBytes} bytes)`)
947
+ }
948
+ }
949
+
950
+ /**
951
+ * Start periodic index maintenance
952
+ */
953
+ private startIndexMaintenance(): void {
954
+ if (this.maintenanceTimer) return
955
+ this.maintenanceTimer = setInterval(async () => {
956
+ try {
957
+ await this.runIndexMaintenance()
958
+ } catch (e) {
959
+ this.config.logger?.warn(`Index maintenance error: ${e instanceof Error ? e.message : String(e)}`)
960
+ }
961
+ }, this.config.indexMaintenanceIntervalMs!)
962
+ }
963
+
964
+ /**
965
+ * Stop periodic index maintenance
966
+ */
967
+ stopIndexMaintenance(): void {
968
+ if (this.maintenanceTimer) {
969
+ clearInterval(this.maintenanceTimer)
970
+ this.maintenanceTimer = undefined
971
+ }
972
+ }
973
+
974
+ /**
975
+ * Run index maintenance - clean up stale entries
976
+ *
977
+ * ## Performance Characteristics
978
+ *
979
+ * **Time Complexity**: O(n * storage_ops) where n = index entries
980
+ * - Each entry may require 1-2 storage lookups
981
+ * - Storage ops are async and may have network latency
982
+ *
983
+ * **When to Run**:
984
+ * - Periodically via indexMaintenanceIntervalMs config
985
+ * - After suspected data loss or corruption
986
+ * - During low-traffic periods to avoid impacting hot path
987
+ *
988
+ * @returns Object with count of removed stale entries and timing info
989
+ */
990
+ async runIndexMaintenance(): Promise<{ removed: number; durationMs: number }> {
991
+ const startNs = this.getNowNs()
992
+ let removed = 0
993
+ const keysToRemove: string[] = []
994
+
995
+ for (const [key, entry] of this.storageIndex) {
996
+ // Check if data still exists in the indicated tier
997
+ let exists = false
998
+
999
+ if (entry.tier === 'hot') {
1000
+ // Hot tier - assume it might have been evicted, check warm/cold
1001
+ const warmData = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1002
+ if (warmData) {
1003
+ entry.tier = 'warm'
1004
+ exists = true
1005
+ } else {
1006
+ const coldData = await this.r2Layer.get(key)
1007
+ if (coldData) {
1008
+ entry.tier = 'cold'
1009
+ exists = true
1010
+ }
1011
+ }
1012
+ } else if (entry.tier === 'warm') {
1013
+ const warmData = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1014
+ if (warmData) {
1015
+ exists = true
1016
+ } else {
1017
+ const coldData = await this.r2Layer.get(key)
1018
+ if (coldData) {
1019
+ entry.tier = 'cold'
1020
+ exists = true
1021
+ }
1022
+ }
1023
+ } else {
1024
+ const coldData = await this.r2Layer.get(key)
1025
+ exists = !!coldData
1026
+ }
1027
+
1028
+ if (!exists) {
1029
+ keysToRemove.push(key)
1030
+ }
1031
+ }
1032
+
1033
+ for (const key of keysToRemove) {
1034
+ const entry = this.storageIndex.get(key)
1035
+ if (entry) {
1036
+ this.storageIndex.delete(key)
1037
+ this.indexMemoryBytes -= TieredStorageOrchestrator.ENTRY_MEMORY_OVERHEAD + key.length * 2
1038
+ removed++
1039
+ // Invalidate cache if needed
1040
+ if (this.lastLookupKey === key) {
1041
+ this.lastLookupKey = null
1042
+ this.lastLookupEntry = null
1043
+ }
1044
+ }
1045
+ }
1046
+
1047
+ if (this.indexMemoryBytes < 0) this.indexMemoryBytes = 0
1048
+
1049
+ const durationNs = this.getNowNs() - startNs
1050
+ this.performanceMetrics.totalMaintenanceTimeNs += durationNs
1051
+
1052
+ return { removed, durationMs: durationNs / 1e6 }
1053
+ }
1054
+
1055
+ /**
1056
+ * Optimized index lookup with single-entry cache
1057
+ *
1058
+ * The hot path often involves looking up the same key multiple times
1059
+ * in quick succession (read -> check promotion -> update index).
1060
+ * This cache avoids redundant Map lookups.
1061
+ *
1062
+ * @param key - The key to look up
1063
+ * @returns The index entry or undefined if not found
1064
+ */
1065
+ private getIndexEntryOptimized(key: string): StorageIndexEntry | undefined {
1066
+ const startNs = this.getNowNs()
1067
+ this.performanceMetrics.lookupCount++
1068
+
1069
+ // Check single-entry cache first (hot path optimization)
1070
+ if (this.lastLookupKey === key && this.lastLookupEntry) {
1071
+ this.performanceMetrics.cacheHitCount++
1072
+ this.performanceMetrics.totalLookupTimeNs += this.getNowNs() - startNs
1073
+ return this.lastLookupEntry
1074
+ }
1075
+
1076
+ this.performanceMetrics.cacheMissCount++
1077
+ const entry = this.storageIndex.get(key)
1078
+
1079
+ // Update cache
1080
+ this.lastLookupKey = key
1081
+ this.lastLookupEntry = entry ?? null
1082
+
1083
+ this.performanceMetrics.totalLookupTimeNs += this.getNowNs() - startNs
1084
+ return entry
1085
+ }
1086
+
1087
+ /**
1088
+ * Check if a key should be promoted to a higher tier
1089
+ *
1090
+ * Uses optimized lookup to avoid redundant Map accesses.
1091
+ *
1092
+ * @param key - The storage key
1093
+ * @param currentTier - The current tier of the data
1094
+ * @returns Promotion decision with target tier if applicable
1095
+ */
1096
+ private shouldPromote(key: string, currentTier: StorageTier): { promote: boolean; targetTier?: StorageTier } {
1097
+ if (!this.config.autoPromote) return { promote: false }
1098
+ const entry = this.getIndexEntryOptimized(key)
1099
+ if (!entry) return { promote: false }
1100
+ if (currentTier === 'cold' && entry.accessCount >= this.config.coldToWarmThreshold) return { promote: true, targetTier: 'warm' }
1101
+ if (currentTier === 'warm' && entry.accessCount >= this.config.warmToHotThreshold) {
1102
+ const age = Date.now() - entry.created
1103
+ if (age <= this.config.promotionWindowMs) return { promote: true, targetTier: 'hot' }
1104
+ }
1105
+ return { promote: false }
1106
+ }
1107
+
1108
+ private isCircuitOpen(tier: StorageTier): boolean {
1109
+ if (!this.config.circuitBreaker?.enabled) return false
1110
+ const cb = this.circuitBreakers[tier]
1111
+ if (cb.state === 'open' && cb.openedAt) {
1112
+ const resetTimeout = this.config.circuitBreaker.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_RESET_TIMEOUT_MS
1113
+ if (Date.now() - cb.openedAt >= resetTimeout) { cb.state = 'half-open'; return false }
1114
+ }
1115
+ return cb.state === 'open'
1116
+ }
1117
+
1118
+ private recordCircuitBreakerFailure(tier: StorageTier, error?: Error): void {
1119
+ if (!this.config.circuitBreaker?.enabled) return
1120
+ const cb = this.circuitBreakers[tier]
1121
+ cb.failures++
1122
+ cb.lastFailure = Date.now()
1123
+ if (cb.failures >= (this.config.circuitBreaker.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_FAILURE_THRESHOLD)) {
1124
+ cb.state = 'open'
1125
+ cb.openedAt = Date.now()
1126
+ this.notifyCircuitBreakerChange({ tier, state: 'open', failures: cb.failures, error })
1127
+ }
1128
+ }
1129
+
1130
+ private recordCircuitBreakerSuccess(tier: StorageTier): void {
1131
+ if (!this.config.circuitBreaker?.enabled) return
1132
+ const cb = this.circuitBreakers[tier]
1133
+ cb.lastSuccess = Date.now()
1134
+ if (cb.state === 'half-open') { cb.state = 'closed'; cb.failures = 0 }
1135
+ }
1136
+
1137
+ async read(key: string, options?: ReadOptions): Promise<ReadResult> {
1138
+ const startTime = this.config.metrics?.trackLatency ? Date.now() : 0
1139
+ const skipPromotion = options?.skipPromotion ?? false
1140
+ const skipTracking = options?.skipTracking ?? false
1141
+ const ctx = options?.ctx
1142
+ const correlationId = `read-${++this.correlationCounter}`
1143
+ const retryCount = this.keyRetryCounters.get(key) ?? 0
1144
+ this.keyRetryCounters.set(key, retryCount + 1)
1145
+ const coldCircuitOpen = this.isCircuitOpen('cold')
1146
+ const recordLatency = () => { if (this.config.metrics?.trackLatency && startTime > 0) { this.latencyMeasurements.push(Date.now() - startTime); if (this.latencyMeasurements.length > 1000) this.latencyMeasurements.shift() } }
1147
+
1148
+ if (this.swrCacheLayer) {
1149
+ try {
1150
+ const result = await this.swrCacheLayer.get(key, ctx)
1151
+ if (result.hit) {
1152
+ if (!skipTracking) this.updateIndex(key, 'hot', result.data?.length ?? 0)
1153
+ this.recordCircuitBreakerSuccess('hot')
1154
+ recordLatency()
1155
+ return { data: result.data, tier: 'hot', stale: result.stale, revalidating: result.revalidating }
1156
+ }
1157
+ } catch (e) {
1158
+ const error = e instanceof Error ? e : new Error(String(e))
1159
+ this.tierHealth.hot = 'degraded'
1160
+ this.recordCircuitBreakerFailure('hot', error)
1161
+ this.notifyError({ context: 'tier_read_failure', error, tier: 'hot', key, timestamp: Date.now(), retryCount, correlationId })
1162
+ }
1163
+ } else if (this.cacheLayer) {
1164
+ try {
1165
+ const cached = await this.cacheLayer.get(key)
1166
+ if (cached) {
1167
+ if (!skipTracking) this.updateIndex(key, 'hot', cached.length)
1168
+ this.recordCircuitBreakerSuccess('hot')
1169
+ recordLatency()
1170
+ return { data: cached, tier: 'hot' }
1171
+ }
1172
+ } catch (e) {
1173
+ const error = e instanceof Error ? e : new Error(String(e))
1174
+ this.tierHealth.hot = 'degraded'
1175
+ this.recordCircuitBreakerFailure('hot', error)
1176
+ this.notifyError({ context: 'tier_read_failure', error, tier: 'hot', key, timestamp: Date.now(), retryCount, correlationId })
1177
+ }
1178
+ }
1179
+
1180
+ try {
1181
+ const doData = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1182
+ if (doData) {
1183
+ this.warmStats.reads++
1184
+ this.warmStats.bytesRead += doData.length
1185
+ if (!skipTracking) this.updateIndex(key, 'warm', doData.length)
1186
+ this.recordCircuitBreakerSuccess('warm')
1187
+ if (!skipPromotion) {
1188
+ const { promote, targetTier } = this.shouldPromote(key, 'warm')
1189
+ if (promote && targetTier === 'hot') this.promoteToHot(key, doData).catch(() => {})
1190
+ }
1191
+ recordLatency()
1192
+ return { data: doData, tier: 'warm', fallback: coldCircuitOpen ? true : undefined }
1193
+ }
1194
+ } catch (e) {
1195
+ const error = e instanceof Error ? e : new Error(String(e))
1196
+ this.tierHealth.warm = 'degraded'
1197
+ this.recordCircuitBreakerFailure('warm', error)
1198
+ this.notifyError({ context: 'tier_read_failure', error, tier: 'warm', key, timestamp: Date.now(), retryCount, correlationId })
1199
+ }
1200
+
1201
+ if (coldCircuitOpen) { recordLatency(); return { data: null } }
1202
+
1203
+ try {
1204
+ const r2Data = await this.r2Layer.get(key)
1205
+ if (r2Data) {
1206
+ if (!skipTracking) this.updateIndex(key, 'cold', r2Data.length)
1207
+ this.recordCircuitBreakerSuccess('cold')
1208
+ if (!skipPromotion) {
1209
+ const { promote, targetTier } = this.shouldPromote(key, 'cold')
1210
+ if (promote && targetTier === 'warm') this.promoteToWarm(key, r2Data).catch(() => {})
1211
+ }
1212
+ recordLatency()
1213
+ return { data: r2Data, tier: 'cold' }
1214
+ }
1215
+ } catch (e) {
1216
+ const error = e instanceof Error ? e : new Error(String(e))
1217
+ this.tierHealth.cold = 'degraded'
1218
+ this.recordCircuitBreakerFailure('cold', error)
1219
+ this.notifyError({ context: 'tier_read_failure', error, tier: 'cold', key, timestamp: Date.now(), retryCount, correlationId })
1220
+ }
1221
+
1222
+ recordLatency()
1223
+ return { data: null }
1224
+ }
1225
+
1226
+ async write(key: string, data: Uint8Array, options?: WriteOptions): Promise<void> {
1227
+ const tier = options?.tier ?? 'warm'
1228
+ const writeThrough = options?.writeThrough ?? this.config.writeThrough
1229
+ const skipIndex = options?.skipIndex ?? false
1230
+
1231
+ switch (tier) {
1232
+ case 'hot':
1233
+ await this.doStorage.put(this.doKey(key), data)
1234
+ this.warmStats.writes++
1235
+ this.warmStats.bytesWritten += data.length
1236
+ if (this.swrCacheLayer) await this.swrCacheLayer.put(key, data)
1237
+ else if (this.cacheLayer) await this.cacheLayer.put(key, data, { lastAccessed: Date.now(), accessCount: 1 })
1238
+ if (!skipIndex) this.updateIndex(key, 'hot', data.length, writeThrough ? false : true)
1239
+ break
1240
+ case 'warm':
1241
+ await this.doStorage.put(this.doKey(key), data)
1242
+ this.warmStats.writes++
1243
+ this.warmStats.bytesWritten += data.length
1244
+ if (!skipIndex) this.updateIndex(key, 'warm', data.length, writeThrough ? false : true)
1245
+ break
1246
+ case 'cold':
1247
+ await this.r2Layer.put(key, data, { customMetadata: options?.metadata })
1248
+ if (!skipIndex) this.updateIndex(key, 'cold', data.length, false)
1249
+ break
1250
+ }
1251
+
1252
+ if (writeThrough && tier !== 'cold') {
1253
+ try {
1254
+ await this.r2Layer.put(key, data, { customMetadata: { ...options?.metadata, writeThroughAt: Date.now().toString(), sourceTier: tier } })
1255
+ const entry = this.storageIndex.get(key)
1256
+ if (entry) entry.dirty = false
1257
+ } catch (e) {
1258
+ const error = e instanceof Error ? e : new Error(String(e))
1259
+ this.tierHealth.cold = 'degraded'
1260
+ this.notifyError({ context: 'write_through_failure', error, tier: 'cold', key, timestamp: Date.now() })
1261
+ }
1262
+ }
1263
+ }
1264
+
1265
+ async delete(key: string): Promise<void> {
1266
+ const errors: Error[] = []
1267
+ if (this.swrCacheLayer) { try { await this.swrCacheLayer.invalidate(key) } catch (e) { errors.push(e instanceof Error ? e : new Error(String(e))) } }
1268
+ else if (this.cacheLayer) { try { await this.cacheLayer.delete(key) } catch (e) { errors.push(e instanceof Error ? e : new Error(String(e))) } }
1269
+ try { await this.doStorage.delete(this.doKey(key)); this.warmStats.deletes++ } catch (e) { errors.push(e instanceof Error ? e : new Error(String(e))) }
1270
+ try { await this.r2Layer.delete(key) } catch (e) { errors.push(e instanceof Error ? e : new Error(String(e))) }
1271
+ // Update memory tracking before deleting from index
1272
+ if (this.storageIndex.has(key)) {
1273
+ this.indexMemoryBytes -= TieredStorageOrchestrator.ENTRY_MEMORY_OVERHEAD + key.length * 2
1274
+ if (this.indexMemoryBytes < 0) this.indexMemoryBytes = 0
1275
+ }
1276
+ this.storageIndex.delete(key)
1277
+ if (errors.length === 3) throw new Error(`Failed to delete from all tiers: ${errors.map(e => e.message).join('; ')}`)
1278
+ }
1279
+
1280
+ async promoteToWarm(key: string, data?: Uint8Array): Promise<TierMigrationEvent> {
1281
+ const timestamp = Date.now()
1282
+ const pageData = data ?? await this.r2Layer.get(key)
1283
+ if (!pageData) {
1284
+ const event: TierMigrationEvent = { key, fromTier: 'cold', toTier: 'warm', success: false, timestamp, error: 'Data not found in cold tier', reason: 'access_pattern' }
1285
+ this.promotionStats.failed++
1286
+ this.notifyMigration(event)
1287
+ return event
1288
+ }
1289
+ try {
1290
+ await this.doStorage.put(this.doKey(key), pageData)
1291
+ this.warmStats.writes++
1292
+ this.warmStats.bytesWritten += pageData.length
1293
+ this.promotionStats.coldToWarm++
1294
+ this.updateIndex(key, 'warm', pageData.length)
1295
+ const event: TierMigrationEvent = { key, fromTier: 'cold', toTier: 'warm', success: true, timestamp, reason: 'access_pattern' }
1296
+ this.notifyMigration(event)
1297
+ return event
1298
+ } catch (e) {
1299
+ this.promotionStats.failed++
1300
+ const event: TierMigrationEvent = { key, fromTier: 'cold', toTier: 'warm', success: false, timestamp, error: e instanceof Error ? e.message : String(e), reason: 'access_pattern' }
1301
+ this.notifyMigration(event)
1302
+ return event
1303
+ }
1304
+ }
1305
+
1306
+ async promoteToHot(key: string, data?: Uint8Array): Promise<TierMigrationEvent> {
1307
+ const timestamp = Date.now()
1308
+ const pageData = data ?? await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1309
+ if (!pageData) {
1310
+ const event: TierMigrationEvent = { key, fromTier: 'warm', toTier: 'hot', success: false, timestamp, error: 'Data not found in warm tier', reason: 'access_pattern' }
1311
+ this.promotionStats.failed++
1312
+ this.notifyMigration(event)
1313
+ return event
1314
+ }
1315
+ try {
1316
+ if (this.swrCacheLayer) await this.swrCacheLayer.put(key, pageData)
1317
+ else if (this.cacheLayer) {
1318
+ const entry = this.storageIndex.get(key)
1319
+ await this.cacheLayer.put(key, pageData, { lastAccessed: timestamp, accessCount: entry?.accessCount ?? 1 })
1320
+ }
1321
+ this.promotionStats.warmToHot++
1322
+ this.updateIndex(key, 'hot', pageData.length)
1323
+ const event: TierMigrationEvent = { key, fromTier: 'warm', toTier: 'hot', success: true, timestamp, reason: 'access_pattern' }
1324
+ this.notifyMigration(event)
1325
+ return event
1326
+ } catch (e) {
1327
+ this.promotionStats.failed++
1328
+ const event: TierMigrationEvent = { key, fromTier: 'warm', toTier: 'hot', success: false, timestamp, error: e instanceof Error ? e.message : String(e), reason: 'access_pattern' }
1329
+ this.notifyMigration(event)
1330
+ return event
1331
+ }
1332
+ }
1333
+
1334
+ async demoteFromHot(key: string, reason: TierMigrationEvent['reason'] = 'ttl_expired'): Promise<TierMigrationEvent> {
1335
+ const timestamp = Date.now()
1336
+ try {
1337
+ if (this.swrCacheLayer) await this.swrCacheLayer.invalidate(key)
1338
+ else if (this.cacheLayer) await this.cacheLayer.delete(key)
1339
+ this.demotionStats.hotToWarm++
1340
+ this.updateIndex(key, 'warm', this.storageIndex.get(key)?.size ?? 0)
1341
+ const event: TierMigrationEvent = { key, fromTier: 'hot', toTier: 'warm', success: true, timestamp, reason }
1342
+ this.notifyMigration(event)
1343
+ return event
1344
+ } catch (e) {
1345
+ this.demotionStats.failed++
1346
+ const event: TierMigrationEvent = { key, fromTier: 'hot', toTier: 'warm', success: false, timestamp, error: e instanceof Error ? e.message : String(e), reason }
1347
+ this.notifyMigration(event)
1348
+ return event
1349
+ }
1350
+ }
1351
+
1352
+ async demoteFromWarm(key: string, reason: TierMigrationEvent['reason'] = 'ttl_expired'): Promise<TierMigrationEvent> {
1353
+ const timestamp = Date.now()
1354
+ try {
1355
+ const data = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1356
+ if (!data) {
1357
+ const event: TierMigrationEvent = { key, fromTier: 'warm', toTier: 'cold', success: false, timestamp, error: 'Data not found in warm tier', reason }
1358
+ this.demotionStats.failed++
1359
+ this.notifyMigration(event)
1360
+ return event
1361
+ }
1362
+ await this.r2Layer.put(key, data, { customMetadata: { demotedAt: timestamp.toString(), reason } })
1363
+ await this.doStorage.delete(this.doKey(key))
1364
+ this.warmStats.deletes++
1365
+ this.demotionStats.warmToCold++
1366
+ this.updateIndex(key, 'cold', data.length)
1367
+ const event: TierMigrationEvent = { key, fromTier: 'warm', toTier: 'cold', success: true, timestamp, reason }
1368
+ this.notifyMigration(event)
1369
+ return event
1370
+ } catch (e) {
1371
+ this.demotionStats.failed++
1372
+ const event: TierMigrationEvent = { key, fromTier: 'warm', toTier: 'cold', success: false, timestamp, error: e instanceof Error ? e.message : String(e), reason }
1373
+ this.notifyMigration(event)
1374
+ return event
1375
+ }
1376
+ }
1377
+
1378
+ async runDemotionCycle(maxDemotions = 10): Promise<TierMigrationEvent[]> {
1379
+ if (!this.config.autoDemote) return []
1380
+ const events: TierMigrationEvent[] = []
1381
+ const now = Date.now()
1382
+ const hotCandidates: StorageIndexEntry[] = []
1383
+ for (const entry of this.storageIndex.values()) {
1384
+ // Skip pinned entries
1385
+ if (this.pinnedEntries.has(entry.key)) continue
1386
+ if (entry.tier === 'hot' && now - entry.lastAccess >= this.config.hotTtlMs) hotCandidates.push(entry)
1387
+ }
1388
+ hotCandidates.sort((a, b) => a.lastAccess - b.lastAccess)
1389
+ for (const entry of hotCandidates.slice(0, maxDemotions)) events.push(await this.demoteFromHot(entry.key))
1390
+ const warmCandidates: StorageIndexEntry[] = []
1391
+ for (const entry of this.storageIndex.values()) {
1392
+ // Skip pinned entries
1393
+ if (this.pinnedEntries.has(entry.key)) continue
1394
+ if (entry.tier === 'warm' && now - entry.lastAccess >= this.config.warmTtlMs) warmCandidates.push(entry)
1395
+ }
1396
+ warmCandidates.sort((a, b) => a.lastAccess - b.lastAccess)
1397
+ const remainingSlots = maxDemotions - events.length
1398
+ for (const entry of warmCandidates.slice(0, remainingSlots)) events.push(await this.demoteFromWarm(entry.key))
1399
+ return events
1400
+ }
1401
+
1402
+ async evictLRUFromHot(count: number): Promise<TierMigrationEvent[]> {
1403
+ const hotPages: StorageIndexEntry[] = []
1404
+ for (const entry of this.storageIndex.values()) { if (entry.tier === 'hot') hotPages.push(entry) }
1405
+ hotPages.sort((a, b) => a.lastAccess - b.lastAccess)
1406
+ const events: TierMigrationEvent[] = []
1407
+ for (const entry of hotPages.slice(0, count)) events.push(await this.demoteFromHot(entry.key, 'lru_eviction'))
1408
+ return events
1409
+ }
1410
+
1411
+ async syncDirtyToCold(): Promise<number> {
1412
+ let synced = 0
1413
+ for (const entry of this.storageIndex.values()) {
1414
+ if (entry.dirty) {
1415
+ try {
1416
+ const data = await this.doStorage.get(this.doKey(entry.key)) as Uint8Array | undefined
1417
+ if (data) {
1418
+ await this.r2Layer.put(entry.key, data, { customMetadata: { syncedAt: Date.now().toString(), sourceTier: entry.tier } })
1419
+ entry.dirty = false
1420
+ synced++
1421
+ }
1422
+ } catch (e) {
1423
+ const error = e instanceof Error ? e : new Error(String(e))
1424
+ this.notifyError({ context: 'sync_dirty_to_cold', error, key: entry.key, timestamp: Date.now() })
1425
+ }
1426
+ }
1427
+ }
1428
+ return synced
1429
+ }
1430
+
1431
+ getIndexEntry(key: string): StorageIndexEntry | undefined { return this.storageIndex.get(key) }
1432
+ hasInIndex(key: string): boolean { return this.storageIndex.has(key) }
1433
+ getTier(key: string): StorageTier | undefined { return this.storageIndex.get(key)?.tier }
1434
+
1435
+ /**
1436
+ * Get detailed memory usage breakdown for the storage index
1437
+ *
1438
+ * ## Memory Components
1439
+ *
1440
+ * - **entryOverhead**: Fixed overhead per entry (~120 bytes)
1441
+ * - **keyStorage**: Memory for key strings (2 bytes per char in JS)
1442
+ * - **mapOverhead**: Estimated Map internal structures (~10% of entry memory)
1443
+ *
1444
+ * @returns Detailed memory breakdown in bytes
1445
+ */
1446
+ getIndexMemoryBreakdown(): {
1447
+ totalBytes: number
1448
+ entryOverhead: number
1449
+ keyStorage: number
1450
+ mapOverheadEstimate: number
1451
+ bytesPerEntry: number
1452
+ entries: number
1453
+ peakBytes: number
1454
+ utilizationPercent: number | null
1455
+ memoryBudgetBytes: number | null
1456
+ } {
1457
+ let keyStorage = 0
1458
+ for (const key of this.storageIndex.keys()) {
1459
+ keyStorage += key.length * 2 // JS strings are UTF-16
1460
+ }
1461
+
1462
+ const entryOverhead = this.storageIndex.size * TieredStorageOrchestrator.ENTRY_MEMORY_OVERHEAD
1463
+ // Estimate Map internal overhead as ~10% of entry memory
1464
+ const mapOverheadEstimate = Math.ceil(entryOverhead * MAP_OVERHEAD_ESTIMATE_PERCENT)
1465
+ const totalBytes = entryOverhead + keyStorage + mapOverheadEstimate
1466
+ const bytesPerEntry = this.storageIndex.size > 0
1467
+ ? totalBytes / this.storageIndex.size
1468
+ : 0
1469
+
1470
+ return {
1471
+ totalBytes,
1472
+ entryOverhead,
1473
+ keyStorage,
1474
+ mapOverheadEstimate,
1475
+ bytesPerEntry,
1476
+ entries: this.storageIndex.size,
1477
+ peakBytes: this.performanceMetrics.peakMemoryBytes,
1478
+ utilizationPercent: this.config.maxIndexMemoryBytes
1479
+ ? (this.indexMemoryBytes / this.config.maxIndexMemoryBytes) * 100
1480
+ : null,
1481
+ memoryBudgetBytes: this.config.maxIndexMemoryBytes ?? null,
1482
+ }
1483
+ }
1484
+
1485
+ /**
1486
+ * Estimate memory for a given number of entries with average key length
1487
+ *
1488
+ * Useful for capacity planning:
1489
+ * ```typescript
1490
+ * // How much memory for 10,000 entries with ~30 char keys?
1491
+ * const estimate = orchestrator.estimateIndexMemory(10000, 30)
1492
+ * console.log(`Estimated: ${estimate / 1024 / 1024}MB`)
1493
+ * ```
1494
+ *
1495
+ * @param entryCount - Number of entries to estimate for
1496
+ * @param avgKeyLength - Average key length in characters (default: 20)
1497
+ * @returns Estimated memory usage in bytes
1498
+ */
1499
+ estimateIndexMemory(entryCount: number, avgKeyLength = DEFAULT_AVG_KEY_LENGTH_CHARS): number {
1500
+ const entryOverhead = entryCount * TieredStorageOrchestrator.ENTRY_MEMORY_OVERHEAD
1501
+ const keyStorage = entryCount * avgKeyLength * 2 // UTF-16
1502
+ const mapOverhead = Math.ceil(entryOverhead * MAP_OVERHEAD_ESTIMATE_PERCENT)
1503
+ return entryOverhead + keyStorage + mapOverhead
1504
+ }
1505
+
1506
+ /**
1507
+ * Get the current memory usage as a percentage of the configured limit
1508
+ *
1509
+ * @returns Percentage (0-100) or null if no limit configured
1510
+ */
1511
+ getIndexMemoryUtilization(): number | null {
1512
+ if (!this.config.maxIndexMemoryBytes) return null
1513
+ return (this.indexMemoryBytes / this.config.maxIndexMemoryBytes) * 100
1514
+ }
1515
+
1516
+ /**
1517
+ * Check if the index is approaching memory limits
1518
+ *
1519
+ * @param threshold - Warning threshold as decimal (default: 0.8 = 80%)
1520
+ * @returns Object with warning status and details
1521
+ */
1522
+ checkIndexMemoryStatus(threshold = 0.8): {
1523
+ isNearLimit: boolean
1524
+ currentBytes: number
1525
+ limitBytes: number | null
1526
+ utilizationPercent: number | null
1527
+ warningThreshold: number
1528
+ } {
1529
+ const utilization = this.getIndexMemoryUtilization()
1530
+ return {
1531
+ isNearLimit: utilization !== null && utilization >= threshold * 100,
1532
+ currentBytes: this.indexMemoryBytes,
1533
+ limitBytes: this.config.maxIndexMemoryBytes ?? null,
1534
+ utilizationPercent: utilization,
1535
+ warningThreshold: threshold * 100,
1536
+ }
1537
+ }
1538
+
1539
+ /**
1540
+ * Get comprehensive statistics including performance metrics
1541
+ *
1542
+ * ## Performance Metrics Included
1543
+ *
1544
+ * - **lookupCount**: Total number of index lookups
1545
+ * - **updateCount**: Total number of index updates
1546
+ * - **evictionCount**: Total entries evicted
1547
+ * - **avgLookupTimeNs**: Average lookup time in nanoseconds
1548
+ * - **avgUpdateTimeNs**: Average update time in nanoseconds
1549
+ * - **peakEntries**: Maximum index size observed
1550
+ * - **peakMemoryBytes**: Maximum memory usage observed
1551
+ * - **cacheHitCount/cacheMissCount**: Hot path cache effectiveness
1552
+ *
1553
+ * @returns Complete orchestrator statistics
1554
+ */
1555
+ getStats(): OrchestratorStats {
1556
+ const indexStats: OrchestratorStats['index'] = {
1557
+ totalEntries: this.storageIndex.size,
1558
+ byTier: { hot: 0, warm: 0, cold: 0 } as Record<StorageTier, number>,
1559
+ totalSize: 0,
1560
+ dirtyEntries: 0,
1561
+ memoryBytes: this.indexMemoryBytes,
1562
+ performance: {
1563
+ lookupCount: this.performanceMetrics.lookupCount,
1564
+ updateCount: this.performanceMetrics.updateCount,
1565
+ evictionCount: this.performanceMetrics.evictionCount,
1566
+ totalLookupTimeNs: this.performanceMetrics.totalLookupTimeNs,
1567
+ totalUpdateTimeNs: this.performanceMetrics.totalUpdateTimeNs,
1568
+ totalEvictionTimeNs: this.performanceMetrics.totalEvictionTimeNs,
1569
+ totalMaintenanceTimeNs: this.performanceMetrics.totalMaintenanceTimeNs,
1570
+ avgLookupTimeNs: this.performanceMetrics.lookupCount > 0
1571
+ ? this.performanceMetrics.totalLookupTimeNs / this.performanceMetrics.lookupCount
1572
+ : 0,
1573
+ avgUpdateTimeNs: this.performanceMetrics.updateCount > 0
1574
+ ? this.performanceMetrics.totalUpdateTimeNs / this.performanceMetrics.updateCount
1575
+ : 0,
1576
+ peakEntries: this.performanceMetrics.peakEntries,
1577
+ peakMemoryBytes: this.performanceMetrics.peakMemoryBytes,
1578
+ evictionBatches: this.performanceMetrics.evictionBatches,
1579
+ entriesPerEvictionBatch: this.performanceMetrics.evictionBatches > 0
1580
+ ? this.performanceMetrics.evictionCount / this.performanceMetrics.evictionBatches
1581
+ : 0,
1582
+ cacheHitCount: this.performanceMetrics.cacheHitCount,
1583
+ cacheMissCount: this.performanceMetrics.cacheMissCount,
1584
+ },
1585
+ }
1586
+ for (const entry of this.storageIndex.values()) {
1587
+ indexStats.byTier[entry.tier]++
1588
+ indexStats.totalSize += entry.size
1589
+ if (entry.dirty) indexStats.dirtyEntries++
1590
+ }
1591
+ return {
1592
+ index: indexStats,
1593
+ hot: this.swrCacheLayer?.getStats() ?? this.cacheLayer?.getStats() ?? { hits: 0, misses: 0, writes: 0, deletes: 0, bytesRead: 0, bytesWritten: 0, errors: 0, hitRatio: 0 },
1594
+ warm: { ...this.warmStats },
1595
+ cold: this.r2Layer.getStats(),
1596
+ promotions: { ...this.promotionStats },
1597
+ demotions: { ...this.demotionStats },
1598
+ health: { ...this.tierHealth },
1599
+ errors: { ...this.errorCounts },
1600
+ }
1601
+ }
1602
+
1603
+ /**
1604
+ * Reset all statistics counters
1605
+ *
1606
+ * Resets:
1607
+ * - Warm tier stats (reads, writes, deletes, bytes)
1608
+ * - Promotion/demotion counters
1609
+ * - Performance metrics (lookup/update/eviction counts and timing)
1610
+ *
1611
+ * Does NOT reset:
1612
+ * - Peak values (peakEntries, peakMemoryBytes)
1613
+ * - Current index state
1614
+ */
1615
+ resetStats(): void {
1616
+ this.warmStats = { reads: 0, writes: 0, deletes: 0, bytesRead: 0, bytesWritten: 0 }
1617
+ this.promotionStats = { coldToWarm: 0, warmToHot: 0, failed: 0 }
1618
+ this.demotionStats = { hotToWarm: 0, warmToCold: 0, failed: 0 }
1619
+ }
1620
+
1621
+ /**
1622
+ * Reset only performance metrics while preserving operational stats
1623
+ *
1624
+ * Useful for:
1625
+ * - Starting a new measurement window
1626
+ * - Benchmarking specific operations
1627
+ * - A/B testing configuration changes
1628
+ *
1629
+ * @param preservePeaks - If true, keeps peakEntries and peakMemoryBytes (default: false)
1630
+ */
1631
+ resetPerformanceMetrics(preservePeaks = false): void {
1632
+ const peakEntries = preservePeaks ? this.performanceMetrics.peakEntries : 0
1633
+ const peakMemoryBytes = preservePeaks ? this.performanceMetrics.peakMemoryBytes : 0
1634
+
1635
+ this.performanceMetrics = {
1636
+ lookupCount: 0,
1637
+ updateCount: 0,
1638
+ evictionCount: 0,
1639
+ totalLookupTimeNs: 0,
1640
+ totalUpdateTimeNs: 0,
1641
+ totalEvictionTimeNs: 0,
1642
+ totalMaintenanceTimeNs: 0,
1643
+ peakEntries,
1644
+ peakMemoryBytes,
1645
+ evictionBatches: 0,
1646
+ cacheHitCount: 0,
1647
+ cacheMissCount: 0,
1648
+ }
1649
+
1650
+ // Clear lookup cache
1651
+ this.lastLookupKey = null
1652
+ this.lastLookupEntry = null
1653
+ }
1654
+
1655
+ /**
1656
+ * Get a human-readable performance summary
1657
+ *
1658
+ * @returns Formatted string with key performance metrics
1659
+ */
1660
+ getPerformanceSummary(): string {
1661
+ const m = this.performanceMetrics
1662
+ const cacheHitRate = (m.cacheHitCount + m.cacheMissCount) > 0
1663
+ ? ((m.cacheHitCount / (m.cacheHitCount + m.cacheMissCount)) * 100).toFixed(1)
1664
+ : '0.0'
1665
+ const avgLookup = m.lookupCount > 0
1666
+ ? (m.totalLookupTimeNs / m.lookupCount / 1000).toFixed(2)
1667
+ : '0.00'
1668
+ const avgUpdate = m.updateCount > 0
1669
+ ? (m.totalUpdateTimeNs / m.updateCount / 1000).toFixed(2)
1670
+ : '0.00'
1671
+ const avgEvictionBatch = m.evictionBatches > 0
1672
+ ? (m.totalEvictionTimeNs / m.evictionBatches / 1000).toFixed(2)
1673
+ : '0.00'
1674
+
1675
+ return [
1676
+ `Index Performance Summary:`,
1677
+ ` Entries: ${this.storageIndex.size} current, ${m.peakEntries} peak`,
1678
+ ` Memory: ${(this.indexMemoryBytes / 1024).toFixed(1)}KB current, ${(m.peakMemoryBytes / 1024).toFixed(1)}KB peak`,
1679
+ ` Lookups: ${m.lookupCount} total, ${avgLookup}μs avg`,
1680
+ ` Updates: ${m.updateCount} total, ${avgUpdate}μs avg`,
1681
+ ` Evictions: ${m.evictionCount} entries in ${m.evictionBatches} batches, ${avgEvictionBatch}μs/batch`,
1682
+ ` Cache: ${cacheHitRate}% hit rate (${m.cacheHitCount} hits, ${m.cacheMissCount} misses)`,
1683
+ ` Maintenance: ${(m.totalMaintenanceTimeNs / 1e6).toFixed(1)}ms total`,
1684
+ ].join('\n')
1685
+ }
1686
+
1687
+ /**
1688
+ * Clear the index and all associated tracking
1689
+ */
1690
+ clearIndex(): void {
1691
+ this.storageIndex.clear()
1692
+ this.indexMemoryBytes = 0
1693
+ this.memoryWarningEmitted = false
1694
+ this.lastLookupKey = null
1695
+ this.lastLookupEntry = null
1696
+ }
1697
+
1698
+ resetHealth(tier?: StorageTier): void {
1699
+ if (tier) this.tierHealth[tier] = 'healthy'
1700
+ else this.tierHealth = { hot: 'healthy', warm: 'healthy', cold: 'healthy' }
1701
+ }
1702
+
1703
+ getConfig(): InternalConfig { return { ...this.config } }
1704
+
1705
+ // DO Lifecycle Hooks
1706
+ async onHibernate(): Promise<HibernateResult> {
1707
+ const timestamp = Date.now()
1708
+ let persistedCount = 0
1709
+ const hotKeys: string[] = []
1710
+ for (const entry of this.storageIndex.values()) { if (entry.tier === 'hot') hotKeys.push(entry.key) }
1711
+ for (const key of hotKeys) {
1712
+ const entry = this.storageIndex.get(key)
1713
+ if (entry) { entry.tier = 'warm'; persistedCount++ }
1714
+ }
1715
+ const syncedToCold = await this.syncDirtyToCold()
1716
+ const indexSnapshot = new Map(this.storageIndex)
1717
+ return { success: true, persistedCount, syncedToCold, hotKeys, indexSnapshot, timestamp }
1718
+ }
1719
+
1720
+ async onWake(hibernateState: HibernateState | null, options?: WakeOptions): Promise<WakeResult> {
1721
+ let restoredCount = 0, prefetchedCount = 0, indexRepairs = 0
1722
+ const indexValidated = options?.validateIndex ?? false
1723
+ if (hibernateState?.hotKeys) {
1724
+ for (const key of hibernateState.hotKeys) {
1725
+ try {
1726
+ const data = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1727
+ if (data) {
1728
+ if (this.swrCacheLayer) await this.swrCacheLayer.put(key, data)
1729
+ else if (this.cacheLayer) await this.cacheLayer.put(key, data, { lastAccessed: Date.now(), accessCount: 1 })
1730
+ this.updateIndex(key, 'hot', data.length)
1731
+ restoredCount++
1732
+ }
1733
+ } catch (e) {
1734
+ const error = e instanceof Error ? e : new Error(String(e))
1735
+ this.notifyError({ context: 'wake_restore', error, key, timestamp: Date.now() })
1736
+ }
1737
+ }
1738
+ }
1739
+ if (options?.prefetchThreshold && hibernateState?.indexSnapshot) {
1740
+ for (const [key, entry] of hibernateState.indexSnapshot) {
1741
+ if (entry.accessCount >= options.prefetchThreshold && entry.tier !== 'hot') {
1742
+ try { const event = await this.promoteToHot(key); if (event.success) prefetchedCount++ } catch (e) {
1743
+ const error = e instanceof Error ? e : new Error(String(e))
1744
+ this.notifyError({ context: 'wake_prefetch', error, key, timestamp: Date.now() })
1745
+ }
1746
+ }
1747
+ }
1748
+ }
1749
+ if (indexValidated) {
1750
+ for (const [key, entry] of this.storageIndex) {
1751
+ let found = false, actualTier: StorageTier | undefined
1752
+ const warmData = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1753
+ if (warmData) { found = true; actualTier = 'warm' }
1754
+ if (!found) {
1755
+ const coldData = await this.r2Layer.get(key)
1756
+ if (coldData) { found = true; actualTier = 'cold' }
1757
+ }
1758
+ if (!found) { this.storageIndex.delete(key); indexRepairs++ }
1759
+ else if (actualTier && actualTier !== entry.tier && entry.tier !== 'hot') { entry.tier = actualTier; indexRepairs++ }
1760
+ }
1761
+ }
1762
+ return { restoredCount, prefetchedCount, indexValidated, indexRepairs }
1763
+ }
1764
+
1765
+ // Background Task Scheduling
1766
+ async scheduleAlarm(alarmConfig: AlarmConfig): Promise<void> {
1767
+ const storage = this.doStorage as DurableObjectStorage & { getAlarm?: () => Promise<number | null>; setAlarm?: (time: number) => Promise<void> }
1768
+ if (alarmConfig.skipIfExists && storage.getAlarm) {
1769
+ const existingAlarm = await storage.getAlarm()
1770
+ if (existingAlarm !== null && existingAlarm !== undefined) return
1771
+ }
1772
+ const scheduledTime = Date.now() + alarmConfig.delayMs
1773
+ if (storage.setAlarm) await storage.setAlarm(scheduledTime)
1774
+ }
1775
+
1776
+ async handleAlarm(alarmConfig: AlarmConfig & { rescheduleMs?: number }): Promise<AlarmResult> {
1777
+ const result: AlarmResult = {}
1778
+ switch (alarmConfig.type) {
1779
+ case 'demotion': { const events = await this.runDemotionCycle(); result.demotionCount = events.length; break }
1780
+ case 'sync': { result.syncedCount = await this.syncDirtyToCold(); break }
1781
+ default: assertNever(alarmConfig.type, 'Unknown alarm type')
1782
+ }
1783
+ if (alarmConfig.rescheduleMs) {
1784
+ await this.scheduleAlarm({ type: alarmConfig.type, delayMs: alarmConfig.rescheduleMs })
1785
+ result.nextAlarmMs = alarmConfig.rescheduleMs
1786
+ }
1787
+ return result
1788
+ }
1789
+
1790
+ // Circuit Breaker Pattern
1791
+ getCircuitBreakerState(tier: StorageTier): CircuitBreakerStateInfo {
1792
+ const state = { ...this.circuitBreakers[tier] }
1793
+ if (state.state === 'open' && state.openedAt) {
1794
+ const resetTimeout = this.config.circuitBreaker?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_RESET_TIMEOUT_MS
1795
+ if (Date.now() - state.openedAt >= resetTimeout) { this.circuitBreakers[tier].state = 'half-open'; state.state = 'half-open' }
1796
+ }
1797
+ return state
1798
+ }
1799
+
1800
+ setCircuitBreakerState(tier: StorageTier, state: CircuitBreakerState): void {
1801
+ this.circuitBreakers[tier].state = state
1802
+ if (state === 'open') this.circuitBreakers[tier].openedAt = Date.now()
1803
+ }
1804
+
1805
+ // Metrics Aggregation
1806
+ getAggregatedMetrics(): AggregatedMetrics {
1807
+ const hotStats = this.swrCacheLayer?.getStats() ?? this.cacheLayer?.getStats() ?? { hits: 0, misses: 0, writes: 0, bytesRead: 0, bytesWritten: 0, hitRatio: 0 }
1808
+ const coldStats = this.r2Layer.getStats()
1809
+ let warmBytesStored = 0, coldBytesStored = 0
1810
+ for (const entry of this.storageIndex.values()) {
1811
+ if (entry.tier === 'warm') warmBytesStored += entry.size
1812
+ if (entry.tier === 'cold') coldBytesStored += entry.size
1813
+ }
1814
+ const metrics: AggregatedMetrics = {
1815
+ timestamp: Date.now(),
1816
+ period: 'current',
1817
+ tiers: {
1818
+ hot: { reads: 'hits' in hotStats ? hotStats.hits + hotStats.misses : 0, writes: hotStats.writes, hitRatio: 'hitRatio' in hotStats ? hotStats.hitRatio : 0, bytesRead: hotStats.bytesRead, bytesWritten: hotStats.bytesWritten },
1819
+ warm: { reads: this.warmStats.reads, writes: this.warmStats.writes, bytesStored: warmBytesStored, bytesRead: this.warmStats.bytesRead, bytesWritten: this.warmStats.bytesWritten },
1820
+ cold: { reads: coldStats.reads, writes: coldStats.writes, bytesStored: coldBytesStored, bytesRead: coldStats.bytesRead, bytesWritten: coldStats.bytesWritten },
1821
+ },
1822
+ migrations: { promotions: this.promotionStats.coldToWarm + this.promotionStats.warmToHot, demotions: this.demotionStats.hotToWarm + this.demotionStats.warmToCold, failures: this.promotionStats.failed + this.demotionStats.failed },
1823
+ circuitBreakers: { hot: this.getCircuitBreakerState('hot'), warm: this.getCircuitBreakerState('warm'), cold: this.getCircuitBreakerState('cold') },
1824
+ errors: { totalErrors: this.errorCounts.hot + this.errorCounts.warm + this.errorCounts.cold, byTier: { ...this.errorCounts } },
1825
+ }
1826
+ if (this.config.metrics?.trackLatency && this.latencyMeasurements.length > 0) {
1827
+ const sorted = [...this.latencyMeasurements].sort((a, b) => a - b)
1828
+ metrics.latency = { p50: sorted[Math.floor(sorted.length * 0.5)] ?? 0, p95: sorted[Math.floor(sorted.length * 0.95)] ?? 0, p99: sorted[Math.floor(sorted.length * 0.99)] ?? 0 }
1829
+ }
1830
+ if (this.config.metrics?.trackCost && this.config.metrics.costConfig) {
1831
+ const cc = this.config.metrics.costConfig
1832
+ const r2Cost = (coldBytesStored / (1024 * 1024 * 1024)) * cc.r2StoragePerGBMonth + coldStats.writes * cc.r2ClassAOperations + coldStats.reads * cc.r2ClassBOperations
1833
+ const doCost = (warmBytesStored / (1024 * 1024 * 1024)) * cc.doStoragePerGBMonth
1834
+ metrics.estimatedCost = { r2: r2Cost, do: doCost, cache: 0, total: r2Cost + doCost }
1835
+ }
1836
+ return metrics
1837
+ }
1838
+
1839
+ exportMetrics(format: 'prometheus' | 'json'): string | AggregatedMetrics {
1840
+ const metrics = this.getAggregatedMetrics()
1841
+ if (format === 'json') return metrics
1842
+ const lines: string[] = [
1843
+ '# HELP tiered_storage_writes_total Total number of writes', '# TYPE tiered_storage_writes_total counter',
1844
+ `tiered_storage_writes_total{tier="hot"} ${metrics.tiers.hot.writes}`, `tiered_storage_writes_total{tier="warm"} ${metrics.tiers.warm.writes}`, `tiered_storage_writes_total{tier="cold"} ${metrics.tiers.cold.writes}`, '',
1845
+ '# HELP tiered_storage_bytes_written Total bytes written', '# TYPE tiered_storage_bytes_written counter',
1846
+ `tiered_storage_bytes_written{tier="hot"} ${metrics.tiers.hot.bytesWritten}`, `tiered_storage_bytes_written{tier="warm"} ${metrics.tiers.warm.bytesWritten}`, `tiered_storage_bytes_written{tier="cold"} ${metrics.tiers.cold.bytesWritten}`, '',
1847
+ '# HELP tiered_storage_reads_total Total number of reads', '# TYPE tiered_storage_reads_total counter',
1848
+ `tiered_storage_reads_total{tier="hot"} ${metrics.tiers.hot.reads}`, `tiered_storage_reads_total{tier="warm"} ${metrics.tiers.warm.reads}`, `tiered_storage_reads_total{tier="cold"} ${metrics.tiers.cold.reads}`, '',
1849
+ '# HELP tiered_storage_errors_total Total number of errors', '# TYPE tiered_storage_errors_total counter',
1850
+ `tiered_storage_errors_total{tier="hot"} ${metrics.errors.byTier.hot}`, `tiered_storage_errors_total{tier="warm"} ${metrics.errors.byTier.warm}`, `tiered_storage_errors_total{tier="cold"} ${metrics.errors.byTier.cold}`,
1851
+ ]
1852
+ return lines.join('\n')
1853
+ }
1854
+
1855
+ // Component Integration
1856
+ createVFSInterface(_options?: { pageSize?: number; promotionThreshold?: number }): VFSInterface {
1857
+ return {
1858
+ readPage: async (pageId: string) => { const result = await this.read(pageId); return result.data },
1859
+ writePage: async (pageId: string, data: Uint8Array) => { await this.write(pageId, data) },
1860
+ deletePage: async (pageId: string) => { await this.delete(pageId) },
1861
+ }
1862
+ }
1863
+
1864
+ configureAutoPromoter(config: AutoPromoterConfig): void {
1865
+ this.autoPromoterConfig = { ...config }
1866
+ this.config.coldToWarmThreshold = config.coldToWarm.accessThreshold
1867
+ this.config.warmToHotThreshold = config.warmToHot.accessThreshold
1868
+ if (config.warmToHot.timeWindowMs) this.config.promotionWindowMs = config.warmToHot.timeWindowMs
1869
+ }
1870
+
1871
+ getAutoPromoterConfig(): AutoPromoterConfig { return { ...this.autoPromoterConfig } }
1872
+
1873
+ configureAutoDemoter(config: AutoDemoterConfig): void {
1874
+ this.autoDemoterConfig = { ...config }
1875
+ this.config.hotTtlMs = config.hotToWarm.ttlMs
1876
+ this.config.warmTtlMs = config.warmToCold.ttlMs
1877
+ }
1878
+
1879
+ getAutoDemoterConfig(): AutoDemoterConfig { return { ...this.autoDemoterConfig } }
1880
+
1881
+ setDemotionStrategy(tier: StorageTier, strategy: DemotionStrategy): void { this.demotionStrategies[tier] = strategy }
1882
+
1883
+ async runDemotionWithStrategy(tier: StorageTier, count: number): Promise<TierMigrationEvent[]> {
1884
+ const strategy = this.demotionStrategies[tier]
1885
+ if (!strategy) { if (tier === 'hot') return this.evictLRUFromHot(count); return [] }
1886
+ const entries: StorageIndexEntry[] = []
1887
+ for (const entry of this.storageIndex.values()) { if (entry.tier === tier) entries.push(entry) }
1888
+ const keysToDemote = strategy(entries, count)
1889
+ const events: TierMigrationEvent[] = []
1890
+ for (const key of keysToDemote) {
1891
+ const event = tier === 'hot' ? await this.demoteFromHot(key, 'lru_eviction') : await this.demoteFromWarm(key, 'lru_eviction')
1892
+ events.push(event)
1893
+ }
1894
+ return events
1895
+ }
1896
+
1897
+ // Error Recovery
1898
+ async runConsistencyCheck(): Promise<{ inconsistencies: number; repairs: { key: string; issue: string; action: string }[] }> {
1899
+ const repairs: { key: string; issue: string; action: string }[] = []
1900
+ for (const [key, entry] of this.storageIndex) {
1901
+ let found = false, actualTier: StorageTier | undefined
1902
+ const warmData = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
1903
+ if (warmData) { found = true; actualTier = 'warm' }
1904
+ if (!found) { const coldData = await this.r2Layer.get(key); if (coldData) { found = true; actualTier = 'cold' } }
1905
+ if (!found) { repairs.push({ key, issue: `Data not found in ${entry.tier} tier`, action: 'removed from index' }); this.storageIndex.delete(key) }
1906
+ else if (actualTier && actualTier !== entry.tier && entry.tier !== 'hot') { repairs.push({ key, issue: `Index says ${entry.tier} but found in ${actualTier}`, action: `updated tier to ${actualTier}` }); entry.tier = actualTier }
1907
+ }
1908
+ return { inconsistencies: repairs.length, repairs }
1909
+ }
1910
+
1911
+ // Advanced Tier Management
1912
+ setIndexEntryTimestamp(key: string, field: 'created' | 'lastAccess' | 'modified', timestamp: number): void {
1913
+ const entry = this.storageIndex.get(key)
1914
+ if (entry) entry[field] = timestamp
1915
+ }
1916
+
1917
+ getAccessPatternMetrics(key: string): { isBurst: boolean; accessRate: number; recentAccesses: number } {
1918
+ const entry = this.storageIndex.get(key)
1919
+ if (!entry) return { isBurst: false, accessRate: 0, recentAccesses: 0 }
1920
+ const ageMs = Math.max(1, Date.now() - entry.created)
1921
+ const accessRate = (entry.accessCount / ageMs) * 1000
1922
+ return { isBurst: accessRate > 5, accessRate, recentAccesses: entry.accessCount }
1923
+ }
1924
+
1925
+ async evictByMemoryPressure(bytesToFree: number): Promise<TierMigrationEvent[]> {
1926
+ const events: TierMigrationEvent[] = []
1927
+ let freedBytes = 0
1928
+ const hotEntries = Array.from(this.storageIndex.values()).filter(e => e.tier === 'hot').sort((a, b) => b.size - a.size)
1929
+ for (const entry of hotEntries) {
1930
+ if (freedBytes >= bytesToFree) break
1931
+ const event = await this.demoteFromHot(entry.key, 'lru_eviction')
1932
+ events.push(event)
1933
+ if (event.success) freedBytes += entry.size
1934
+ }
1935
+ return events
1936
+ }
1937
+
1938
+ setEntryPriority(key: string, priority: 'low' | 'normal' | 'high'): void { this.entryPriorities.set(key, priority) }
1939
+ getEntryPriority(key: string): 'low' | 'normal' | 'high' { return this.entryPriorities.get(key) ?? 'normal' }
1940
+
1941
+ async batchPromoteToWarm(keys: string[]): Promise<TierMigrationEvent[]> { return Promise.all(keys.map(key => this.promoteToWarm(key))) }
1942
+ async batchDemoteFromWarm(keys: string[]): Promise<TierMigrationEvent[]> { return Promise.all(keys.map(key => this.demoteFromWarm(key))) }
1943
+
1944
+ async healthCheck(): Promise<{ overall: 'healthy' | 'degraded' | 'unhealthy'; tiers: Record<StorageTier, { status: string; latency: number; error?: string }>; latency: Record<StorageTier, number> }> {
1945
+ const checkTier = async (tier: StorageTier): Promise<{ status: string; latency: number; error?: string }> => {
1946
+ const start = Date.now()
1947
+ try {
1948
+ if (tier === 'hot') return { status: this.cacheLayer || this.swrCacheLayer ? 'healthy' : 'unavailable', latency: Date.now() - start }
1949
+ if (tier === 'warm') { await this.doStorage.get('__health_check__'); return { status: 'healthy', latency: Date.now() - start } }
1950
+ await this.r2Layer.get('__health_check__'); return { status: 'healthy', latency: Date.now() - start }
1951
+ } catch (e) { return { status: 'unhealthy', latency: Date.now() - start, error: e instanceof Error ? e.message : String(e) } }
1952
+ }
1953
+ const [hot, warm, cold] = await Promise.all([checkTier('hot'), checkTier('warm'), checkTier('cold')])
1954
+ const statuses = [hot.status, warm.status, cold.status]
1955
+ let overall: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'
1956
+ if (statuses.some(s => s === 'unhealthy')) overall = 'degraded'
1957
+ if (statuses.every(s => s === 'unhealthy')) overall = 'unhealthy'
1958
+ return { overall, tiers: { hot, warm, cold }, latency: { hot: hot.latency, warm: warm.latency, cold: cold.latency } }
1959
+ }
1960
+
1961
+ getMemoryUsage(): { hot: number; warm: number; cold: number; total: number; limit: number; utilizationPercent: number } {
1962
+ let hot = 0, warm = 0, cold = 0
1963
+ for (const entry of this.storageIndex.values()) {
1964
+ if (entry.tier === 'hot') hot += entry.size
1965
+ else if (entry.tier === 'warm') warm += entry.size
1966
+ else cold += entry.size
1967
+ }
1968
+ const total = hot + warm + cold
1969
+ const limit = (this.memoryLimits.hot ?? Infinity) + (this.memoryLimits.warm ?? Infinity)
1970
+ return { hot, warm, cold, total, limit, utilizationPercent: limit === Infinity ? 0 : (total / limit) * 100 }
1971
+ }
1972
+
1973
+ setMemoryLimit(limits: Partial<Record<StorageTier, number>>): void { this.memoryLimits = { ...this.memoryLimits, ...limits } }
1974
+ getMemoryLimits(): Partial<Record<StorageTier, number>> { return { ...this.memoryLimits } }
1975
+
1976
+ async prefetch(keys: string[], options?: { targetTier?: StorageTier; maxConcurrency?: number }): Promise<{ prefetchedCount: number; failedKeys: string[] }> {
1977
+ const targetTier = options?.targetTier ?? 'warm'
1978
+ const maxConcurrency = options?.maxConcurrency ?? 5
1979
+ const failedKeys: string[] = []
1980
+ let prefetchedCount = 0
1981
+ for (let i = 0; i < keys.length; i += maxConcurrency) {
1982
+ const batch = keys.slice(i, i + maxConcurrency)
1983
+ const results = await Promise.allSettled(batch.map(async key => {
1984
+ const event = targetTier === 'hot' ? await this.promoteToHot(key) : await this.promoteToWarm(key)
1985
+ if (!event.success) throw new Error(event.error)
1986
+ return key
1987
+ }))
1988
+ for (let j = 0; j < results.length; j++) {
1989
+ const result = results[j]
1990
+ if (result?.status === 'fulfilled') prefetchedCount++
1991
+ else if (result?.status === 'rejected' && batch[j]) failedKeys.push(batch[j]!)
1992
+ }
1993
+ }
1994
+ return { prefetchedCount, failedKeys }
1995
+ }
1996
+
1997
+ subscribeToEvents(callback: TierMigrationCallback, options?: { filter?: { type?: 'promotion' | 'demotion' } }): () => void {
1998
+ const subscriber = { callback, filter: options?.filter }
1999
+ this.eventSubscribers.push(subscriber)
2000
+ return () => { const idx = this.eventSubscribers.indexOf(subscriber); if (idx !== -1) this.eventSubscribers.splice(idx, 1) }
2001
+ }
2002
+
2003
+ async createSnapshot(): Promise<{ version: number; timestamp: number; index: StorageIndexEntry[]; stats: OrchestratorStats }> {
2004
+ return { version: 1, timestamp: Date.now(), index: Array.from(this.storageIndex.values()), stats: this.getStats() }
2005
+ }
2006
+
2007
+ async restoreFromSnapshot(snapshot: { version: number; timestamp: number; index: StorageIndexEntry[]; stats: Record<string, unknown> }): Promise<void> {
2008
+ this.storageIndex.clear()
2009
+ this.indexMemoryBytes = 0
2010
+ for (const entry of snapshot.index) {
2011
+ this.storageIndex.set(entry.key, { ...entry })
2012
+ this.indexMemoryBytes += TieredStorageOrchestrator.ENTRY_MEMORY_OVERHEAD + entry.key.length * 2
2013
+ }
2014
+ }
2015
+
2016
+ async warmup(options: { strategy: 'all' | 'most-accessed' | 'recent'; targetTier: StorageTier; maxBytes?: number; limit?: number }): Promise<{ warmedUp: number; bytesTransferred: number; keys: string[] }> {
2017
+ let candidates = Array.from(this.storageIndex.values()).filter(e => e.tier === 'cold')
2018
+ if (options.strategy === 'most-accessed') candidates.sort((a, b) => b.accessCount - a.accessCount)
2019
+ else if (options.strategy === 'recent') candidates.sort((a, b) => b.lastAccess - a.lastAccess)
2020
+ if (options.limit) candidates = candidates.slice(0, options.limit)
2021
+ let bytesTransferred = 0
2022
+ const warmedKeys: string[] = []
2023
+ for (const entry of candidates) {
2024
+ if (options.maxBytes && bytesTransferred + entry.size > options.maxBytes) break
2025
+ const event = options.targetTier === 'hot' ? await this.promoteToHot(entry.key) : await this.promoteToWarm(entry.key)
2026
+ if (event.success) { bytesTransferred += entry.size; warmedKeys.push(entry.key) }
2027
+ }
2028
+ return { warmedUp: warmedKeys.length, bytesTransferred, keys: warmedKeys }
2029
+ }
2030
+
2031
+ async garbageCollect(options?: { compactCold?: boolean }): Promise<{ removedEntries: number; compactionStats?: { bytesReclaimed: number } }> {
2032
+ let removedEntries = 0
2033
+ const keysToRemove: string[] = []
2034
+ for (const [key, entry] of this.storageIndex) {
2035
+ if (entry.tier === 'hot') continue
2036
+ let exists = false
2037
+ if (entry.tier === 'warm') { const data = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined; exists = !!data }
2038
+ else { const data = await this.r2Layer.get(key); exists = !!data }
2039
+ if (!exists) keysToRemove.push(key)
2040
+ }
2041
+ for (const key of keysToRemove) { this.storageIndex.delete(key); removedEntries++ }
2042
+ return options?.compactCold ? { removedEntries, compactionStats: { bytesReclaimed: 0 } } : { removedEntries }
2043
+ }
2044
+
2045
+ pinToTier(key: string, _tier: StorageTier): void { this.pinnedEntries.add(key) }
2046
+ unpinFromTier(key: string): void { this.pinnedEntries.delete(key) }
2047
+ isPinned(key: string): boolean { return this.pinnedEntries.has(key) }
2048
+
2049
+ getCostBreakdown(): { storage: { r2: number; do: number; cache: number }; operations: { r2Writes: number; r2Reads: number; doWrites: number; doReads: number }; total: number; projectedMonthly: number } {
2050
+ const metrics = this.getAggregatedMetrics()
2051
+ const cc = this.config.metrics?.costConfig ?? { r2StoragePerGBMonth: 0.015, r2ClassAOperations: 0.0000045, r2ClassBOperations: 0.00000036, doStoragePerGBMonth: 0.20, cacheOperations: 0 }
2052
+ const r2Storage = (metrics.tiers.cold.bytesStored / (1024 ** 3)) * cc.r2StoragePerGBMonth
2053
+ const doStorage = (metrics.tiers.warm.bytesStored / (1024 ** 3)) * cc.doStoragePerGBMonth
2054
+ const r2Writes = metrics.tiers.cold.writes * cc.r2ClassAOperations
2055
+ const r2Reads = metrics.tiers.cold.reads * cc.r2ClassBOperations
2056
+ const total = r2Storage + doStorage + r2Writes + r2Reads
2057
+ return { storage: { r2: r2Storage, do: doStorage, cache: 0 }, operations: { r2Writes, r2Reads, doWrites: 0, doReads: 0 }, total, projectedMonthly: total }
2058
+ }
2059
+
2060
+ async optimizeTierPlacement(): Promise<{ promotions: Array<{ key: string; currentTier: StorageTier; suggestedTier: StorageTier; reason: string }>; demotions: Array<{ key: string; currentTier: StorageTier; suggestedTier: StorageTier; reason: string }>; estimatedSavings: number }> {
2061
+ const promotions: Array<{ key: string; currentTier: StorageTier; suggestedTier: StorageTier; reason: string }> = []
2062
+ const demotions: Array<{ key: string; currentTier: StorageTier; suggestedTier: StorageTier; reason: string }> = []
2063
+ for (const entry of this.storageIndex.values()) {
2064
+ if (entry.tier === 'cold' && entry.accessCount > 10) promotions.push({ key: entry.key, currentTier: 'cold', suggestedTier: 'warm', reason: 'High access count' })
2065
+ if (entry.tier === 'warm' && entry.accessCount < 5 && Date.now() - entry.lastAccess > 86400000) demotions.push({ key: entry.key, currentTier: 'warm', suggestedTier: 'cold', reason: 'Low access, stale' })
2066
+ }
2067
+ return { promotions, demotions, estimatedSavings: 0 }
2068
+ }
2069
+
2070
+ async ensureDurability(key: string): Promise<{ durable: boolean; tier: StorageTier | null; copied: boolean }> {
2071
+ const coldData = await this.r2Layer.get(key)
2072
+ if (coldData) return { durable: true, tier: 'cold', copied: false }
2073
+ const warmData = await this.doStorage.get(this.doKey(key)) as Uint8Array | undefined
2074
+ if (warmData) { await this.r2Layer.put(key, warmData, { customMetadata: { durabilityBackup: 'true' } }); return { durable: true, tier: 'warm', copied: true } }
2075
+ return { durable: false, tier: null, copied: false }
2076
+ }
2077
+ }
2078
+
2079
+ export function createTieredStorageOrchestrator(config: TieredOrchestratorConfig): TieredStorageOrchestrator {
2080
+ return new TieredStorageOrchestrator(config)
2081
+ }