@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,2650 @@
1
+ /**
2
+ * Memory Pressure Monitoring and OOM Protection for PostgresDO
3
+ *
4
+ * This module provides memory monitoring capabilities for PGLite running
5
+ * in Cloudflare Workers (128MB limit). It detects high memory usage and
6
+ * can automatically take protective actions to prevent OOM crashes.
7
+ *
8
+ * ## Key Features
9
+ *
10
+ * - **Real-time memory pressure tracking** - Monitors heap usage and classifies
11
+ * pressure levels (normal, warning, critical, oom)
12
+ * - **Automatic extension unloading** - Drops least-important PostgreSQL extensions
13
+ * when memory is critically low
14
+ * - **GC hint triggering** - Suggests garbage collection at configurable thresholds
15
+ * with rate limiting to prevent thrashing
16
+ * - **Cache eviction** - Automatically evicts cache entries under pressure using
17
+ * configurable strategies (LRU, largest-first)
18
+ * - **Query management** - Pauses or rejects queries under extreme pressure
19
+ * - **Recovery with hysteresis** - Prevents oscillation by requiring sustained
20
+ * normal pressure before full recovery
21
+ * - **Metrics export** - Prometheus and JSON format metrics for observability
22
+ *
23
+ * ## Usage
24
+ *
25
+ * ```typescript
26
+ * import { createMemoryPressureManager } from './memory-pressure'
27
+ *
28
+ * const manager = createMemoryPressureManager({
29
+ * warningThresholdPercent: 70,
30
+ * criticalThresholdPercent: 85,
31
+ * autoUnloadExtensions: true,
32
+ * })
33
+ *
34
+ * // Listen for pressure changes
35
+ * const unsubscribe = manager.onPressureChange((event) => {
36
+ * console.log(`Pressure: ${event.previousLevel} -> ${event.currentLevel}`)
37
+ * })
38
+ *
39
+ * // Start monitoring
40
+ * manager.startMonitoring()
41
+ *
42
+ * // Check status at any time
43
+ * const stats = manager.getMemoryStats()
44
+ *
45
+ * // Clean up when done
46
+ * manager.dispose()
47
+ * ```
48
+ *
49
+ * ## Cloudflare Workers Constraints
50
+ *
51
+ * - 128MB total memory limit per isolate
52
+ * - ~15MB static (WASM + data + JS)
53
+ * - ~16MB shared_buffers
54
+ * - ~16MB WAL + buffers
55
+ * - ~10MB PostgreSQL catalog/metadata
56
+ * - ~71MB available for queries, results, and runtime
57
+ *
58
+ * @module worker/memory-pressure
59
+ */
60
+
61
+ import type {
62
+ MemoryPressureLevel,
63
+ MemoryStats,
64
+ MemoryPressureEvent,
65
+ MemoryPressureAction,
66
+ MemoryPressureActionResult,
67
+ MemoryPressureConfig,
68
+ MemoryPressureListener,
69
+ } from './types'
70
+
71
+ import type { TieredStorageOrchestrator, TierMigrationEvent } from '../storage/tiered-orchestrator'
72
+
73
+ // =============================================================================
74
+ // Imported Constants (from centralized config)
75
+ // =============================================================================
76
+
77
+ import {
78
+ WORKER_MEMORY_LIMIT_BYTES,
79
+ PGLITE_BASELINE_BYTES,
80
+ DEFAULT_WARNING_THRESHOLD_PERCENT,
81
+ DEFAULT_CRITICAL_THRESHOLD_PERCENT,
82
+ DEFAULT_OOM_THRESHOLD_PERCENT,
83
+ MEMORY_SPIKE_THRESHOLD_BYTES,
84
+ DEFAULT_METRICS_HISTORY_SIZE,
85
+ } from '../config/memory'
86
+
87
+ import {
88
+ DEFAULT_CHECK_INTERVAL_MS,
89
+ DEFAULT_GC_HINT_MIN_INTERVAL_MS,
90
+ DEFAULT_RECOVERY_HYSTERESIS_MS,
91
+ DEFAULT_QUERY_PAUSE_TIMEOUT_MS,
92
+ DEFAULT_RECOVERY_STEP_INTERVAL_MS,
93
+ DEFAULT_SUSTAINED_PRESSURE_THRESHOLD_MS,
94
+ DEFAULT_MAX_LISTENERS,
95
+ } from '../config/timeouts'
96
+
97
+ // =============================================================================
98
+ // Local Constant Aliases (for backward compatibility in this module)
99
+ // =============================================================================
100
+
101
+ /** Default Cloudflare Worker memory limit: 128MB */
102
+ const DEFAULT_MEMORY_LIMIT_BYTES = WORKER_MEMORY_LIMIT_BYTES
103
+
104
+ /** Default pressure thresholds (percentage of memory limit) */
105
+ const DEFAULT_WARNING_THRESHOLD = DEFAULT_WARNING_THRESHOLD_PERCENT
106
+ const DEFAULT_CRITICAL_THRESHOLD = DEFAULT_CRITICAL_THRESHOLD_PERCENT
107
+ const DEFAULT_OOM_THRESHOLD = DEFAULT_OOM_THRESHOLD_PERCENT
108
+
109
+ /** Spike detection threshold: 10MB sudden increase */
110
+ const SPIKE_DETECTION_THRESHOLD_BYTES = MEMORY_SPIKE_THRESHOLD_BYTES
111
+
112
+ /**
113
+ * Extensions safe to unload under memory pressure, ordered by priority.
114
+ * First = least important = dropped first.
115
+ */
116
+ const DEFAULT_EXTENSION_UNLOAD_PRIORITY = [
117
+ 'tablefunc', // Rarely used in typical workloads
118
+ 'fuzzystrmatch', // Nice to have for fuzzy matching
119
+ 'unaccent', // Text processing utility
120
+ 'btree_gist', // Index type - usually not critical
121
+ 'btree_gin', // Index type - usually not critical
122
+ 'intarray', // Array functions
123
+ 'cube', // Multi-dimensional data
124
+ 'earthdistance', // Geo calculations (depends on cube)
125
+ 'ltree', // Tree data
126
+ 'hstore', // Key-value pairs
127
+ 'citext', // Case-insensitive text
128
+ 'pg_trgm', // Trigram matching
129
+ 'pgcrypto', // Crypto functions - often important
130
+ 'uuid_ossp', // UUID generation - often important
131
+ 'vector', // Vector search - often critical for AI apps
132
+ ]
133
+
134
+ /** Default features that can be disabled under pressure */
135
+ const DEFAULT_FEATURES_TO_DISABLE = ['caching', 'prefetching', 'parallel-queries']
136
+
137
+ /** Default eviction target percentages by pressure level */
138
+ const DEFAULT_EVICTION_TARGET_PERCENT: Record<MemoryPressureLevel, number> = {
139
+ normal: 0,
140
+ warning: 20,
141
+ critical: 50,
142
+ oom: 100,
143
+ }
144
+
145
+ /** Pressure level numeric indices for fast comparison */
146
+ const PRESSURE_LEVEL_INDEX: Record<MemoryPressureLevel, number> = {
147
+ normal: 0,
148
+ warning: 1,
149
+ critical: 2,
150
+ oom: 3,
151
+ }
152
+
153
+ /** Default demotion configuration per pressure level */
154
+ const DEFAULT_DEMOTION_CONFIG: DemotionConfig = {
155
+ warning: { hotEvictCount: 10, warmDemoteCount: 0 },
156
+ critical: { hotEvictCount: 25, warmDemoteCount: 5 },
157
+ oom: { hotEvictCount: 50, warmDemoteCount: 15 },
158
+ }
159
+
160
+ /** Default minimum interval between pressure-triggered demotions */
161
+ const DEFAULT_DEMOTION_MIN_INTERVAL_MS = 500
162
+
163
+ // =============================================================================
164
+ // Optimized Constants for Memory Pressure Heuristics
165
+ // =============================================================================
166
+
167
+ /**
168
+ * EMA (Exponential Moving Average) smoothing factor for heap usage.
169
+ * Range: 0-1, higher = more weight on recent samples, lower = more smoothing.
170
+ * 0.3 provides good balance between responsiveness and noise reduction.
171
+ */
172
+ const EMA_SMOOTHING_FACTOR = 0.3
173
+
174
+ /**
175
+ * Minimum samples required for reliable trend detection.
176
+ * Below this, predictions have low confidence.
177
+ */
178
+ const MIN_SAMPLES_FOR_TREND = 5
179
+
180
+ /**
181
+ * Minimum samples for high-confidence OOM prediction.
182
+ */
183
+ const MIN_SAMPLES_FOR_OOM_PREDICTION = 10
184
+
185
+ /**
186
+ * Growth rate threshold for "stable" classification (1KB/s).
187
+ * Memory changes below this are considered noise.
188
+ */
189
+ const STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC = 1024
190
+
191
+ /**
192
+ * Rapid growth threshold (5MB/s) - triggers more aggressive responses.
193
+ */
194
+ const RAPID_GROWTH_THRESHOLD_BYTES_PER_SEC = 5 * 1024 * 1024
195
+
196
+ /**
197
+ * OOM prediction horizon - warn if OOM predicted within this time.
198
+ */
199
+ const OOM_PREDICTION_HORIZON_MS = 60000
200
+
201
+ /**
202
+ * Hysteresis margin for pressure level changes (2%).
203
+ * Prevents oscillation when hovering near thresholds.
204
+ * Reserved for future hysteresis implementation.
205
+ */
206
+ const _PRESSURE_HYSTERESIS_PERCENT = 2
207
+ void _PRESSURE_HYSTERESIS_PERCENT // Suppress unused warning - reserved for future use
208
+
209
+ /**
210
+ * Heap sampling constants - optimized for balance between accuracy and speed.
211
+ * Reserved for future heap sampling implementation.
212
+ */
213
+ const _HEAP_SAMPLE_STRIDE_SHIFT = 8 // ~256 samples, uses bit shift for speed
214
+ const _HEAP_SAMPLE_UNROLL_FACTOR = 4 // Unroll factor for loop optimization
215
+ void _HEAP_SAMPLE_STRIDE_SHIFT // Suppress unused warning - reserved for future use
216
+ void _HEAP_SAMPLE_UNROLL_FACTOR // Suppress unused warning - reserved for future use
217
+
218
+ // =============================================================================
219
+ // Types
220
+ // =============================================================================
221
+
222
+ /**
223
+ * Demotion counts per pressure level
224
+ */
225
+ export interface DemotionLevelConfig {
226
+ /** Number of entries to evict from hot tier */
227
+ hotEvictCount: number
228
+ /** Number of entries to demote from warm tier */
229
+ warmDemoteCount: number
230
+ }
231
+
232
+ /**
233
+ * Configuration for demotions per pressure level
234
+ */
235
+ export interface DemotionConfig {
236
+ warning: DemotionLevelConfig
237
+ critical: DemotionLevelConfig
238
+ oom: DemotionLevelConfig
239
+ }
240
+
241
+ /**
242
+ * Demotion metrics tracking pressure-triggered demotions
243
+ */
244
+ export interface DemotionMetrics {
245
+ /** Total number of pressure-triggered demotions */
246
+ pressureTriggeredDemotions: number
247
+ /** Demotions by pressure level */
248
+ demotionsByPressureLevel: Record<MemoryPressureLevel, number>
249
+ /** Failed demotions */
250
+ failedDemotions: number
251
+ /** Total bytes freed by demotions */
252
+ bytesFreedByDemotion: number
253
+ /** Last demotion timestamp */
254
+ lastDemotionTimestamp: number | null
255
+ }
256
+
257
+ /**
258
+ * Pressure demotion event emitted when pressure-triggered demotion occurs
259
+ */
260
+ export interface PressureDemotionEvent {
261
+ /** Pressure level that triggered the demotion */
262
+ pressureLevel: MemoryPressureLevel
263
+ /** Demotions performed */
264
+ demotions: TierMigrationEvent[]
265
+ /** Total number of entries demoted */
266
+ totalDemoted: number
267
+ /** Timestamp of the event */
268
+ timestamp: Date
269
+ }
270
+
271
+ /**
272
+ * Result of a manual demotion trigger
273
+ */
274
+ export interface TriggerDemotionResult {
275
+ /** Whether the demotion was successful */
276
+ success: boolean
277
+ /** Number of entries demoted */
278
+ demotedCount: number
279
+ /** Error message if failed */
280
+ error?: string
281
+ }
282
+
283
+ /**
284
+ * Options for triggering demotions manually
285
+ */
286
+ export interface TriggerDemotionOptions {
287
+ /** Number of entries to evict from hot tier */
288
+ hotEvictCount: number
289
+ /** Number of entries to demote from warm tier */
290
+ warmDemoteCount: number
291
+ }
292
+
293
+ /**
294
+ * Memory spike event - emitted when heap grows suddenly
295
+ */
296
+ export interface MemorySpikeEvent {
297
+ /** Bytes gained since last sample */
298
+ deltaBytes: number
299
+ /** Previous heap usage */
300
+ previousBytes: number
301
+ /** Current heap usage */
302
+ currentBytes: number
303
+ /** When the spike was detected */
304
+ timestamp: Date
305
+ }
306
+
307
+ /**
308
+ * Single metrics sample for history tracking
309
+ */
310
+ export interface MetricsSample {
311
+ timestamp: Date
312
+ heapUsed: number
313
+ pressureLevel: MemoryPressureLevel
314
+ }
315
+
316
+ /**
317
+ * Collection of metrics samples
318
+ */
319
+ export interface MetricsHistory {
320
+ samples: MetricsSample[]
321
+ }
322
+
323
+ /**
324
+ * Memory growth rate calculation result
325
+ */
326
+ export interface MemoryGrowthRate {
327
+ /** Bytes per second growth rate (negative = decreasing) */
328
+ bytesPerSecond: number
329
+ /** Overall trend direction */
330
+ trend: 'increasing' | 'stable' | 'decreasing'
331
+ }
332
+
333
+ /**
334
+ * OOM prediction based on current growth rate
335
+ */
336
+ export interface OOMPrediction {
337
+ /** Estimated milliseconds until OOM (Infinity if not growing) */
338
+ estimatedMs: number
339
+ /** Confidence level (0-1) based on sample count and consistency */
340
+ confidence: number
341
+ /** Whether OOM is predicted within 60 seconds */
342
+ willOOM: boolean
343
+ /** Regression quality (R-squared) - how well the linear model fits */
344
+ rSquared?: number
345
+ /** Predicted memory usage at OOM horizon */
346
+ predictedUsageAtHorizon?: number
347
+ }
348
+
349
+ /**
350
+ * Smoothed pressure state for EMA-based detection
351
+ */
352
+ export interface SmoothedPressureState {
353
+ /** EMA of heap usage in bytes */
354
+ emaHeapUsage: number
355
+ /** EMA of pressure level (as numeric index 0-3) */
356
+ emaPressureLevel: number
357
+ /** Variance in recent samples */
358
+ recentVariance: number
359
+ /** Whether pressure is trending up or down */
360
+ trend: 'rising' | 'falling' | 'stable'
361
+ /** Last raw heap usage for delta calculation */
362
+ lastRawHeapUsage: number
363
+ }
364
+
365
+ /**
366
+ * Result of a GC hint request
367
+ */
368
+ export interface GCResult {
369
+ /** Whether GC was actually triggered */
370
+ triggered: boolean
371
+ /** Estimated bytes freed (if measurable) */
372
+ bytesFreedEstimate: number
373
+ /** Time spent in GC (if triggered) */
374
+ durationMs: number
375
+ }
376
+
377
+ /**
378
+ * Aggregated GC metrics
379
+ */
380
+ export interface GCMetrics {
381
+ /** Total number of GC hints issued */
382
+ totalHints: number
383
+ /** Average bytes freed per hint */
384
+ avgBytesFreed: number
385
+ /** Average duration per hint */
386
+ avgDurationMs: number
387
+ /** Percentage of hints that freed memory */
388
+ successRate: number
389
+ }
390
+
391
+ /**
392
+ * Recovery event - emitted when pressure drops
393
+ */
394
+ export interface RecoveryEvent {
395
+ /** Pressure level we're recovering from */
396
+ fromLevel: MemoryPressureLevel
397
+ /** New pressure level */
398
+ toLevel: MemoryPressureLevel
399
+ /** How long we were at elevated pressure */
400
+ recoveryDurationMs: number
401
+ }
402
+
403
+ /**
404
+ * Paused operation tracking
405
+ */
406
+ export interface PausedOperation {
407
+ /** Unique operation identifier */
408
+ id: string
409
+ /** Current status */
410
+ status: 'paused' | 'resumed' | 'cancelled'
411
+ /** When the operation was paused */
412
+ pausedAt: Date
413
+ /** Promise that resolves when operation can resume */
414
+ resumePromise: Promise<void>
415
+ /** Resolve the resume promise */
416
+ resolve: () => void
417
+ /** Reject the resume promise */
418
+ reject: (error: Error) => void
419
+ }
420
+
421
+ /**
422
+ * Time spent at each pressure level
423
+ */
424
+ export interface PressureDurations {
425
+ normal: number
426
+ warning: number
427
+ critical: number
428
+ oom: number
429
+ }
430
+
431
+ /**
432
+ * JSON metrics export format
433
+ */
434
+ export interface JSONMetricsExport {
435
+ current: MemoryStats
436
+ history: MetricsHistory
437
+ aggregates: {
438
+ avgHeapUsed: number
439
+ peakHeapUsed: number
440
+ totalGCCount: number
441
+ pressureTriggeredDemotions: number
442
+ failedDemotions: number
443
+ bytesFreedByDemotion: number
444
+ }
445
+ /** Enhanced heuristics metrics (new in optimized version) */
446
+ heuristics?: {
447
+ /** Smoothed pressure state from EMA */
448
+ smoothedState: SmoothedPressureState
449
+ /** Adaptive thresholds based on memory behavior */
450
+ adaptiveThresholds: {
451
+ warningThresholdPercent: number
452
+ criticalThresholdPercent: number
453
+ oomThresholdPercent: number
454
+ }
455
+ /** OOM prediction with confidence */
456
+ oomPrediction: OOMPrediction
457
+ /** Memory growth rate */
458
+ growthRate: MemoryGrowthRate
459
+ /** Whether pressure is currently volatile */
460
+ isVolatile: boolean
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Cache manager interface for eviction
466
+ */
467
+ export interface CacheManager {
468
+ /** Evict entries until targetBytes are freed, returns actual bytes freed */
469
+ evictLRU?: (targetBytes: number) => number
470
+ /** Evict the single largest entry, returns bytes freed */
471
+ evictLargest?: () => number
472
+ /** Get total size of cached data */
473
+ getTotalSize: () => number
474
+ /** Clear all entries */
475
+ clear?: () => void
476
+ }
477
+
478
+ /**
479
+ * Error thrown when memory pressure prevents an operation
480
+ */
481
+ export class MemoryPressureError extends Error {
482
+ readonly code: string = 'MEMORY_PRESSURE_HIGH'
483
+ readonly memoryStats: MemoryStats
484
+
485
+ constructor(message: string, memoryStats: MemoryStats) {
486
+ super(message)
487
+ this.name = 'MemoryPressureError'
488
+ this.memoryStats = memoryStats
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Extended configuration options for the memory pressure manager.
494
+ *
495
+ * These options control advanced behaviors like GC hints, cache eviction,
496
+ * query handling, and recovery behavior.
497
+ */
498
+ export interface ExtendedMemoryPressureConfig extends MemoryPressureConfig {
499
+ // === GC Hint Configuration ===
500
+
501
+ /** Callback invoked when a GC hint should be issued */
502
+ onGCHint?: (level: MemoryPressureLevel, options?: { aggressive: boolean }) => void
503
+ /** Thresholds for different GC intensities */
504
+ gcHintThresholds?: { mild: number; normal: number; aggressive: number }
505
+ /** Minimum interval between GC hints to prevent thrashing */
506
+ gcHintMinIntervalMs?: number
507
+
508
+ // === Cache Eviction Configuration ===
509
+
510
+ /** Enable automatic cache eviction under pressure */
511
+ autoCacheEviction?: boolean
512
+ /** Strategy for cache eviction */
513
+ cacheEvictionStrategy?: 'lru' | 'largest-first'
514
+ /** Percentage of cache to evict at each pressure level */
515
+ evictionTargetPercent?: Record<MemoryPressureLevel, number>
516
+ /** Callback when a cache entry is evicted */
517
+ onCacheEviction?: (entry: { key: string; size: number }) => void
518
+
519
+ // === Query Handling Configuration ===
520
+
521
+ /** Enable pausing queries at critical pressure */
522
+ queryPausingEnabled?: boolean
523
+ /** Enable rejecting queries at OOM pressure */
524
+ queryRejectionEnabled?: boolean
525
+ /** Resume paused queries when usage drops below this percentage */
526
+ queryResumeThresholdPercent?: number
527
+ /** Maximum time to pause a query before timing out */
528
+ queryPauseTimeoutMs?: number
529
+ /** Enable query memory estimation */
530
+ estimateQueryMemory?: boolean
531
+ /** Queries with estimated results below this are always allowed */
532
+ smallQueryThresholdBytes?: number
533
+ /** Allow queries that started before pressure to complete */
534
+ allowInFlightCompletion?: boolean
535
+ /** Cancel queries running longer than threshold at OOM */
536
+ cancelLongRunningAtOOM?: boolean
537
+ /** Threshold for "long running" query */
538
+ longRunningThresholdMs?: number
539
+
540
+ // === Metrics Configuration ===
541
+
542
+ /** Number of samples to keep in metrics history */
543
+ metricsHistorySize?: number
544
+
545
+ // === Recovery Configuration ===
546
+
547
+ /** Callback when recovery completes */
548
+ onRecovery?: (event: RecoveryEvent) => void
549
+ /** Automatically disable features under pressure */
550
+ autoDisableFeatures?: boolean
551
+ /** Features to disable under pressure */
552
+ featuresToDisable?: string[]
553
+ /** Enable gradual feature re-enablement during recovery */
554
+ gradualRecovery?: boolean
555
+ /** Interval between recovery steps */
556
+ recoveryStepIntervalMs?: number
557
+ /** Callback for each recovery step */
558
+ onRecoveryStep?: (step: string) => void
559
+ /** Time to wait at normal pressure before full recovery */
560
+ recoveryHysteresisMs?: number
561
+
562
+ // === Sustained Pressure Configuration ===
563
+
564
+ /** Time at elevated pressure before alert */
565
+ sustainedPressureThresholdMs?: number
566
+ /** Callback for sustained pressure alerts */
567
+ onSustainedPressure?: (level: MemoryPressureLevel, durationMs: number) => void
568
+
569
+ // === Hard Limit Enforcement ===
570
+
571
+ /** Strictly enforce memory limit for guarded operations */
572
+ hardLimitEnforcement?: boolean
573
+
574
+ // === Tiered Storage Demotion Configuration ===
575
+
576
+ /** Enable automatic demotion on memory pressure (default: true) */
577
+ autoDemoteOnPressure?: boolean
578
+ /** Configuration for demotion counts per pressure level */
579
+ demotionConfig?: DemotionConfig
580
+ /** Minimum interval between pressure-triggered demotions in ms */
581
+ demotionMinIntervalMs?: number
582
+ /** Trigger GC hint before demotion (default: false) */
583
+ gcBeforeDemotion?: boolean
584
+ }
585
+
586
+ // =============================================================================
587
+ // Helper Functions
588
+ // =============================================================================
589
+
590
+ // Cache performance.memory reference to avoid repeated property access
591
+ let cachedPerformanceMemory: { usedJSHeapSize: number } | null | undefined
592
+
593
+ /**
594
+ * Estimate WASM heap memory usage.
595
+ *
596
+ * In Cloudflare Workers, we can't directly access Emscripten's HEAP.
597
+ * This function provides an estimate based on available metrics.
598
+ *
599
+ * Optimized: Caches performance.memory reference to reduce property lookups.
600
+ *
601
+ * Priority:
602
+ * 1. performance.memory.usedJSHeapSize (if available)
603
+ * 2. Conservative estimate based on typical PGLite footprint
604
+ */
605
+ function estimateHeapUsage(): number {
606
+ // Check cache first (undefined = not checked, null = not available)
607
+ if (cachedPerformanceMemory === undefined) {
608
+ if (typeof performance !== 'undefined' && 'memory' in performance) {
609
+ cachedPerformanceMemory = (performance as Performance & { memory?: { usedJSHeapSize: number } }).memory ?? null
610
+ } else {
611
+ cachedPerformanceMemory = null
612
+ }
613
+ }
614
+
615
+ if (cachedPerformanceMemory && typeof cachedPerformanceMemory.usedJSHeapSize === 'number') {
616
+ return cachedPerformanceMemory.usedJSHeapSize
617
+ }
618
+
619
+ // Fallback: use pre-computed constant
620
+ return PGLITE_BASELINE_BYTES
621
+ }
622
+
623
+ /**
624
+ * Determine memory pressure level from usage percentage.
625
+ * Optimized: Uses direct comparisons in descending order for early exit.
626
+ */
627
+ function getPressureLevelFromPercent(
628
+ usagePercent: number,
629
+ thresholds: { warningThresholdPercent: number; criticalThresholdPercent: number; oomThresholdPercent: number }
630
+ ): MemoryPressureLevel {
631
+ // Check highest threshold first for fast rejection
632
+ if (usagePercent >= thresholds.oomThresholdPercent) return 'oom'
633
+ if (usagePercent >= thresholds.criticalThresholdPercent) return 'critical'
634
+ if (usagePercent >= thresholds.warningThresholdPercent) return 'warning'
635
+ return 'normal'
636
+ }
637
+
638
+ /**
639
+ * Get numeric index for pressure level comparison.
640
+ * Optimized: Uses pre-computed lookup table instead of array search.
641
+ */
642
+ function getPressureLevelIndex(level: MemoryPressureLevel): number {
643
+ return PRESSURE_LEVEL_INDEX[level]
644
+ }
645
+
646
+ /**
647
+ * Calculate average of an array of numbers.
648
+ * Optimized: Uses for loop instead of reduce for better performance.
649
+ */
650
+ function average(values: number[]): number {
651
+ const len = values.length
652
+ if (len === 0) return 0
653
+ let sum = 0
654
+ for (let i = 0; i < len; i++) {
655
+ sum += values[i] ?? 0
656
+ }
657
+ return sum / len
658
+ }
659
+
660
+ /**
661
+ * Calculate variance of an array of numbers.
662
+ * Optimized: Single-pass Welford's algorithm for numerical stability.
663
+ */
664
+ function variance(values: number[]): number {
665
+ const len = values.length
666
+ if (len < 2) return 0
667
+
668
+ let mean = 0
669
+ let m2 = 0
670
+
671
+ for (let i = 0; i < len; i++) {
672
+ const value = values[i] ?? 0
673
+ const delta = value - mean
674
+ mean += delta / (i + 1)
675
+ m2 += delta * (value - mean)
676
+ }
677
+
678
+ return m2 / (len - 1)
679
+ }
680
+
681
+ /**
682
+ * Simple linear regression for trend analysis.
683
+ * Returns slope (bytes/ms), intercept, and R-squared.
684
+ * Optimized: Single-pass calculation.
685
+ */
686
+ function linearRegression(
687
+ samples: { timestamp: Date; heapUsed: number }[]
688
+ ): { slope: number; intercept: number; rSquared: number } {
689
+ const n = samples.length
690
+ if (n < 2) return { slope: 0, intercept: 0, rSquared: 0 }
691
+
692
+ // Use first timestamp as origin for numerical stability
693
+ const t0 = samples[0]!.timestamp.getTime()
694
+
695
+ let sumX = 0
696
+ let sumY = 0
697
+ let sumXY = 0
698
+ let sumX2 = 0
699
+ let sumY2 = 0
700
+
701
+ for (let i = 0; i < n; i++) {
702
+ const sample = samples[i]!
703
+ const x = sample.timestamp.getTime() - t0
704
+ const y = sample.heapUsed
705
+ sumX += x
706
+ sumY += y
707
+ sumXY += x * y
708
+ sumX2 += x * x
709
+ sumY2 += y * y
710
+ }
711
+
712
+ const denom = n * sumX2 - sumX * sumX
713
+ if (Math.abs(denom) < 1e-10) return { slope: 0, intercept: sumY / n, rSquared: 0 }
714
+
715
+ const slope = (n * sumXY - sumX * sumY) / denom
716
+ const intercept = (sumY - slope * sumX) / n
717
+
718
+ // Calculate R-squared
719
+ const meanY = sumY / n
720
+ let ssRes = 0
721
+ let ssTot = 0
722
+
723
+ for (let i = 0; i < n; i++) {
724
+ const sample = samples[i]!
725
+ const x = sample.timestamp.getTime() - t0
726
+ const y = sample.heapUsed
727
+ const yPred = slope * x + intercept
728
+ ssRes += (y - yPred) ** 2
729
+ ssTot += (y - meanY) ** 2
730
+ }
731
+
732
+ const rSquared = ssTot > 0 ? 1 - ssRes / ssTot : 0
733
+
734
+ return { slope, intercept, rSquared }
735
+ }
736
+
737
+ /**
738
+ * Apply EMA (Exponential Moving Average) update.
739
+ * @param current - Current EMA value
740
+ * @param newValue - New sample value
741
+ * @param alpha - Smoothing factor (0-1)
742
+ */
743
+ function updateEMA(current: number, newValue: number, alpha: number = EMA_SMOOTHING_FACTOR): number {
744
+ return alpha * newValue + (1 - alpha) * current
745
+ }
746
+
747
+ /**
748
+ * Ring buffer for efficient fixed-size history storage.
749
+ * Avoids shift() operations which are O(n) on arrays.
750
+ */
751
+ class RingBuffer<T> {
752
+ private readonly buffer: (T | undefined)[]
753
+ private readonly capacity: number
754
+ private head = 0
755
+ private _size = 0
756
+
757
+ constructor(capacity: number) {
758
+ this.capacity = capacity
759
+ this.buffer = new Array(capacity)
760
+ }
761
+
762
+ push(item: T): void {
763
+ this.buffer[this.head] = item
764
+ this.head = (this.head + 1) % this.capacity
765
+ if (this._size < this.capacity) this._size++
766
+ }
767
+
768
+ get size(): number {
769
+ return this._size
770
+ }
771
+
772
+ /** Get item at logical index (0 = oldest, size-1 = newest) */
773
+ get(index: number): T | undefined {
774
+ if (index < 0 || index >= this._size) return undefined
775
+ const bufferIndex = (this.head - this._size + index + this.capacity) % this.capacity
776
+ return this.buffer[bufferIndex]
777
+ }
778
+
779
+ /** Get the oldest item */
780
+ first(): T | undefined {
781
+ return this._size > 0 ? this.get(0) : undefined
782
+ }
783
+
784
+ /** Get the newest item */
785
+ last(): T | undefined {
786
+ return this._size > 0 ? this.get(this._size - 1) : undefined
787
+ }
788
+
789
+ /** Convert to array (oldest to newest) */
790
+ toArray(): T[] {
791
+ const result: T[] = []
792
+ for (let i = 0; i < this._size; i++) {
793
+ const item = this.get(i)
794
+ if (item !== undefined) result.push(item)
795
+ }
796
+ return result
797
+ }
798
+
799
+ clear(): void {
800
+ this.head = 0
801
+ this._size = 0
802
+ // Don't clear buffer - items will be overwritten
803
+ }
804
+ }
805
+
806
+ // =============================================================================
807
+ // Memory Pressure Manager
808
+ // =============================================================================
809
+
810
+ /**
811
+ * Memory Pressure Manager for PostgresDO
812
+ *
813
+ * Monitors memory usage and takes protective actions to prevent OOM crashes
814
+ * in Cloudflare Workers. Provides configurable thresholds, automatic actions,
815
+ * and detailed metrics for observability.
816
+ *
817
+ * @example
818
+ * ```typescript
819
+ * const manager = new MemoryPressureManager({
820
+ * warningThresholdPercent: 70,
821
+ * criticalThresholdPercent: 85,
822
+ * onGCHint: (level, { aggressive }) => {
823
+ * if (aggressive) globalThis.gc?.()
824
+ * },
825
+ * })
826
+ *
827
+ * manager.onPressureChange((event) => {
828
+ * console.log(`Pressure: ${event.currentLevel}`)
829
+ * })
830
+ *
831
+ * manager.startMonitoring()
832
+ * ```
833
+ */
834
+ export class MemoryPressureManager {
835
+ // === Configuration ===
836
+ private readonly config: Required<MemoryPressureConfig>
837
+ private readonly extendedConfig: ExtendedMemoryPressureConfig
838
+ private readonly metricsHistorySize: number
839
+
840
+ // === External Dependencies ===
841
+ private pglite: {
842
+ query: (sql: string, params?: unknown[]) => Promise<{ rows: unknown[]; fields: unknown[] }>
843
+ } | null = null
844
+ private wasmModule: { HEAPU8?: Uint8Array } | null = null
845
+ private cacheManager: CacheManager | null = null
846
+
847
+ // === State Tracking ===
848
+ private lastPressureLevel: MemoryPressureLevel = 'normal'
849
+ private pressureLevelStartTime: number = Date.now()
850
+ private pressureDurations: PressureDurations = { normal: 0, warning: 0, critical: 0, oom: 0 }
851
+ private sustainedPressureAlerted = false
852
+
853
+ // === Recovery State ===
854
+ private inRecoveryMode = false
855
+ private recoveryProgress = 1.0
856
+ private recoveryStartTime: number | null = null
857
+ private recoveryPaused = false
858
+ private recoveryStepsExecuted: string[] = []
859
+
860
+ // === Feature Management ===
861
+ private readonly enabledFeatures: Set<string>
862
+ private readonly disabledFeatures = new Set<string>()
863
+ private unloadedExtensions = new Set<string>()
864
+
865
+ // === Query Management ===
866
+ private readonly pausedQueries = new Map<Promise<unknown>, PausedOperation>()
867
+ private readonly inFlightQueries = new Map<Promise<unknown>, { startTime: number; cancel?: () => void }>()
868
+
869
+ // === Metrics & History (using ring buffers for efficiency) ===
870
+ private metricsHistory!: RingBuffer<MetricsSample>
871
+ private lastHeapSample = 0
872
+ private gcMetrics!: { hints: number; bytesFreed: RingBuffer<number>; durations: RingBuffer<number> }
873
+ private lastGCHintTime = 0
874
+
875
+ // === Listeners ===
876
+ private listeners: MemoryPressureListener[] = []
877
+ private memorySpikeListeners: Array<(spike: MemorySpikeEvent) => void> = []
878
+ private recoveryListeners: Array<(event: RecoveryEvent) => void> = []
879
+ private pressureDemotionListeners: Array<(event: PressureDemotionEvent) => void> = []
880
+
881
+ // === Tiered Storage Integration ===
882
+ private tieredStorageOrchestrator: TieredStorageOrchestrator | null = null
883
+ private demotionMetrics: DemotionMetrics = {
884
+ pressureTriggeredDemotions: 0,
885
+ demotionsByPressureLevel: { normal: 0, warning: 0, critical: 0, oom: 0 },
886
+ failedDemotions: 0,
887
+ bytesFreedByDemotion: 0,
888
+ lastDemotionTimestamp: null,
889
+ }
890
+ private lastPressureDemotionTime = 0
891
+
892
+ // === Intervals ===
893
+ private checkInterval: ReturnType<typeof setInterval> | null = null
894
+ private sustainedPressureCheckInterval: ReturnType<typeof setInterval> | null = null
895
+ private gradualRecoveryInterval: ReturnType<typeof setInterval> | null = null
896
+
897
+ // === Smoothed Pressure State (for EMA-based detection) ===
898
+ private smoothedState: SmoothedPressureState = {
899
+ emaHeapUsage: 0,
900
+ emaPressureLevel: 0,
901
+ recentVariance: 0,
902
+ trend: 'stable',
903
+ lastRawHeapUsage: 0,
904
+ }
905
+ private smoothedStateInitialized = false
906
+
907
+ // === Testing Support ===
908
+ private simulatedHeapBytes = 0
909
+ private forcedHeapUsage: number | null = null
910
+
911
+ constructor(config: ExtendedMemoryPressureConfig = {}) {
912
+ this.extendedConfig = config
913
+ this.config = {
914
+ enabled: config.enabled ?? true,
915
+ memoryLimitBytes: config.memoryLimitBytes ?? DEFAULT_MEMORY_LIMIT_BYTES,
916
+ warningThresholdPercent: config.warningThresholdPercent ?? DEFAULT_WARNING_THRESHOLD,
917
+ criticalThresholdPercent: config.criticalThresholdPercent ?? DEFAULT_CRITICAL_THRESHOLD,
918
+ oomThresholdPercent: config.oomThresholdPercent ?? DEFAULT_OOM_THRESHOLD,
919
+ checkIntervalMs: config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS,
920
+ autoUnloadExtensions: config.autoUnloadExtensions ?? true,
921
+ extensionUnloadPriority: config.extensionUnloadPriority ?? DEFAULT_EXTENSION_UNLOAD_PRIORITY,
922
+ maxListeners: config.maxListeners ?? DEFAULT_MAX_LISTENERS,
923
+ }
924
+ this.metricsHistorySize = config.metricsHistorySize ?? DEFAULT_METRICS_HISTORY_SIZE
925
+ this.enabledFeatures = new Set(config.featuresToDisable ?? DEFAULT_FEATURES_TO_DISABLE)
926
+
927
+ // Initialize ring buffers for efficient history storage
928
+ this.metricsHistory = new RingBuffer<MetricsSample>(this.metricsHistorySize)
929
+ this.gcMetrics = {
930
+ hints: 0,
931
+ bytesFreed: new RingBuffer<number>(50), // Keep last 50 GC results
932
+ durations: new RingBuffer<number>(50),
933
+ }
934
+ }
935
+
936
+ // ===========================================================================
937
+ // Configuration & Setup
938
+ // ===========================================================================
939
+
940
+ /**
941
+ * Set the PGlite instance for extension unloading operations.
942
+ *
943
+ * @param pglite - PGLite instance with query method
944
+ */
945
+ setPGlite(pglite: MemoryPressureManager['pglite']): void {
946
+ this.pglite = pglite
947
+ }
948
+
949
+ /**
950
+ * Set the WASM module for real-time heap tracking.
951
+ *
952
+ * When set, heap usage is calculated by sampling the HEAPU8 array
953
+ * instead of using estimates.
954
+ *
955
+ * @param module - Emscripten module with HEAPU8 property
956
+ */
957
+ setWasmHeapSource(module: { HEAPU8?: Uint8Array }): void {
958
+ this.wasmModule = module
959
+ }
960
+
961
+ /**
962
+ * Set the cache manager for eviction operations.
963
+ *
964
+ * @param manager - Cache manager with eviction methods
965
+ */
966
+ setCacheManager(manager: CacheManager): void {
967
+ this.cacheManager = manager
968
+ }
969
+
970
+ /**
971
+ * Get the current configuration.
972
+ *
973
+ * @returns Copy of the current configuration
974
+ */
975
+ getConfig(): Required<MemoryPressureConfig> {
976
+ return { ...this.config }
977
+ }
978
+
979
+ /**
980
+ * Update configuration at runtime.
981
+ *
982
+ * If checkIntervalMs is changed and monitoring is active, it will be restarted.
983
+ *
984
+ * @param config - Partial configuration to merge
985
+ */
986
+ setConfig(config: Partial<MemoryPressureConfig>): void {
987
+ Object.assign(this.config, config)
988
+
989
+ // Restart monitoring if interval changed
990
+ if (config.checkIntervalMs !== undefined && this.checkInterval) {
991
+ this.stopMonitoring()
992
+ this.startMonitoring()
993
+ }
994
+ }
995
+
996
+ // ===========================================================================
997
+ // Tiered Storage Orchestrator Integration
998
+ // ===========================================================================
999
+
1000
+ /**
1001
+ * Connect a TieredStorageOrchestrator for pressure-triggered demotions.
1002
+ *
1003
+ * When connected, the manager will automatically trigger demotions
1004
+ * when memory pressure rises above warning level.
1005
+ *
1006
+ * @param orchestrator - TieredStorageOrchestrator instance or null to disconnect
1007
+ */
1008
+ setTieredStorageOrchestrator(orchestrator: TieredStorageOrchestrator | null): void {
1009
+ this.tieredStorageOrchestrator = orchestrator
1010
+ }
1011
+
1012
+ /**
1013
+ * Check if a TieredStorageOrchestrator is connected.
1014
+ *
1015
+ * @returns True if an orchestrator is connected
1016
+ */
1017
+ hasTieredStorageOrchestrator(): boolean {
1018
+ return this.tieredStorageOrchestrator !== null
1019
+ }
1020
+
1021
+ /**
1022
+ * Get demotion metrics for pressure-triggered demotions.
1023
+ *
1024
+ * @returns Demotion metrics
1025
+ */
1026
+ getDemotionMetrics(): DemotionMetrics {
1027
+ return { ...this.demotionMetrics }
1028
+ }
1029
+
1030
+ /**
1031
+ * Add a listener for pressure-triggered demotion events.
1032
+ *
1033
+ * @param listener - Callback for demotion events
1034
+ * @returns Unsubscribe function
1035
+ */
1036
+ onPressureDemotion(listener: (event: PressureDemotionEvent) => void): () => void {
1037
+ this.pressureDemotionListeners.push(listener)
1038
+ return () => {
1039
+ const index = this.pressureDemotionListeners.indexOf(listener)
1040
+ if (index >= 0) this.pressureDemotionListeners.splice(index, 1)
1041
+ }
1042
+ }
1043
+
1044
+ /**
1045
+ * Manually trigger demotions.
1046
+ *
1047
+ * @param options - Demotion options specifying counts
1048
+ * @returns Result of the demotion operation
1049
+ */
1050
+ async triggerDemotion(options: TriggerDemotionOptions): Promise<TriggerDemotionResult> {
1051
+ if (!this.tieredStorageOrchestrator) {
1052
+ return { success: false, demotedCount: 0, error: 'Tiered storage orchestrator not connected' }
1053
+ }
1054
+
1055
+ try {
1056
+ let totalDemoted = 0
1057
+
1058
+ // Evict from hot tier
1059
+ if (options.hotEvictCount > 0) {
1060
+ const hotEvents = await this.tieredStorageOrchestrator.evictLRUFromHot(options.hotEvictCount)
1061
+ totalDemoted += hotEvents.filter((e) => e.success).length
1062
+ }
1063
+
1064
+ // Demote from warm tier
1065
+ if (options.warmDemoteCount > 0) {
1066
+ const warmEvents = await this.tieredStorageOrchestrator.runDemotionCycle(options.warmDemoteCount)
1067
+ totalDemoted += warmEvents.filter((e) => e.success).length
1068
+ }
1069
+
1070
+ return { success: true, demotedCount: totalDemoted }
1071
+ } catch (error) {
1072
+ return {
1073
+ success: false,
1074
+ demotedCount: 0,
1075
+ error: error instanceof Error ? error.message : String(error),
1076
+ }
1077
+ }
1078
+ }
1079
+
1080
+ /**
1081
+ * Handle pressure-triggered demotion based on the current level.
1082
+ *
1083
+ * This is called automatically when pressure changes if autoDemoteOnPressure is enabled.
1084
+ *
1085
+ * @param level - Current pressure level
1086
+ * @param forceBypassRateLimit - If true, bypass rate limiting (used when pressure escalates)
1087
+ */
1088
+ private async handlePressureDemotion(level: MemoryPressureLevel, forceBypassRateLimit = false): Promise<void> {
1089
+ // Skip if auto-demotion is disabled
1090
+ if (this.extendedConfig.autoDemoteOnPressure === false) return
1091
+
1092
+ // Skip if no orchestrator connected
1093
+ if (!this.tieredStorageOrchestrator) return
1094
+
1095
+ // Skip for normal pressure
1096
+ if (level === 'normal') return
1097
+
1098
+ // Check rate limiting (can be bypassed for escalation)
1099
+ const now = Date.now()
1100
+ const minInterval = this.extendedConfig.demotionMinIntervalMs ?? DEFAULT_DEMOTION_MIN_INTERVAL_MS
1101
+ if (!forceBypassRateLimit && now - this.lastPressureDemotionTime < minInterval) return
1102
+
1103
+ // Get demotion config for this level
1104
+ const demotionConfig = this.extendedConfig.demotionConfig ?? DEFAULT_DEMOTION_CONFIG
1105
+ const levelConfig = demotionConfig[level]
1106
+ if (!levelConfig || (levelConfig.hotEvictCount === 0 && levelConfig.warmDemoteCount === 0)) return
1107
+
1108
+ // Optionally trigger GC before demotion
1109
+ if (this.extendedConfig.gcBeforeDemotion && this.extendedConfig.onGCHint) {
1110
+ this.extendedConfig.onGCHint(level, { aggressive: level === 'critical' || level === 'oom' })
1111
+ }
1112
+
1113
+ this.lastPressureDemotionTime = now
1114
+
1115
+ try {
1116
+ const allDemotions: TierMigrationEvent[] = []
1117
+ let bytesFreed = 0
1118
+
1119
+ // Evict from hot tier
1120
+ if (levelConfig.hotEvictCount > 0) {
1121
+ const hotEvents = await this.tieredStorageOrchestrator.evictLRUFromHot(levelConfig.hotEvictCount)
1122
+ allDemotions.push(...hotEvents)
1123
+
1124
+ // Track bytes freed
1125
+ for (const event of hotEvents) {
1126
+ if (event.success) {
1127
+ const entry = this.tieredStorageOrchestrator.getIndexEntry(event.key)
1128
+ if (entry) bytesFreed += entry.size
1129
+ }
1130
+ }
1131
+ }
1132
+
1133
+ // Demote from warm tier if critical or oom
1134
+ if (levelConfig.warmDemoteCount > 0 && (level === 'critical' || level === 'oom')) {
1135
+ const warmEvents = await this.tieredStorageOrchestrator.runDemotionCycle(levelConfig.warmDemoteCount)
1136
+ allDemotions.push(...warmEvents)
1137
+ }
1138
+
1139
+ // Update metrics
1140
+ const successCount = allDemotions.filter((e) => e.success).length
1141
+ const failCount = allDemotions.filter((e) => !e.success).length
1142
+
1143
+ this.demotionMetrics.pressureTriggeredDemotions += successCount
1144
+ this.demotionMetrics.demotionsByPressureLevel[level] += successCount
1145
+ this.demotionMetrics.failedDemotions += failCount
1146
+ this.demotionMetrics.bytesFreedByDemotion += bytesFreed
1147
+ this.demotionMetrics.lastDemotionTimestamp = now
1148
+
1149
+ // Emit demotion event
1150
+ if (allDemotions.length > 0) {
1151
+ const event: PressureDemotionEvent = {
1152
+ pressureLevel: level,
1153
+ demotions: allDemotions,
1154
+ totalDemoted: successCount,
1155
+ timestamp: new Date(),
1156
+ }
1157
+
1158
+ for (const listener of this.pressureDemotionListeners) {
1159
+ try {
1160
+ listener(event)
1161
+ } catch {
1162
+ // Ignore listener errors
1163
+ }
1164
+ }
1165
+ }
1166
+ } catch (error) {
1167
+ this.demotionMetrics.failedDemotions++
1168
+ console.error('Pressure-triggered demotion failed:', error)
1169
+ }
1170
+ }
1171
+
1172
+ // ===========================================================================
1173
+ // Monitoring Control
1174
+ // ===========================================================================
1175
+
1176
+ /**
1177
+ * Start periodic memory monitoring.
1178
+ *
1179
+ * Checks memory pressure at the configured interval and takes
1180
+ * automatic actions when thresholds are crossed.
1181
+ */
1182
+ startMonitoring(): void {
1183
+ if (!this.config.enabled || this.checkInterval) return
1184
+
1185
+ this.checkInterval = setInterval(() => {
1186
+ this.checkMemoryPressure()
1187
+ }, this.config.checkIntervalMs)
1188
+ }
1189
+
1190
+ /**
1191
+ * Stop periodic memory monitoring.
1192
+ */
1193
+ stopMonitoring(): void {
1194
+ if (this.checkInterval) {
1195
+ clearInterval(this.checkInterval)
1196
+ this.checkInterval = null
1197
+ }
1198
+ }
1199
+
1200
+ // ===========================================================================
1201
+ // Memory Stats & Pressure Detection
1202
+ // ===========================================================================
1203
+
1204
+ /**
1205
+ * Get current memory statistics.
1206
+ *
1207
+ * @returns Current memory stats including pressure level
1208
+ */
1209
+ getMemoryStats(): MemoryStats {
1210
+ const heapUsed = this.getCurrentHeapUsage()
1211
+ const heapLimit = this.config.memoryLimitBytes
1212
+ const usagePercent = (heapUsed / heapLimit) * 100
1213
+ const pressureLevel = getPressureLevelFromPercent(usagePercent, this.config)
1214
+
1215
+ return {
1216
+ heapUsed,
1217
+ heapLimit,
1218
+ usagePercent,
1219
+ pressureLevel,
1220
+ timestamp: new Date(),
1221
+ }
1222
+ }
1223
+
1224
+ /**
1225
+ * Get current memory pressure level.
1226
+ *
1227
+ * This calculates the level based on current heap usage,
1228
+ * not the cached lastPressureLevel.
1229
+ *
1230
+ * @returns Current pressure level
1231
+ */
1232
+ getPressureLevel(): MemoryPressureLevel {
1233
+ return this.getMemoryStats().pressureLevel
1234
+ }
1235
+
1236
+ /**
1237
+ * Check if current pressure is at or above a threshold.
1238
+ *
1239
+ * Uses the cached lastPressureLevel for efficiency.
1240
+ *
1241
+ * @param level - Level to compare against
1242
+ * @returns True if current pressure >= level
1243
+ */
1244
+ isPressureAtOrAbove(level: MemoryPressureLevel): boolean {
1245
+ return getPressureLevelIndex(this.lastPressureLevel) >= getPressureLevelIndex(level)
1246
+ }
1247
+
1248
+ /**
1249
+ * Get current heap usage from the best available source.
1250
+ *
1251
+ * Priority:
1252
+ * 1. Forced heap usage (for testing)
1253
+ * 2. Simulated heap (for testing)
1254
+ * 3. WASM module HEAPU8 (sampled)
1255
+ * 4. Estimation fallback
1256
+ */
1257
+ private getCurrentHeapUsage(): number {
1258
+ // Testing overrides
1259
+ if (this.forcedHeapUsage !== null) return this.forcedHeapUsage
1260
+ if (this.simulatedHeapBytes > 0) return this.simulatedHeapBytes
1261
+
1262
+ // Real WASM heap measurement via sampling
1263
+ if (this.wasmModule?.HEAPU8) {
1264
+ return this.sampleHeapUsage(this.wasmModule.HEAPU8)
1265
+ }
1266
+
1267
+ // Fallback estimation
1268
+ return estimateHeapUsage()
1269
+ }
1270
+
1271
+ /**
1272
+ * Sample WASM heap to estimate used bytes.
1273
+ *
1274
+ * Optimized: Uses bit-shift for stride calculation (avoiding division),
1275
+ * reduced sample size for lower overhead, and unrolled loop for better
1276
+ * CPU branch prediction.
1277
+ */
1278
+ private sampleHeapUsage(heap: Uint8Array): number {
1279
+ const len = heap.length
1280
+ if (len === 0) return 0
1281
+
1282
+ // Use bit shift for fast stride calculation (4KB stride = 2^12)
1283
+ // This gives us len >> 12 samples, capped at HEAP_SAMPLE_SIZE
1284
+ const stride = Math.max(1, len >> 8) // ~256 samples for typical heaps
1285
+ let nonZeroCount = 0
1286
+ let sampledCount = 0
1287
+
1288
+ // Unrolled loop: check 4 positions per iteration when possible
1289
+ const end = len - (stride * 3)
1290
+ let i = 0
1291
+ for (; i < end; i += stride * 4) {
1292
+ if (heap[i] !== 0) nonZeroCount++
1293
+ if (heap[i + stride] !== 0) nonZeroCount++
1294
+ if (heap[i + stride * 2] !== 0) nonZeroCount++
1295
+ if (heap[i + stride * 3] !== 0) nonZeroCount++
1296
+ sampledCount += 4
1297
+ }
1298
+
1299
+ // Handle remaining samples
1300
+ for (; i < len; i += stride) {
1301
+ if (heap[i] !== 0) nonZeroCount++
1302
+ sampledCount++
1303
+ }
1304
+
1305
+ if (sampledCount === 0) return 0
1306
+
1307
+ // Use integer math where possible
1308
+ return ((len * nonZeroCount) / sampledCount) | 0
1309
+ }
1310
+
1311
+ // ===========================================================================
1312
+ // Pressure Check & Response
1313
+ // ===========================================================================
1314
+
1315
+ /**
1316
+ * Check current memory pressure and take appropriate actions.
1317
+ *
1318
+ * This is called periodically by startMonitoring() and can also
1319
+ * be called manually before expensive operations.
1320
+ *
1321
+ * @returns Pressure event if level changed, null otherwise
1322
+ */
1323
+ async checkMemoryPressure(): Promise<MemoryPressureEvent | null> {
1324
+ if (!this.config.enabled) return null
1325
+
1326
+ const stats = this.getMemoryStats()
1327
+ const previousLevel = this.lastPressureLevel
1328
+ const currentLevel = stats.pressureLevel
1329
+ const now = Date.now()
1330
+
1331
+ // Always record metrics
1332
+ this.recordMetricSample()
1333
+
1334
+ // No change - handle sustained pressure check
1335
+ if (previousLevel === currentLevel) {
1336
+ this.handleSustainedPressure(currentLevel, now)
1337
+ return null
1338
+ }
1339
+
1340
+ // Level changed - update tracking
1341
+ this.updatePressureDuration(previousLevel, now)
1342
+ this.lastPressureLevel = currentLevel
1343
+ this.sustainedPressureAlerted = false
1344
+
1345
+ // Handle transitions
1346
+ const previousIndex = getPressureLevelIndex(previousLevel)
1347
+ const currentIndex = getPressureLevelIndex(currentLevel)
1348
+
1349
+ const actionsTaken: MemoryPressureAction[] = []
1350
+
1351
+ if (currentIndex < previousIndex) {
1352
+ // Pressure dropped - handle recovery
1353
+ this.handleRecovery(previousLevel, currentLevel, now)
1354
+ } else {
1355
+ // Pressure rose - exit recovery mode
1356
+ this.exitRecoveryMode()
1357
+ }
1358
+
1359
+ // Take actions based on new pressure level
1360
+ if (currentLevel !== 'normal') {
1361
+ actionsTaken.push(...(await this.handleElevatedPressure(currentLevel, now)))
1362
+ }
1363
+
1364
+ // Emit event
1365
+ const event: MemoryPressureEvent = {
1366
+ previousLevel,
1367
+ currentLevel,
1368
+ stats,
1369
+ actionsTaken,
1370
+ timestamp: new Date(),
1371
+ }
1372
+
1373
+ this.emitPressureEvent(event)
1374
+ return event
1375
+ }
1376
+
1377
+ /**
1378
+ * Handle sustained pressure alerts.
1379
+ */
1380
+ private handleSustainedPressure(level: MemoryPressureLevel, now: number): void {
1381
+ if (level === 'normal' || this.sustainedPressureAlerted) return
1382
+
1383
+ const threshold = this.extendedConfig.sustainedPressureThresholdMs ?? DEFAULT_SUSTAINED_PRESSURE_THRESHOLD_MS
1384
+ const elapsed = now - this.pressureLevelStartTime
1385
+
1386
+ if (elapsed >= threshold && this.extendedConfig.onSustainedPressure) {
1387
+ this.extendedConfig.onSustainedPressure(level, elapsed)
1388
+ this.sustainedPressureAlerted = true
1389
+ }
1390
+ }
1391
+
1392
+ /**
1393
+ * Handle recovery from elevated pressure.
1394
+ */
1395
+ private handleRecovery(fromLevel: MemoryPressureLevel, toLevel: MemoryPressureLevel, now: number): void {
1396
+ this.inRecoveryMode = true
1397
+ this.recoveryStartTime = now
1398
+ this.recoveryProgress = 0
1399
+
1400
+ if (toLevel === 'normal') {
1401
+ this.resumePausedOperations()
1402
+
1403
+ if (this.extendedConfig.autoDisableFeatures) {
1404
+ this.enableFeaturesAfterRecovery()
1405
+ }
1406
+
1407
+ const elapsed = now - this.pressureLevelStartTime
1408
+ const recoveryEvent: RecoveryEvent = {
1409
+ fromLevel,
1410
+ toLevel,
1411
+ recoveryDurationMs: elapsed,
1412
+ }
1413
+
1414
+ this.extendedConfig.onRecovery?.(recoveryEvent)
1415
+ this.recoveryListeners.forEach((listener) => listener(recoveryEvent))
1416
+ }
1417
+ }
1418
+
1419
+ /**
1420
+ * Exit recovery mode when pressure rises.
1421
+ */
1422
+ private exitRecoveryMode(): void {
1423
+ this.inRecoveryMode = false
1424
+ this.recoveryProgress = 0
1425
+ this.recoveryStartTime = null
1426
+ this.pauseGradualRecovery()
1427
+ }
1428
+
1429
+ /**
1430
+ * Handle actions for elevated pressure levels.
1431
+ * Optimized: Uses adaptive GC timing based on pressure level and memory growth rate.
1432
+ */
1433
+ private async handleElevatedPressure(level: MemoryPressureLevel, now: number): Promise<MemoryPressureAction[]> {
1434
+ const actionsTaken: MemoryPressureAction[] = []
1435
+
1436
+ // GC hint with adaptive timing based on pressure level
1437
+ if (this.extendedConfig.onGCHint) {
1438
+ const minInterval = this.getAdaptiveGCInterval(level, now)
1439
+ if (now - this.lastGCHintTime >= minInterval) {
1440
+ this.lastGCHintTime = now
1441
+ const aggressive = level === 'critical' || level === 'oom'
1442
+ this.extendedConfig.onGCHint(level, { aggressive })
1443
+ actionsTaken.push('gc_triggered')
1444
+ }
1445
+ }
1446
+
1447
+ // Cache eviction
1448
+ if (this.extendedConfig.autoCacheEviction && this.cacheManager) {
1449
+ const evicted = this.evictCache(level)
1450
+ if (evicted) actionsTaken.push('cache_cleared')
1451
+ }
1452
+
1453
+ // Feature disabling
1454
+ if ((level === 'critical' || level === 'oom') && this.extendedConfig.autoDisableFeatures) {
1455
+ this.disableFeaturesUnderPressure()
1456
+ }
1457
+
1458
+ // Extension unloading
1459
+ if ((level === 'critical' || level === 'oom') && this.config.autoUnloadExtensions && this.pglite) {
1460
+ const result = await this.unloadLeastImportantExtension()
1461
+ if (result.success) actionsTaken.push('extension_unloaded')
1462
+ actionsTaken.push('warning_emitted')
1463
+ }
1464
+
1465
+ return actionsTaken
1466
+ }
1467
+
1468
+ /**
1469
+ * Calculate adaptive GC interval based on pressure level and memory growth rate.
1470
+ *
1471
+ * - At OOM: Minimal delay (500ms) to maximize chances of freeing memory
1472
+ * - At critical: Reduced delay (2s) with faster triggering if memory is growing rapidly
1473
+ * - At warning: Standard delay (5s) unless memory is growing rapidly
1474
+ *
1475
+ * @param level - Current pressure level
1476
+ * @param now - Current timestamp
1477
+ * @returns Minimum interval in milliseconds before next GC hint
1478
+ */
1479
+ private getAdaptiveGCInterval(level: MemoryPressureLevel, _now: number): number {
1480
+ const baseInterval = this.extendedConfig.gcHintMinIntervalMs ?? DEFAULT_GC_HINT_MIN_INTERVAL_MS
1481
+
1482
+ // At OOM, be very aggressive
1483
+ if (level === 'oom') {
1484
+ return Math.min(500, baseInterval)
1485
+ }
1486
+
1487
+ // At critical, reduce interval and consider growth rate
1488
+ if (level === 'critical') {
1489
+ const growthRate = this.getMemoryGrowthRate()
1490
+ // If memory is growing rapidly (>5MB/s), be more aggressive
1491
+ if (growthRate.bytesPerSecond > 5 * 1024 * 1024) {
1492
+ return Math.min(1000, baseInterval / 2)
1493
+ }
1494
+ return Math.min(2000, baseInterval)
1495
+ }
1496
+
1497
+ // At warning level, check if we should trigger earlier based on OOM prediction
1498
+ if (level === 'warning') {
1499
+ const prediction = this.predictTimeToOOM()
1500
+ // If OOM predicted within 30 seconds with high confidence, reduce interval
1501
+ if (prediction.willOOM && prediction.estimatedMs < 30000 && prediction.confidence > 0.7) {
1502
+ return Math.min(2000, baseInterval / 2)
1503
+ }
1504
+ }
1505
+
1506
+ return baseInterval
1507
+ }
1508
+
1509
+ /**
1510
+ * Evict cache entries based on pressure level.
1511
+ */
1512
+ private evictCache(level: MemoryPressureLevel): boolean {
1513
+ if (!this.cacheManager) return false
1514
+
1515
+ const evictionTargets = this.extendedConfig.evictionTargetPercent ?? DEFAULT_EVICTION_TARGET_PERCENT
1516
+ const targetPercent = evictionTargets[level] ?? 0
1517
+ if (targetPercent <= 0) return false
1518
+
1519
+ const totalCacheSize = this.cacheManager.getTotalSize()
1520
+ const targetBytes = Math.floor(totalCacheSize * (targetPercent / 100))
1521
+
1522
+ if (level === 'oom' && this.cacheManager.clear) {
1523
+ this.cacheManager.clear()
1524
+ return true
1525
+ }
1526
+
1527
+ if (this.cacheManager.evictLRU) {
1528
+ this.cacheManager.evictLRU(targetBytes)
1529
+ return true
1530
+ }
1531
+
1532
+ return false
1533
+ }
1534
+
1535
+ /**
1536
+ * Update pressure duration tracking.
1537
+ */
1538
+ private updatePressureDuration(level: MemoryPressureLevel, now: number): void {
1539
+ const elapsed = now - this.pressureLevelStartTime
1540
+ this.pressureDurations[level] += elapsed
1541
+ this.pressureLevelStartTime = now
1542
+ }
1543
+
1544
+ // ===========================================================================
1545
+ // Extension Management
1546
+ // ===========================================================================
1547
+
1548
+ /**
1549
+ * Unload the least important extension to free memory.
1550
+ *
1551
+ * Uses the extensionUnloadPriority list to determine which extension
1552
+ * to drop first.
1553
+ *
1554
+ * @returns Result of the unload operation
1555
+ */
1556
+ async unloadLeastImportantExtension(): Promise<MemoryPressureActionResult> {
1557
+ if (!this.pglite) {
1558
+ return { action: 'extension_unloaded', success: false, error: 'PGlite not available' }
1559
+ }
1560
+
1561
+ // Get installed extensions
1562
+ let installedExtensions: Set<string>
1563
+ try {
1564
+ const result = await this.pglite.query(`SELECT extname FROM pg_extension WHERE extname != 'plpgsql'`)
1565
+ installedExtensions = new Set(
1566
+ (result.rows as { extname: string }[])
1567
+ .map((r) => r.extname)
1568
+ .filter((name) => !this.unloadedExtensions.has(name))
1569
+ )
1570
+ } catch (error) {
1571
+ return {
1572
+ action: 'extension_unloaded',
1573
+ success: false,
1574
+ error: `Failed to get extensions: ${error instanceof Error ? error.message : 'Unknown'}`,
1575
+ }
1576
+ }
1577
+
1578
+ // Find and unload the least important
1579
+ for (const extName of this.config.extensionUnloadPriority) {
1580
+ if (installedExtensions.has(extName)) {
1581
+ try {
1582
+ await this.pglite.query(`DROP EXTENSION IF EXISTS "${extName}" CASCADE`)
1583
+ this.unloadedExtensions.add(extName)
1584
+ return { action: 'extension_unloaded', success: true, extensionName: extName }
1585
+ } catch (error) {
1586
+ return {
1587
+ action: 'extension_unloaded',
1588
+ success: false,
1589
+ extensionName: extName,
1590
+ error: `Failed to unload: ${error instanceof Error ? error.message : 'Unknown'}`,
1591
+ }
1592
+ }
1593
+ }
1594
+ }
1595
+
1596
+ return { action: 'extension_unloaded', success: false, error: 'No extensions available to unload' }
1597
+ }
1598
+
1599
+ /**
1600
+ * Get list of extensions unloaded due to memory pressure.
1601
+ */
1602
+ getUnloadedExtensions(): string[] {
1603
+ return Array.from(this.unloadedExtensions)
1604
+ }
1605
+
1606
+ /**
1607
+ * Check if an extension was unloaded due to memory pressure.
1608
+ */
1609
+ wasExtensionUnloaded(extensionName: string): boolean {
1610
+ return this.unloadedExtensions.has(extensionName)
1611
+ }
1612
+
1613
+ /**
1614
+ * Clear the list of unloaded extensions.
1615
+ */
1616
+ clearUnloadedExtensions(): void {
1617
+ this.unloadedExtensions.clear()
1618
+ }
1619
+
1620
+ // ===========================================================================
1621
+ // GC Hints
1622
+ // ===========================================================================
1623
+
1624
+ /**
1625
+ * Request a garbage collection hint.
1626
+ *
1627
+ * Attempts to trigger GC if available and tracks effectiveness.
1628
+ * Optimized: Uses performance.now() for higher precision timing.
1629
+ *
1630
+ * @returns Result including bytes freed estimate
1631
+ */
1632
+ async requestGC(): Promise<GCResult> {
1633
+ const startTime = performance.now()
1634
+ const heapBefore = this.getCurrentHeapUsage()
1635
+ let triggered = false
1636
+
1637
+ // Try to trigger GC if available
1638
+ const gc = (globalThis as { gc?: () => void }).gc
1639
+ if (typeof gc === 'function') {
1640
+ try {
1641
+ gc()
1642
+ triggered = true
1643
+ } catch {
1644
+ // GC not available or failed
1645
+ }
1646
+ }
1647
+
1648
+ const durationMs = performance.now() - startTime
1649
+ const heapAfter = this.getCurrentHeapUsage()
1650
+ const bytesFreedEstimate = Math.max(0, heapBefore - heapAfter)
1651
+
1652
+ // Track metrics using ring buffers
1653
+ this.gcMetrics.hints++
1654
+ this.gcMetrics.bytesFreed.push(bytesFreedEstimate)
1655
+ this.gcMetrics.durations.push(durationMs)
1656
+
1657
+ return { triggered, bytesFreedEstimate, durationMs }
1658
+ }
1659
+
1660
+ /**
1661
+ * Get aggregated GC metrics.
1662
+ * Optimized: Uses ring buffer's toArray() for efficient conversion.
1663
+ */
1664
+ getGCMetrics(): GCMetrics {
1665
+ const { hints, bytesFreed, durations } = this.gcMetrics
1666
+ const bytesFreedArray = bytesFreed.toArray()
1667
+ const durationsArray = durations.toArray()
1668
+
1669
+ // Count successful GC hints (where memory was actually freed)
1670
+ let successCount = 0
1671
+ for (let i = 0; i < bytesFreedArray.length; i++) {
1672
+ if ((bytesFreedArray[i] ?? 0) > 0) successCount++
1673
+ }
1674
+
1675
+ return {
1676
+ totalHints: hints,
1677
+ avgBytesFreed: average(bytesFreedArray),
1678
+ avgDurationMs: average(durationsArray),
1679
+ successRate: hints > 0 ? successCount / Math.min(hints, bytesFreedArray.length) : 0,
1680
+ }
1681
+ }
1682
+
1683
+ // ===========================================================================
1684
+ // Metrics & History
1685
+ // ===========================================================================
1686
+
1687
+ /**
1688
+ * Record a metric sample for history tracking.
1689
+ *
1690
+ * Also detects memory spikes, updates EMA state, and emits events.
1691
+ * Optimized: Uses ring buffer (no shift operations).
1692
+ */
1693
+ recordMetricSample(): void {
1694
+ const stats = this.getMemoryStats()
1695
+ const sample: MetricsSample = {
1696
+ timestamp: new Date(),
1697
+ heapUsed: stats.heapUsed,
1698
+ pressureLevel: stats.pressureLevel,
1699
+ }
1700
+
1701
+ // Add to ring buffer (automatically handles size limit)
1702
+ this.metricsHistory.push(sample)
1703
+
1704
+ // Update smoothed state for EMA-based detection
1705
+ this.updateSmoothedState(stats.heapUsed, PRESSURE_LEVEL_INDEX[stats.pressureLevel])
1706
+
1707
+ // Spike detection
1708
+ if (this.lastHeapSample > 0 && stats.heapUsed - this.lastHeapSample >= SPIKE_DETECTION_THRESHOLD_BYTES) {
1709
+ const spike: MemorySpikeEvent = {
1710
+ deltaBytes: stats.heapUsed - this.lastHeapSample,
1711
+ previousBytes: this.lastHeapSample,
1712
+ currentBytes: stats.heapUsed,
1713
+ timestamp: new Date(),
1714
+ }
1715
+ // Use for loop instead of forEach for better performance
1716
+ const listeners = this.memorySpikeListeners
1717
+ for (let i = 0; i < listeners.length; i++) {
1718
+ const listener = listeners[i]
1719
+ if (listener) listener(spike)
1720
+ }
1721
+ }
1722
+
1723
+ this.lastHeapSample = stats.heapUsed
1724
+ }
1725
+
1726
+ /**
1727
+ * Update smoothed pressure state using EMA.
1728
+ * This provides noise-resistant pressure tracking.
1729
+ */
1730
+ private updateSmoothedState(rawHeapUsage: number, rawPressureLevel: number): void {
1731
+ if (!this.smoothedStateInitialized) {
1732
+ // Initialize with first sample
1733
+ this.smoothedState = {
1734
+ emaHeapUsage: rawHeapUsage,
1735
+ emaPressureLevel: rawPressureLevel,
1736
+ recentVariance: 0,
1737
+ trend: 'stable',
1738
+ lastRawHeapUsage: rawHeapUsage,
1739
+ }
1740
+ this.smoothedStateInitialized = true
1741
+ return
1742
+ }
1743
+
1744
+ // Update EMAs
1745
+ this.smoothedState.emaHeapUsage = updateEMA(
1746
+ this.smoothedState.emaHeapUsage,
1747
+ rawHeapUsage
1748
+ )
1749
+ this.smoothedState.emaPressureLevel = updateEMA(
1750
+ this.smoothedState.emaPressureLevel,
1751
+ rawPressureLevel
1752
+ )
1753
+
1754
+ // Calculate variance from recent samples
1755
+ if (this.metricsHistory.size >= 3) {
1756
+ const recentSamples = this.metricsHistory.toArray().slice(-10)
1757
+ const heapValues = recentSamples.map((s) => s.heapUsed)
1758
+ this.smoothedState.recentVariance = variance(heapValues)
1759
+ }
1760
+
1761
+ // Determine trend from delta
1762
+ const delta = rawHeapUsage - this.smoothedState.lastRawHeapUsage
1763
+ if (delta > STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC) {
1764
+ this.smoothedState.trend = 'rising'
1765
+ } else if (delta < -STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC) {
1766
+ this.smoothedState.trend = 'falling'
1767
+ } else {
1768
+ this.smoothedState.trend = 'stable'
1769
+ }
1770
+
1771
+ this.smoothedState.lastRawHeapUsage = rawHeapUsage
1772
+ }
1773
+
1774
+ /**
1775
+ * Get the smoothed pressure state.
1776
+ * Useful for observability and debugging.
1777
+ */
1778
+ getSmoothedPressureState(): SmoothedPressureState {
1779
+ return { ...this.smoothedState }
1780
+ }
1781
+
1782
+ /**
1783
+ * Get the smoothed pressure level (EMA-based).
1784
+ * This is less sensitive to momentary spikes than raw pressure level.
1785
+ */
1786
+ getSmoothedPressureLevel(): MemoryPressureLevel {
1787
+ const levelIndex = Math.round(this.smoothedState.emaPressureLevel)
1788
+ const levels: MemoryPressureLevel[] = ['normal', 'warning', 'critical', 'oom']
1789
+ return levels[Math.max(0, Math.min(3, levelIndex))] ?? 'normal'
1790
+ }
1791
+
1792
+ /**
1793
+ * Check if pressure is volatile (high variance in recent samples).
1794
+ * Useful for deciding whether to take aggressive action.
1795
+ */
1796
+ isPressureVolatile(): boolean {
1797
+ const stats = this.getMemoryStats()
1798
+ // Coefficient of variation > 10% indicates volatility
1799
+ const cv = stats.heapUsed > 0
1800
+ ? Math.sqrt(this.smoothedState.recentVariance) / stats.heapUsed
1801
+ : 0
1802
+ return cv > 0.1
1803
+ }
1804
+
1805
+ /**
1806
+ * Get adaptive thresholds based on current memory behavior.
1807
+ *
1808
+ * Adjusts thresholds based on:
1809
+ * 1. Memory growth rate - faster growth = lower thresholds
1810
+ * 2. Volatility - high volatility = higher thresholds (less sensitive)
1811
+ * 3. OOM prediction - imminent OOM = lower thresholds
1812
+ */
1813
+ getAdaptiveThresholds(): {
1814
+ warningThresholdPercent: number
1815
+ criticalThresholdPercent: number
1816
+ oomThresholdPercent: number
1817
+ } {
1818
+ const baseConfig = this.config
1819
+
1820
+ // Return base thresholds if insufficient data
1821
+ if (this.metricsHistory.size < MIN_SAMPLES_FOR_TREND) {
1822
+ return {
1823
+ warningThresholdPercent: baseConfig.warningThresholdPercent,
1824
+ criticalThresholdPercent: baseConfig.criticalThresholdPercent,
1825
+ oomThresholdPercent: baseConfig.oomThresholdPercent,
1826
+ }
1827
+ }
1828
+
1829
+ const growthRate = this.getMemoryGrowthRate()
1830
+ const oomPrediction = this.predictTimeToOOM()
1831
+
1832
+ let warningAdjustment = 0
1833
+ let criticalAdjustment = 0
1834
+
1835
+ // Adjust for growth rate
1836
+ if (growthRate.bytesPerSecond > RAPID_GROWTH_THRESHOLD_BYTES_PER_SEC) {
1837
+ // Rapid growth - lower thresholds by up to 10%
1838
+ warningAdjustment -= 10
1839
+ criticalAdjustment -= 5
1840
+ } else if (growthRate.trend === 'increasing') {
1841
+ // Moderate growth - lower thresholds by up to 5%
1842
+ warningAdjustment -= 5
1843
+ criticalAdjustment -= 3
1844
+ }
1845
+
1846
+ // Adjust for volatility (increase thresholds if volatile to reduce false alarms)
1847
+ if (this.isPressureVolatile()) {
1848
+ warningAdjustment += 3
1849
+ criticalAdjustment += 2
1850
+ }
1851
+
1852
+ // Adjust for imminent OOM prediction
1853
+ if (oomPrediction.willOOM && (oomPrediction.rSquared ?? 0) > 0.5) {
1854
+ warningAdjustment -= 5
1855
+ criticalAdjustment -= 3
1856
+ }
1857
+
1858
+ // Apply adjustments with clamping
1859
+ const warningThresholdPercent = Math.max(50, Math.min(80,
1860
+ baseConfig.warningThresholdPercent + warningAdjustment
1861
+ ))
1862
+ const criticalThresholdPercent = Math.max(
1863
+ warningThresholdPercent + 5,
1864
+ Math.min(90, baseConfig.criticalThresholdPercent + criticalAdjustment)
1865
+ )
1866
+ const oomThresholdPercent = Math.max(
1867
+ criticalThresholdPercent + 5,
1868
+ baseConfig.oomThresholdPercent
1869
+ )
1870
+
1871
+ return { warningThresholdPercent, criticalThresholdPercent, oomThresholdPercent }
1872
+ }
1873
+
1874
+ /**
1875
+ * Get pressure level using adaptive thresholds.
1876
+ * This provides more intelligent pressure detection based on memory behavior.
1877
+ */
1878
+ getAdaptivePressureLevel(): MemoryPressureLevel {
1879
+ const stats = this.getMemoryStats()
1880
+ const adaptiveThresholds = this.getAdaptiveThresholds()
1881
+ return getPressureLevelFromPercent(stats.usagePercent, adaptiveThresholds)
1882
+ }
1883
+
1884
+ /**
1885
+ * Get metrics history.
1886
+ * Returns a copy of the samples array.
1887
+ */
1888
+ getMetricsHistory(): MetricsHistory {
1889
+ return { samples: this.metricsHistory.toArray() }
1890
+ }
1891
+
1892
+ /**
1893
+ * Calculate memory growth rate using linear regression.
1894
+ *
1895
+ * Optimized improvements over simple first/last calculation:
1896
+ * 1. Uses all samples via linear regression for more accurate trend
1897
+ * 2. Filters out noise using configurable threshold
1898
+ * 3. Provides confidence via R-squared value
1899
+ *
1900
+ * Falls back to simple calculation if insufficient samples.
1901
+ */
1902
+ getMemoryGrowthRate(): MemoryGrowthRate {
1903
+ const historySize = this.metricsHistory.size
1904
+
1905
+ if (historySize < 2) {
1906
+ return { bytesPerSecond: 0, trend: 'stable' }
1907
+ }
1908
+
1909
+ // For small sample counts, use simple first/last calculation
1910
+ if (historySize < MIN_SAMPLES_FOR_TREND) {
1911
+ const first = this.metricsHistory.first()
1912
+ const last = this.metricsHistory.last()
1913
+
1914
+ if (!first || !last) return { bytesPerSecond: 0, trend: 'stable' }
1915
+
1916
+ const timeDeltaMs = last.timestamp.getTime() - first.timestamp.getTime()
1917
+ if (timeDeltaMs <= 0) return { bytesPerSecond: 0, trend: 'stable' }
1918
+
1919
+ const bytesDelta = last.heapUsed - first.heapUsed
1920
+ const bytesPerSecond = (bytesDelta / timeDeltaMs) * 1000
1921
+
1922
+ return {
1923
+ bytesPerSecond,
1924
+ trend: this.classifyGrowthTrend(bytesPerSecond),
1925
+ }
1926
+ }
1927
+
1928
+ // Use linear regression for more accurate trend analysis
1929
+ const samples = this.metricsHistory.toArray()
1930
+ const { slope, rSquared } = linearRegression(samples)
1931
+
1932
+ // slope is in bytes/ms, convert to bytes/second
1933
+ const bytesPerSecond = slope * 1000
1934
+
1935
+ // If regression fit is poor (R² < 0.5), the trend is likely noisy/unstable
1936
+ // Use a higher threshold for stable classification
1937
+ const effectiveThreshold = rSquared < 0.5
1938
+ ? STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC * 2
1939
+ : STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC
1940
+
1941
+ return {
1942
+ bytesPerSecond,
1943
+ trend: this.classifyGrowthTrend(bytesPerSecond, effectiveThreshold),
1944
+ }
1945
+ }
1946
+
1947
+ /**
1948
+ * Classify growth trend based on bytes per second.
1949
+ */
1950
+ private classifyGrowthTrend(
1951
+ bytesPerSecond: number,
1952
+ threshold: number = STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC
1953
+ ): 'increasing' | 'stable' | 'decreasing' {
1954
+ if (bytesPerSecond > threshold) return 'increasing'
1955
+ if (bytesPerSecond < -threshold) return 'decreasing'
1956
+ return 'stable'
1957
+ }
1958
+
1959
+ /**
1960
+ * Predict time to OOM using linear regression and statistical analysis.
1961
+ *
1962
+ * Improved prediction algorithm:
1963
+ * 1. Uses linear regression for trend-based prediction
1964
+ * 2. Includes R-squared for confidence estimation
1965
+ * 3. Accounts for variance in samples (high variance = lower confidence)
1966
+ * 4. Predicts usage at OOM horizon for early warning
1967
+ */
1968
+ predictTimeToOOM(): OOMPrediction {
1969
+ const stats = this.getMemoryStats()
1970
+ const historySize = this.metricsHistory.size
1971
+
1972
+ // Default return for insufficient data
1973
+ const defaultPrediction: OOMPrediction = {
1974
+ estimatedMs: Infinity,
1975
+ confidence: 0,
1976
+ willOOM: false,
1977
+ rSquared: 0,
1978
+ predictedUsageAtHorizon: stats.heapUsed,
1979
+ }
1980
+
1981
+ if (historySize < 2) {
1982
+ return defaultPrediction
1983
+ }
1984
+
1985
+ const samples = this.metricsHistory.toArray()
1986
+ const { slope, intercept, rSquared } = linearRegression(samples)
1987
+
1988
+ // slope is bytes/ms
1989
+ // If not growing (slope <= 0), no OOM predicted
1990
+ if (slope <= 0) {
1991
+ return {
1992
+ ...defaultPrediction,
1993
+ rSquared,
1994
+ predictedUsageAtHorizon: Math.max(0, stats.heapUsed + slope * OOM_PREDICTION_HORIZON_MS),
1995
+ }
1996
+ }
1997
+
1998
+ // Calculate time to reach OOM threshold
1999
+ const oomThresholdBytes = (stats.heapLimit * this.config.oomThresholdPercent) / 100
2000
+ const bytesRemaining = oomThresholdBytes - stats.heapUsed
2001
+
2002
+ if (bytesRemaining <= 0) {
2003
+ // Already at or above OOM threshold
2004
+ return {
2005
+ estimatedMs: 0,
2006
+ confidence: 1.0,
2007
+ willOOM: true,
2008
+ rSquared,
2009
+ predictedUsageAtHorizon: stats.heapUsed,
2010
+ }
2011
+ }
2012
+
2013
+ // Time to OOM in milliseconds: (oomThreshold - currentUsage) / slope
2014
+ const estimatedMs = bytesRemaining / slope
2015
+
2016
+ // Calculate confidence based on multiple factors:
2017
+ // 1. Sample count (more samples = more confidence)
2018
+ // 2. R-squared (better fit = more confidence)
2019
+ // 3. Variance consistency (less variance = more confidence)
2020
+ const sampleConfidence = Math.min(1.0, historySize / MIN_SAMPLES_FOR_OOM_PREDICTION)
2021
+ const fitConfidence = Math.max(0, rSquared) // R² is already 0-1
2022
+
2023
+ // Calculate variance confidence
2024
+ const heapValues = samples.map((s) => s.heapUsed)
2025
+ const sampleVariance = variance(heapValues)
2026
+ const meanHeap = average(heapValues)
2027
+ // Coefficient of variation - lower is more consistent
2028
+ const cv = meanHeap > 0 ? Math.sqrt(sampleVariance) / meanHeap : 0
2029
+ const varianceConfidence = Math.max(0, 1 - cv * 10) // Scale CV to 0-1 range
2030
+
2031
+ // Combined confidence (weighted average)
2032
+ const confidence = Math.min(1.0,
2033
+ sampleConfidence * 0.3 +
2034
+ fitConfidence * 0.5 +
2035
+ varianceConfidence * 0.2
2036
+ )
2037
+
2038
+ // Predict usage at the OOM horizon
2039
+ const now = Date.now()
2040
+ const horizonTime = now + OOM_PREDICTION_HORIZON_MS
2041
+ const t0 = samples[0]?.timestamp.getTime() ?? now
2042
+ const predictedUsageAtHorizon = Math.max(0, slope * (horizonTime - t0) + intercept)
2043
+
2044
+ return {
2045
+ estimatedMs: Math.max(0, estimatedMs),
2046
+ confidence,
2047
+ willOOM: estimatedMs < OOM_PREDICTION_HORIZON_MS && confidence > 0.5,
2048
+ rSquared,
2049
+ predictedUsageAtHorizon,
2050
+ }
2051
+ }
2052
+
2053
+ /**
2054
+ * Get time spent at each pressure level.
2055
+ */
2056
+ getPressureDurations(): PressureDurations {
2057
+ const now = Date.now()
2058
+ const currentElapsed = now - this.pressureLevelStartTime
2059
+ const durations = { ...this.pressureDurations }
2060
+ durations[this.lastPressureLevel] += currentElapsed
2061
+ return durations
2062
+ }
2063
+
2064
+ /**
2065
+ * Export metrics in Prometheus format.
2066
+ * Enhanced: Includes heuristics metrics for better observability.
2067
+ */
2068
+ exportPrometheusMetrics(): string {
2069
+ const stats = this.getMemoryStats()
2070
+ const gcMetrics = this.getGCMetrics()
2071
+ const demotionMetrics = this.getDemotionMetrics()
2072
+ const growthRate = this.getMemoryGrowthRate()
2073
+ const oomPrediction = this.predictTimeToOOM()
2074
+ const smoothedState = this.getSmoothedPressureState()
2075
+ const adaptiveThresholds = this.getAdaptiveThresholds()
2076
+
2077
+ const levels: Record<MemoryPressureLevel, number> = { normal: 0, warning: 1, critical: 2, oom: 3 }
2078
+ const trends: Record<string, number> = { decreasing: -1, stable: 0, increasing: 1 }
2079
+
2080
+ return [
2081
+ // Core metrics
2082
+ `# HELP postgres_memory_heap_bytes Current WASM heap usage in bytes`,
2083
+ `# TYPE postgres_memory_heap_bytes gauge`,
2084
+ `postgres_memory_heap_bytes ${stats.heapUsed}`,
2085
+ `# HELP postgres_memory_pressure_level Current memory pressure level (0=normal, 1=warning, 2=critical, 3=oom)`,
2086
+ `# TYPE postgres_memory_pressure_level gauge`,
2087
+ `postgres_memory_pressure_level ${levels[stats.pressureLevel]}`,
2088
+ `# HELP postgres_memory_gc_count Total number of GC hints triggered`,
2089
+ `# TYPE postgres_memory_gc_count counter`,
2090
+ `postgres_memory_gc_count ${gcMetrics.totalHints}`,
2091
+ `# HELP postgres_memory_pressure_demotions_total Total number of pressure-triggered demotions`,
2092
+ `# TYPE postgres_memory_pressure_demotions_total counter`,
2093
+ `postgres_memory_pressure_demotions_total ${demotionMetrics.pressureTriggeredDemotions}`,
2094
+ `# HELP postgres_memory_pressure_demotions_failed Total number of failed pressure-triggered demotions`,
2095
+ `# TYPE postgres_memory_pressure_demotions_failed counter`,
2096
+ `postgres_memory_pressure_demotions_failed ${demotionMetrics.failedDemotions}`,
2097
+
2098
+ // Heuristics metrics
2099
+ `# HELP postgres_memory_heap_ema_bytes Exponential moving average of heap usage`,
2100
+ `# TYPE postgres_memory_heap_ema_bytes gauge`,
2101
+ `postgres_memory_heap_ema_bytes ${Math.round(smoothedState.emaHeapUsage)}`,
2102
+ `# HELP postgres_memory_growth_rate_bytes_per_sec Memory growth rate in bytes per second`,
2103
+ `# TYPE postgres_memory_growth_rate_bytes_per_sec gauge`,
2104
+ `postgres_memory_growth_rate_bytes_per_sec ${Math.round(growthRate.bytesPerSecond)}`,
2105
+ `# HELP postgres_memory_growth_trend Memory growth trend (-1=decreasing, 0=stable, 1=increasing)`,
2106
+ `# TYPE postgres_memory_growth_trend gauge`,
2107
+ `postgres_memory_growth_trend ${trends[growthRate.trend] ?? 0}`,
2108
+ `# HELP postgres_memory_oom_prediction_ms Predicted time to OOM in milliseconds (-1 if not growing)`,
2109
+ `# TYPE postgres_memory_oom_prediction_ms gauge`,
2110
+ `postgres_memory_oom_prediction_ms ${oomPrediction.estimatedMs === Infinity ? -1 : Math.round(oomPrediction.estimatedMs)}`,
2111
+ `# HELP postgres_memory_oom_prediction_confidence Confidence in OOM prediction (0-1)`,
2112
+ `# TYPE postgres_memory_oom_prediction_confidence gauge`,
2113
+ `postgres_memory_oom_prediction_confidence ${oomPrediction.confidence.toFixed(3)}`,
2114
+ `# HELP postgres_memory_pressure_volatile Whether pressure readings are currently volatile (0=stable, 1=volatile)`,
2115
+ `# TYPE postgres_memory_pressure_volatile gauge`,
2116
+ `postgres_memory_pressure_volatile ${this.isPressureVolatile() ? 1 : 0}`,
2117
+ `# HELP postgres_memory_adaptive_warning_threshold Current adaptive warning threshold percentage`,
2118
+ `# TYPE postgres_memory_adaptive_warning_threshold gauge`,
2119
+ `postgres_memory_adaptive_warning_threshold ${adaptiveThresholds.warningThresholdPercent}`,
2120
+ `# HELP postgres_memory_adaptive_critical_threshold Current adaptive critical threshold percentage`,
2121
+ `# TYPE postgres_memory_adaptive_critical_threshold gauge`,
2122
+ `postgres_memory_adaptive_critical_threshold ${adaptiveThresholds.criticalThresholdPercent}`,
2123
+ ].join('\n')
2124
+ }
2125
+
2126
+ /**
2127
+ * Export metrics in JSON format.
2128
+ * Optimized: Calculates aggregates in a single pass.
2129
+ * Enhanced: Includes heuristics data for observability.
2130
+ */
2131
+ exportJSONMetrics(): JSONMetricsExport {
2132
+ const current = this.getMemoryStats()
2133
+ const history = this.getMetricsHistory()
2134
+ const gcMetrics = this.getGCMetrics()
2135
+
2136
+ // Single-pass calculation of avg and peak
2137
+ const samples = history.samples
2138
+ let sum = 0
2139
+ let peak = current.heapUsed
2140
+
2141
+ if (samples.length > 0) {
2142
+ for (let i = 0; i < samples.length; i++) {
2143
+ const sample = samples[i]
2144
+ if (sample) {
2145
+ const heapUsed = sample.heapUsed
2146
+ sum += heapUsed
2147
+ if (heapUsed > peak) peak = heapUsed
2148
+ }
2149
+ }
2150
+ }
2151
+
2152
+ const avgHeapUsed = samples.length > 0 ? sum / samples.length : current.heapUsed
2153
+ const demotionMetrics = this.getDemotionMetrics()
2154
+
2155
+ return {
2156
+ current,
2157
+ history,
2158
+ aggregates: {
2159
+ avgHeapUsed,
2160
+ peakHeapUsed: peak,
2161
+ totalGCCount: gcMetrics.totalHints,
2162
+ pressureTriggeredDemotions: demotionMetrics.pressureTriggeredDemotions,
2163
+ failedDemotions: demotionMetrics.failedDemotions,
2164
+ bytesFreedByDemotion: demotionMetrics.bytesFreedByDemotion,
2165
+ },
2166
+ heuristics: {
2167
+ smoothedState: this.getSmoothedPressureState(),
2168
+ adaptiveThresholds: this.getAdaptiveThresholds(),
2169
+ oomPrediction: this.predictTimeToOOM(),
2170
+ growthRate: this.getMemoryGrowthRate(),
2171
+ isVolatile: this.isPressureVolatile(),
2172
+ },
2173
+ }
2174
+ }
2175
+
2176
+ // ===========================================================================
2177
+ // Query Management
2178
+ // ===========================================================================
2179
+
2180
+ /**
2181
+ * Execute a query with memory pressure guards.
2182
+ *
2183
+ * - Pauses at critical pressure (with timeout)
2184
+ * - Rejects at OOM pressure
2185
+ * - Enforces hard limits if configured
2186
+ */
2187
+ async guardedQuery<T>(
2188
+ sql: string,
2189
+ params: unknown[],
2190
+ options?: { estimatedResultBytes?: number }
2191
+ ): Promise<{ rows: T[]; fields: unknown[] }> {
2192
+ const stats = this.getMemoryStats()
2193
+
2194
+ // Hard limit enforcement
2195
+ if (this.extendedConfig.hardLimitEnforcement && this.forcedHeapUsage !== null) {
2196
+ const estimatedUsage = options?.estimatedResultBytes ?? 0
2197
+ if (this.forcedHeapUsage + estimatedUsage > this.config.memoryLimitBytes) {
2198
+ throw new MemoryPressureError('Would exceed Worker memory limit', stats)
2199
+ }
2200
+ }
2201
+
2202
+ // OOM rejection
2203
+ if (this.extendedConfig.queryRejectionEnabled && this.lastPressureLevel === 'oom') {
2204
+ throw new MemoryPressureError('Memory pressure too high', stats)
2205
+ }
2206
+
2207
+ // Critical pausing
2208
+ if (this.extendedConfig.queryPausingEnabled && this.lastPressureLevel === 'critical') {
2209
+ await this.waitForPressureRelief()
2210
+ }
2211
+
2212
+ // Execute query
2213
+ if (this.pglite) {
2214
+ const result = await this.pglite.query(sql, params)
2215
+ return result as { rows: T[]; fields: unknown[] }
2216
+ }
2217
+
2218
+ return { rows: [], fields: [] }
2219
+ }
2220
+
2221
+ /**
2222
+ * Wait for pressure to drop with timeout.
2223
+ */
2224
+ private async waitForPressureRelief(): Promise<void> {
2225
+ const timeoutMs = this.extendedConfig.queryPauseTimeoutMs ?? DEFAULT_QUERY_PAUSE_TIMEOUT_MS
2226
+ const operation = this.pauseOperation(`query-${Date.now()}`)
2227
+
2228
+ // Track the operation so it can be resumed when pressure drops
2229
+ const dummyPromise = Promise.resolve() as Promise<unknown>
2230
+ this.pausedQueries.set(dummyPromise, operation)
2231
+
2232
+ const timeoutPromise = new Promise<never>((_, reject) => {
2233
+ setTimeout(() => {
2234
+ operation.status = 'cancelled'
2235
+ this.pausedQueries.delete(dummyPromise)
2236
+ reject(new Error('Query paused due to memory pressure and timed out'))
2237
+ }, timeoutMs)
2238
+ })
2239
+
2240
+ try {
2241
+ await Promise.race([operation.resumePromise, timeoutPromise])
2242
+ } finally {
2243
+ this.pausedQueries.delete(dummyPromise)
2244
+ }
2245
+ }
2246
+
2247
+ /**
2248
+ * Get status of a query promise.
2249
+ */
2250
+ getQueryStatus(queryPromise: Promise<unknown>): 'paused' | 'executing' | 'completed' | 'unknown' {
2251
+ const operation = this.pausedQueries.get(queryPromise)
2252
+ if (operation) {
2253
+ return operation.status === 'paused' ? 'paused' : 'executing'
2254
+ }
2255
+ return 'unknown'
2256
+ }
2257
+
2258
+ /**
2259
+ * Pause an operation and return a handle.
2260
+ */
2261
+ pauseOperation(id: string): PausedOperation {
2262
+ let resolveFunc: () => void = () => {}
2263
+ let rejectFunc: (error: Error) => void = () => {}
2264
+
2265
+ const resumePromise = new Promise<void>((resolve, reject) => {
2266
+ resolveFunc = resolve
2267
+ rejectFunc = reject
2268
+ })
2269
+
2270
+ return {
2271
+ id,
2272
+ status: 'paused',
2273
+ pausedAt: new Date(),
2274
+ resumePromise,
2275
+ resolve: resolveFunc,
2276
+ reject: rejectFunc,
2277
+ }
2278
+ }
2279
+
2280
+ /**
2281
+ * Resume all paused operations.
2282
+ */
2283
+ resumePausedOperations(): void {
2284
+ for (const [, operation] of this.pausedQueries) {
2285
+ if (operation.status === 'paused') {
2286
+ operation.status = 'resumed'
2287
+ operation.resolve()
2288
+ }
2289
+ }
2290
+ this.pausedQueries.clear()
2291
+ }
2292
+
2293
+ /**
2294
+ * Enforce a hard allocation limit.
2295
+ *
2296
+ * Throws if the allocation would exceed the memory limit.
2297
+ */
2298
+ async guardedAllocation(bytes: number): Promise<{ allowed: boolean }> {
2299
+ const currentUsage = this.forcedHeapUsage ?? this.getCurrentHeapUsage()
2300
+ const totalAfter = currentUsage + bytes
2301
+
2302
+ if (this.extendedConfig.hardLimitEnforcement && totalAfter > this.config.memoryLimitBytes) {
2303
+ throw new MemoryPressureError('Would exceed Worker memory limit', this.getMemoryStats())
2304
+ }
2305
+
2306
+ return { allowed: true }
2307
+ }
2308
+
2309
+ // ===========================================================================
2310
+ // Feature Management
2311
+ // ===========================================================================
2312
+
2313
+ /**
2314
+ * Check if a feature is currently enabled.
2315
+ */
2316
+ isFeatureEnabled(feature: string): boolean {
2317
+ return this.enabledFeatures.has(feature) && !this.disabledFeatures.has(feature)
2318
+ }
2319
+
2320
+ /**
2321
+ * Get count of enabled features.
2322
+ */
2323
+ getEnabledFeatureCount(): number {
2324
+ let count = 0
2325
+ for (const feature of this.enabledFeatures) {
2326
+ if (!this.disabledFeatures.has(feature)) count++
2327
+ }
2328
+ return count
2329
+ }
2330
+
2331
+ /**
2332
+ * Disable features under pressure.
2333
+ */
2334
+ private disableFeaturesUnderPressure(): void {
2335
+ if (!this.extendedConfig.autoDisableFeatures) return
2336
+
2337
+ const features = this.extendedConfig.featuresToDisable ?? DEFAULT_FEATURES_TO_DISABLE
2338
+ for (const feature of features) {
2339
+ this.disabledFeatures.add(feature)
2340
+ }
2341
+ }
2342
+
2343
+ /**
2344
+ * Re-enable features after recovery.
2345
+ */
2346
+ private enableFeaturesAfterRecovery(): void {
2347
+ if (!this.extendedConfig.autoDisableFeatures) return
2348
+ this.disabledFeatures.clear()
2349
+ }
2350
+
2351
+ // ===========================================================================
2352
+ // Recovery Management
2353
+ // ===========================================================================
2354
+
2355
+ /**
2356
+ * Check if manager is in recovery mode.
2357
+ */
2358
+ isInRecoveryMode(): boolean {
2359
+ return this.inRecoveryMode
2360
+ }
2361
+
2362
+ /**
2363
+ * Get recovery progress (0.0 to 1.0).
2364
+ */
2365
+ getRecoveryProgress(): number {
2366
+ if (!this.inRecoveryMode) {
2367
+ return this.lastPressureLevel === 'normal' ? 1.0 : 0.0
2368
+ }
2369
+
2370
+ const hysteresisMs = this.extendedConfig.recoveryHysteresisMs ?? DEFAULT_RECOVERY_HYSTERESIS_MS
2371
+ if (this.recoveryStartTime && hysteresisMs > 0) {
2372
+ const elapsed = Date.now() - this.recoveryStartTime
2373
+ this.recoveryProgress = Math.min(1.0, elapsed / hysteresisMs)
2374
+
2375
+ if (this.recoveryProgress >= 1.0 && this.lastPressureLevel === 'normal') {
2376
+ this.inRecoveryMode = false
2377
+ }
2378
+ }
2379
+
2380
+ return this.recoveryProgress
2381
+ }
2382
+
2383
+ /**
2384
+ * Start gradual recovery of features.
2385
+ */
2386
+ async startGradualRecovery(): Promise<void> {
2387
+ if (!this.extendedConfig.gradualRecovery) return
2388
+
2389
+ this.recoveryPaused = false
2390
+ const intervalMs = this.extendedConfig.recoveryStepIntervalMs ?? DEFAULT_RECOVERY_STEP_INTERVAL_MS
2391
+ const features = this.extendedConfig.featuresToDisable ?? DEFAULT_FEATURES_TO_DISABLE
2392
+ let featureIndex = 0
2393
+
2394
+ this.gradualRecoveryInterval = setInterval(() => {
2395
+ if (this.recoveryPaused || featureIndex >= features.length) {
2396
+ if (this.gradualRecoveryInterval) {
2397
+ clearInterval(this.gradualRecoveryInterval)
2398
+ this.gradualRecoveryInterval = null
2399
+ }
2400
+ return
2401
+ }
2402
+
2403
+ const feature = features[featureIndex]
2404
+ if (feature) {
2405
+ this.disabledFeatures.delete(feature)
2406
+ this.recoveryStepsExecuted.push(feature)
2407
+ this.extendedConfig.onRecoveryStep?.(feature)
2408
+ }
2409
+ featureIndex++
2410
+ }, intervalMs)
2411
+ }
2412
+
2413
+ /**
2414
+ * Pause gradual recovery.
2415
+ */
2416
+ pauseGradualRecovery(): void {
2417
+ this.recoveryPaused = true
2418
+ if (this.gradualRecoveryInterval) {
2419
+ clearInterval(this.gradualRecoveryInterval)
2420
+ this.gradualRecoveryInterval = null
2421
+ }
2422
+ }
2423
+
2424
+ /**
2425
+ * Check if recovery is paused.
2426
+ */
2427
+ isRecoveryPaused(): boolean {
2428
+ return this.recoveryPaused
2429
+ }
2430
+
2431
+ // ===========================================================================
2432
+ // Event Listeners
2433
+ // ===========================================================================
2434
+
2435
+ /**
2436
+ * Add a memory pressure event listener.
2437
+ *
2438
+ * @param listener - Callback for pressure events
2439
+ * @returns Unsubscribe function
2440
+ */
2441
+ onPressureChange(listener: MemoryPressureListener): () => void {
2442
+ // Warn when approaching limit
2443
+ const warningThreshold = Math.floor(this.config.maxListeners * 0.8)
2444
+ if (this.listeners.length >= warningThreshold && this.listeners.length < this.config.maxListeners) {
2445
+ console.warn(`Approaching memory pressure listener limit: ${this.listeners.length}/${this.config.maxListeners}`)
2446
+ }
2447
+
2448
+ // Cap at max listeners (drop oldest)
2449
+ if (this.listeners.length >= this.config.maxListeners) {
2450
+ console.warn(`Memory pressure listener limit (${this.config.maxListeners}) reached. Dropping oldest listener.`)
2451
+ this.listeners.shift()
2452
+ }
2453
+
2454
+ this.listeners.push(listener)
2455
+
2456
+ return () => {
2457
+ const index = this.listeners.indexOf(listener)
2458
+ if (index >= 0) this.listeners.splice(index, 1)
2459
+ }
2460
+ }
2461
+
2462
+ /**
2463
+ * Get current listener count.
2464
+ */
2465
+ getListenerCount(): number {
2466
+ return this.listeners.length
2467
+ }
2468
+
2469
+ /**
2470
+ * Add a memory spike listener.
2471
+ *
2472
+ * @param listener - Callback for spike events
2473
+ * @returns Unsubscribe function
2474
+ */
2475
+ onMemorySpike(listener: (spike: MemorySpikeEvent) => void): () => void {
2476
+ this.memorySpikeListeners.push(listener)
2477
+ return () => {
2478
+ const index = this.memorySpikeListeners.indexOf(listener)
2479
+ if (index >= 0) this.memorySpikeListeners.splice(index, 1)
2480
+ }
2481
+ }
2482
+
2483
+ /**
2484
+ * Add a recovery event listener.
2485
+ *
2486
+ * @param listener - Callback for recovery events
2487
+ * @returns Unsubscribe function
2488
+ */
2489
+ onRecovery(listener: (event: RecoveryEvent) => void): () => void {
2490
+ this.recoveryListeners.push(listener)
2491
+ return () => {
2492
+ const index = this.recoveryListeners.indexOf(listener)
2493
+ if (index >= 0) this.recoveryListeners.splice(index, 1)
2494
+ }
2495
+ }
2496
+
2497
+ /**
2498
+ * Emit a pressure event to all listeners.
2499
+ */
2500
+ private emitPressureEvent(event: MemoryPressureEvent): void {
2501
+ for (const listener of this.listeners) {
2502
+ try {
2503
+ listener(event)
2504
+ } catch (error) {
2505
+ console.error('Memory pressure listener error:', error)
2506
+ }
2507
+ }
2508
+ }
2509
+
2510
+ // ===========================================================================
2511
+ // Testing Support
2512
+ // ===========================================================================
2513
+
2514
+ /**
2515
+ * Simulate heap growth for testing.
2516
+ * @internal
2517
+ */
2518
+ _simulateHeapGrowth(bytes: number): void {
2519
+ this.simulatedHeapBytes += bytes
2520
+ }
2521
+
2522
+ /**
2523
+ * Set current heap usage for testing.
2524
+ * @internal
2525
+ */
2526
+ _setCurrentHeapUsage(bytes: number): void {
2527
+ this.forcedHeapUsage = bytes
2528
+ }
2529
+
2530
+ /**
2531
+ * Force a memory pressure level for testing.
2532
+ *
2533
+ * This updates the internal state and emits events as if the pressure
2534
+ * had actually changed. Useful for testing pressure handling without
2535
+ * actually consuming memory.
2536
+ *
2537
+ * @internal
2538
+ */
2539
+ _forceMemoryPressure(level: MemoryPressureLevel): MemoryPressureEvent | null {
2540
+ const previousLevel = this.lastPressureLevel
2541
+ if (previousLevel === level) return null
2542
+
2543
+ const now = Date.now()
2544
+ this.updatePressureDuration(previousLevel, now)
2545
+
2546
+ // Handle recovery/escalation transitions
2547
+ const previousIndex = getPressureLevelIndex(previousLevel)
2548
+ const newIndex = getPressureLevelIndex(level)
2549
+
2550
+ if (previousIndex > 0 && newIndex < previousIndex) {
2551
+ // Pressure dropped
2552
+ this.handleRecovery(previousLevel, level, now)
2553
+ } else if (newIndex > 0 && newIndex >= previousIndex) {
2554
+ // Pressure rose
2555
+ this.exitRecoveryMode()
2556
+ }
2557
+
2558
+ this.lastPressureLevel = level
2559
+
2560
+ // Create synthetic stats
2561
+ const usagePercent = level === 'oom' ? 96 : level === 'critical' ? 88 : level === 'warning' ? 75 : 50
2562
+ const stats: MemoryStats = {
2563
+ heapUsed: this.config.memoryLimitBytes * (usagePercent / 100),
2564
+ heapLimit: this.config.memoryLimitBytes,
2565
+ usagePercent,
2566
+ pressureLevel: level,
2567
+ timestamp: new Date(),
2568
+ }
2569
+
2570
+ const event: MemoryPressureEvent = {
2571
+ previousLevel,
2572
+ currentLevel: level,
2573
+ stats,
2574
+ actionsTaken: [],
2575
+ timestamp: new Date(),
2576
+ }
2577
+
2578
+ this.emitPressureEvent(event)
2579
+
2580
+ // Trigger pressure-triggered demotion if pressure rose
2581
+ if (newIndex > previousIndex && level !== 'normal') {
2582
+ // Fire and forget - don't await to avoid blocking test
2583
+ // Pass true to bypass rate limiting since this is a pressure escalation
2584
+ this.handlePressureDemotion(level, true).catch(() => {})
2585
+ }
2586
+
2587
+ return event
2588
+ }
2589
+
2590
+ // ===========================================================================
2591
+ // Cleanup
2592
+ // ===========================================================================
2593
+
2594
+ /**
2595
+ * Dispose of the manager and clean up all resources.
2596
+ *
2597
+ * This stops all intervals, clears all listeners, and releases references.
2598
+ * Always call this when the manager is no longer needed.
2599
+ */
2600
+ dispose(): void {
2601
+ // Stop all intervals
2602
+ this.stopMonitoring()
2603
+ this.pauseGradualRecovery()
2604
+
2605
+ if (this.sustainedPressureCheckInterval) {
2606
+ clearInterval(this.sustainedPressureCheckInterval)
2607
+ this.sustainedPressureCheckInterval = null
2608
+ }
2609
+
2610
+ // Clear all listeners (set to empty arrays to avoid memory leaks)
2611
+ this.listeners.length = 0
2612
+ this.memorySpikeListeners.length = 0
2613
+ this.recoveryListeners.length = 0
2614
+ this.pressureDemotionListeners.length = 0
2615
+
2616
+ // Release external references
2617
+ this.pglite = null
2618
+ this.wasmModule = null
2619
+ this.cacheManager = null
2620
+ this.tieredStorageOrchestrator = null
2621
+
2622
+ // Clear state
2623
+ this.unloadedExtensions.clear()
2624
+ this.pausedQueries.clear()
2625
+ this.inFlightQueries.clear()
2626
+ this.metricsHistory.clear()
2627
+ this.gcMetrics.bytesFreed.clear()
2628
+ this.gcMetrics.durations.clear()
2629
+ this.disabledFeatures.clear()
2630
+ this.recoveryStepsExecuted.length = 0
2631
+
2632
+ // Reset testing state
2633
+ this.simulatedHeapBytes = 0
2634
+ this.forcedHeapUsage = null
2635
+ }
2636
+ }
2637
+
2638
+ // =============================================================================
2639
+ // Factory Function
2640
+ // =============================================================================
2641
+
2642
+ /**
2643
+ * Create a memory pressure manager with default configuration.
2644
+ *
2645
+ * @param config - Optional configuration overrides
2646
+ * @returns Configured MemoryPressureManager instance
2647
+ */
2648
+ export function createMemoryPressureManager(config?: ExtendedMemoryPressureConfig): MemoryPressureManager {
2649
+ return new MemoryPressureManager(config)
2650
+ }