@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.
- package/README.md +868 -0
- package/dist/cdc/change-stream.d.ts +44 -0
- package/dist/cdc/change-stream.d.ts.map +1 -0
- package/dist/cdc/change-stream.js +95 -0
- package/dist/cdc/change-stream.js.map +1 -0
- package/dist/cdc/filter.d.ts +58 -0
- package/dist/cdc/filter.d.ts.map +1 -0
- package/dist/cdc/filter.js +520 -0
- package/dist/cdc/filter.js.map +1 -0
- package/dist/cdc/index.d.ts +47 -0
- package/dist/cdc/index.d.ts.map +1 -0
- package/dist/cdc/index.js +50 -0
- package/dist/cdc/index.js.map +1 -0
- package/dist/cdc/resume-token.d.ts +60 -0
- package/dist/cdc/resume-token.d.ts.map +1 -0
- package/dist/cdc/resume-token.js +228 -0
- package/dist/cdc/resume-token.js.map +1 -0
- package/dist/cdc/transport/index.d.ts +7 -0
- package/dist/cdc/transport/index.d.ts.map +1 -0
- package/dist/cdc/transport/index.js +7 -0
- package/dist/cdc/transport/index.js.map +1 -0
- package/dist/cdc/transport/sse.d.ts +120 -0
- package/dist/cdc/transport/sse.d.ts.map +1 -0
- package/dist/cdc/transport/sse.js +590 -0
- package/dist/cdc/transport/sse.js.map +1 -0
- package/dist/cdc/transport/websocket.d.ts +130 -0
- package/dist/cdc/transport/websocket.d.ts.map +1 -0
- package/dist/cdc/transport/websocket.js +688 -0
- package/dist/cdc/transport/websocket.js.map +1 -0
- package/dist/cdc/types.d.ts +306 -0
- package/dist/cdc/types.d.ts.map +1 -0
- package/dist/cdc/types.js +8 -0
- package/dist/cdc/types.js.map +1 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +25 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/memory.d.ts +139 -0
- package/dist/config/memory.d.ts.map +1 -0
- package/dist/config/memory.js +157 -0
- package/dist/config/memory.js.map +1 -0
- package/dist/config/storage.d.ts +157 -0
- package/dist/config/storage.d.ts.map +1 -0
- package/dist/config/storage.js +178 -0
- package/dist/config/storage.js.map +1 -0
- package/dist/config/streaming.d.ts +117 -0
- package/dist/config/streaming.d.ts.map +1 -0
- package/dist/config/streaming.js +132 -0
- package/dist/config/streaming.js.map +1 -0
- package/dist/config/timeouts.d.ts +168 -0
- package/dist/config/timeouts.d.ts.map +1 -0
- package/dist/config/timeouts.js +192 -0
- package/dist/config/timeouts.js.map +1 -0
- package/dist/extensions/config.d.ts +89 -0
- package/dist/extensions/config.d.ts.map +1 -0
- package/dist/extensions/config.js +216 -0
- package/dist/extensions/config.js.map +1 -0
- package/dist/extensions/geo.d.ts +452 -0
- package/dist/extensions/geo.d.ts.map +1 -0
- package/dist/extensions/geo.js +583 -0
- package/dist/extensions/geo.js.map +1 -0
- package/dist/extensions/index.d.ts +167 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +99 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/loader.d.ts +226 -0
- package/dist/extensions/loader.d.ts.map +1 -0
- package/dist/extensions/loader.js +456 -0
- package/dist/extensions/loader.js.map +1 -0
- package/dist/extensions/pgmq-lite.d.ts +330 -0
- package/dist/extensions/pgmq-lite.d.ts.map +1 -0
- package/dist/extensions/pgmq-lite.js +648 -0
- package/dist/extensions/pgmq-lite.js.map +1 -0
- package/dist/extensions/plugins.d.ts +260 -0
- package/dist/extensions/plugins.d.ts.map +1 -0
- package/dist/extensions/plugins.js +535 -0
- package/dist/extensions/plugins.js.map +1 -0
- package/dist/extensions/registry.d.ts +93 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/extensions/registry.js +182 -0
- package/dist/extensions/registry.js.map +1 -0
- package/dist/extensions/vector.d.ts +106 -0
- package/dist/extensions/vector.d.ts.map +1 -0
- package/dist/extensions/vector.js +129 -0
- package/dist/extensions/vector.js.map +1 -0
- package/dist/iceberg/analytics.d.ts +279 -0
- package/dist/iceberg/analytics.d.ts.map +1 -0
- package/dist/iceberg/analytics.js +448 -0
- package/dist/iceberg/analytics.js.map +1 -0
- package/dist/iceberg/catalog-api.d.ts +39 -0
- package/dist/iceberg/catalog-api.d.ts.map +1 -0
- package/dist/iceberg/catalog-api.js +388 -0
- package/dist/iceberg/catalog-api.js.map +1 -0
- package/dist/iceberg/catalog.d.ts +401 -0
- package/dist/iceberg/catalog.d.ts.map +1 -0
- package/dist/iceberg/catalog.js +677 -0
- package/dist/iceberg/catalog.js.map +1 -0
- package/dist/iceberg/duckdb-wasm.d.ts +447 -0
- package/dist/iceberg/duckdb-wasm.d.ts.map +1 -0
- package/dist/iceberg/duckdb-wasm.js +600 -0
- package/dist/iceberg/duckdb-wasm.js.map +1 -0
- package/dist/iceberg/index.d.ts +92 -0
- package/dist/iceberg/index.d.ts.map +1 -0
- package/dist/iceberg/index.js +119 -0
- package/dist/iceberg/index.js.map +1 -0
- package/dist/iceberg/metadata.d.ts +214 -0
- package/dist/iceberg/metadata.d.ts.map +1 -0
- package/dist/iceberg/metadata.js +535 -0
- package/dist/iceberg/metadata.js.map +1 -0
- package/dist/iceberg/optimizer.d.ts +296 -0
- package/dist/iceberg/optimizer.d.ts.map +1 -0
- package/dist/iceberg/optimizer.js +889 -0
- package/dist/iceberg/optimizer.js.map +1 -0
- package/dist/iceberg/parquet.d.ts +447 -0
- package/dist/iceberg/parquet.d.ts.map +1 -0
- package/dist/iceberg/parquet.js +1225 -0
- package/dist/iceberg/parquet.js.map +1 -0
- package/dist/iceberg/r2-organization.d.ts +422 -0
- package/dist/iceberg/r2-organization.d.ts.map +1 -0
- package/dist/iceberg/r2-organization.js +672 -0
- package/dist/iceberg/r2-organization.js.map +1 -0
- package/dist/iceberg/scheduler-do-example.d.ts +158 -0
- package/dist/iceberg/scheduler-do-example.d.ts.map +1 -0
- package/dist/iceberg/scheduler-do-example.js +261 -0
- package/dist/iceberg/scheduler-do-example.js.map +1 -0
- package/dist/iceberg/scheduler.d.ts +434 -0
- package/dist/iceberg/scheduler.d.ts.map +1 -0
- package/dist/iceberg/scheduler.js +818 -0
- package/dist/iceberg/scheduler.js.map +1 -0
- package/dist/iceberg/schema.d.ts +149 -0
- package/dist/iceberg/schema.d.ts.map +1 -0
- package/dist/iceberg/schema.js +525 -0
- package/dist/iceberg/schema.js.map +1 -0
- package/dist/iceberg/snapshot-manager.d.ts +406 -0
- package/dist/iceberg/snapshot-manager.d.ts.map +1 -0
- package/dist/iceberg/snapshot-manager.js +934 -0
- package/dist/iceberg/snapshot-manager.js.map +1 -0
- package/dist/iceberg/sql-router.d.ts +194 -0
- package/dist/iceberg/sql-router.d.ts.map +1 -0
- package/dist/iceberg/sql-router.js +180 -0
- package/dist/iceberg/sql-router.js.map +1 -0
- package/dist/iceberg/test-fixtures.d.ts +151 -0
- package/dist/iceberg/test-fixtures.d.ts.map +1 -0
- package/dist/iceberg/test-fixtures.js +446 -0
- package/dist/iceberg/test-fixtures.js.map +1 -0
- package/dist/iceberg/time-travel-api.d.ts +102 -0
- package/dist/iceberg/time-travel-api.d.ts.map +1 -0
- package/dist/iceberg/time-travel-api.js +437 -0
- package/dist/iceberg/time-travel-api.js.map +1 -0
- package/dist/iceberg/time-travel.d.ts +293 -0
- package/dist/iceberg/time-travel.d.ts.map +1 -0
- package/dist/iceberg/time-travel.js +689 -0
- package/dist/iceberg/time-travel.js.map +1 -0
- package/dist/iceberg/transformer.d.ts +356 -0
- package/dist/iceberg/transformer.d.ts.map +1 -0
- package/dist/iceberg/transformer.js +770 -0
- package/dist/iceberg/transformer.js.map +1 -0
- package/dist/iceberg/types.d.ts +318 -0
- package/dist/iceberg/types.d.ts.map +1 -0
- package/dist/iceberg/types.js +9 -0
- package/dist/iceberg/types.js.map +1 -0
- package/dist/iceberg/writer.d.ts +144 -0
- package/dist/iceberg/writer.d.ts.map +1 -0
- package/dist/iceberg/writer.js +452 -0
- package/dist/iceberg/writer.js.map +1 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/lineage/index.d.ts +11 -0
- package/dist/lineage/index.d.ts.map +1 -0
- package/dist/lineage/index.js +11 -0
- package/dist/lineage/index.js.map +1 -0
- package/dist/lineage/integration.d.ts +134 -0
- package/dist/lineage/integration.d.ts.map +1 -0
- package/dist/lineage/integration.js +258 -0
- package/dist/lineage/integration.js.map +1 -0
- package/dist/lineage/tracker.d.ts +189 -0
- package/dist/lineage/tracker.d.ts.map +1 -0
- package/dist/lineage/tracker.js +1352 -0
- package/dist/lineage/tracker.js.map +1 -0
- package/dist/lineage/types.d.ts +318 -0
- package/dist/lineage/types.d.ts.map +1 -0
- package/dist/lineage/types.js +9 -0
- package/dist/lineage/types.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +16 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +397 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +507 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/migration-tooling/external-migration.d.ts +601 -0
- package/dist/migration-tooling/external-migration.d.ts.map +1 -0
- package/dist/migration-tooling/external-migration.js +1612 -0
- package/dist/migration-tooling/external-migration.js.map +1 -0
- package/dist/migration-tooling/index.d.ts +19 -0
- package/dist/migration-tooling/index.d.ts.map +1 -0
- package/dist/migration-tooling/index.js +19 -0
- package/dist/migration-tooling/index.js.map +1 -0
- package/dist/migrations/auto-migrator.d.ts +289 -0
- package/dist/migrations/auto-migrator.d.ts.map +1 -0
- package/dist/migrations/auto-migrator.js +396 -0
- package/dist/migrations/auto-migrator.js.map +1 -0
- package/dist/migrations/bulk-orchestrator.d.ts +403 -0
- package/dist/migrations/bulk-orchestrator.d.ts.map +1 -0
- package/dist/migrations/bulk-orchestrator.js +646 -0
- package/dist/migrations/bulk-orchestrator.js.map +1 -0
- package/dist/migrations/compatibility.d.ts +216 -0
- package/dist/migrations/compatibility.d.ts.map +1 -0
- package/dist/migrations/compatibility.js +651 -0
- package/dist/migrations/compatibility.js.map +1 -0
- package/dist/migrations/do-migrations.d.ts +101 -0
- package/dist/migrations/do-migrations.d.ts.map +1 -0
- package/dist/migrations/do-migrations.js +1060 -0
- package/dist/migrations/do-migrations.js.map +1 -0
- package/dist/migrations/do-migrations.types.d.ts +550 -0
- package/dist/migrations/do-migrations.types.d.ts.map +1 -0
- package/dist/migrations/do-migrations.types.js +15 -0
- package/dist/migrations/do-migrations.types.js.map +1 -0
- package/dist/migrations/drizzle-compat.d.ts +163 -0
- package/dist/migrations/drizzle-compat.d.ts.map +1 -0
- package/dist/migrations/drizzle-compat.js +273 -0
- package/dist/migrations/drizzle-compat.js.map +1 -0
- package/dist/migrations/index.d.ts +109 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +127 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/migration-api.d.ts +161 -0
- package/dist/migrations/migration-api.d.ts.map +1 -0
- package/dist/migrations/migration-api.js +499 -0
- package/dist/migrations/migration-api.js.map +1 -0
- package/dist/migrations/progress-tracker-do.d.ts +195 -0
- package/dist/migrations/progress-tracker-do.d.ts.map +1 -0
- package/dist/migrations/progress-tracker-do.js +339 -0
- package/dist/migrations/progress-tracker-do.js.map +1 -0
- package/dist/migrations/progress-tracker-kv.d.ts +103 -0
- package/dist/migrations/progress-tracker-kv.d.ts.map +1 -0
- package/dist/migrations/progress-tracker-kv.js +231 -0
- package/dist/migrations/progress-tracker-kv.js.map +1 -0
- package/dist/migrations/progress-tracker.d.ts +320 -0
- package/dist/migrations/progress-tracker.d.ts.map +1 -0
- package/dist/migrations/progress-tracker.js +443 -0
- package/dist/migrations/progress-tracker.js.map +1 -0
- package/dist/migrations/registry.d.ts +231 -0
- package/dist/migrations/registry.d.ts.map +1 -0
- package/dist/migrations/registry.js +376 -0
- package/dist/migrations/registry.js.map +1 -0
- package/dist/migrations/runner.d.ts +197 -0
- package/dist/migrations/runner.d.ts.map +1 -0
- package/dist/migrations/runner.js +1167 -0
- package/dist/migrations/runner.js.map +1 -0
- package/dist/migrations/schema-generator.d.ts +111 -0
- package/dist/migrations/schema-generator.d.ts.map +1 -0
- package/dist/migrations/schema-generator.js +335 -0
- package/dist/migrations/schema-generator.js.map +1 -0
- package/dist/migrations/testing.d.ts +321 -0
- package/dist/migrations/testing.d.ts.map +1 -0
- package/dist/migrations/testing.js +645 -0
- package/dist/migrations/testing.js.map +1 -0
- package/dist/migrations/types.d.ts +503 -0
- package/dist/migrations/types.d.ts.map +1 -0
- package/dist/migrations/types.js +11 -0
- package/dist/migrations/types.js.map +1 -0
- package/dist/migrations/validator.d.ts +215 -0
- package/dist/migrations/validator.d.ts.map +1 -0
- package/dist/migrations/validator.js +494 -0
- package/dist/migrations/validator.js.map +1 -0
- package/dist/observability/alerting.d.ts +116 -0
- package/dist/observability/alerting.d.ts.map +1 -0
- package/dist/observability/alerting.js +353 -0
- package/dist/observability/alerting.js.map +1 -0
- package/dist/observability/analytics-engine.d.ts +357 -0
- package/dist/observability/analytics-engine.d.ts.map +1 -0
- package/dist/observability/analytics-engine.js +430 -0
- package/dist/observability/analytics-engine.js.map +1 -0
- package/dist/observability/cost-metrics.d.ts +269 -0
- package/dist/observability/cost-metrics.d.ts.map +1 -0
- package/dist/observability/cost-metrics.js +560 -0
- package/dist/observability/cost-metrics.js.map +1 -0
- package/dist/observability/cross-do-tracing.d.ts +305 -0
- package/dist/observability/cross-do-tracing.d.ts.map +1 -0
- package/dist/observability/cross-do-tracing.js +431 -0
- package/dist/observability/cross-do-tracing.js.map +1 -0
- package/dist/observability/error-rate-collector.d.ts +163 -0
- package/dist/observability/error-rate-collector.d.ts.map +1 -0
- package/dist/observability/error-rate-collector.js +306 -0
- package/dist/observability/error-rate-collector.js.map +1 -0
- package/dist/observability/exporters.d.ts +231 -0
- package/dist/observability/exporters.d.ts.map +1 -0
- package/dist/observability/exporters.js +479 -0
- package/dist/observability/exporters.js.map +1 -0
- package/dist/observability/health-check.d.ts +106 -0
- package/dist/observability/health-check.d.ts.map +1 -0
- package/dist/observability/health-check.js +243 -0
- package/dist/observability/health-check.js.map +1 -0
- package/dist/observability/index.d.ts +297 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +455 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/instrumentation.d.ts +222 -0
- package/dist/observability/instrumentation.d.ts.map +1 -0
- package/dist/observability/instrumentation.js +532 -0
- package/dist/observability/instrumentation.js.map +1 -0
- package/dist/observability/memory-metrics.d.ts +227 -0
- package/dist/observability/memory-metrics.d.ts.map +1 -0
- package/dist/observability/memory-metrics.js +688 -0
- package/dist/observability/memory-metrics.js.map +1 -0
- package/dist/observability/metrics-endpoint.d.ts +91 -0
- package/dist/observability/metrics-endpoint.d.ts.map +1 -0
- package/dist/observability/metrics-endpoint.js +246 -0
- package/dist/observability/metrics-endpoint.js.map +1 -0
- package/dist/observability/metrics.d.ts +88 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +253 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/observability-features.d.ts +488 -0
- package/dist/observability/observability-features.d.ts.map +1 -0
- package/dist/observability/observability-features.js +773 -0
- package/dist/observability/observability-features.js.map +1 -0
- package/dist/observability/prometheus.d.ts +39 -0
- package/dist/observability/prometheus.d.ts.map +1 -0
- package/dist/observability/prometheus.js +120 -0
- package/dist/observability/prometheus.js.map +1 -0
- package/dist/observability/propagation.d.ts +126 -0
- package/dist/observability/propagation.d.ts.map +1 -0
- package/dist/observability/propagation.js +234 -0
- package/dist/observability/propagation.js.map +1 -0
- package/dist/observability/query-latency.d.ts +243 -0
- package/dist/observability/query-latency.d.ts.map +1 -0
- package/dist/observability/query-latency.js +292 -0
- package/dist/observability/query-latency.js.map +1 -0
- package/dist/observability/query-performance.d.ts +169 -0
- package/dist/observability/query-performance.d.ts.map +1 -0
- package/dist/observability/query-performance.js +290 -0
- package/dist/observability/query-performance.js.map +1 -0
- package/dist/observability/storage-tier-metrics.d.ts +174 -0
- package/dist/observability/storage-tier-metrics.d.ts.map +1 -0
- package/dist/observability/storage-tier-metrics.js +306 -0
- package/dist/observability/storage-tier-metrics.js.map +1 -0
- package/dist/observability/tier-cost-optimizer.d.ts +155 -0
- package/dist/observability/tier-cost-optimizer.d.ts.map +1 -0
- package/dist/observability/tier-cost-optimizer.js +536 -0
- package/dist/observability/tier-cost-optimizer.js.map +1 -0
- package/dist/observability/tracer.d.ts +149 -0
- package/dist/observability/tracer.d.ts.map +1 -0
- package/dist/observability/tracer.js +435 -0
- package/dist/observability/tracer.js.map +1 -0
- package/dist/observability/types.d.ts +402 -0
- package/dist/observability/types.d.ts.map +1 -0
- package/dist/observability/types.js +103 -0
- package/dist/observability/types.js.map +1 -0
- package/dist/pglite/workers-pglite.d.ts +138 -0
- package/dist/pglite/workers-pglite.d.ts.map +1 -0
- package/dist/pglite/workers-pglite.js +143 -0
- package/dist/pglite/workers-pglite.js.map +1 -0
- package/dist/pglite-assets/pglite.data +0 -0
- package/dist/pglite-assets/pglite.wasm +0 -0
- package/dist/playground/index.d.ts +52 -0
- package/dist/playground/index.d.ts.map +1 -0
- package/dist/playground/index.js +55 -0
- package/dist/playground/index.js.map +1 -0
- package/dist/playground/keyboard-shortcuts.d.ts +116 -0
- package/dist/playground/keyboard-shortcuts.d.ts.map +1 -0
- package/dist/playground/keyboard-shortcuts.js +588 -0
- package/dist/playground/keyboard-shortcuts.js.map +1 -0
- package/dist/playground/playground.d.ts +82 -0
- package/dist/playground/playground.d.ts.map +1 -0
- package/dist/playground/playground.js +271 -0
- package/dist/playground/playground.js.map +1 -0
- package/dist/playground/query-executor.d.ts +115 -0
- package/dist/playground/query-executor.d.ts.map +1 -0
- package/dist/playground/query-executor.js +558 -0
- package/dist/playground/query-executor.js.map +1 -0
- package/dist/playground/query-history.d.ts +92 -0
- package/dist/playground/query-history.d.ts.map +1 -0
- package/dist/playground/query-history.js +259 -0
- package/dist/playground/query-history.js.map +1 -0
- package/dist/playground/result-formatter.d.ts +59 -0
- package/dist/playground/result-formatter.d.ts.map +1 -0
- package/dist/playground/result-formatter.js +341 -0
- package/dist/playground/result-formatter.js.map +1 -0
- package/dist/playground/sample-datasets.d.ts +77 -0
- package/dist/playground/sample-datasets.d.ts.map +1 -0
- package/dist/playground/sample-datasets.js +641 -0
- package/dist/playground/sample-datasets.js.map +1 -0
- package/dist/playground/sample-queries.d.ts +73 -0
- package/dist/playground/sample-queries.d.ts.map +1 -0
- package/dist/playground/sample-queries.js +1095 -0
- package/dist/playground/sample-queries.js.map +1 -0
- package/dist/playground/schema-explorer.d.ts +55 -0
- package/dist/playground/schema-explorer.d.ts.map +1 -0
- package/dist/playground/schema-explorer.js +473 -0
- package/dist/playground/schema-explorer.js.map +1 -0
- package/dist/playground/types.d.ts +430 -0
- package/dist/playground/types.d.ts.map +1 -0
- package/dist/playground/types.js +10 -0
- package/dist/playground/types.js.map +1 -0
- package/dist/readonly/cache-reader.d.ts +145 -0
- package/dist/readonly/cache-reader.d.ts.map +1 -0
- package/dist/readonly/cache-reader.js +198 -0
- package/dist/readonly/cache-reader.js.map +1 -0
- package/dist/readonly/config.d.ts +74 -0
- package/dist/readonly/config.d.ts.map +1 -0
- package/dist/readonly/config.js +67 -0
- package/dist/readonly/config.js.map +1 -0
- package/dist/readonly/index.d.ts +22 -0
- package/dist/readonly/index.d.ts.map +1 -0
- package/dist/readonly/index.js +17 -0
- package/dist/readonly/index.js.map +1 -0
- package/dist/readonly/pglite-wrapper.d.ts +82 -0
- package/dist/readonly/pglite-wrapper.d.ts.map +1 -0
- package/dist/readonly/pglite-wrapper.js +123 -0
- package/dist/readonly/pglite-wrapper.js.map +1 -0
- package/dist/readonly/worker.d.ts +142 -0
- package/dist/readonly/worker.d.ts.map +1 -0
- package/dist/readonly/worker.js +187 -0
- package/dist/readonly/worker.js.map +1 -0
- package/dist/readonly/write-blocker.d.ts +47 -0
- package/dist/readonly/write-blocker.d.ts.map +1 -0
- package/dist/readonly/write-blocker.js +136 -0
- package/dist/readonly/write-blocker.js.map +1 -0
- package/dist/recovery/disaster-recovery.d.ts +326 -0
- package/dist/recovery/disaster-recovery.d.ts.map +1 -0
- package/dist/recovery/disaster-recovery.js +799 -0
- package/dist/recovery/disaster-recovery.js.map +1 -0
- package/dist/recovery/index.d.ts +12 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/index.js +12 -0
- package/dist/recovery/index.js.map +1 -0
- package/dist/recovery/parquet-parser.d.ts +321 -0
- package/dist/recovery/parquet-parser.d.ts.map +1 -0
- package/dist/recovery/parquet-parser.js +797 -0
- package/dist/recovery/parquet-parser.js.map +1 -0
- package/dist/retention/index.d.ts +50 -0
- package/dist/retention/index.d.ts.map +1 -0
- package/dist/retention/index.js +50 -0
- package/dist/retention/index.js.map +1 -0
- package/dist/retention/policy.d.ts +344 -0
- package/dist/retention/policy.d.ts.map +1 -0
- package/dist/retention/policy.js +472 -0
- package/dist/retention/policy.js.map +1 -0
- package/dist/retention/purger.d.ts +187 -0
- package/dist/retention/purger.d.ts.map +1 -0
- package/dist/retention/purger.js +411 -0
- package/dist/retention/purger.js.map +1 -0
- package/dist/rls/auth-integration.d.ts +280 -0
- package/dist/rls/auth-integration.d.ts.map +1 -0
- package/dist/rls/auth-integration.js +399 -0
- package/dist/rls/auth-integration.js.map +1 -0
- package/dist/rls/generator.d.ts +249 -0
- package/dist/rls/generator.d.ts.map +1 -0
- package/dist/rls/generator.js +495 -0
- package/dist/rls/generator.js.map +1 -0
- package/dist/rls/index.d.ts +26 -0
- package/dist/rls/index.d.ts.map +1 -0
- package/dist/rls/index.js +58 -0
- package/dist/rls/index.js.map +1 -0
- package/dist/rls/policy.d.ts +116 -0
- package/dist/rls/policy.d.ts.map +1 -0
- package/dist/rls/policy.js +77 -0
- package/dist/rls/policy.js.map +1 -0
- package/dist/rls/validator.d.ts +155 -0
- package/dist/rls/validator.d.ts.map +1 -0
- package/dist/rls/validator.js +792 -0
- package/dist/rls/validator.js.map +1 -0
- package/dist/routing/adaptive-router.d.ts +317 -0
- package/dist/routing/adaptive-router.d.ts.map +1 -0
- package/dist/routing/adaptive-router.js +554 -0
- package/dist/routing/adaptive-router.js.map +1 -0
- package/dist/routing/circuit-breaker.d.ts +339 -0
- package/dist/routing/circuit-breaker.d.ts.map +1 -0
- package/dist/routing/circuit-breaker.js +620 -0
- package/dist/routing/circuit-breaker.js.map +1 -0
- package/dist/routing/cost-metrics.d.ts +133 -0
- package/dist/routing/cost-metrics.d.ts.map +1 -0
- package/dist/routing/cost-metrics.js +259 -0
- package/dist/routing/cost-metrics.js.map +1 -0
- package/dist/routing/do-connection-pool.d.ts +243 -0
- package/dist/routing/do-connection-pool.d.ts.map +1 -0
- package/dist/routing/do-connection-pool.js +572 -0
- package/dist/routing/do-connection-pool.js.map +1 -0
- package/dist/routing/index.d.ts +59 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +59 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/query-complexity-estimator.d.ts +73 -0
- package/dist/routing/query-complexity-estimator.d.ts.map +1 -0
- package/dist/routing/query-complexity-estimator.js +327 -0
- package/dist/routing/query-complexity-estimator.js.map +1 -0
- package/dist/routing/request-coalescing.d.ts +178 -0
- package/dist/routing/request-coalescing.d.ts.map +1 -0
- package/dist/routing/request-coalescing.js +325 -0
- package/dist/routing/request-coalescing.js.map +1 -0
- package/dist/routing/runtime-router.d.ts +107 -0
- package/dist/routing/runtime-router.d.ts.map +1 -0
- package/dist/routing/runtime-router.js +246 -0
- package/dist/routing/runtime-router.js.map +1 -0
- package/dist/routing/tenant-router.d.ts +848 -0
- package/dist/routing/tenant-router.d.ts.map +1 -0
- package/dist/routing/tenant-router.js +1056 -0
- package/dist/routing/tenant-router.js.map +1 -0
- package/dist/routing/websocket-pool.d.ts +119 -0
- package/dist/routing/websocket-pool.d.ts.map +1 -0
- package/dist/routing/websocket-pool.js +436 -0
- package/dist/routing/websocket-pool.js.map +1 -0
- package/dist/storage/cache-layer.d.ts +159 -0
- package/dist/storage/cache-layer.d.ts.map +1 -0
- package/dist/storage/cache-layer.js +245 -0
- package/dist/storage/cache-layer.js.map +1 -0
- package/dist/storage/cost-aware-tiering.d.ts +258 -0
- package/dist/storage/cost-aware-tiering.d.ts.map +1 -0
- package/dist/storage/cost-aware-tiering.js +526 -0
- package/dist/storage/cost-aware-tiering.js.map +1 -0
- package/dist/storage/index.d.ts +87 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +78 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/interfaces.d.ts +856 -0
- package/dist/storage/interfaces.d.ts.map +1 -0
- package/dist/storage/interfaces.js +69 -0
- package/dist/storage/interfaces.js.map +1 -0
- package/dist/storage/r2-layer.d.ts +226 -0
- package/dist/storage/r2-layer.d.ts.map +1 -0
- package/dist/storage/r2-layer.js +307 -0
- package/dist/storage/r2-layer.js.map +1 -0
- package/dist/storage/r2-overflow.d.ts +344 -0
- package/dist/storage/r2-overflow.d.ts.map +1 -0
- package/dist/storage/r2-overflow.js +730 -0
- package/dist/storage/r2-overflow.js.map +1 -0
- package/dist/storage/r2-page-vfs.d.ts +374 -0
- package/dist/storage/r2-page-vfs.d.ts.map +1 -0
- package/dist/storage/r2-page-vfs.js +754 -0
- package/dist/storage/r2-page-vfs.js.map +1 -0
- package/dist/storage/swr-cache.d.ts +181 -0
- package/dist/storage/swr-cache.d.ts.map +1 -0
- package/dist/storage/swr-cache.js +295 -0
- package/dist/storage/swr-cache.js.map +1 -0
- package/dist/storage/tiered-orchestrator.d.ts +951 -0
- package/dist/storage/tiered-orchestrator.d.ts.map +1 -0
- package/dist/storage/tiered-orchestrator.js +1731 -0
- package/dist/storage/tiered-orchestrator.js.map +1 -0
- package/dist/storage/tiered-vfs-swr.d.ts +279 -0
- package/dist/storage/tiered-vfs-swr.d.ts.map +1 -0
- package/dist/storage/tiered-vfs-swr.js +584 -0
- package/dist/storage/tiered-vfs-swr.js.map +1 -0
- package/dist/storage/tiered-vfs.d.ts +405 -0
- package/dist/storage/tiered-vfs.d.ts.map +1 -0
- package/dist/storage/tiered-vfs.js +833 -0
- package/dist/storage/tiered-vfs.js.map +1 -0
- package/dist/streaming/backpressure-controller.d.ts +173 -0
- package/dist/streaming/backpressure-controller.d.ts.map +1 -0
- package/dist/streaming/backpressure-controller.js +344 -0
- package/dist/streaming/backpressure-controller.js.map +1 -0
- package/dist/streaming/buffer-pool.d.ts +241 -0
- package/dist/streaming/buffer-pool.d.ts.map +1 -0
- package/dist/streaming/buffer-pool.js +381 -0
- package/dist/streaming/buffer-pool.js.map +1 -0
- package/dist/streaming/cdc-iceberg-connector.d.ts +272 -0
- package/dist/streaming/cdc-iceberg-connector.d.ts.map +1 -0
- package/dist/streaming/cdc-iceberg-connector.js +408 -0
- package/dist/streaming/cdc-iceberg-connector.js.map +1 -0
- package/dist/streaming/index.d.ts +111 -0
- package/dist/streaming/index.d.ts.map +1 -0
- package/dist/streaming/index.js +128 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/streaming/live-cdc-stream.d.ts +400 -0
- package/dist/streaming/live-cdc-stream.d.ts.map +1 -0
- package/dist/streaming/live-cdc-stream.js +703 -0
- package/dist/streaming/live-cdc-stream.js.map +1 -0
- package/dist/streaming/memory-bounded-stream.d.ts +207 -0
- package/dist/streaming/memory-bounded-stream.d.ts.map +1 -0
- package/dist/streaming/memory-bounded-stream.js +340 -0
- package/dist/streaming/memory-bounded-stream.js.map +1 -0
- package/dist/streaming/query-streamer.d.ts +379 -0
- package/dist/streaming/query-streamer.d.ts.map +1 -0
- package/dist/streaming/query-streamer.js +495 -0
- package/dist/streaming/query-streamer.js.map +1 -0
- package/dist/streaming/response-streaming.d.ts +203 -0
- package/dist/streaming/response-streaming.d.ts.map +1 -0
- package/dist/streaming/response-streaming.js +449 -0
- package/dist/streaming/response-streaming.js.map +1 -0
- package/dist/types/branded.d.ts +859 -0
- package/dist/types/branded.d.ts.map +1 -0
- package/dist/types/branded.js +891 -0
- package/dist/types/branded.js.map +1 -0
- package/dist/types/utilities.d.ts +757 -0
- package/dist/types/utilities.d.ts.map +1 -0
- package/dist/types/utilities.js +447 -0
- package/dist/types/utilities.js.map +1 -0
- package/dist/wal/replay-engine.d.ts +344 -0
- package/dist/wal/replay-engine.d.ts.map +1 -0
- package/dist/wal/replay-engine.js +975 -0
- package/dist/wal/replay-engine.js.map +1 -0
- package/dist/worker/__mocks__/capnweb.d.ts +13 -0
- package/dist/worker/__mocks__/capnweb.d.ts.map +1 -0
- package/dist/worker/__mocks__/capnweb.js +15 -0
- package/dist/worker/__mocks__/capnweb.js.map +1 -0
- package/dist/worker/__mocks__/cloudflare-workers.d.ts +31 -0
- package/dist/worker/__mocks__/cloudflare-workers.d.ts.map +1 -0
- package/dist/worker/__mocks__/cloudflare-workers.js +33 -0
- package/dist/worker/__mocks__/cloudflare-workers.js.map +1 -0
- package/dist/worker/__mocks__/pglite.data.d.ts +3 -0
- package/dist/worker/__mocks__/pglite.data.d.ts.map +1 -0
- package/dist/worker/__mocks__/pglite.data.js +20 -0
- package/dist/worker/__mocks__/pglite.data.js.map +1 -0
- package/dist/worker/__mocks__/pglite.wasm.d.ts +3 -0
- package/dist/worker/__mocks__/pglite.wasm.d.ts.map +1 -0
- package/dist/worker/__mocks__/pglite.wasm.js +30 -0
- package/dist/worker/__mocks__/pglite.wasm.js.map +1 -0
- package/dist/worker/auth-rate-limiter.d.ts +270 -0
- package/dist/worker/auth-rate-limiter.d.ts.map +1 -0
- package/dist/worker/auth-rate-limiter.js +332 -0
- package/dist/worker/auth-rate-limiter.js.map +1 -0
- package/dist/worker/auth.d.ts +345 -0
- package/dist/worker/auth.d.ts.map +1 -0
- package/dist/worker/auth.js +837 -0
- package/dist/worker/auth.js.map +1 -0
- package/dist/worker/cdc-backpressure.d.ts +338 -0
- package/dist/worker/cdc-backpressure.d.ts.map +1 -0
- package/dist/worker/cdc-backpressure.js +619 -0
- package/dist/worker/cdc-backpressure.js.map +1 -0
- package/dist/worker/cdc-sse.d.ts +277 -0
- package/dist/worker/cdc-sse.d.ts.map +1 -0
- package/dist/worker/cdc-sse.js +528 -0
- package/dist/worker/cdc-sse.js.map +1 -0
- package/dist/worker/cdc-websocket.d.ts +252 -0
- package/dist/worker/cdc-websocket.d.ts.map +1 -0
- package/dist/worker/cdc-websocket.js +940 -0
- package/dist/worker/cdc-websocket.js.map +1 -0
- package/dist/worker/cdc.d.ts +95 -0
- package/dist/worker/cdc.d.ts.map +1 -0
- package/dist/worker/cdc.js +211 -0
- package/dist/worker/cdc.js.map +1 -0
- package/dist/worker/concerns/auth-concern.d.ts +50 -0
- package/dist/worker/concerns/auth-concern.d.ts.map +1 -0
- package/dist/worker/concerns/auth-concern.js +131 -0
- package/dist/worker/concerns/auth-concern.js.map +1 -0
- package/dist/worker/concerns/cdc-concern.d.ts +99 -0
- package/dist/worker/concerns/cdc-concern.d.ts.map +1 -0
- package/dist/worker/concerns/cdc-concern.js +137 -0
- package/dist/worker/concerns/cdc-concern.js.map +1 -0
- package/dist/worker/concerns/index.d.ts +22 -0
- package/dist/worker/concerns/index.d.ts.map +1 -0
- package/dist/worker/concerns/index.js +13 -0
- package/dist/worker/concerns/index.js.map +1 -0
- package/dist/worker/concerns/query-execution-concern.d.ts +104 -0
- package/dist/worker/concerns/query-execution-concern.d.ts.map +1 -0
- package/dist/worker/concerns/query-execution-concern.js +95 -0
- package/dist/worker/concerns/query-execution-concern.js.map +1 -0
- package/dist/worker/concerns/storage-orchestration-concern.d.ts +78 -0
- package/dist/worker/concerns/storage-orchestration-concern.d.ts.map +1 -0
- package/dist/worker/concerns/storage-orchestration-concern.js +240 -0
- package/dist/worker/concerns/storage-orchestration-concern.js.map +1 -0
- package/dist/worker/do-auth-manager.d.ts +108 -0
- package/dist/worker/do-auth-manager.d.ts.map +1 -0
- package/dist/worker/do-auth-manager.js +212 -0
- package/dist/worker/do-auth-manager.js.map +1 -0
- package/dist/worker/do-pglite-manager.d.ts +137 -0
- package/dist/worker/do-pglite-manager.d.ts.map +1 -0
- package/dist/worker/do-pglite-manager.js +228 -0
- package/dist/worker/do-pglite-manager.js.map +1 -0
- package/dist/worker/do.d.ts +556 -0
- package/dist/worker/do.d.ts.map +1 -0
- package/dist/worker/do.js +1441 -0
- package/dist/worker/do.js.map +1 -0
- package/dist/worker/entry.d.ts +23 -0
- package/dist/worker/entry.d.ts.map +1 -0
- package/dist/worker/entry.js +362 -0
- package/dist/worker/entry.js.map +1 -0
- package/dist/worker/errors.d.ts +106 -0
- package/dist/worker/errors.d.ts.map +1 -0
- package/dist/worker/errors.js +178 -0
- package/dist/worker/errors.js.map +1 -0
- package/dist/worker/health-check-manager.d.ts +141 -0
- package/dist/worker/health-check-manager.d.ts.map +1 -0
- package/dist/worker/health-check-manager.js +145 -0
- package/dist/worker/health-check-manager.js.map +1 -0
- package/dist/worker/index.d.ts +60 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +67 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/memory-pressure.d.ts +892 -0
- package/dist/worker/memory-pressure.d.ts.map +1 -0
- package/dist/worker/memory-pressure.js +1990 -0
- package/dist/worker/memory-pressure.js.map +1 -0
- package/dist/worker/migration-manager.d.ts +153 -0
- package/dist/worker/migration-manager.d.ts.map +1 -0
- package/dist/worker/migration-manager.js +461 -0
- package/dist/worker/migration-manager.js.map +1 -0
- package/dist/worker/plugin-manager.d.ts +147 -0
- package/dist/worker/plugin-manager.d.ts.map +1 -0
- package/dist/worker/plugin-manager.js +408 -0
- package/dist/worker/plugin-manager.js.map +1 -0
- package/dist/worker/proxy.d.ts +330 -0
- package/dist/worker/proxy.d.ts.map +1 -0
- package/dist/worker/proxy.js +504 -0
- package/dist/worker/proxy.js.map +1 -0
- package/dist/worker/query-execution-manager.d.ts +107 -0
- package/dist/worker/query-execution-manager.d.ts.map +1 -0
- package/dist/worker/query-execution-manager.js +155 -0
- package/dist/worker/query-execution-manager.js.map +1 -0
- package/dist/worker/query-executor.d.ts +163 -0
- package/dist/worker/query-executor.d.ts.map +1 -0
- package/dist/worker/query-executor.js +413 -0
- package/dist/worker/query-executor.js.map +1 -0
- package/dist/worker/query-stats-manager.d.ts +117 -0
- package/dist/worker/query-stats-manager.d.ts.map +1 -0
- package/dist/worker/query-stats-manager.js +162 -0
- package/dist/worker/query-stats-manager.js.map +1 -0
- package/dist/worker/result-handler.d.ts +192 -0
- package/dist/worker/result-handler.d.ts.map +1 -0
- package/dist/worker/result-handler.js +346 -0
- package/dist/worker/result-handler.js.map +1 -0
- package/dist/worker/routes.d.ts +135 -0
- package/dist/worker/routes.d.ts.map +1 -0
- package/dist/worker/routes.js +460 -0
- package/dist/worker/routes.js.map +1 -0
- package/dist/worker/rpc-methods-manager.d.ts +142 -0
- package/dist/worker/rpc-methods-manager.d.ts.map +1 -0
- package/dist/worker/rpc-methods-manager.js +195 -0
- package/dist/worker/rpc-methods-manager.js.map +1 -0
- package/dist/worker/rpc.d.ts +259 -0
- package/dist/worker/rpc.d.ts.map +1 -0
- package/dist/worker/rpc.js +398 -0
- package/dist/worker/rpc.js.map +1 -0
- package/dist/worker/schema-version.d.ts +209 -0
- package/dist/worker/schema-version.d.ts.map +1 -0
- package/dist/worker/schema-version.js +450 -0
- package/dist/worker/schema-version.js.map +1 -0
- package/dist/worker/session-manager.d.ts +282 -0
- package/dist/worker/session-manager.d.ts.map +1 -0
- package/dist/worker/session-manager.js +523 -0
- package/dist/worker/session-manager.js.map +1 -0
- package/dist/worker/shutdown-manager.d.ts +188 -0
- package/dist/worker/shutdown-manager.d.ts.map +1 -0
- package/dist/worker/shutdown-manager.js +347 -0
- package/dist/worker/shutdown-manager.js.map +1 -0
- package/dist/worker/sql-transform.d.ts +61 -0
- package/dist/worker/sql-transform.d.ts.map +1 -0
- package/dist/worker/sql-transform.js +312 -0
- package/dist/worker/sql-transform.js.map +1 -0
- package/dist/worker/types.d.ts +738 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +6 -0
- package/dist/worker/types.js.map +1 -0
- package/dist/worker/user-routes.d.ts +76 -0
- package/dist/worker/user-routes.d.ts.map +1 -0
- package/dist/worker/user-routes.js +188 -0
- package/dist/worker/user-routes.js.map +1 -0
- package/dist/worker/wal-facade.d.ts +138 -0
- package/dist/worker/wal-facade.d.ts.map +1 -0
- package/dist/worker/wal-facade.js +184 -0
- package/dist/worker/wal-facade.js.map +1 -0
- package/dist/worker/wal-r2.d.ts +271 -0
- package/dist/worker/wal-r2.d.ts.map +1 -0
- package/dist/worker/wal-r2.js +689 -0
- package/dist/worker/wal-r2.js.map +1 -0
- package/dist/worker/wal-replay.d.ts +361 -0
- package/dist/worker/wal-replay.d.ts.map +1 -0
- package/dist/worker/wal-replay.js +628 -0
- package/dist/worker/wal-replay.js.map +1 -0
- package/dist/worker/wal-retention.d.ts +389 -0
- package/dist/worker/wal-retention.d.ts.map +1 -0
- package/dist/worker/wal-retention.js +763 -0
- package/dist/worker/wal-retention.js.map +1 -0
- package/dist/worker/wal.d.ts +278 -0
- package/dist/worker/wal.d.ts.map +1 -0
- package/dist/worker/wal.js +467 -0
- package/dist/worker/wal.js.map +1 -0
- package/dist/worker/websocket.d.ts +85 -0
- package/dist/worker/websocket.d.ts.map +1 -0
- package/dist/worker/websocket.js +227 -0
- package/dist/worker/websocket.js.map +1 -0
- package/package.json +108 -0
- package/src/cdc/change-stream.ts +137 -0
- package/src/cdc/filter.ts +646 -0
- package/src/cdc/index.ts +112 -0
- package/src/cdc/resume-token.ts +280 -0
- package/src/cdc/transport/index.ts +7 -0
- package/src/cdc/transport/sse.ts +723 -0
- package/src/cdc/transport/websocket.ts +873 -0
- package/src/cdc/types.ts +346 -0
- package/src/config/index.ts +25 -0
- package/src/config/memory.ts +177 -0
- package/src/config/storage.ts +204 -0
- package/src/config/streaming.ts +147 -0
- package/src/config/timeouts.ts +221 -0
- package/src/extensions/config.test.ts +187 -0
- package/src/extensions/config.ts +278 -0
- package/src/extensions/geo.test.ts +455 -0
- package/src/extensions/geo.ts +858 -0
- package/src/extensions/index.test.ts +259 -0
- package/src/extensions/index.ts +227 -0
- package/src/extensions/loader.test.ts +555 -0
- package/src/extensions/loader.ts +588 -0
- package/src/extensions/pgmq-lite.test.ts +727 -0
- package/src/extensions/pgmq-lite.ts +770 -0
- package/src/extensions/plugins.test.ts +528 -0
- package/src/extensions/plugins.ts +718 -0
- package/src/extensions/registry.test.ts +202 -0
- package/src/extensions/registry.ts +267 -0
- package/src/extensions/vector.test.ts +195 -0
- package/src/extensions/vector.ts +217 -0
- package/src/iceberg/SCHEDULER.md +580 -0
- package/src/iceberg/analytics.test.ts +703 -0
- package/src/iceberg/analytics.ts +727 -0
- package/src/iceberg/catalog-api.test.ts +838 -0
- package/src/iceberg/catalog-api.ts +520 -0
- package/src/iceberg/catalog.test.ts +680 -0
- package/src/iceberg/catalog.ts +1007 -0
- package/src/iceberg/iceberg.test.ts +705 -0
- package/src/iceberg/index.ts +406 -0
- package/src/iceberg/metadata.test.ts +632 -0
- package/src/iceberg/metadata.ts +649 -0
- package/src/iceberg/optimizer.test.ts +868 -0
- package/src/iceberg/optimizer.ts +1287 -0
- package/src/iceberg/parquet.test.ts +899 -0
- package/src/iceberg/parquet.ts +1640 -0
- package/src/iceberg/r2-organization.test.ts +615 -0
- package/src/iceberg/r2-organization.ts +951 -0
- package/src/iceberg/scheduler-do-example.ts +364 -0
- package/src/iceberg/scheduler.test.ts +861 -0
- package/src/iceberg/scheduler.ts +1201 -0
- package/src/iceberg/schema.test.ts +547 -0
- package/src/iceberg/schema.ts +616 -0
- package/src/iceberg/snapshot-manager.test.ts +919 -0
- package/src/iceberg/snapshot-manager.ts +1369 -0
- package/src/iceberg/sql-router.test.ts +334 -0
- package/src/iceberg/sql-router.ts +337 -0
- package/src/iceberg/test-fixtures.ts +605 -0
- package/src/iceberg/time-travel-api.test.ts +1029 -0
- package/src/iceberg/time-travel-api.ts +731 -0
- package/src/iceberg/time-travel.test.ts +1218 -0
- package/src/iceberg/time-travel.ts +1052 -0
- package/src/iceberg/transformer.test.ts +689 -0
- package/src/iceberg/transformer.ts +1029 -0
- package/src/iceberg/types.ts +373 -0
- package/src/iceberg/writer.test.ts +716 -0
- package/src/iceberg/writer.ts +590 -0
- package/src/index.ts +212 -0
- package/src/lineage/index.ts +42 -0
- package/src/lineage/integration.ts +334 -0
- package/src/lineage/tracker.ts +1618 -0
- package/src/lineage/types.ts +354 -0
- package/src/middleware/index.ts +36 -0
- package/src/middleware/rate-limit-concurrent.test.ts +794 -0
- package/src/middleware/rate-limit.test.ts +1568 -0
- package/src/middleware/rate-limit.ts +840 -0
- package/src/migration-tooling/external-migration.test.ts +1864 -0
- package/src/migration-tooling/external-migration.ts +2355 -0
- package/src/migration-tooling/index.ts +19 -0
- package/src/migrations/ARCHITECTURE.md +474 -0
- package/src/migrations/PROGRESS_TRACKING.md +485 -0
- package/src/migrations/auto-migrator.test.ts +732 -0
- package/src/migrations/auto-migrator.ts +531 -0
- package/src/migrations/bulk-orchestrator.test.ts +801 -0
- package/src/migrations/bulk-orchestrator.ts +1039 -0
- package/src/migrations/compatibility.test.ts +958 -0
- package/src/migrations/compatibility.ts +902 -0
- package/src/migrations/do-migrations.test.ts +2620 -0
- package/src/migrations/do-migrations.ts +1289 -0
- package/src/migrations/do-migrations.types.ts +715 -0
- package/src/migrations/drizzle-compat.test.ts +210 -0
- package/src/migrations/drizzle-compat.ts +337 -0
- package/src/migrations/index.ts +334 -0
- package/src/migrations/migration-api.test.ts +438 -0
- package/src/migrations/migration-api.ts +704 -0
- package/src/migrations/progress-tracker-do.ts +518 -0
- package/src/migrations/progress-tracker-kv.ts +305 -0
- package/src/migrations/progress-tracker.test.ts +937 -0
- package/src/migrations/progress-tracker.ts +665 -0
- package/src/migrations/registry.test.ts +331 -0
- package/src/migrations/registry.ts +468 -0
- package/src/migrations/rollback.test.ts +644 -0
- package/src/migrations/runner.test.ts +807 -0
- package/src/migrations/runner.test.ts.backup +759 -0
- package/src/migrations/runner.ts +1459 -0
- package/src/migrations/schema-generator.test.ts +649 -0
- package/src/migrations/schema-generator.ts +513 -0
- package/src/migrations/testing.ts +1037 -0
- package/src/migrations/types.ts +573 -0
- package/src/migrations/validator.test.ts +660 -0
- package/src/migrations/validator.ts +741 -0
- package/src/observability/alerting.test.ts +1133 -0
- package/src/observability/alerting.ts +455 -0
- package/src/observability/analytics-engine.ts +733 -0
- package/src/observability/cost-metrics.ts +804 -0
- package/src/observability/cross-do-tracing.test.ts +516 -0
- package/src/observability/cross-do-tracing.ts +588 -0
- package/src/observability/dashboards/postgres-do-overview.json +1656 -0
- package/src/observability/error-rate-collector.test.ts +977 -0
- package/src/observability/error-rate-collector.ts +518 -0
- package/src/observability/exporters.test.ts +365 -0
- package/src/observability/exporters.ts +650 -0
- package/src/observability/health-check.test.ts +353 -0
- package/src/observability/health-check.ts +341 -0
- package/src/observability/index.test.ts +298 -0
- package/src/observability/index.ts +885 -0
- package/src/observability/instrumentation.test.ts +428 -0
- package/src/observability/instrumentation.ts +788 -0
- package/src/observability/memory-metrics.test.ts +355 -0
- package/src/observability/memory-metrics.ts +990 -0
- package/src/observability/metrics-endpoint.test.ts +402 -0
- package/src/observability/metrics-endpoint.ts +374 -0
- package/src/observability/metrics.test.ts +291 -0
- package/src/observability/metrics.ts +315 -0
- package/src/observability/observability-features.ts +1296 -0
- package/src/observability/prometheus.test.ts +292 -0
- package/src/observability/prometheus.ts +170 -0
- package/src/observability/propagation.test.ts +417 -0
- package/src/observability/propagation.ts +294 -0
- package/src/observability/query-latency.ts +586 -0
- package/src/observability/query-performance.test.ts +406 -0
- package/src/observability/query-performance.ts +491 -0
- package/src/observability/storage-tier-metrics.test.ts +633 -0
- package/src/observability/storage-tier-metrics.ts +570 -0
- package/src/observability/tier-cost-optimizer.ts +740 -0
- package/src/observability/tracer.test.ts +346 -0
- package/src/observability/tracer.ts +585 -0
- package/src/observability/types.test.ts +726 -0
- package/src/observability/types.ts +434 -0
- package/src/pglite/auto-demotion.test.ts +477 -0
- package/src/pglite/auto-demotion.ts +385 -0
- package/src/pglite/auto-promotion.test.ts +824 -0
- package/src/pglite/auto-promotion.ts +547 -0
- package/src/pglite/cache-layer.test.ts +469 -0
- package/src/pglite/cache-layer.ts +271 -0
- package/src/pglite/cold-start-manager.ts +1260 -0
- package/src/pglite/cold-start-optimizer.test.ts +937 -0
- package/src/pglite/cold-start-optimizer.ts +1895 -0
- package/src/pglite/dovfs-adapter.ts +1122 -0
- package/src/pglite/dovfs.ts +1258 -0
- package/src/pglite/etag-cache.test.ts +844 -0
- package/src/pglite/etag-cache.ts +526 -0
- package/src/pglite/index.ts +442 -0
- package/src/pglite/init.test.ts +455 -0
- package/src/pglite/init.ts +574 -0
- package/src/pglite/lifecycle.test.ts +599 -0
- package/src/pglite/lifecycle.ts +704 -0
- package/src/pglite/parallel-loader.test.ts +586 -0
- package/src/pglite/parallel-loader.ts +481 -0
- package/src/pglite/production-pglite.test.ts +666 -0
- package/src/pglite/production-pglite.ts +537 -0
- package/src/pglite/query-executor.ts +614 -0
- package/src/pglite/r2-layer.test.ts +501 -0
- package/src/pglite/r2-layer.ts +322 -0
- package/src/pglite/tiered-init.test.ts +725 -0
- package/src/pglite/tiered-init.ts +556 -0
- package/src/pglite/tiered-vfs.test.ts +726 -0
- package/src/pglite/tiered-vfs.ts +33 -0
- package/src/pglite/tiering-stats.test.ts +531 -0
- package/src/pglite/tiering-stats.ts +407 -0
- package/src/pglite/transaction-hooks.ts +343 -0
- package/src/pglite/warm-loader.test.ts +1701 -0
- package/src/pglite/warm-loader.ts +528 -0
- package/src/pglite/workers-pglite.ts +224 -0
- package/src/pglite-assets/pglite.data +0 -0
- package/src/pglite-assets/pglite.wasm +0 -0
- package/src/pglite.d.ts +47 -0
- package/src/playground/index.ts +137 -0
- package/src/playground/keyboard-shortcuts.ts +677 -0
- package/src/playground/playground.ts +323 -0
- package/src/playground/query-executor.ts +669 -0
- package/src/playground/query-history.ts +328 -0
- package/src/playground/result-formatter.ts +420 -0
- package/src/playground/sample-datasets.ts +674 -0
- package/src/playground/sample-queries.ts +1168 -0
- package/src/playground/schema-explorer.ts +558 -0
- package/src/playground/types.ts +518 -0
- package/src/readonly/cache-reader.test.ts +460 -0
- package/src/readonly/cache-reader.ts +313 -0
- package/src/readonly/config.test.ts +187 -0
- package/src/readonly/config.ts +128 -0
- package/src/readonly/index.ts +50 -0
- package/src/readonly/pglite-wrapper.test.ts +278 -0
- package/src/readonly/pglite-wrapper.ts +184 -0
- package/src/readonly/worker.test.ts +533 -0
- package/src/readonly/worker.ts +341 -0
- package/src/readonly/write-blocker.test.ts +459 -0
- package/src/readonly/write-blocker.ts +175 -0
- package/src/recovery/disaster-recovery.test.ts +618 -0
- package/src/recovery/disaster-recovery.ts +1181 -0
- package/src/recovery/index.ts +43 -0
- package/src/recovery/parquet-parser.ts +974 -0
- package/src/retention/index.ts +74 -0
- package/src/retention/policy.test.ts +571 -0
- package/src/retention/policy.ts +774 -0
- package/src/retention/purger.test.ts +465 -0
- package/src/retention/purger.ts +558 -0
- package/src/rls/auth-integration.test.ts +752 -0
- package/src/rls/auth-integration.ts +533 -0
- package/src/rls/generator.test.ts +829 -0
- package/src/rls/generator.ts +573 -0
- package/src/rls/index.ts +128 -0
- package/src/rls/policy.ts +208 -0
- package/src/rls/rls.test.ts +1071 -0
- package/src/rls/validator.test.ts +930 -0
- package/src/rls/validator.ts +895 -0
- package/src/routing/adaptive-router.test.ts +884 -0
- package/src/routing/adaptive-router.ts +845 -0
- package/src/routing/circuit-breaker.test.ts +1505 -0
- package/src/routing/circuit-breaker.ts +852 -0
- package/src/routing/cost-metrics.test.ts +565 -0
- package/src/routing/cost-metrics.ts +408 -0
- package/src/routing/do-connection-pool.test.ts +1109 -0
- package/src/routing/do-connection-pool.ts +828 -0
- package/src/routing/index.ts +158 -0
- package/src/routing/query-complexity-estimator.test.ts +356 -0
- package/src/routing/query-complexity-estimator.ts +444 -0
- package/src/routing/request-coalescing.test.ts +738 -0
- package/src/routing/request-coalescing.ts +475 -0
- package/src/routing/runtime-router.test.ts +436 -0
- package/src/routing/runtime-router.ts +357 -0
- package/src/routing/tenant-router.test.ts +2493 -0
- package/src/routing/tenant-router.ts +1908 -0
- package/src/routing/websocket-pool.test.ts +551 -0
- package/src/routing/websocket-pool.ts +577 -0
- package/src/storage/access-pattern-tracker.test.ts +874 -0
- package/src/storage/cache-layer.test.ts +560 -0
- package/src/storage/cache-layer.ts +328 -0
- package/src/storage/cost-aware-tiering.test.ts +652 -0
- package/src/storage/cost-aware-tiering.ts +794 -0
- package/src/storage/do-sqlite-blobs.test.ts +937 -0
- package/src/storage/index.ts +272 -0
- package/src/storage/interfaces.ts +974 -0
- package/src/storage/r2-layer.test.ts +653 -0
- package/src/storage/r2-layer.ts +434 -0
- package/src/storage/r2-overflow.ts +920 -0
- package/src/storage/r2-page-vfs.test.ts +2348 -0
- package/src/storage/r2-page-vfs.ts +1054 -0
- package/src/storage/swr-cache.test.ts +832 -0
- package/src/storage/swr-cache.ts +398 -0
- package/src/storage/swr-tiered-integration.test.ts +617 -0
- package/src/storage/tiered-orchestrator.test.ts +2441 -0
- package/src/storage/tiered-orchestrator.ts +2081 -0
- package/src/storage/tiered-vfs-swr.test.ts +736 -0
- package/src/storage/tiered-vfs-swr.ts +735 -0
- package/src/storage/tiered-vfs.test.ts +793 -0
- package/src/storage/tiered-vfs.ts +1082 -0
- package/src/streaming/backpressure-controller.ts +452 -0
- package/src/streaming/buffer-pool.ts +484 -0
- package/src/streaming/cdc-iceberg-connector.ts +605 -0
- package/src/streaming/index.ts +225 -0
- package/src/streaming/live-cdc-stream.ts +985 -0
- package/src/streaming/memory-bounded-stream.ts +443 -0
- package/src/streaming/query-streamer.ts +662 -0
- package/src/streaming/response-streaming.ts +557 -0
- package/src/types/branded.ts +1075 -0
- package/src/types/branded.ts.backup +273 -0
- package/src/types/utilities.ts +1023 -0
- package/src/types/wasm.d.ts +30 -0
- package/src/validation/typed-errors.test.ts +420 -0
- package/src/wal/replay-engine.ts +1264 -0
- package/src/worker/__mocks__/capnweb.ts +15 -0
- package/src/worker/__mocks__/pglite.data.ts +22 -0
- package/src/worker/__mocks__/pglite.wasm.ts +33 -0
- package/src/worker/auth-rate-limiter.test.ts +272 -0
- package/src/worker/auth-rate-limiter.ts +448 -0
- package/src/worker/auth.security-red.test.ts +1236 -0
- package/src/worker/auth.security.test.ts +822 -0
- package/src/worker/auth.test.ts +469 -0
- package/src/worker/auth.ts +1104 -0
- package/src/worker/cdc-backpressure.test.ts +726 -0
- package/src/worker/cdc-backpressure.ts +866 -0
- package/src/worker/cdc-sse.test.ts +780 -0
- package/src/worker/cdc-sse.ts +728 -0
- package/src/worker/cdc-websocket.ts +1229 -0
- package/src/worker/cdc-ws.test.ts +1009 -0
- package/src/worker/cdc.test.ts +327 -0
- package/src/worker/cdc.ts +289 -0
- package/src/worker/concerns/auth-concern.ts +179 -0
- package/src/worker/concerns/cdc-concern.ts +247 -0
- package/src/worker/concerns/index.ts +58 -0
- package/src/worker/concerns/query-execution-concern.ts +194 -0
- package/src/worker/concerns/storage-orchestration-concern.ts +373 -0
- package/src/worker/discriminated-types.test.ts +280 -0
- package/src/worker/do-auth-manager.ts +257 -0
- package/src/worker/do-decomposition.test.ts +1236 -0
- package/src/worker/do-pglite-manager.ts +302 -0
- package/src/worker/do.test.ts +2254 -0
- package/src/worker/do.ts +1878 -0
- package/src/worker/entry.ts +417 -0
- package/src/worker/errors.ts +285 -0
- package/src/worker/health-check-manager.test.ts +261 -0
- package/src/worker/health-check-manager.ts +231 -0
- package/src/worker/index.ts +389 -0
- package/src/worker/memory-pressure.test.ts +1460 -0
- package/src/worker/memory-pressure.ts +2650 -0
- package/src/worker/migration-manager.ts +582 -0
- package/src/worker/neon-compat.test.ts +332 -0
- package/src/worker/plugin-manager.ts +485 -0
- package/src/worker/postgres.do-rpc.d.ts +76 -0
- package/src/worker/proxy.ts +694 -0
- package/src/worker/query-execution-manager.test.ts +303 -0
- package/src/worker/query-execution-manager.ts +219 -0
- package/src/worker/query-executor.test.ts +282 -0
- package/src/worker/query-executor.ts +560 -0
- package/src/worker/query-stats-manager.ts +229 -0
- package/src/worker/result-handler.test.ts +364 -0
- package/src/worker/result-handler.ts +510 -0
- package/src/worker/routes.test.ts +795 -0
- package/src/worker/routes.ts +650 -0
- package/src/worker/rpc-methods-manager.test.ts +326 -0
- package/src/worker/rpc-methods-manager.ts +276 -0
- package/src/worker/rpc.ts +524 -0
- package/src/worker/schema-version.ts +605 -0
- package/src/worker/session-manager.test.ts +506 -0
- package/src/worker/session-manager.ts +732 -0
- package/src/worker/shutdown-manager.ts +469 -0
- package/src/worker/sql-transform.test.ts +286 -0
- package/src/worker/sql-transform.ts +368 -0
- package/src/worker/supabase-compat.test.ts +621 -0
- package/src/worker/types.test.ts +292 -0
- package/src/worker/types.ts +873 -0
- package/src/worker/user-routes.test.ts +703 -0
- package/src/worker/user-routes.ts +303 -0
- package/src/worker/wal-facade.ts +235 -0
- package/src/worker/wal-r2.test.ts +570 -0
- package/src/worker/wal-r2.ts +930 -0
- package/src/worker/wal-replay.test.ts +845 -0
- package/src/worker/wal-replay.ts +897 -0
- package/src/worker/wal-retention.test.ts +758 -0
- package/src/worker/wal-retention.ts +1075 -0
- package/src/worker/wal.test.ts +618 -0
- package/src/worker/wal.ts +697 -0
- package/src/worker/websocket.test.ts +296 -0
- package/src/worker/websocket.ts +284 -0
|
@@ -0,0 +1,1229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDC WebSocket Handler
|
|
3
|
+
* Task: postgres-0n39 - WebSocket Protocol for CDC Streaming
|
|
4
|
+
* Refactor: postgres-ib23 - Improve CDC WebSocket transport reliability
|
|
5
|
+
*
|
|
6
|
+
* Provides a complete WebSocket endpoint for Change Data Capture (CDC) streaming.
|
|
7
|
+
* Features:
|
|
8
|
+
* - Subscribe/unsubscribe to table changes
|
|
9
|
+
* - Event filtering by table and operation type
|
|
10
|
+
* - Heartbeat mechanism for connection health
|
|
11
|
+
* - Reconnection support with subscription restoration
|
|
12
|
+
* - Improved connection state management
|
|
13
|
+
* - Centralized timer management
|
|
14
|
+
* - Comprehensive metrics tracking
|
|
15
|
+
*/
|
|
16
|
+
import type {
|
|
17
|
+
WSMessage,
|
|
18
|
+
CDCEventType,
|
|
19
|
+
SubscribePayload,
|
|
20
|
+
UnsubscribePayload,
|
|
21
|
+
ErrorPayload,
|
|
22
|
+
HeartbeatPayload,
|
|
23
|
+
WelcomePayload,
|
|
24
|
+
ReconnectPayload,
|
|
25
|
+
SubscribedPayload,
|
|
26
|
+
UnsubscribedPayload,
|
|
27
|
+
CDCWebSocketConfig,
|
|
28
|
+
QueryParam,
|
|
29
|
+
} from './types'
|
|
30
|
+
|
|
31
|
+
// Re-export for external use
|
|
32
|
+
export type { WSMessageType, ChangePayload, QueryResultRow } from './types'
|
|
33
|
+
import { parseWSMessage, serializeWSMessage, type QueryExecutor } from './websocket'
|
|
34
|
+
import { CDCManager, type Subscription, type SubscribeOptions, type ChangeEvent, type Broadcaster } from './cdc'
|
|
35
|
+
import { createLogger, LogLevel, type ILogger } from '@dotdo/postgres-shared'
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Constants
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* WebSocket ready state constants (matching W3C spec)
|
|
43
|
+
*/
|
|
44
|
+
const WS_OPEN = 1
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Default CDC WebSocket configuration
|
|
48
|
+
*/
|
|
49
|
+
const DEFAULT_CDC_CONFIG: Required<CDCWebSocketConfig> = {
|
|
50
|
+
heartbeatInterval: 30000,
|
|
51
|
+
heartbeatTimeout: 10000,
|
|
52
|
+
maxReconnectAttempts: 5,
|
|
53
|
+
reconnectDelay: 1000,
|
|
54
|
+
maxSubscriptions: 100,
|
|
55
|
+
bufferChanges: true,
|
|
56
|
+
maxBufferedChanges: 1000,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Error codes for CDC WebSocket operations
|
|
61
|
+
*/
|
|
62
|
+
export const CDCErrorCodes = {
|
|
63
|
+
/** Invalid or missing request payload */
|
|
64
|
+
INVALID_REQUEST: 'INVALID_REQUEST',
|
|
65
|
+
/** WebSocket connection not registered */
|
|
66
|
+
NO_CONNECTION: 'NO_CONNECTION',
|
|
67
|
+
/** Subscription limit reached */
|
|
68
|
+
LIMIT_EXCEEDED: 'LIMIT_EXCEEDED',
|
|
69
|
+
/** Subscription not found */
|
|
70
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
71
|
+
/** Unknown message type received */
|
|
72
|
+
UNKNOWN_TYPE: 'UNKNOWN_TYPE',
|
|
73
|
+
/** Failed to parse message */
|
|
74
|
+
PARSE_ERROR: 'PARSE_ERROR',
|
|
75
|
+
/** Query execution failed */
|
|
76
|
+
QUERY_ERROR: 'QUERY_ERROR',
|
|
77
|
+
/** Internal server error */
|
|
78
|
+
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
79
|
+
} as const
|
|
80
|
+
|
|
81
|
+
export type CDCErrorCode = typeof CDCErrorCodes[keyof typeof CDCErrorCodes]
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Connection State Management
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Connection lifecycle states
|
|
89
|
+
* Provides clear state transitions for connection management
|
|
90
|
+
*/
|
|
91
|
+
export enum ConnectionLifecycle {
|
|
92
|
+
/** Connection is being established */
|
|
93
|
+
CONNECTING = 'connecting',
|
|
94
|
+
/** Connection is active and ready */
|
|
95
|
+
ACTIVE = 'active',
|
|
96
|
+
/** Connection is in process of reconnecting */
|
|
97
|
+
RECONNECTING = 'reconnecting',
|
|
98
|
+
/** Connection is closing */
|
|
99
|
+
CLOSING = 'closing',
|
|
100
|
+
/** Connection is closed */
|
|
101
|
+
CLOSED = 'closed',
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Connection state for a WebSocket
|
|
106
|
+
*/
|
|
107
|
+
interface ConnectionState {
|
|
108
|
+
/** Unique connection ID for reconnection */
|
|
109
|
+
connectionId: string
|
|
110
|
+
/** WebSocket instance */
|
|
111
|
+
ws: WebSocket
|
|
112
|
+
/** Current lifecycle state */
|
|
113
|
+
lifecycle: ConnectionLifecycle
|
|
114
|
+
/** Last heartbeat received timestamp */
|
|
115
|
+
lastHeartbeat: number
|
|
116
|
+
/** Heartbeat sequence number */
|
|
117
|
+
heartbeatSeq: number
|
|
118
|
+
/** Active subscriptions for this connection */
|
|
119
|
+
subscriptions: Set<string>
|
|
120
|
+
/** Buffered changes during reconnection */
|
|
121
|
+
bufferedChanges: WSMessage[]
|
|
122
|
+
/** Count of active message processing operations (prevents heartbeat timeout during processing) */
|
|
123
|
+
activeOperations: number
|
|
124
|
+
/** Connection creation timestamp */
|
|
125
|
+
createdAt: number
|
|
126
|
+
/** Total messages received */
|
|
127
|
+
messagesReceived: number
|
|
128
|
+
/** Total messages sent */
|
|
129
|
+
messagesSent: number
|
|
130
|
+
/** Last error encountered */
|
|
131
|
+
lastError?: string
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Timer Management
|
|
136
|
+
// ============================================================================
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Centralized timer management for connection-related timers
|
|
140
|
+
* Ensures proper cleanup and prevents timer leaks
|
|
141
|
+
*/
|
|
142
|
+
class TimerManager {
|
|
143
|
+
private heartbeatTimers: Map<string, ReturnType<typeof setInterval>> = new Map()
|
|
144
|
+
private cleanupTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
|
|
145
|
+
private logger: ILogger
|
|
146
|
+
|
|
147
|
+
constructor(logger: ILogger) {
|
|
148
|
+
this.logger = logger.child({ component: 'TimerManager' })
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Start a heartbeat timer for a connection
|
|
153
|
+
*/
|
|
154
|
+
startHeartbeat(connectionId: string, interval: number, callback: () => void): void {
|
|
155
|
+
// Clear any existing timer first
|
|
156
|
+
this.stopHeartbeat(connectionId)
|
|
157
|
+
|
|
158
|
+
const timer = setInterval(callback, interval)
|
|
159
|
+
this.heartbeatTimers.set(connectionId, timer)
|
|
160
|
+
this.logger.debug('Started heartbeat timer', { connectionId, interval })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Stop heartbeat timer for a connection
|
|
165
|
+
*/
|
|
166
|
+
stopHeartbeat(connectionId: string): boolean {
|
|
167
|
+
const timer = this.heartbeatTimers.get(connectionId)
|
|
168
|
+
if (timer) {
|
|
169
|
+
clearInterval(timer)
|
|
170
|
+
this.heartbeatTimers.delete(connectionId)
|
|
171
|
+
this.logger.debug('Stopped heartbeat timer', { connectionId })
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Start a cleanup timer (for pending reconnect state cleanup)
|
|
179
|
+
*/
|
|
180
|
+
startCleanupTimer(connectionId: string, delay: number, callback: () => void): void {
|
|
181
|
+
// Clear any existing timer first
|
|
182
|
+
this.stopCleanupTimer(connectionId)
|
|
183
|
+
|
|
184
|
+
const timer = setTimeout(callback, delay)
|
|
185
|
+
this.cleanupTimers.set(connectionId, timer)
|
|
186
|
+
this.logger.debug('Started cleanup timer', { connectionId, delay })
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Stop cleanup timer for a connection
|
|
191
|
+
*/
|
|
192
|
+
stopCleanupTimer(connectionId: string): boolean {
|
|
193
|
+
const timer = this.cleanupTimers.get(connectionId)
|
|
194
|
+
if (timer) {
|
|
195
|
+
clearTimeout(timer)
|
|
196
|
+
this.cleanupTimers.delete(connectionId)
|
|
197
|
+
this.logger.debug('Stopped cleanup timer', { connectionId })
|
|
198
|
+
return true
|
|
199
|
+
}
|
|
200
|
+
return false
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Clear all timers for a connection
|
|
205
|
+
*/
|
|
206
|
+
clearAllForConnection(connectionId: string): void {
|
|
207
|
+
this.stopHeartbeat(connectionId)
|
|
208
|
+
this.stopCleanupTimer(connectionId)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Clear all timers (for shutdown)
|
|
213
|
+
*/
|
|
214
|
+
clearAll(): void {
|
|
215
|
+
for (const [connectionId] of this.heartbeatTimers) {
|
|
216
|
+
this.stopHeartbeat(connectionId)
|
|
217
|
+
}
|
|
218
|
+
for (const [connectionId] of this.cleanupTimers) {
|
|
219
|
+
this.stopCleanupTimer(connectionId)
|
|
220
|
+
}
|
|
221
|
+
this.logger.debug('Cleared all timers')
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get timer counts for metrics
|
|
226
|
+
*/
|
|
227
|
+
getTimerCounts(): { heartbeat: number; cleanup: number } {
|
|
228
|
+
return {
|
|
229
|
+
heartbeat: this.heartbeatTimers.size,
|
|
230
|
+
cleanup: this.cleanupTimers.size,
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// Metrics
|
|
237
|
+
// ============================================================================
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Metrics for CDC WebSocket operations
|
|
241
|
+
*/
|
|
242
|
+
export interface CDCWebSocketMetrics {
|
|
243
|
+
/** Total connections established */
|
|
244
|
+
connectionsTotal: number
|
|
245
|
+
/** Currently active connections */
|
|
246
|
+
connectionsActive: number
|
|
247
|
+
/** Total messages received */
|
|
248
|
+
messagesReceived: number
|
|
249
|
+
/** Total messages sent */
|
|
250
|
+
messagesSent: number
|
|
251
|
+
/** Total subscription operations */
|
|
252
|
+
subscriptionsCreated: number
|
|
253
|
+
/** Total unsubscription operations */
|
|
254
|
+
subscriptionsRemoved: number
|
|
255
|
+
/** Total errors encountered */
|
|
256
|
+
errorsTotal: number
|
|
257
|
+
/** Errors by code */
|
|
258
|
+
errorsByCode: Record<string, number>
|
|
259
|
+
/** Total heartbeat timeouts */
|
|
260
|
+
heartbeatTimeouts: number
|
|
261
|
+
/** Total reconnection attempts */
|
|
262
|
+
reconnectAttempts: number
|
|
263
|
+
/** Successful reconnections */
|
|
264
|
+
reconnectSuccesses: number
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ============================================================================
|
|
268
|
+
// Utilities
|
|
269
|
+
// ============================================================================
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Generate a unique ID
|
|
273
|
+
*/
|
|
274
|
+
function generateId(prefix: string): string {
|
|
275
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Filter function for matching change events
|
|
280
|
+
*/
|
|
281
|
+
export type ChangeFilter = (change: ChangeEvent, subscription: Subscription) => boolean
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Event filter configuration
|
|
285
|
+
*/
|
|
286
|
+
export interface EventFilterConfig {
|
|
287
|
+
/** Tables to include (if empty, all tables) */
|
|
288
|
+
tables?: string[]
|
|
289
|
+
/** Operations to include (if empty, all operations) */
|
|
290
|
+
operations?: CDCEventType[]
|
|
291
|
+
/** Custom filter function */
|
|
292
|
+
filter?: ChangeFilter
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Pending reconnect state
|
|
297
|
+
*/
|
|
298
|
+
interface PendingReconnectState {
|
|
299
|
+
/** Buffered changes during disconnection */
|
|
300
|
+
bufferedChanges: WSMessage[]
|
|
301
|
+
/** Subscription IDs that were active */
|
|
302
|
+
subscriptions: Set<string>
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* CDC WebSocket Handler
|
|
307
|
+
*
|
|
308
|
+
* Manages WebSocket connections for CDC streaming with support for:
|
|
309
|
+
* - Multiple subscriptions per connection
|
|
310
|
+
* - Heartbeat-based health monitoring
|
|
311
|
+
* - Reconnection with subscription restoration
|
|
312
|
+
* - Event filtering by table and operation
|
|
313
|
+
* - Comprehensive metrics tracking
|
|
314
|
+
* - Improved error handling and recovery
|
|
315
|
+
*/
|
|
316
|
+
export class CDCWebSocketHandler {
|
|
317
|
+
private state: DurableObjectState
|
|
318
|
+
private executor: QueryExecutor
|
|
319
|
+
private cdcManager: CDCManager
|
|
320
|
+
private config: Required<CDCWebSocketConfig>
|
|
321
|
+
private connections: Map<string, ConnectionState> = new Map()
|
|
322
|
+
private wsToConnection: Map<WebSocket, string> = new Map()
|
|
323
|
+
private timerManager: TimerManager
|
|
324
|
+
/** Pending reconnection state - kept separate from active connections */
|
|
325
|
+
private pendingReconnect: Map<string, PendingReconnectState> = new Map()
|
|
326
|
+
private version: string = '1.0.0'
|
|
327
|
+
private logger: ILogger
|
|
328
|
+
private metrics: CDCWebSocketMetrics
|
|
329
|
+
|
|
330
|
+
constructor(
|
|
331
|
+
state: DurableObjectState,
|
|
332
|
+
executor: QueryExecutor,
|
|
333
|
+
cdcManager?: CDCManager,
|
|
334
|
+
config?: CDCWebSocketConfig,
|
|
335
|
+
logger?: ILogger
|
|
336
|
+
) {
|
|
337
|
+
this.state = state
|
|
338
|
+
this.executor = executor
|
|
339
|
+
this.config = { ...DEFAULT_CDC_CONFIG, ...config }
|
|
340
|
+
|
|
341
|
+
// Initialize logger
|
|
342
|
+
this.logger = (logger ?? createLogger({ level: LogLevel.INFO })).child({ component: 'CDCWebSocketHandler' })
|
|
343
|
+
|
|
344
|
+
// Initialize timer manager
|
|
345
|
+
this.timerManager = new TimerManager(this.logger)
|
|
346
|
+
|
|
347
|
+
// Initialize metrics
|
|
348
|
+
this.metrics = {
|
|
349
|
+
connectionsTotal: 0,
|
|
350
|
+
connectionsActive: 0,
|
|
351
|
+
messagesReceived: 0,
|
|
352
|
+
messagesSent: 0,
|
|
353
|
+
subscriptionsCreated: 0,
|
|
354
|
+
subscriptionsRemoved: 0,
|
|
355
|
+
errorsTotal: 0,
|
|
356
|
+
errorsByCode: {},
|
|
357
|
+
heartbeatTimeouts: 0,
|
|
358
|
+
reconnectAttempts: 0,
|
|
359
|
+
reconnectSuccesses: 0,
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Create a broadcaster that sends to specific connections
|
|
363
|
+
const broadcaster: Broadcaster = {
|
|
364
|
+
broadcast: (message: WSMessage, filter?: (ws: WebSocket) => boolean) => {
|
|
365
|
+
for (const connState of this.connections.values()) {
|
|
366
|
+
if (connState.ws.readyState !== WS_OPEN) continue
|
|
367
|
+
if (filter && !filter(connState.ws)) continue
|
|
368
|
+
this.sendMessage(connState.ws, message)
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
this.cdcManager = cdcManager ?? new CDCManager(broadcaster)
|
|
374
|
+
|
|
375
|
+
this.logger.debug('CDCWebSocketHandler initialized', {
|
|
376
|
+
heartbeatInterval: this.config.heartbeatInterval,
|
|
377
|
+
heartbeatTimeout: this.config.heartbeatTimeout,
|
|
378
|
+
maxSubscriptions: this.config.maxSubscriptions,
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Handle WebSocket upgrade request
|
|
384
|
+
*/
|
|
385
|
+
handleUpgrade(request: Request, webSocketPair: { 0: WebSocket; 1: WebSocket } | null): Response {
|
|
386
|
+
const upgradeHeader = request.headers.get('Upgrade')
|
|
387
|
+
|
|
388
|
+
if (upgradeHeader !== 'websocket' || !webSocketPair) {
|
|
389
|
+
this.logger.warn('Invalid WebSocket upgrade request', {
|
|
390
|
+
upgradeHeader,
|
|
391
|
+
hasPair: webSocketPair !== null,
|
|
392
|
+
})
|
|
393
|
+
return new Response('Expected WebSocket upgrade', { status: 400 })
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const client = webSocketPair[0]
|
|
397
|
+
const server = webSocketPair[1]
|
|
398
|
+
|
|
399
|
+
// Accept the WebSocket with hibernation
|
|
400
|
+
this.state.acceptWebSocket(server)
|
|
401
|
+
|
|
402
|
+
// Create connection state
|
|
403
|
+
const connectionId = generateId('conn')
|
|
404
|
+
const now = Date.now()
|
|
405
|
+
const connState: ConnectionState = {
|
|
406
|
+
connectionId,
|
|
407
|
+
ws: server,
|
|
408
|
+
lifecycle: ConnectionLifecycle.CONNECTING,
|
|
409
|
+
lastHeartbeat: now,
|
|
410
|
+
heartbeatSeq: 0,
|
|
411
|
+
subscriptions: new Set(),
|
|
412
|
+
bufferedChanges: [],
|
|
413
|
+
activeOperations: 0,
|
|
414
|
+
createdAt: now,
|
|
415
|
+
messagesReceived: 0,
|
|
416
|
+
messagesSent: 0,
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
this.connections.set(connectionId, connState)
|
|
420
|
+
this.wsToConnection.set(server, connectionId)
|
|
421
|
+
|
|
422
|
+
// Update metrics
|
|
423
|
+
this.metrics.connectionsTotal++
|
|
424
|
+
this.metrics.connectionsActive++
|
|
425
|
+
|
|
426
|
+
// Start heartbeat for this connection
|
|
427
|
+
this.startHeartbeat(connectionId)
|
|
428
|
+
|
|
429
|
+
// Transition to active state
|
|
430
|
+
connState.lifecycle = ConnectionLifecycle.ACTIVE
|
|
431
|
+
|
|
432
|
+
// Send welcome message
|
|
433
|
+
this.sendMessage(server, {
|
|
434
|
+
type: 'welcome',
|
|
435
|
+
id: generateId('msg'),
|
|
436
|
+
payload: {
|
|
437
|
+
connectionId,
|
|
438
|
+
timestamp: now,
|
|
439
|
+
heartbeatInterval: this.config.heartbeatInterval,
|
|
440
|
+
version: this.version,
|
|
441
|
+
} as WelcomePayload,
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
this.logger.info('WebSocket connection established', { connectionId })
|
|
445
|
+
|
|
446
|
+
return new Response(null, {
|
|
447
|
+
status: 101,
|
|
448
|
+
webSocket: client,
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Handle incoming WebSocket message
|
|
454
|
+
*/
|
|
455
|
+
async handleMessage(ws: WebSocket, rawMessage: string | ArrayBuffer): Promise<void> {
|
|
456
|
+
const messageStr = typeof rawMessage === 'string'
|
|
457
|
+
? rawMessage
|
|
458
|
+
: new TextDecoder().decode(rawMessage)
|
|
459
|
+
|
|
460
|
+
// Update last activity timestamp for heartbeat tracking
|
|
461
|
+
// Any message activity keeps the connection alive
|
|
462
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
463
|
+
const connState = connectionId ? this.connections.get(connectionId) : undefined
|
|
464
|
+
|
|
465
|
+
if (connState) {
|
|
466
|
+
connState.lastHeartbeat = Date.now()
|
|
467
|
+
connState.messagesReceived++
|
|
468
|
+
// Track that we're processing a message (prevents heartbeat timeout during long operations)
|
|
469
|
+
connState.activeOperations++
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Update global metrics
|
|
473
|
+
this.metrics.messagesReceived++
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
const message = parseWSMessage(messageStr)
|
|
477
|
+
|
|
478
|
+
this.logger.debug('Message received', {
|
|
479
|
+
connectionId,
|
|
480
|
+
type: message.type,
|
|
481
|
+
id: message.id,
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
switch (message.type) {
|
|
485
|
+
case 'ping':
|
|
486
|
+
this.sendMessage(ws, {
|
|
487
|
+
type: 'pong',
|
|
488
|
+
id: message.id,
|
|
489
|
+
})
|
|
490
|
+
break
|
|
491
|
+
|
|
492
|
+
case 'heartbeat':
|
|
493
|
+
this.handleHeartbeat(ws, message)
|
|
494
|
+
break
|
|
495
|
+
|
|
496
|
+
case 'query':
|
|
497
|
+
await this.handleQuery(ws, message)
|
|
498
|
+
break
|
|
499
|
+
|
|
500
|
+
case 'subscribe':
|
|
501
|
+
await this.handleSubscribe(ws, message)
|
|
502
|
+
break
|
|
503
|
+
|
|
504
|
+
case 'unsubscribe':
|
|
505
|
+
await this.handleUnsubscribe(ws, message)
|
|
506
|
+
break
|
|
507
|
+
|
|
508
|
+
case 'reconnect':
|
|
509
|
+
await this.handleReconnect(ws, message)
|
|
510
|
+
break
|
|
511
|
+
|
|
512
|
+
default:
|
|
513
|
+
this.sendError(ws, message.id, CDCErrorCodes.UNKNOWN_TYPE, `Unknown message type: ${message.type}`)
|
|
514
|
+
}
|
|
515
|
+
} catch (error) {
|
|
516
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
517
|
+
this.logger.error('Failed to process message', {
|
|
518
|
+
connectionId,
|
|
519
|
+
error: errorMessage,
|
|
520
|
+
})
|
|
521
|
+
this.sendError(ws, 'unknown', CDCErrorCodes.PARSE_ERROR, errorMessage)
|
|
522
|
+
} finally {
|
|
523
|
+
// Decrement active operations count when done processing
|
|
524
|
+
if (connState) {
|
|
525
|
+
connState.activeOperations--
|
|
526
|
+
// Update heartbeat timestamp after operation completes
|
|
527
|
+
connState.lastHeartbeat = Date.now()
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Handle heartbeat message
|
|
534
|
+
*/
|
|
535
|
+
private handleHeartbeat(ws: WebSocket, message: WSMessage): void {
|
|
536
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
537
|
+
if (!connectionId) {
|
|
538
|
+
this.logger.warn('Heartbeat received for unregistered WebSocket')
|
|
539
|
+
return
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const connState = this.connections.get(connectionId)
|
|
543
|
+
if (!connState) {
|
|
544
|
+
this.logger.warn('Heartbeat received for missing connection state', { connectionId })
|
|
545
|
+
return
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Update last heartbeat time
|
|
549
|
+
const now = Date.now()
|
|
550
|
+
connState.lastHeartbeat = now
|
|
551
|
+
connState.heartbeatSeq++
|
|
552
|
+
|
|
553
|
+
// Send heartbeat acknowledgment
|
|
554
|
+
this.sendMessage(ws, {
|
|
555
|
+
type: 'heartbeat_ack',
|
|
556
|
+
id: message.id,
|
|
557
|
+
payload: {
|
|
558
|
+
timestamp: now,
|
|
559
|
+
seq: connState.heartbeatSeq,
|
|
560
|
+
} as HeartbeatPayload,
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
this.logger.debug('Heartbeat acknowledged', {
|
|
564
|
+
connectionId,
|
|
565
|
+
seq: connState.heartbeatSeq,
|
|
566
|
+
})
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Handle query message
|
|
571
|
+
*/
|
|
572
|
+
private async handleQuery(ws: WebSocket, message: WSMessage): Promise<void> {
|
|
573
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
const payload = message.payload as { sql?: string; params?: unknown[] }
|
|
577
|
+
|
|
578
|
+
if (!payload?.sql) {
|
|
579
|
+
this.sendError(ws, message.id, CDCErrorCodes.INVALID_REQUEST, 'Missing sql in payload')
|
|
580
|
+
return
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
this.logger.debug('Executing query', {
|
|
584
|
+
connectionId,
|
|
585
|
+
messageId: message.id,
|
|
586
|
+
sqlLength: payload.sql.length,
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
const result = await this.executor.executeQuery({
|
|
590
|
+
sql: payload.sql,
|
|
591
|
+
params: payload.params as QueryParam[] | undefined,
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
this.sendMessage(ws, {
|
|
595
|
+
type: 'result',
|
|
596
|
+
id: message.id,
|
|
597
|
+
payload: result,
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
this.logger.debug('Query completed', {
|
|
601
|
+
connectionId,
|
|
602
|
+
messageId: message.id,
|
|
603
|
+
rowCount: result.rowCount,
|
|
604
|
+
durationMs: result.durationMs,
|
|
605
|
+
})
|
|
606
|
+
} catch (error) {
|
|
607
|
+
const errorMessage = error instanceof Error ? error.message : 'Query failed'
|
|
608
|
+
this.logger.error('Query execution failed', {
|
|
609
|
+
connectionId,
|
|
610
|
+
messageId: message.id,
|
|
611
|
+
error: errorMessage,
|
|
612
|
+
})
|
|
613
|
+
this.sendError(ws, message.id, CDCErrorCodes.QUERY_ERROR, errorMessage)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Handle subscribe message
|
|
619
|
+
*/
|
|
620
|
+
private async handleSubscribe(ws: WebSocket, message: WSMessage): Promise<void> {
|
|
621
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
622
|
+
if (!connectionId) {
|
|
623
|
+
this.sendError(ws, message.id, CDCErrorCodes.NO_CONNECTION, 'WebSocket not registered')
|
|
624
|
+
return
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const connState = this.connections.get(connectionId)
|
|
628
|
+
if (!connState) {
|
|
629
|
+
this.sendError(ws, message.id, CDCErrorCodes.NO_CONNECTION, 'Connection state not found')
|
|
630
|
+
return
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const payload = message.payload as SubscribePayload
|
|
634
|
+
|
|
635
|
+
if (!payload?.table) {
|
|
636
|
+
this.sendError(ws, message.id, CDCErrorCodes.INVALID_REQUEST, 'Missing table in payload')
|
|
637
|
+
return
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Check max subscriptions limit
|
|
641
|
+
if (connState.subscriptions.size >= this.config.maxSubscriptions) {
|
|
642
|
+
this.logger.warn('Subscription limit reached', {
|
|
643
|
+
connectionId,
|
|
644
|
+
currentCount: connState.subscriptions.size,
|
|
645
|
+
maxSubscriptions: this.config.maxSubscriptions,
|
|
646
|
+
})
|
|
647
|
+
this.sendError(ws, message.id, CDCErrorCodes.LIMIT_EXCEEDED, `Max subscriptions (${this.config.maxSubscriptions}) reached`)
|
|
648
|
+
return
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Create subscription
|
|
652
|
+
const options: SubscribeOptions = {
|
|
653
|
+
table: payload.table,
|
|
654
|
+
events: payload.events,
|
|
655
|
+
filter: payload.filter,
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const subscription = this.cdcManager.subscribe(ws, options)
|
|
659
|
+
connState.subscriptions.add(subscription.id)
|
|
660
|
+
|
|
661
|
+
// Update metrics
|
|
662
|
+
this.metrics.subscriptionsCreated++
|
|
663
|
+
|
|
664
|
+
// Send confirmation
|
|
665
|
+
this.sendMessage(ws, {
|
|
666
|
+
type: 'subscribed',
|
|
667
|
+
id: message.id,
|
|
668
|
+
payload: {
|
|
669
|
+
subscriptionId: subscription.id,
|
|
670
|
+
table: subscription.table,
|
|
671
|
+
events: subscription.events,
|
|
672
|
+
filter: subscription.filter,
|
|
673
|
+
} as SubscribedPayload,
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
this.logger.info('Subscription created', {
|
|
677
|
+
connectionId,
|
|
678
|
+
subscriptionId: subscription.id,
|
|
679
|
+
table: subscription.table,
|
|
680
|
+
events: subscription.events,
|
|
681
|
+
})
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Handle unsubscribe message
|
|
686
|
+
*/
|
|
687
|
+
private async handleUnsubscribe(ws: WebSocket, message: WSMessage): Promise<void> {
|
|
688
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
689
|
+
if (!connectionId) {
|
|
690
|
+
this.sendError(ws, message.id, CDCErrorCodes.NO_CONNECTION, 'WebSocket not registered')
|
|
691
|
+
return
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const connState = this.connections.get(connectionId)
|
|
695
|
+
if (!connState) {
|
|
696
|
+
this.sendError(ws, message.id, CDCErrorCodes.NO_CONNECTION, 'Connection state not found')
|
|
697
|
+
return
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const payload = message.payload as UnsubscribePayload
|
|
701
|
+
|
|
702
|
+
if (!payload?.subscriptionId) {
|
|
703
|
+
this.sendError(ws, message.id, CDCErrorCodes.INVALID_REQUEST, 'Missing subscriptionId in payload')
|
|
704
|
+
return
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Check if this connection owns the subscription
|
|
708
|
+
if (!connState.subscriptions.has(payload.subscriptionId)) {
|
|
709
|
+
this.logger.warn('Unsubscribe attempted for unknown subscription', {
|
|
710
|
+
connectionId,
|
|
711
|
+
subscriptionId: payload.subscriptionId,
|
|
712
|
+
})
|
|
713
|
+
this.sendError(ws, message.id, CDCErrorCodes.NOT_FOUND, 'Subscription not found for this connection')
|
|
714
|
+
return
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Remove subscription
|
|
718
|
+
const removed = this.cdcManager.unsubscribe(payload.subscriptionId)
|
|
719
|
+
if (removed) {
|
|
720
|
+
connState.subscriptions.delete(payload.subscriptionId)
|
|
721
|
+
this.metrics.subscriptionsRemoved++
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Send confirmation
|
|
725
|
+
this.sendMessage(ws, {
|
|
726
|
+
type: 'unsubscribed',
|
|
727
|
+
id: message.id,
|
|
728
|
+
payload: {
|
|
729
|
+
subscriptionId: payload.subscriptionId,
|
|
730
|
+
} as UnsubscribedPayload,
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
this.logger.info('Subscription removed', {
|
|
734
|
+
connectionId,
|
|
735
|
+
subscriptionId: payload.subscriptionId,
|
|
736
|
+
})
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Handle reconnect message
|
|
741
|
+
*/
|
|
742
|
+
private async handleReconnect(ws: WebSocket, message: WSMessage): Promise<void> {
|
|
743
|
+
const payload = message.payload as ReconnectPayload
|
|
744
|
+
|
|
745
|
+
if (!payload?.connectionId) {
|
|
746
|
+
this.sendError(ws, message.id, CDCErrorCodes.INVALID_REQUEST, 'Missing connectionId in payload')
|
|
747
|
+
return
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const newConnectionId = this.wsToConnection.get(ws)
|
|
751
|
+
if (!newConnectionId) {
|
|
752
|
+
this.sendError(ws, message.id, CDCErrorCodes.NO_CONNECTION, 'WebSocket not registered')
|
|
753
|
+
return
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
const newConnState = this.connections.get(newConnectionId)
|
|
757
|
+
if (!newConnState) {
|
|
758
|
+
this.sendError(ws, message.id, CDCErrorCodes.NO_CONNECTION, 'Connection state not found')
|
|
759
|
+
return
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Update metrics
|
|
763
|
+
this.metrics.reconnectAttempts++
|
|
764
|
+
|
|
765
|
+
this.logger.info('Reconnection attempt', {
|
|
766
|
+
newConnectionId,
|
|
767
|
+
oldConnectionId: payload.connectionId,
|
|
768
|
+
subscriptionCount: payload.subscriptionIds?.length ?? 0,
|
|
769
|
+
})
|
|
770
|
+
|
|
771
|
+
// Check if old connection has pending reconnect state (buffered changes)
|
|
772
|
+
const pendingState = this.pendingReconnect.get(payload.connectionId)
|
|
773
|
+
|
|
774
|
+
// Mark as reconnecting
|
|
775
|
+
newConnState.lifecycle = ConnectionLifecycle.RECONNECTING
|
|
776
|
+
|
|
777
|
+
// Restore subscriptions if provided
|
|
778
|
+
const restoredSubscriptions: string[] = []
|
|
779
|
+
if (payload.subscriptionIds && payload.subscriptionIds.length > 0) {
|
|
780
|
+
for (const subId of payload.subscriptionIds) {
|
|
781
|
+
const subscription = this.cdcManager.getSubscription(subId)
|
|
782
|
+
if (subscription) {
|
|
783
|
+
// Re-subscribe with same settings
|
|
784
|
+
const newSub = this.cdcManager.subscribe(ws, {
|
|
785
|
+
table: subscription.table,
|
|
786
|
+
events: subscription.events,
|
|
787
|
+
filter: subscription.filter,
|
|
788
|
+
})
|
|
789
|
+
newConnState.subscriptions.add(newSub.id)
|
|
790
|
+
restoredSubscriptions.push(newSub.id)
|
|
791
|
+
this.metrics.subscriptionsCreated++
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Send any buffered changes from pending reconnect state
|
|
797
|
+
if (pendingState && pendingState.bufferedChanges.length > 0) {
|
|
798
|
+
this.logger.debug('Delivering buffered changes', {
|
|
799
|
+
connectionId: newConnectionId,
|
|
800
|
+
bufferedCount: pendingState.bufferedChanges.length,
|
|
801
|
+
})
|
|
802
|
+
for (const bufferedMessage of pendingState.bufferedChanges) {
|
|
803
|
+
this.sendMessage(ws, bufferedMessage)
|
|
804
|
+
}
|
|
805
|
+
pendingState.bufferedChanges = []
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// Clean up the pending reconnect state if it exists
|
|
809
|
+
if (pendingState) {
|
|
810
|
+
this.timerManager.stopCleanupTimer(payload.connectionId)
|
|
811
|
+
this.pendingReconnect.delete(payload.connectionId)
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Transition to active state
|
|
815
|
+
newConnState.lifecycle = ConnectionLifecycle.ACTIVE
|
|
816
|
+
|
|
817
|
+
// Update metrics for successful reconnection
|
|
818
|
+
this.metrics.reconnectSuccesses++
|
|
819
|
+
|
|
820
|
+
// Send welcome with restored info
|
|
821
|
+
this.sendMessage(ws, {
|
|
822
|
+
type: 'welcome',
|
|
823
|
+
id: message.id,
|
|
824
|
+
payload: {
|
|
825
|
+
connectionId: newConnectionId,
|
|
826
|
+
timestamp: Date.now(),
|
|
827
|
+
heartbeatInterval: this.config.heartbeatInterval,
|
|
828
|
+
version: this.version,
|
|
829
|
+
restoredSubscriptions,
|
|
830
|
+
} as WelcomePayload & { restoredSubscriptions: string[] },
|
|
831
|
+
})
|
|
832
|
+
|
|
833
|
+
this.logger.info('Reconnection successful', {
|
|
834
|
+
connectionId: newConnectionId,
|
|
835
|
+
restoredSubscriptions: restoredSubscriptions.length,
|
|
836
|
+
})
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Handle WebSocket close
|
|
841
|
+
*/
|
|
842
|
+
handleClose(ws: WebSocket, code: number, reason: string): void {
|
|
843
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
844
|
+
if (!connectionId) return
|
|
845
|
+
|
|
846
|
+
const connState = this.connections.get(connectionId)
|
|
847
|
+
if (connState) {
|
|
848
|
+
// Transition to closing state
|
|
849
|
+
connState.lifecycle = ConnectionLifecycle.CLOSING
|
|
850
|
+
|
|
851
|
+
// Stop heartbeat timer FIRST to prevent any further timer callbacks
|
|
852
|
+
this.timerManager.stopHeartbeat(connectionId)
|
|
853
|
+
|
|
854
|
+
// Track subscription count before cleanup for metrics
|
|
855
|
+
const subscriptionCount = connState.subscriptions.size
|
|
856
|
+
|
|
857
|
+
// Clean up subscriptions from CDCManager
|
|
858
|
+
this.cdcManager.unsubscribeAll(ws)
|
|
859
|
+
|
|
860
|
+
// Clear the connection's subscription set
|
|
861
|
+
connState.subscriptions.clear()
|
|
862
|
+
|
|
863
|
+
// Update metrics
|
|
864
|
+
this.metrics.subscriptionsRemoved += subscriptionCount
|
|
865
|
+
this.metrics.connectionsActive--
|
|
866
|
+
|
|
867
|
+
// Store state for potential reconnection (buffered changes and subscription info)
|
|
868
|
+
// This is kept separate from active connections
|
|
869
|
+
this.pendingReconnect.set(connectionId, {
|
|
870
|
+
bufferedChanges: connState.bufferedChanges,
|
|
871
|
+
subscriptions: new Set(), // Already cleared, but could store subscription metadata here
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
// Start cleanup timer for pending reconnect state
|
|
875
|
+
const cleanupDelay = this.config.reconnectDelay * this.config.maxReconnectAttempts
|
|
876
|
+
this.timerManager.startCleanupTimer(connectionId, cleanupDelay, () => {
|
|
877
|
+
this.pendingReconnect.delete(connectionId)
|
|
878
|
+
this.logger.debug('Pending reconnect state cleaned up', { connectionId })
|
|
879
|
+
})
|
|
880
|
+
|
|
881
|
+
// Transition to closed state
|
|
882
|
+
connState.lifecycle = ConnectionLifecycle.CLOSED
|
|
883
|
+
|
|
884
|
+
// Remove from active connections immediately
|
|
885
|
+
this.connections.delete(connectionId)
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
this.wsToConnection.delete(ws)
|
|
889
|
+
this.logger.info('WebSocket closed', { connectionId, code, reason })
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Handle WebSocket error
|
|
894
|
+
*/
|
|
895
|
+
handleError(ws: WebSocket, error: Error): void {
|
|
896
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
897
|
+
|
|
898
|
+
this.logger.error('WebSocket error', {
|
|
899
|
+
connectionId,
|
|
900
|
+
error: error.message,
|
|
901
|
+
stack: error.stack,
|
|
902
|
+
})
|
|
903
|
+
|
|
904
|
+
// Update metrics
|
|
905
|
+
this.metrics.errorsTotal++
|
|
906
|
+
this.metrics.errorsByCode[CDCErrorCodes.INTERNAL_ERROR] =
|
|
907
|
+
(this.metrics.errorsByCode[CDCErrorCodes.INTERNAL_ERROR] || 0) + 1
|
|
908
|
+
|
|
909
|
+
// Update connection state
|
|
910
|
+
const connState = connectionId ? this.connections.get(connectionId) : undefined
|
|
911
|
+
if (connState) {
|
|
912
|
+
connState.lastError = error.message
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (connectionId) {
|
|
916
|
+
this.timerManager.stopHeartbeat(connectionId)
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
try {
|
|
920
|
+
ws.close(1011, 'Internal error')
|
|
921
|
+
} catch {
|
|
922
|
+
// WebSocket might already be closed
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Broadcast a change event to all matching subscribers
|
|
928
|
+
*/
|
|
929
|
+
broadcastChange(change: ChangeEvent): void {
|
|
930
|
+
this.cdcManager.broadcastChange(change)
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Broadcast a change event with filtering
|
|
935
|
+
*/
|
|
936
|
+
broadcastChangeWithFilter(change: ChangeEvent, filterConfig?: EventFilterConfig): void {
|
|
937
|
+
// Check table filter
|
|
938
|
+
if (filterConfig?.tables && filterConfig.tables.length > 0) {
|
|
939
|
+
if (!filterConfig.tables.includes(change.table)) {
|
|
940
|
+
return
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Check operation filter
|
|
945
|
+
if (filterConfig?.operations && filterConfig.operations.length > 0) {
|
|
946
|
+
if (!filterConfig.operations.includes(change.operation)) {
|
|
947
|
+
return
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
this.cdcManager.broadcastChange(change)
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Get connected WebSocket count
|
|
956
|
+
*/
|
|
957
|
+
getConnectedCount(): number {
|
|
958
|
+
return this.connections.size
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Get total subscription count
|
|
963
|
+
*/
|
|
964
|
+
getSubscriptionCount(): number {
|
|
965
|
+
return this.cdcManager.getSubscriptionCount()
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Get subscription count for a table
|
|
970
|
+
*/
|
|
971
|
+
getTableSubscriberCount(table: string): number {
|
|
972
|
+
return this.cdcManager.getTableSubscriberCount(table)
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Get subscriptions for a connection
|
|
977
|
+
*/
|
|
978
|
+
getConnectionSubscriptions(connectionId: string): Subscription[] {
|
|
979
|
+
const connState = this.connections.get(connectionId)
|
|
980
|
+
if (!connState) return []
|
|
981
|
+
|
|
982
|
+
return Array.from(connState.subscriptions)
|
|
983
|
+
.map(id => this.cdcManager.getSubscription(id))
|
|
984
|
+
.filter((s): s is Subscription => s !== undefined)
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Get connection info
|
|
989
|
+
*/
|
|
990
|
+
getConnectionInfo(connectionId: string): { subscriptionCount: number; lastHeartbeat: number } | null {
|
|
991
|
+
const connState = this.connections.get(connectionId)
|
|
992
|
+
if (!connState) return null
|
|
993
|
+
|
|
994
|
+
return {
|
|
995
|
+
subscriptionCount: connState.subscriptions.size,
|
|
996
|
+
lastHeartbeat: connState.lastHeartbeat,
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Start heartbeat timer for a connection
|
|
1002
|
+
*/
|
|
1003
|
+
private startHeartbeat(connectionId: string): void {
|
|
1004
|
+
this.timerManager.startHeartbeat(connectionId, this.config.heartbeatInterval, () => {
|
|
1005
|
+
const connState = this.connections.get(connectionId)
|
|
1006
|
+
if (!connState || connState.ws.readyState !== WS_OPEN) {
|
|
1007
|
+
this.timerManager.stopHeartbeat(connectionId)
|
|
1008
|
+
return
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Skip heartbeat timeout check if there are active operations in progress
|
|
1012
|
+
// This prevents closing the connection during long-running queries
|
|
1013
|
+
if (connState.activeOperations > 0) {
|
|
1014
|
+
// Still send heartbeat but don't check timeout
|
|
1015
|
+
connState.heartbeatSeq++
|
|
1016
|
+
this.sendMessage(connState.ws, {
|
|
1017
|
+
type: 'heartbeat',
|
|
1018
|
+
id: generateId('hb'),
|
|
1019
|
+
payload: {
|
|
1020
|
+
timestamp: Date.now(),
|
|
1021
|
+
seq: connState.heartbeatSeq,
|
|
1022
|
+
} as HeartbeatPayload,
|
|
1023
|
+
})
|
|
1024
|
+
this.logger.debug('Heartbeat sent (skipped timeout check due to active operations)', {
|
|
1025
|
+
connectionId,
|
|
1026
|
+
activeOperations: connState.activeOperations,
|
|
1027
|
+
})
|
|
1028
|
+
return
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Check if last heartbeat was too long ago
|
|
1032
|
+
const timeSinceLastHeartbeat = Date.now() - connState.lastHeartbeat
|
|
1033
|
+
if (timeSinceLastHeartbeat > this.config.heartbeatInterval + this.config.heartbeatTimeout) {
|
|
1034
|
+
this.logger.warn('Connection heartbeat timeout', {
|
|
1035
|
+
connectionId,
|
|
1036
|
+
timeSinceLastHeartbeat,
|
|
1037
|
+
threshold: this.config.heartbeatInterval + this.config.heartbeatTimeout,
|
|
1038
|
+
})
|
|
1039
|
+
|
|
1040
|
+
// Update metrics
|
|
1041
|
+
this.metrics.heartbeatTimeouts++
|
|
1042
|
+
|
|
1043
|
+
try {
|
|
1044
|
+
connState.ws.close(1000, 'Heartbeat timeout')
|
|
1045
|
+
} catch {
|
|
1046
|
+
// Ignore errors on close
|
|
1047
|
+
}
|
|
1048
|
+
this.timerManager.stopHeartbeat(connectionId)
|
|
1049
|
+
return
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// Send heartbeat
|
|
1053
|
+
connState.heartbeatSeq++
|
|
1054
|
+
this.sendMessage(connState.ws, {
|
|
1055
|
+
type: 'heartbeat',
|
|
1056
|
+
id: generateId('hb'),
|
|
1057
|
+
payload: {
|
|
1058
|
+
timestamp: Date.now(),
|
|
1059
|
+
seq: connState.heartbeatSeq,
|
|
1060
|
+
} as HeartbeatPayload,
|
|
1061
|
+
})
|
|
1062
|
+
})
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* Send a message to a WebSocket
|
|
1067
|
+
*/
|
|
1068
|
+
private sendMessage(ws: WebSocket, message: WSMessage): void {
|
|
1069
|
+
if (ws.readyState !== WS_OPEN) return
|
|
1070
|
+
|
|
1071
|
+
try {
|
|
1072
|
+
ws.send(serializeWSMessage(message))
|
|
1073
|
+
|
|
1074
|
+
// Update metrics
|
|
1075
|
+
this.metrics.messagesSent++
|
|
1076
|
+
|
|
1077
|
+
// Update connection-specific metrics
|
|
1078
|
+
const connectionId = this.wsToConnection.get(ws)
|
|
1079
|
+
if (connectionId) {
|
|
1080
|
+
const connState = this.connections.get(connectionId)
|
|
1081
|
+
if (connState) {
|
|
1082
|
+
connState.messagesSent++
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
1087
|
+
this.logger.error('Failed to send message', {
|
|
1088
|
+
messageType: message.type,
|
|
1089
|
+
error: errorMessage,
|
|
1090
|
+
})
|
|
1091
|
+
|
|
1092
|
+
// Update error metrics
|
|
1093
|
+
this.metrics.errorsTotal++
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* Send an error message
|
|
1099
|
+
*/
|
|
1100
|
+
private sendError(ws: WebSocket, id: string, code: string, message: string): void {
|
|
1101
|
+
// Update error metrics
|
|
1102
|
+
this.metrics.errorsTotal++
|
|
1103
|
+
this.metrics.errorsByCode[code] = (this.metrics.errorsByCode[code] || 0) + 1
|
|
1104
|
+
|
|
1105
|
+
this.sendMessage(ws, {
|
|
1106
|
+
type: 'error',
|
|
1107
|
+
id,
|
|
1108
|
+
payload: {
|
|
1109
|
+
code,
|
|
1110
|
+
message,
|
|
1111
|
+
} as ErrorPayload,
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
this.logger.warn('Error sent to client', { id, code, message })
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Get the CDC manager instance
|
|
1119
|
+
*/
|
|
1120
|
+
getCDCManager(): CDCManager {
|
|
1121
|
+
return this.cdcManager
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Get configuration
|
|
1126
|
+
*/
|
|
1127
|
+
getConfig(): Required<CDCWebSocketConfig> {
|
|
1128
|
+
return { ...this.config }
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Get current metrics
|
|
1133
|
+
*/
|
|
1134
|
+
getMetrics(): CDCWebSocketMetrics {
|
|
1135
|
+
return { ...this.metrics }
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Get detailed connection info with metrics
|
|
1140
|
+
*/
|
|
1141
|
+
getDetailedConnectionInfo(connectionId: string): {
|
|
1142
|
+
lifecycle: ConnectionLifecycle
|
|
1143
|
+
subscriptionCount: number
|
|
1144
|
+
lastHeartbeat: number
|
|
1145
|
+
createdAt: number
|
|
1146
|
+
messagesReceived: number
|
|
1147
|
+
messagesSent: number
|
|
1148
|
+
lastError?: string
|
|
1149
|
+
} | null {
|
|
1150
|
+
const connState = this.connections.get(connectionId)
|
|
1151
|
+
if (!connState) return null
|
|
1152
|
+
|
|
1153
|
+
// Build result object conditionally to avoid undefined values
|
|
1154
|
+
const result: {
|
|
1155
|
+
lifecycle: ConnectionLifecycle
|
|
1156
|
+
subscriptionCount: number
|
|
1157
|
+
lastHeartbeat: number
|
|
1158
|
+
createdAt: number
|
|
1159
|
+
messagesReceived: number
|
|
1160
|
+
messagesSent: number
|
|
1161
|
+
lastError?: string
|
|
1162
|
+
} = {
|
|
1163
|
+
lifecycle: connState.lifecycle,
|
|
1164
|
+
subscriptionCount: connState.subscriptions.size,
|
|
1165
|
+
lastHeartbeat: connState.lastHeartbeat,
|
|
1166
|
+
createdAt: connState.createdAt,
|
|
1167
|
+
messagesReceived: connState.messagesReceived,
|
|
1168
|
+
messagesSent: connState.messagesSent,
|
|
1169
|
+
}
|
|
1170
|
+
if (connState.lastError !== undefined) {
|
|
1171
|
+
result.lastError = connState.lastError
|
|
1172
|
+
}
|
|
1173
|
+
return result
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* Get all connection IDs
|
|
1178
|
+
*/
|
|
1179
|
+
getConnectionIds(): string[] {
|
|
1180
|
+
return Array.from(this.connections.keys())
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Get timer statistics
|
|
1185
|
+
*/
|
|
1186
|
+
getTimerStats(): { heartbeat: number; cleanup: number } {
|
|
1187
|
+
return this.timerManager.getTimerCounts()
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Clean up all resources (for graceful shutdown)
|
|
1192
|
+
*/
|
|
1193
|
+
shutdown(): void {
|
|
1194
|
+
this.logger.info('Shutting down CDCWebSocketHandler', {
|
|
1195
|
+
activeConnections: this.connections.size,
|
|
1196
|
+
})
|
|
1197
|
+
|
|
1198
|
+
// Clear all timers
|
|
1199
|
+
this.timerManager.clearAll()
|
|
1200
|
+
|
|
1201
|
+
// Close all connections
|
|
1202
|
+
for (const connState of this.connections.values()) {
|
|
1203
|
+
try {
|
|
1204
|
+
connState.ws.close(1001, 'Server shutting down')
|
|
1205
|
+
} catch {
|
|
1206
|
+
// Ignore errors
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
this.connections.clear()
|
|
1211
|
+
this.wsToConnection.clear()
|
|
1212
|
+
this.pendingReconnect.clear()
|
|
1213
|
+
|
|
1214
|
+
this.logger.info('CDCWebSocketHandler shutdown complete')
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
* Factory function to create a CDCWebSocketHandler
|
|
1220
|
+
*/
|
|
1221
|
+
export function createCDCWebSocketHandler(
|
|
1222
|
+
state: DurableObjectState,
|
|
1223
|
+
executor: QueryExecutor,
|
|
1224
|
+
cdcManager?: CDCManager,
|
|
1225
|
+
config?: CDCWebSocketConfig,
|
|
1226
|
+
logger?: ILogger
|
|
1227
|
+
): CDCWebSocketHandler {
|
|
1228
|
+
return new CDCWebSocketHandler(state, executor, cdcManager, config, logger)
|
|
1229
|
+
}
|