@dotdo/postgres 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1129) hide show
  1. package/README.md +868 -0
  2. package/dist/cdc/change-stream.d.ts +44 -0
  3. package/dist/cdc/change-stream.d.ts.map +1 -0
  4. package/dist/cdc/change-stream.js +95 -0
  5. package/dist/cdc/change-stream.js.map +1 -0
  6. package/dist/cdc/filter.d.ts +58 -0
  7. package/dist/cdc/filter.d.ts.map +1 -0
  8. package/dist/cdc/filter.js +520 -0
  9. package/dist/cdc/filter.js.map +1 -0
  10. package/dist/cdc/index.d.ts +47 -0
  11. package/dist/cdc/index.d.ts.map +1 -0
  12. package/dist/cdc/index.js +50 -0
  13. package/dist/cdc/index.js.map +1 -0
  14. package/dist/cdc/resume-token.d.ts +60 -0
  15. package/dist/cdc/resume-token.d.ts.map +1 -0
  16. package/dist/cdc/resume-token.js +228 -0
  17. package/dist/cdc/resume-token.js.map +1 -0
  18. package/dist/cdc/transport/index.d.ts +7 -0
  19. package/dist/cdc/transport/index.d.ts.map +1 -0
  20. package/dist/cdc/transport/index.js +7 -0
  21. package/dist/cdc/transport/index.js.map +1 -0
  22. package/dist/cdc/transport/sse.d.ts +120 -0
  23. package/dist/cdc/transport/sse.d.ts.map +1 -0
  24. package/dist/cdc/transport/sse.js +590 -0
  25. package/dist/cdc/transport/sse.js.map +1 -0
  26. package/dist/cdc/transport/websocket.d.ts +130 -0
  27. package/dist/cdc/transport/websocket.d.ts.map +1 -0
  28. package/dist/cdc/transport/websocket.js +688 -0
  29. package/dist/cdc/transport/websocket.js.map +1 -0
  30. package/dist/cdc/types.d.ts +306 -0
  31. package/dist/cdc/types.d.ts.map +1 -0
  32. package/dist/cdc/types.js +8 -0
  33. package/dist/cdc/types.js.map +1 -0
  34. package/dist/config/index.d.ts +25 -0
  35. package/dist/config/index.d.ts.map +1 -0
  36. package/dist/config/index.js +25 -0
  37. package/dist/config/index.js.map +1 -0
  38. package/dist/config/memory.d.ts +139 -0
  39. package/dist/config/memory.d.ts.map +1 -0
  40. package/dist/config/memory.js +157 -0
  41. package/dist/config/memory.js.map +1 -0
  42. package/dist/config/storage.d.ts +157 -0
  43. package/dist/config/storage.d.ts.map +1 -0
  44. package/dist/config/storage.js +178 -0
  45. package/dist/config/storage.js.map +1 -0
  46. package/dist/config/streaming.d.ts +117 -0
  47. package/dist/config/streaming.d.ts.map +1 -0
  48. package/dist/config/streaming.js +132 -0
  49. package/dist/config/streaming.js.map +1 -0
  50. package/dist/config/timeouts.d.ts +168 -0
  51. package/dist/config/timeouts.d.ts.map +1 -0
  52. package/dist/config/timeouts.js +192 -0
  53. package/dist/config/timeouts.js.map +1 -0
  54. package/dist/extensions/config.d.ts +89 -0
  55. package/dist/extensions/config.d.ts.map +1 -0
  56. package/dist/extensions/config.js +216 -0
  57. package/dist/extensions/config.js.map +1 -0
  58. package/dist/extensions/geo.d.ts +452 -0
  59. package/dist/extensions/geo.d.ts.map +1 -0
  60. package/dist/extensions/geo.js +583 -0
  61. package/dist/extensions/geo.js.map +1 -0
  62. package/dist/extensions/index.d.ts +167 -0
  63. package/dist/extensions/index.d.ts.map +1 -0
  64. package/dist/extensions/index.js +99 -0
  65. package/dist/extensions/index.js.map +1 -0
  66. package/dist/extensions/loader.d.ts +226 -0
  67. package/dist/extensions/loader.d.ts.map +1 -0
  68. package/dist/extensions/loader.js +456 -0
  69. package/dist/extensions/loader.js.map +1 -0
  70. package/dist/extensions/pgmq-lite.d.ts +330 -0
  71. package/dist/extensions/pgmq-lite.d.ts.map +1 -0
  72. package/dist/extensions/pgmq-lite.js +648 -0
  73. package/dist/extensions/pgmq-lite.js.map +1 -0
  74. package/dist/extensions/plugins.d.ts +260 -0
  75. package/dist/extensions/plugins.d.ts.map +1 -0
  76. package/dist/extensions/plugins.js +535 -0
  77. package/dist/extensions/plugins.js.map +1 -0
  78. package/dist/extensions/registry.d.ts +93 -0
  79. package/dist/extensions/registry.d.ts.map +1 -0
  80. package/dist/extensions/registry.js +182 -0
  81. package/dist/extensions/registry.js.map +1 -0
  82. package/dist/extensions/vector.d.ts +106 -0
  83. package/dist/extensions/vector.d.ts.map +1 -0
  84. package/dist/extensions/vector.js +129 -0
  85. package/dist/extensions/vector.js.map +1 -0
  86. package/dist/iceberg/analytics.d.ts +279 -0
  87. package/dist/iceberg/analytics.d.ts.map +1 -0
  88. package/dist/iceberg/analytics.js +448 -0
  89. package/dist/iceberg/analytics.js.map +1 -0
  90. package/dist/iceberg/catalog-api.d.ts +39 -0
  91. package/dist/iceberg/catalog-api.d.ts.map +1 -0
  92. package/dist/iceberg/catalog-api.js +388 -0
  93. package/dist/iceberg/catalog-api.js.map +1 -0
  94. package/dist/iceberg/catalog.d.ts +401 -0
  95. package/dist/iceberg/catalog.d.ts.map +1 -0
  96. package/dist/iceberg/catalog.js +677 -0
  97. package/dist/iceberg/catalog.js.map +1 -0
  98. package/dist/iceberg/duckdb-wasm.d.ts +447 -0
  99. package/dist/iceberg/duckdb-wasm.d.ts.map +1 -0
  100. package/dist/iceberg/duckdb-wasm.js +600 -0
  101. package/dist/iceberg/duckdb-wasm.js.map +1 -0
  102. package/dist/iceberg/index.d.ts +92 -0
  103. package/dist/iceberg/index.d.ts.map +1 -0
  104. package/dist/iceberg/index.js +119 -0
  105. package/dist/iceberg/index.js.map +1 -0
  106. package/dist/iceberg/metadata.d.ts +214 -0
  107. package/dist/iceberg/metadata.d.ts.map +1 -0
  108. package/dist/iceberg/metadata.js +535 -0
  109. package/dist/iceberg/metadata.js.map +1 -0
  110. package/dist/iceberg/optimizer.d.ts +296 -0
  111. package/dist/iceberg/optimizer.d.ts.map +1 -0
  112. package/dist/iceberg/optimizer.js +889 -0
  113. package/dist/iceberg/optimizer.js.map +1 -0
  114. package/dist/iceberg/parquet.d.ts +447 -0
  115. package/dist/iceberg/parquet.d.ts.map +1 -0
  116. package/dist/iceberg/parquet.js +1225 -0
  117. package/dist/iceberg/parquet.js.map +1 -0
  118. package/dist/iceberg/r2-organization.d.ts +422 -0
  119. package/dist/iceberg/r2-organization.d.ts.map +1 -0
  120. package/dist/iceberg/r2-organization.js +672 -0
  121. package/dist/iceberg/r2-organization.js.map +1 -0
  122. package/dist/iceberg/scheduler-do-example.d.ts +158 -0
  123. package/dist/iceberg/scheduler-do-example.d.ts.map +1 -0
  124. package/dist/iceberg/scheduler-do-example.js +261 -0
  125. package/dist/iceberg/scheduler-do-example.js.map +1 -0
  126. package/dist/iceberg/scheduler.d.ts +434 -0
  127. package/dist/iceberg/scheduler.d.ts.map +1 -0
  128. package/dist/iceberg/scheduler.js +818 -0
  129. package/dist/iceberg/scheduler.js.map +1 -0
  130. package/dist/iceberg/schema.d.ts +149 -0
  131. package/dist/iceberg/schema.d.ts.map +1 -0
  132. package/dist/iceberg/schema.js +525 -0
  133. package/dist/iceberg/schema.js.map +1 -0
  134. package/dist/iceberg/snapshot-manager.d.ts +406 -0
  135. package/dist/iceberg/snapshot-manager.d.ts.map +1 -0
  136. package/dist/iceberg/snapshot-manager.js +934 -0
  137. package/dist/iceberg/snapshot-manager.js.map +1 -0
  138. package/dist/iceberg/sql-router.d.ts +194 -0
  139. package/dist/iceberg/sql-router.d.ts.map +1 -0
  140. package/dist/iceberg/sql-router.js +180 -0
  141. package/dist/iceberg/sql-router.js.map +1 -0
  142. package/dist/iceberg/test-fixtures.d.ts +151 -0
  143. package/dist/iceberg/test-fixtures.d.ts.map +1 -0
  144. package/dist/iceberg/test-fixtures.js +446 -0
  145. package/dist/iceberg/test-fixtures.js.map +1 -0
  146. package/dist/iceberg/time-travel-api.d.ts +102 -0
  147. package/dist/iceberg/time-travel-api.d.ts.map +1 -0
  148. package/dist/iceberg/time-travel-api.js +437 -0
  149. package/dist/iceberg/time-travel-api.js.map +1 -0
  150. package/dist/iceberg/time-travel.d.ts +293 -0
  151. package/dist/iceberg/time-travel.d.ts.map +1 -0
  152. package/dist/iceberg/time-travel.js +689 -0
  153. package/dist/iceberg/time-travel.js.map +1 -0
  154. package/dist/iceberg/transformer.d.ts +356 -0
  155. package/dist/iceberg/transformer.d.ts.map +1 -0
  156. package/dist/iceberg/transformer.js +770 -0
  157. package/dist/iceberg/transformer.js.map +1 -0
  158. package/dist/iceberg/types.d.ts +318 -0
  159. package/dist/iceberg/types.d.ts.map +1 -0
  160. package/dist/iceberg/types.js +9 -0
  161. package/dist/iceberg/types.js.map +1 -0
  162. package/dist/iceberg/writer.d.ts +144 -0
  163. package/dist/iceberg/writer.d.ts.map +1 -0
  164. package/dist/iceberg/writer.js +452 -0
  165. package/dist/iceberg/writer.js.map +1 -0
  166. package/dist/index.d.ts +50 -0
  167. package/dist/index.d.ts.map +1 -0
  168. package/dist/index.js +69 -0
  169. package/dist/index.js.map +1 -0
  170. package/dist/lineage/index.d.ts +11 -0
  171. package/dist/lineage/index.d.ts.map +1 -0
  172. package/dist/lineage/index.js +11 -0
  173. package/dist/lineage/index.js.map +1 -0
  174. package/dist/lineage/integration.d.ts +134 -0
  175. package/dist/lineage/integration.d.ts.map +1 -0
  176. package/dist/lineage/integration.js +258 -0
  177. package/dist/lineage/integration.js.map +1 -0
  178. package/dist/lineage/tracker.d.ts +189 -0
  179. package/dist/lineage/tracker.d.ts.map +1 -0
  180. package/dist/lineage/tracker.js +1352 -0
  181. package/dist/lineage/tracker.js.map +1 -0
  182. package/dist/lineage/types.d.ts +318 -0
  183. package/dist/lineage/types.d.ts.map +1 -0
  184. package/dist/lineage/types.js +9 -0
  185. package/dist/lineage/types.js.map +1 -0
  186. package/dist/middleware/index.d.ts +11 -0
  187. package/dist/middleware/index.d.ts.map +1 -0
  188. package/dist/middleware/index.js +16 -0
  189. package/dist/middleware/index.js.map +1 -0
  190. package/dist/middleware/rate-limit.d.ts +397 -0
  191. package/dist/middleware/rate-limit.d.ts.map +1 -0
  192. package/dist/middleware/rate-limit.js +507 -0
  193. package/dist/middleware/rate-limit.js.map +1 -0
  194. package/dist/migration-tooling/external-migration.d.ts +601 -0
  195. package/dist/migration-tooling/external-migration.d.ts.map +1 -0
  196. package/dist/migration-tooling/external-migration.js +1612 -0
  197. package/dist/migration-tooling/external-migration.js.map +1 -0
  198. package/dist/migration-tooling/index.d.ts +19 -0
  199. package/dist/migration-tooling/index.d.ts.map +1 -0
  200. package/dist/migration-tooling/index.js +19 -0
  201. package/dist/migration-tooling/index.js.map +1 -0
  202. package/dist/migrations/auto-migrator.d.ts +289 -0
  203. package/dist/migrations/auto-migrator.d.ts.map +1 -0
  204. package/dist/migrations/auto-migrator.js +396 -0
  205. package/dist/migrations/auto-migrator.js.map +1 -0
  206. package/dist/migrations/bulk-orchestrator.d.ts +403 -0
  207. package/dist/migrations/bulk-orchestrator.d.ts.map +1 -0
  208. package/dist/migrations/bulk-orchestrator.js +646 -0
  209. package/dist/migrations/bulk-orchestrator.js.map +1 -0
  210. package/dist/migrations/compatibility.d.ts +216 -0
  211. package/dist/migrations/compatibility.d.ts.map +1 -0
  212. package/dist/migrations/compatibility.js +651 -0
  213. package/dist/migrations/compatibility.js.map +1 -0
  214. package/dist/migrations/do-migrations.d.ts +101 -0
  215. package/dist/migrations/do-migrations.d.ts.map +1 -0
  216. package/dist/migrations/do-migrations.js +1060 -0
  217. package/dist/migrations/do-migrations.js.map +1 -0
  218. package/dist/migrations/do-migrations.types.d.ts +550 -0
  219. package/dist/migrations/do-migrations.types.d.ts.map +1 -0
  220. package/dist/migrations/do-migrations.types.js +15 -0
  221. package/dist/migrations/do-migrations.types.js.map +1 -0
  222. package/dist/migrations/drizzle-compat.d.ts +163 -0
  223. package/dist/migrations/drizzle-compat.d.ts.map +1 -0
  224. package/dist/migrations/drizzle-compat.js +273 -0
  225. package/dist/migrations/drizzle-compat.js.map +1 -0
  226. package/dist/migrations/index.d.ts +109 -0
  227. package/dist/migrations/index.d.ts.map +1 -0
  228. package/dist/migrations/index.js +127 -0
  229. package/dist/migrations/index.js.map +1 -0
  230. package/dist/migrations/migration-api.d.ts +161 -0
  231. package/dist/migrations/migration-api.d.ts.map +1 -0
  232. package/dist/migrations/migration-api.js +499 -0
  233. package/dist/migrations/migration-api.js.map +1 -0
  234. package/dist/migrations/progress-tracker-do.d.ts +195 -0
  235. package/dist/migrations/progress-tracker-do.d.ts.map +1 -0
  236. package/dist/migrations/progress-tracker-do.js +339 -0
  237. package/dist/migrations/progress-tracker-do.js.map +1 -0
  238. package/dist/migrations/progress-tracker-kv.d.ts +103 -0
  239. package/dist/migrations/progress-tracker-kv.d.ts.map +1 -0
  240. package/dist/migrations/progress-tracker-kv.js +231 -0
  241. package/dist/migrations/progress-tracker-kv.js.map +1 -0
  242. package/dist/migrations/progress-tracker.d.ts +320 -0
  243. package/dist/migrations/progress-tracker.d.ts.map +1 -0
  244. package/dist/migrations/progress-tracker.js +443 -0
  245. package/dist/migrations/progress-tracker.js.map +1 -0
  246. package/dist/migrations/registry.d.ts +231 -0
  247. package/dist/migrations/registry.d.ts.map +1 -0
  248. package/dist/migrations/registry.js +376 -0
  249. package/dist/migrations/registry.js.map +1 -0
  250. package/dist/migrations/runner.d.ts +197 -0
  251. package/dist/migrations/runner.d.ts.map +1 -0
  252. package/dist/migrations/runner.js +1167 -0
  253. package/dist/migrations/runner.js.map +1 -0
  254. package/dist/migrations/schema-generator.d.ts +111 -0
  255. package/dist/migrations/schema-generator.d.ts.map +1 -0
  256. package/dist/migrations/schema-generator.js +335 -0
  257. package/dist/migrations/schema-generator.js.map +1 -0
  258. package/dist/migrations/testing.d.ts +321 -0
  259. package/dist/migrations/testing.d.ts.map +1 -0
  260. package/dist/migrations/testing.js +645 -0
  261. package/dist/migrations/testing.js.map +1 -0
  262. package/dist/migrations/types.d.ts +503 -0
  263. package/dist/migrations/types.d.ts.map +1 -0
  264. package/dist/migrations/types.js +11 -0
  265. package/dist/migrations/types.js.map +1 -0
  266. package/dist/migrations/validator.d.ts +215 -0
  267. package/dist/migrations/validator.d.ts.map +1 -0
  268. package/dist/migrations/validator.js +494 -0
  269. package/dist/migrations/validator.js.map +1 -0
  270. package/dist/observability/alerting.d.ts +116 -0
  271. package/dist/observability/alerting.d.ts.map +1 -0
  272. package/dist/observability/alerting.js +353 -0
  273. package/dist/observability/alerting.js.map +1 -0
  274. package/dist/observability/analytics-engine.d.ts +357 -0
  275. package/dist/observability/analytics-engine.d.ts.map +1 -0
  276. package/dist/observability/analytics-engine.js +430 -0
  277. package/dist/observability/analytics-engine.js.map +1 -0
  278. package/dist/observability/cost-metrics.d.ts +269 -0
  279. package/dist/observability/cost-metrics.d.ts.map +1 -0
  280. package/dist/observability/cost-metrics.js +560 -0
  281. package/dist/observability/cost-metrics.js.map +1 -0
  282. package/dist/observability/cross-do-tracing.d.ts +305 -0
  283. package/dist/observability/cross-do-tracing.d.ts.map +1 -0
  284. package/dist/observability/cross-do-tracing.js +431 -0
  285. package/dist/observability/cross-do-tracing.js.map +1 -0
  286. package/dist/observability/error-rate-collector.d.ts +163 -0
  287. package/dist/observability/error-rate-collector.d.ts.map +1 -0
  288. package/dist/observability/error-rate-collector.js +306 -0
  289. package/dist/observability/error-rate-collector.js.map +1 -0
  290. package/dist/observability/exporters.d.ts +231 -0
  291. package/dist/observability/exporters.d.ts.map +1 -0
  292. package/dist/observability/exporters.js +479 -0
  293. package/dist/observability/exporters.js.map +1 -0
  294. package/dist/observability/health-check.d.ts +106 -0
  295. package/dist/observability/health-check.d.ts.map +1 -0
  296. package/dist/observability/health-check.js +243 -0
  297. package/dist/observability/health-check.js.map +1 -0
  298. package/dist/observability/index.d.ts +297 -0
  299. package/dist/observability/index.d.ts.map +1 -0
  300. package/dist/observability/index.js +455 -0
  301. package/dist/observability/index.js.map +1 -0
  302. package/dist/observability/instrumentation.d.ts +222 -0
  303. package/dist/observability/instrumentation.d.ts.map +1 -0
  304. package/dist/observability/instrumentation.js +532 -0
  305. package/dist/observability/instrumentation.js.map +1 -0
  306. package/dist/observability/memory-metrics.d.ts +227 -0
  307. package/dist/observability/memory-metrics.d.ts.map +1 -0
  308. package/dist/observability/memory-metrics.js +688 -0
  309. package/dist/observability/memory-metrics.js.map +1 -0
  310. package/dist/observability/metrics-endpoint.d.ts +91 -0
  311. package/dist/observability/metrics-endpoint.d.ts.map +1 -0
  312. package/dist/observability/metrics-endpoint.js +246 -0
  313. package/dist/observability/metrics-endpoint.js.map +1 -0
  314. package/dist/observability/metrics.d.ts +88 -0
  315. package/dist/observability/metrics.d.ts.map +1 -0
  316. package/dist/observability/metrics.js +253 -0
  317. package/dist/observability/metrics.js.map +1 -0
  318. package/dist/observability/observability-features.d.ts +488 -0
  319. package/dist/observability/observability-features.d.ts.map +1 -0
  320. package/dist/observability/observability-features.js +773 -0
  321. package/dist/observability/observability-features.js.map +1 -0
  322. package/dist/observability/prometheus.d.ts +39 -0
  323. package/dist/observability/prometheus.d.ts.map +1 -0
  324. package/dist/observability/prometheus.js +120 -0
  325. package/dist/observability/prometheus.js.map +1 -0
  326. package/dist/observability/propagation.d.ts +126 -0
  327. package/dist/observability/propagation.d.ts.map +1 -0
  328. package/dist/observability/propagation.js +234 -0
  329. package/dist/observability/propagation.js.map +1 -0
  330. package/dist/observability/query-latency.d.ts +243 -0
  331. package/dist/observability/query-latency.d.ts.map +1 -0
  332. package/dist/observability/query-latency.js +292 -0
  333. package/dist/observability/query-latency.js.map +1 -0
  334. package/dist/observability/query-performance.d.ts +169 -0
  335. package/dist/observability/query-performance.d.ts.map +1 -0
  336. package/dist/observability/query-performance.js +290 -0
  337. package/dist/observability/query-performance.js.map +1 -0
  338. package/dist/observability/storage-tier-metrics.d.ts +174 -0
  339. package/dist/observability/storage-tier-metrics.d.ts.map +1 -0
  340. package/dist/observability/storage-tier-metrics.js +306 -0
  341. package/dist/observability/storage-tier-metrics.js.map +1 -0
  342. package/dist/observability/tier-cost-optimizer.d.ts +155 -0
  343. package/dist/observability/tier-cost-optimizer.d.ts.map +1 -0
  344. package/dist/observability/tier-cost-optimizer.js +536 -0
  345. package/dist/observability/tier-cost-optimizer.js.map +1 -0
  346. package/dist/observability/tracer.d.ts +149 -0
  347. package/dist/observability/tracer.d.ts.map +1 -0
  348. package/dist/observability/tracer.js +435 -0
  349. package/dist/observability/tracer.js.map +1 -0
  350. package/dist/observability/types.d.ts +402 -0
  351. package/dist/observability/types.d.ts.map +1 -0
  352. package/dist/observability/types.js +103 -0
  353. package/dist/observability/types.js.map +1 -0
  354. package/dist/pglite/workers-pglite.d.ts +138 -0
  355. package/dist/pglite/workers-pglite.d.ts.map +1 -0
  356. package/dist/pglite/workers-pglite.js +143 -0
  357. package/dist/pglite/workers-pglite.js.map +1 -0
  358. package/dist/pglite-assets/pglite.data +0 -0
  359. package/dist/pglite-assets/pglite.wasm +0 -0
  360. package/dist/playground/index.d.ts +52 -0
  361. package/dist/playground/index.d.ts.map +1 -0
  362. package/dist/playground/index.js +55 -0
  363. package/dist/playground/index.js.map +1 -0
  364. package/dist/playground/keyboard-shortcuts.d.ts +116 -0
  365. package/dist/playground/keyboard-shortcuts.d.ts.map +1 -0
  366. package/dist/playground/keyboard-shortcuts.js +588 -0
  367. package/dist/playground/keyboard-shortcuts.js.map +1 -0
  368. package/dist/playground/playground.d.ts +82 -0
  369. package/dist/playground/playground.d.ts.map +1 -0
  370. package/dist/playground/playground.js +271 -0
  371. package/dist/playground/playground.js.map +1 -0
  372. package/dist/playground/query-executor.d.ts +115 -0
  373. package/dist/playground/query-executor.d.ts.map +1 -0
  374. package/dist/playground/query-executor.js +558 -0
  375. package/dist/playground/query-executor.js.map +1 -0
  376. package/dist/playground/query-history.d.ts +92 -0
  377. package/dist/playground/query-history.d.ts.map +1 -0
  378. package/dist/playground/query-history.js +259 -0
  379. package/dist/playground/query-history.js.map +1 -0
  380. package/dist/playground/result-formatter.d.ts +59 -0
  381. package/dist/playground/result-formatter.d.ts.map +1 -0
  382. package/dist/playground/result-formatter.js +341 -0
  383. package/dist/playground/result-formatter.js.map +1 -0
  384. package/dist/playground/sample-datasets.d.ts +77 -0
  385. package/dist/playground/sample-datasets.d.ts.map +1 -0
  386. package/dist/playground/sample-datasets.js +641 -0
  387. package/dist/playground/sample-datasets.js.map +1 -0
  388. package/dist/playground/sample-queries.d.ts +73 -0
  389. package/dist/playground/sample-queries.d.ts.map +1 -0
  390. package/dist/playground/sample-queries.js +1095 -0
  391. package/dist/playground/sample-queries.js.map +1 -0
  392. package/dist/playground/schema-explorer.d.ts +55 -0
  393. package/dist/playground/schema-explorer.d.ts.map +1 -0
  394. package/dist/playground/schema-explorer.js +473 -0
  395. package/dist/playground/schema-explorer.js.map +1 -0
  396. package/dist/playground/types.d.ts +430 -0
  397. package/dist/playground/types.d.ts.map +1 -0
  398. package/dist/playground/types.js +10 -0
  399. package/dist/playground/types.js.map +1 -0
  400. package/dist/readonly/cache-reader.d.ts +145 -0
  401. package/dist/readonly/cache-reader.d.ts.map +1 -0
  402. package/dist/readonly/cache-reader.js +198 -0
  403. package/dist/readonly/cache-reader.js.map +1 -0
  404. package/dist/readonly/config.d.ts +74 -0
  405. package/dist/readonly/config.d.ts.map +1 -0
  406. package/dist/readonly/config.js +67 -0
  407. package/dist/readonly/config.js.map +1 -0
  408. package/dist/readonly/index.d.ts +22 -0
  409. package/dist/readonly/index.d.ts.map +1 -0
  410. package/dist/readonly/index.js +17 -0
  411. package/dist/readonly/index.js.map +1 -0
  412. package/dist/readonly/pglite-wrapper.d.ts +82 -0
  413. package/dist/readonly/pglite-wrapper.d.ts.map +1 -0
  414. package/dist/readonly/pglite-wrapper.js +123 -0
  415. package/dist/readonly/pglite-wrapper.js.map +1 -0
  416. package/dist/readonly/worker.d.ts +142 -0
  417. package/dist/readonly/worker.d.ts.map +1 -0
  418. package/dist/readonly/worker.js +187 -0
  419. package/dist/readonly/worker.js.map +1 -0
  420. package/dist/readonly/write-blocker.d.ts +47 -0
  421. package/dist/readonly/write-blocker.d.ts.map +1 -0
  422. package/dist/readonly/write-blocker.js +136 -0
  423. package/dist/readonly/write-blocker.js.map +1 -0
  424. package/dist/recovery/disaster-recovery.d.ts +326 -0
  425. package/dist/recovery/disaster-recovery.d.ts.map +1 -0
  426. package/dist/recovery/disaster-recovery.js +799 -0
  427. package/dist/recovery/disaster-recovery.js.map +1 -0
  428. package/dist/recovery/index.d.ts +12 -0
  429. package/dist/recovery/index.d.ts.map +1 -0
  430. package/dist/recovery/index.js +12 -0
  431. package/dist/recovery/index.js.map +1 -0
  432. package/dist/recovery/parquet-parser.d.ts +321 -0
  433. package/dist/recovery/parquet-parser.d.ts.map +1 -0
  434. package/dist/recovery/parquet-parser.js +797 -0
  435. package/dist/recovery/parquet-parser.js.map +1 -0
  436. package/dist/retention/index.d.ts +50 -0
  437. package/dist/retention/index.d.ts.map +1 -0
  438. package/dist/retention/index.js +50 -0
  439. package/dist/retention/index.js.map +1 -0
  440. package/dist/retention/policy.d.ts +344 -0
  441. package/dist/retention/policy.d.ts.map +1 -0
  442. package/dist/retention/policy.js +472 -0
  443. package/dist/retention/policy.js.map +1 -0
  444. package/dist/retention/purger.d.ts +187 -0
  445. package/dist/retention/purger.d.ts.map +1 -0
  446. package/dist/retention/purger.js +411 -0
  447. package/dist/retention/purger.js.map +1 -0
  448. package/dist/rls/auth-integration.d.ts +280 -0
  449. package/dist/rls/auth-integration.d.ts.map +1 -0
  450. package/dist/rls/auth-integration.js +399 -0
  451. package/dist/rls/auth-integration.js.map +1 -0
  452. package/dist/rls/generator.d.ts +249 -0
  453. package/dist/rls/generator.d.ts.map +1 -0
  454. package/dist/rls/generator.js +495 -0
  455. package/dist/rls/generator.js.map +1 -0
  456. package/dist/rls/index.d.ts +26 -0
  457. package/dist/rls/index.d.ts.map +1 -0
  458. package/dist/rls/index.js +58 -0
  459. package/dist/rls/index.js.map +1 -0
  460. package/dist/rls/policy.d.ts +116 -0
  461. package/dist/rls/policy.d.ts.map +1 -0
  462. package/dist/rls/policy.js +77 -0
  463. package/dist/rls/policy.js.map +1 -0
  464. package/dist/rls/validator.d.ts +155 -0
  465. package/dist/rls/validator.d.ts.map +1 -0
  466. package/dist/rls/validator.js +792 -0
  467. package/dist/rls/validator.js.map +1 -0
  468. package/dist/routing/adaptive-router.d.ts +317 -0
  469. package/dist/routing/adaptive-router.d.ts.map +1 -0
  470. package/dist/routing/adaptive-router.js +554 -0
  471. package/dist/routing/adaptive-router.js.map +1 -0
  472. package/dist/routing/circuit-breaker.d.ts +339 -0
  473. package/dist/routing/circuit-breaker.d.ts.map +1 -0
  474. package/dist/routing/circuit-breaker.js +620 -0
  475. package/dist/routing/circuit-breaker.js.map +1 -0
  476. package/dist/routing/cost-metrics.d.ts +133 -0
  477. package/dist/routing/cost-metrics.d.ts.map +1 -0
  478. package/dist/routing/cost-metrics.js +259 -0
  479. package/dist/routing/cost-metrics.js.map +1 -0
  480. package/dist/routing/do-connection-pool.d.ts +243 -0
  481. package/dist/routing/do-connection-pool.d.ts.map +1 -0
  482. package/dist/routing/do-connection-pool.js +572 -0
  483. package/dist/routing/do-connection-pool.js.map +1 -0
  484. package/dist/routing/index.d.ts +59 -0
  485. package/dist/routing/index.d.ts.map +1 -0
  486. package/dist/routing/index.js +59 -0
  487. package/dist/routing/index.js.map +1 -0
  488. package/dist/routing/query-complexity-estimator.d.ts +73 -0
  489. package/dist/routing/query-complexity-estimator.d.ts.map +1 -0
  490. package/dist/routing/query-complexity-estimator.js +327 -0
  491. package/dist/routing/query-complexity-estimator.js.map +1 -0
  492. package/dist/routing/request-coalescing.d.ts +178 -0
  493. package/dist/routing/request-coalescing.d.ts.map +1 -0
  494. package/dist/routing/request-coalescing.js +325 -0
  495. package/dist/routing/request-coalescing.js.map +1 -0
  496. package/dist/routing/runtime-router.d.ts +107 -0
  497. package/dist/routing/runtime-router.d.ts.map +1 -0
  498. package/dist/routing/runtime-router.js +246 -0
  499. package/dist/routing/runtime-router.js.map +1 -0
  500. package/dist/routing/tenant-router.d.ts +848 -0
  501. package/dist/routing/tenant-router.d.ts.map +1 -0
  502. package/dist/routing/tenant-router.js +1056 -0
  503. package/dist/routing/tenant-router.js.map +1 -0
  504. package/dist/routing/websocket-pool.d.ts +119 -0
  505. package/dist/routing/websocket-pool.d.ts.map +1 -0
  506. package/dist/routing/websocket-pool.js +436 -0
  507. package/dist/routing/websocket-pool.js.map +1 -0
  508. package/dist/storage/cache-layer.d.ts +159 -0
  509. package/dist/storage/cache-layer.d.ts.map +1 -0
  510. package/dist/storage/cache-layer.js +245 -0
  511. package/dist/storage/cache-layer.js.map +1 -0
  512. package/dist/storage/cost-aware-tiering.d.ts +258 -0
  513. package/dist/storage/cost-aware-tiering.d.ts.map +1 -0
  514. package/dist/storage/cost-aware-tiering.js +526 -0
  515. package/dist/storage/cost-aware-tiering.js.map +1 -0
  516. package/dist/storage/index.d.ts +87 -0
  517. package/dist/storage/index.d.ts.map +1 -0
  518. package/dist/storage/index.js +78 -0
  519. package/dist/storage/index.js.map +1 -0
  520. package/dist/storage/interfaces.d.ts +856 -0
  521. package/dist/storage/interfaces.d.ts.map +1 -0
  522. package/dist/storage/interfaces.js +69 -0
  523. package/dist/storage/interfaces.js.map +1 -0
  524. package/dist/storage/r2-layer.d.ts +226 -0
  525. package/dist/storage/r2-layer.d.ts.map +1 -0
  526. package/dist/storage/r2-layer.js +307 -0
  527. package/dist/storage/r2-layer.js.map +1 -0
  528. package/dist/storage/r2-overflow.d.ts +344 -0
  529. package/dist/storage/r2-overflow.d.ts.map +1 -0
  530. package/dist/storage/r2-overflow.js +730 -0
  531. package/dist/storage/r2-overflow.js.map +1 -0
  532. package/dist/storage/r2-page-vfs.d.ts +374 -0
  533. package/dist/storage/r2-page-vfs.d.ts.map +1 -0
  534. package/dist/storage/r2-page-vfs.js +754 -0
  535. package/dist/storage/r2-page-vfs.js.map +1 -0
  536. package/dist/storage/swr-cache.d.ts +181 -0
  537. package/dist/storage/swr-cache.d.ts.map +1 -0
  538. package/dist/storage/swr-cache.js +295 -0
  539. package/dist/storage/swr-cache.js.map +1 -0
  540. package/dist/storage/tiered-orchestrator.d.ts +951 -0
  541. package/dist/storage/tiered-orchestrator.d.ts.map +1 -0
  542. package/dist/storage/tiered-orchestrator.js +1731 -0
  543. package/dist/storage/tiered-orchestrator.js.map +1 -0
  544. package/dist/storage/tiered-vfs-swr.d.ts +279 -0
  545. package/dist/storage/tiered-vfs-swr.d.ts.map +1 -0
  546. package/dist/storage/tiered-vfs-swr.js +584 -0
  547. package/dist/storage/tiered-vfs-swr.js.map +1 -0
  548. package/dist/storage/tiered-vfs.d.ts +405 -0
  549. package/dist/storage/tiered-vfs.d.ts.map +1 -0
  550. package/dist/storage/tiered-vfs.js +833 -0
  551. package/dist/storage/tiered-vfs.js.map +1 -0
  552. package/dist/streaming/backpressure-controller.d.ts +173 -0
  553. package/dist/streaming/backpressure-controller.d.ts.map +1 -0
  554. package/dist/streaming/backpressure-controller.js +344 -0
  555. package/dist/streaming/backpressure-controller.js.map +1 -0
  556. package/dist/streaming/buffer-pool.d.ts +241 -0
  557. package/dist/streaming/buffer-pool.d.ts.map +1 -0
  558. package/dist/streaming/buffer-pool.js +381 -0
  559. package/dist/streaming/buffer-pool.js.map +1 -0
  560. package/dist/streaming/cdc-iceberg-connector.d.ts +272 -0
  561. package/dist/streaming/cdc-iceberg-connector.d.ts.map +1 -0
  562. package/dist/streaming/cdc-iceberg-connector.js +408 -0
  563. package/dist/streaming/cdc-iceberg-connector.js.map +1 -0
  564. package/dist/streaming/index.d.ts +111 -0
  565. package/dist/streaming/index.d.ts.map +1 -0
  566. package/dist/streaming/index.js +128 -0
  567. package/dist/streaming/index.js.map +1 -0
  568. package/dist/streaming/live-cdc-stream.d.ts +400 -0
  569. package/dist/streaming/live-cdc-stream.d.ts.map +1 -0
  570. package/dist/streaming/live-cdc-stream.js +703 -0
  571. package/dist/streaming/live-cdc-stream.js.map +1 -0
  572. package/dist/streaming/memory-bounded-stream.d.ts +207 -0
  573. package/dist/streaming/memory-bounded-stream.d.ts.map +1 -0
  574. package/dist/streaming/memory-bounded-stream.js +340 -0
  575. package/dist/streaming/memory-bounded-stream.js.map +1 -0
  576. package/dist/streaming/query-streamer.d.ts +379 -0
  577. package/dist/streaming/query-streamer.d.ts.map +1 -0
  578. package/dist/streaming/query-streamer.js +495 -0
  579. package/dist/streaming/query-streamer.js.map +1 -0
  580. package/dist/streaming/response-streaming.d.ts +203 -0
  581. package/dist/streaming/response-streaming.d.ts.map +1 -0
  582. package/dist/streaming/response-streaming.js +449 -0
  583. package/dist/streaming/response-streaming.js.map +1 -0
  584. package/dist/types/branded.d.ts +859 -0
  585. package/dist/types/branded.d.ts.map +1 -0
  586. package/dist/types/branded.js +891 -0
  587. package/dist/types/branded.js.map +1 -0
  588. package/dist/types/utilities.d.ts +757 -0
  589. package/dist/types/utilities.d.ts.map +1 -0
  590. package/dist/types/utilities.js +447 -0
  591. package/dist/types/utilities.js.map +1 -0
  592. package/dist/wal/replay-engine.d.ts +344 -0
  593. package/dist/wal/replay-engine.d.ts.map +1 -0
  594. package/dist/wal/replay-engine.js +975 -0
  595. package/dist/wal/replay-engine.js.map +1 -0
  596. package/dist/worker/__mocks__/capnweb.d.ts +13 -0
  597. package/dist/worker/__mocks__/capnweb.d.ts.map +1 -0
  598. package/dist/worker/__mocks__/capnweb.js +15 -0
  599. package/dist/worker/__mocks__/capnweb.js.map +1 -0
  600. package/dist/worker/__mocks__/cloudflare-workers.d.ts +31 -0
  601. package/dist/worker/__mocks__/cloudflare-workers.d.ts.map +1 -0
  602. package/dist/worker/__mocks__/cloudflare-workers.js +33 -0
  603. package/dist/worker/__mocks__/cloudflare-workers.js.map +1 -0
  604. package/dist/worker/__mocks__/pglite.data.d.ts +3 -0
  605. package/dist/worker/__mocks__/pglite.data.d.ts.map +1 -0
  606. package/dist/worker/__mocks__/pglite.data.js +20 -0
  607. package/dist/worker/__mocks__/pglite.data.js.map +1 -0
  608. package/dist/worker/__mocks__/pglite.wasm.d.ts +3 -0
  609. package/dist/worker/__mocks__/pglite.wasm.d.ts.map +1 -0
  610. package/dist/worker/__mocks__/pglite.wasm.js +30 -0
  611. package/dist/worker/__mocks__/pglite.wasm.js.map +1 -0
  612. package/dist/worker/auth-rate-limiter.d.ts +270 -0
  613. package/dist/worker/auth-rate-limiter.d.ts.map +1 -0
  614. package/dist/worker/auth-rate-limiter.js +332 -0
  615. package/dist/worker/auth-rate-limiter.js.map +1 -0
  616. package/dist/worker/auth.d.ts +345 -0
  617. package/dist/worker/auth.d.ts.map +1 -0
  618. package/dist/worker/auth.js +837 -0
  619. package/dist/worker/auth.js.map +1 -0
  620. package/dist/worker/cdc-backpressure.d.ts +338 -0
  621. package/dist/worker/cdc-backpressure.d.ts.map +1 -0
  622. package/dist/worker/cdc-backpressure.js +619 -0
  623. package/dist/worker/cdc-backpressure.js.map +1 -0
  624. package/dist/worker/cdc-sse.d.ts +277 -0
  625. package/dist/worker/cdc-sse.d.ts.map +1 -0
  626. package/dist/worker/cdc-sse.js +528 -0
  627. package/dist/worker/cdc-sse.js.map +1 -0
  628. package/dist/worker/cdc-websocket.d.ts +252 -0
  629. package/dist/worker/cdc-websocket.d.ts.map +1 -0
  630. package/dist/worker/cdc-websocket.js +940 -0
  631. package/dist/worker/cdc-websocket.js.map +1 -0
  632. package/dist/worker/cdc.d.ts +95 -0
  633. package/dist/worker/cdc.d.ts.map +1 -0
  634. package/dist/worker/cdc.js +211 -0
  635. package/dist/worker/cdc.js.map +1 -0
  636. package/dist/worker/concerns/auth-concern.d.ts +50 -0
  637. package/dist/worker/concerns/auth-concern.d.ts.map +1 -0
  638. package/dist/worker/concerns/auth-concern.js +131 -0
  639. package/dist/worker/concerns/auth-concern.js.map +1 -0
  640. package/dist/worker/concerns/cdc-concern.d.ts +99 -0
  641. package/dist/worker/concerns/cdc-concern.d.ts.map +1 -0
  642. package/dist/worker/concerns/cdc-concern.js +137 -0
  643. package/dist/worker/concerns/cdc-concern.js.map +1 -0
  644. package/dist/worker/concerns/index.d.ts +22 -0
  645. package/dist/worker/concerns/index.d.ts.map +1 -0
  646. package/dist/worker/concerns/index.js +13 -0
  647. package/dist/worker/concerns/index.js.map +1 -0
  648. package/dist/worker/concerns/query-execution-concern.d.ts +104 -0
  649. package/dist/worker/concerns/query-execution-concern.d.ts.map +1 -0
  650. package/dist/worker/concerns/query-execution-concern.js +95 -0
  651. package/dist/worker/concerns/query-execution-concern.js.map +1 -0
  652. package/dist/worker/concerns/storage-orchestration-concern.d.ts +78 -0
  653. package/dist/worker/concerns/storage-orchestration-concern.d.ts.map +1 -0
  654. package/dist/worker/concerns/storage-orchestration-concern.js +240 -0
  655. package/dist/worker/concerns/storage-orchestration-concern.js.map +1 -0
  656. package/dist/worker/do-auth-manager.d.ts +108 -0
  657. package/dist/worker/do-auth-manager.d.ts.map +1 -0
  658. package/dist/worker/do-auth-manager.js +212 -0
  659. package/dist/worker/do-auth-manager.js.map +1 -0
  660. package/dist/worker/do-pglite-manager.d.ts +137 -0
  661. package/dist/worker/do-pglite-manager.d.ts.map +1 -0
  662. package/dist/worker/do-pglite-manager.js +228 -0
  663. package/dist/worker/do-pglite-manager.js.map +1 -0
  664. package/dist/worker/do.d.ts +556 -0
  665. package/dist/worker/do.d.ts.map +1 -0
  666. package/dist/worker/do.js +1441 -0
  667. package/dist/worker/do.js.map +1 -0
  668. package/dist/worker/entry.d.ts +23 -0
  669. package/dist/worker/entry.d.ts.map +1 -0
  670. package/dist/worker/entry.js +362 -0
  671. package/dist/worker/entry.js.map +1 -0
  672. package/dist/worker/errors.d.ts +106 -0
  673. package/dist/worker/errors.d.ts.map +1 -0
  674. package/dist/worker/errors.js +178 -0
  675. package/dist/worker/errors.js.map +1 -0
  676. package/dist/worker/health-check-manager.d.ts +141 -0
  677. package/dist/worker/health-check-manager.d.ts.map +1 -0
  678. package/dist/worker/health-check-manager.js +145 -0
  679. package/dist/worker/health-check-manager.js.map +1 -0
  680. package/dist/worker/index.d.ts +60 -0
  681. package/dist/worker/index.d.ts.map +1 -0
  682. package/dist/worker/index.js +67 -0
  683. package/dist/worker/index.js.map +1 -0
  684. package/dist/worker/memory-pressure.d.ts +892 -0
  685. package/dist/worker/memory-pressure.d.ts.map +1 -0
  686. package/dist/worker/memory-pressure.js +1990 -0
  687. package/dist/worker/memory-pressure.js.map +1 -0
  688. package/dist/worker/migration-manager.d.ts +153 -0
  689. package/dist/worker/migration-manager.d.ts.map +1 -0
  690. package/dist/worker/migration-manager.js +461 -0
  691. package/dist/worker/migration-manager.js.map +1 -0
  692. package/dist/worker/plugin-manager.d.ts +147 -0
  693. package/dist/worker/plugin-manager.d.ts.map +1 -0
  694. package/dist/worker/plugin-manager.js +408 -0
  695. package/dist/worker/plugin-manager.js.map +1 -0
  696. package/dist/worker/proxy.d.ts +330 -0
  697. package/dist/worker/proxy.d.ts.map +1 -0
  698. package/dist/worker/proxy.js +504 -0
  699. package/dist/worker/proxy.js.map +1 -0
  700. package/dist/worker/query-execution-manager.d.ts +107 -0
  701. package/dist/worker/query-execution-manager.d.ts.map +1 -0
  702. package/dist/worker/query-execution-manager.js +155 -0
  703. package/dist/worker/query-execution-manager.js.map +1 -0
  704. package/dist/worker/query-executor.d.ts +163 -0
  705. package/dist/worker/query-executor.d.ts.map +1 -0
  706. package/dist/worker/query-executor.js +413 -0
  707. package/dist/worker/query-executor.js.map +1 -0
  708. package/dist/worker/query-stats-manager.d.ts +117 -0
  709. package/dist/worker/query-stats-manager.d.ts.map +1 -0
  710. package/dist/worker/query-stats-manager.js +162 -0
  711. package/dist/worker/query-stats-manager.js.map +1 -0
  712. package/dist/worker/result-handler.d.ts +192 -0
  713. package/dist/worker/result-handler.d.ts.map +1 -0
  714. package/dist/worker/result-handler.js +346 -0
  715. package/dist/worker/result-handler.js.map +1 -0
  716. package/dist/worker/routes.d.ts +135 -0
  717. package/dist/worker/routes.d.ts.map +1 -0
  718. package/dist/worker/routes.js +460 -0
  719. package/dist/worker/routes.js.map +1 -0
  720. package/dist/worker/rpc-methods-manager.d.ts +142 -0
  721. package/dist/worker/rpc-methods-manager.d.ts.map +1 -0
  722. package/dist/worker/rpc-methods-manager.js +195 -0
  723. package/dist/worker/rpc-methods-manager.js.map +1 -0
  724. package/dist/worker/rpc.d.ts +259 -0
  725. package/dist/worker/rpc.d.ts.map +1 -0
  726. package/dist/worker/rpc.js +398 -0
  727. package/dist/worker/rpc.js.map +1 -0
  728. package/dist/worker/schema-version.d.ts +209 -0
  729. package/dist/worker/schema-version.d.ts.map +1 -0
  730. package/dist/worker/schema-version.js +450 -0
  731. package/dist/worker/schema-version.js.map +1 -0
  732. package/dist/worker/session-manager.d.ts +282 -0
  733. package/dist/worker/session-manager.d.ts.map +1 -0
  734. package/dist/worker/session-manager.js +523 -0
  735. package/dist/worker/session-manager.js.map +1 -0
  736. package/dist/worker/shutdown-manager.d.ts +188 -0
  737. package/dist/worker/shutdown-manager.d.ts.map +1 -0
  738. package/dist/worker/shutdown-manager.js +347 -0
  739. package/dist/worker/shutdown-manager.js.map +1 -0
  740. package/dist/worker/sql-transform.d.ts +61 -0
  741. package/dist/worker/sql-transform.d.ts.map +1 -0
  742. package/dist/worker/sql-transform.js +312 -0
  743. package/dist/worker/sql-transform.js.map +1 -0
  744. package/dist/worker/types.d.ts +738 -0
  745. package/dist/worker/types.d.ts.map +1 -0
  746. package/dist/worker/types.js +6 -0
  747. package/dist/worker/types.js.map +1 -0
  748. package/dist/worker/user-routes.d.ts +76 -0
  749. package/dist/worker/user-routes.d.ts.map +1 -0
  750. package/dist/worker/user-routes.js +188 -0
  751. package/dist/worker/user-routes.js.map +1 -0
  752. package/dist/worker/wal-facade.d.ts +138 -0
  753. package/dist/worker/wal-facade.d.ts.map +1 -0
  754. package/dist/worker/wal-facade.js +184 -0
  755. package/dist/worker/wal-facade.js.map +1 -0
  756. package/dist/worker/wal-r2.d.ts +271 -0
  757. package/dist/worker/wal-r2.d.ts.map +1 -0
  758. package/dist/worker/wal-r2.js +689 -0
  759. package/dist/worker/wal-r2.js.map +1 -0
  760. package/dist/worker/wal-replay.d.ts +361 -0
  761. package/dist/worker/wal-replay.d.ts.map +1 -0
  762. package/dist/worker/wal-replay.js +628 -0
  763. package/dist/worker/wal-replay.js.map +1 -0
  764. package/dist/worker/wal-retention.d.ts +389 -0
  765. package/dist/worker/wal-retention.d.ts.map +1 -0
  766. package/dist/worker/wal-retention.js +763 -0
  767. package/dist/worker/wal-retention.js.map +1 -0
  768. package/dist/worker/wal.d.ts +278 -0
  769. package/dist/worker/wal.d.ts.map +1 -0
  770. package/dist/worker/wal.js +467 -0
  771. package/dist/worker/wal.js.map +1 -0
  772. package/dist/worker/websocket.d.ts +85 -0
  773. package/dist/worker/websocket.d.ts.map +1 -0
  774. package/dist/worker/websocket.js +227 -0
  775. package/dist/worker/websocket.js.map +1 -0
  776. package/package.json +108 -0
  777. package/src/cdc/change-stream.ts +137 -0
  778. package/src/cdc/filter.ts +646 -0
  779. package/src/cdc/index.ts +112 -0
  780. package/src/cdc/resume-token.ts +280 -0
  781. package/src/cdc/transport/index.ts +7 -0
  782. package/src/cdc/transport/sse.ts +723 -0
  783. package/src/cdc/transport/websocket.ts +873 -0
  784. package/src/cdc/types.ts +346 -0
  785. package/src/config/index.ts +25 -0
  786. package/src/config/memory.ts +177 -0
  787. package/src/config/storage.ts +204 -0
  788. package/src/config/streaming.ts +147 -0
  789. package/src/config/timeouts.ts +221 -0
  790. package/src/extensions/config.test.ts +187 -0
  791. package/src/extensions/config.ts +278 -0
  792. package/src/extensions/geo.test.ts +455 -0
  793. package/src/extensions/geo.ts +858 -0
  794. package/src/extensions/index.test.ts +259 -0
  795. package/src/extensions/index.ts +227 -0
  796. package/src/extensions/loader.test.ts +555 -0
  797. package/src/extensions/loader.ts +588 -0
  798. package/src/extensions/pgmq-lite.test.ts +727 -0
  799. package/src/extensions/pgmq-lite.ts +770 -0
  800. package/src/extensions/plugins.test.ts +528 -0
  801. package/src/extensions/plugins.ts +718 -0
  802. package/src/extensions/registry.test.ts +202 -0
  803. package/src/extensions/registry.ts +267 -0
  804. package/src/extensions/vector.test.ts +195 -0
  805. package/src/extensions/vector.ts +217 -0
  806. package/src/iceberg/SCHEDULER.md +580 -0
  807. package/src/iceberg/analytics.test.ts +703 -0
  808. package/src/iceberg/analytics.ts +727 -0
  809. package/src/iceberg/catalog-api.test.ts +838 -0
  810. package/src/iceberg/catalog-api.ts +520 -0
  811. package/src/iceberg/catalog.test.ts +680 -0
  812. package/src/iceberg/catalog.ts +1007 -0
  813. package/src/iceberg/iceberg.test.ts +705 -0
  814. package/src/iceberg/index.ts +406 -0
  815. package/src/iceberg/metadata.test.ts +632 -0
  816. package/src/iceberg/metadata.ts +649 -0
  817. package/src/iceberg/optimizer.test.ts +868 -0
  818. package/src/iceberg/optimizer.ts +1287 -0
  819. package/src/iceberg/parquet.test.ts +899 -0
  820. package/src/iceberg/parquet.ts +1640 -0
  821. package/src/iceberg/r2-organization.test.ts +615 -0
  822. package/src/iceberg/r2-organization.ts +951 -0
  823. package/src/iceberg/scheduler-do-example.ts +364 -0
  824. package/src/iceberg/scheduler.test.ts +861 -0
  825. package/src/iceberg/scheduler.ts +1201 -0
  826. package/src/iceberg/schema.test.ts +547 -0
  827. package/src/iceberg/schema.ts +616 -0
  828. package/src/iceberg/snapshot-manager.test.ts +919 -0
  829. package/src/iceberg/snapshot-manager.ts +1369 -0
  830. package/src/iceberg/sql-router.test.ts +334 -0
  831. package/src/iceberg/sql-router.ts +337 -0
  832. package/src/iceberg/test-fixtures.ts +605 -0
  833. package/src/iceberg/time-travel-api.test.ts +1029 -0
  834. package/src/iceberg/time-travel-api.ts +731 -0
  835. package/src/iceberg/time-travel.test.ts +1218 -0
  836. package/src/iceberg/time-travel.ts +1052 -0
  837. package/src/iceberg/transformer.test.ts +689 -0
  838. package/src/iceberg/transformer.ts +1029 -0
  839. package/src/iceberg/types.ts +373 -0
  840. package/src/iceberg/writer.test.ts +716 -0
  841. package/src/iceberg/writer.ts +590 -0
  842. package/src/index.ts +212 -0
  843. package/src/lineage/index.ts +42 -0
  844. package/src/lineage/integration.ts +334 -0
  845. package/src/lineage/tracker.ts +1618 -0
  846. package/src/lineage/types.ts +354 -0
  847. package/src/middleware/index.ts +36 -0
  848. package/src/middleware/rate-limit-concurrent.test.ts +794 -0
  849. package/src/middleware/rate-limit.test.ts +1568 -0
  850. package/src/middleware/rate-limit.ts +840 -0
  851. package/src/migration-tooling/external-migration.test.ts +1864 -0
  852. package/src/migration-tooling/external-migration.ts +2355 -0
  853. package/src/migration-tooling/index.ts +19 -0
  854. package/src/migrations/ARCHITECTURE.md +474 -0
  855. package/src/migrations/PROGRESS_TRACKING.md +485 -0
  856. package/src/migrations/auto-migrator.test.ts +732 -0
  857. package/src/migrations/auto-migrator.ts +531 -0
  858. package/src/migrations/bulk-orchestrator.test.ts +801 -0
  859. package/src/migrations/bulk-orchestrator.ts +1039 -0
  860. package/src/migrations/compatibility.test.ts +958 -0
  861. package/src/migrations/compatibility.ts +902 -0
  862. package/src/migrations/do-migrations.test.ts +2620 -0
  863. package/src/migrations/do-migrations.ts +1289 -0
  864. package/src/migrations/do-migrations.types.ts +715 -0
  865. package/src/migrations/drizzle-compat.test.ts +210 -0
  866. package/src/migrations/drizzle-compat.ts +337 -0
  867. package/src/migrations/index.ts +334 -0
  868. package/src/migrations/migration-api.test.ts +438 -0
  869. package/src/migrations/migration-api.ts +704 -0
  870. package/src/migrations/progress-tracker-do.ts +518 -0
  871. package/src/migrations/progress-tracker-kv.ts +305 -0
  872. package/src/migrations/progress-tracker.test.ts +937 -0
  873. package/src/migrations/progress-tracker.ts +665 -0
  874. package/src/migrations/registry.test.ts +331 -0
  875. package/src/migrations/registry.ts +468 -0
  876. package/src/migrations/rollback.test.ts +644 -0
  877. package/src/migrations/runner.test.ts +807 -0
  878. package/src/migrations/runner.test.ts.backup +759 -0
  879. package/src/migrations/runner.ts +1459 -0
  880. package/src/migrations/schema-generator.test.ts +649 -0
  881. package/src/migrations/schema-generator.ts +513 -0
  882. package/src/migrations/testing.ts +1037 -0
  883. package/src/migrations/types.ts +573 -0
  884. package/src/migrations/validator.test.ts +660 -0
  885. package/src/migrations/validator.ts +741 -0
  886. package/src/observability/alerting.test.ts +1133 -0
  887. package/src/observability/alerting.ts +455 -0
  888. package/src/observability/analytics-engine.ts +733 -0
  889. package/src/observability/cost-metrics.ts +804 -0
  890. package/src/observability/cross-do-tracing.test.ts +516 -0
  891. package/src/observability/cross-do-tracing.ts +588 -0
  892. package/src/observability/dashboards/postgres-do-overview.json +1656 -0
  893. package/src/observability/error-rate-collector.test.ts +977 -0
  894. package/src/observability/error-rate-collector.ts +518 -0
  895. package/src/observability/exporters.test.ts +365 -0
  896. package/src/observability/exporters.ts +650 -0
  897. package/src/observability/health-check.test.ts +353 -0
  898. package/src/observability/health-check.ts +341 -0
  899. package/src/observability/index.test.ts +298 -0
  900. package/src/observability/index.ts +885 -0
  901. package/src/observability/instrumentation.test.ts +428 -0
  902. package/src/observability/instrumentation.ts +788 -0
  903. package/src/observability/memory-metrics.test.ts +355 -0
  904. package/src/observability/memory-metrics.ts +990 -0
  905. package/src/observability/metrics-endpoint.test.ts +402 -0
  906. package/src/observability/metrics-endpoint.ts +374 -0
  907. package/src/observability/metrics.test.ts +291 -0
  908. package/src/observability/metrics.ts +315 -0
  909. package/src/observability/observability-features.ts +1296 -0
  910. package/src/observability/prometheus.test.ts +292 -0
  911. package/src/observability/prometheus.ts +170 -0
  912. package/src/observability/propagation.test.ts +417 -0
  913. package/src/observability/propagation.ts +294 -0
  914. package/src/observability/query-latency.ts +586 -0
  915. package/src/observability/query-performance.test.ts +406 -0
  916. package/src/observability/query-performance.ts +491 -0
  917. package/src/observability/storage-tier-metrics.test.ts +633 -0
  918. package/src/observability/storage-tier-metrics.ts +570 -0
  919. package/src/observability/tier-cost-optimizer.ts +740 -0
  920. package/src/observability/tracer.test.ts +346 -0
  921. package/src/observability/tracer.ts +585 -0
  922. package/src/observability/types.test.ts +726 -0
  923. package/src/observability/types.ts +434 -0
  924. package/src/pglite/auto-demotion.test.ts +477 -0
  925. package/src/pglite/auto-demotion.ts +385 -0
  926. package/src/pglite/auto-promotion.test.ts +824 -0
  927. package/src/pglite/auto-promotion.ts +547 -0
  928. package/src/pglite/cache-layer.test.ts +469 -0
  929. package/src/pglite/cache-layer.ts +271 -0
  930. package/src/pglite/cold-start-manager.ts +1260 -0
  931. package/src/pglite/cold-start-optimizer.test.ts +937 -0
  932. package/src/pglite/cold-start-optimizer.ts +1895 -0
  933. package/src/pglite/dovfs-adapter.ts +1122 -0
  934. package/src/pglite/dovfs.ts +1258 -0
  935. package/src/pglite/etag-cache.test.ts +844 -0
  936. package/src/pglite/etag-cache.ts +526 -0
  937. package/src/pglite/index.ts +442 -0
  938. package/src/pglite/init.test.ts +455 -0
  939. package/src/pglite/init.ts +574 -0
  940. package/src/pglite/lifecycle.test.ts +599 -0
  941. package/src/pglite/lifecycle.ts +704 -0
  942. package/src/pglite/parallel-loader.test.ts +586 -0
  943. package/src/pglite/parallel-loader.ts +481 -0
  944. package/src/pglite/production-pglite.test.ts +666 -0
  945. package/src/pglite/production-pglite.ts +537 -0
  946. package/src/pglite/query-executor.ts +614 -0
  947. package/src/pglite/r2-layer.test.ts +501 -0
  948. package/src/pglite/r2-layer.ts +322 -0
  949. package/src/pglite/tiered-init.test.ts +725 -0
  950. package/src/pglite/tiered-init.ts +556 -0
  951. package/src/pglite/tiered-vfs.test.ts +726 -0
  952. package/src/pglite/tiered-vfs.ts +33 -0
  953. package/src/pglite/tiering-stats.test.ts +531 -0
  954. package/src/pglite/tiering-stats.ts +407 -0
  955. package/src/pglite/transaction-hooks.ts +343 -0
  956. package/src/pglite/warm-loader.test.ts +1701 -0
  957. package/src/pglite/warm-loader.ts +528 -0
  958. package/src/pglite/workers-pglite.ts +224 -0
  959. package/src/pglite-assets/pglite.data +0 -0
  960. package/src/pglite-assets/pglite.wasm +0 -0
  961. package/src/pglite.d.ts +47 -0
  962. package/src/playground/index.ts +137 -0
  963. package/src/playground/keyboard-shortcuts.ts +677 -0
  964. package/src/playground/playground.ts +323 -0
  965. package/src/playground/query-executor.ts +669 -0
  966. package/src/playground/query-history.ts +328 -0
  967. package/src/playground/result-formatter.ts +420 -0
  968. package/src/playground/sample-datasets.ts +674 -0
  969. package/src/playground/sample-queries.ts +1168 -0
  970. package/src/playground/schema-explorer.ts +558 -0
  971. package/src/playground/types.ts +518 -0
  972. package/src/readonly/cache-reader.test.ts +460 -0
  973. package/src/readonly/cache-reader.ts +313 -0
  974. package/src/readonly/config.test.ts +187 -0
  975. package/src/readonly/config.ts +128 -0
  976. package/src/readonly/index.ts +50 -0
  977. package/src/readonly/pglite-wrapper.test.ts +278 -0
  978. package/src/readonly/pglite-wrapper.ts +184 -0
  979. package/src/readonly/worker.test.ts +533 -0
  980. package/src/readonly/worker.ts +341 -0
  981. package/src/readonly/write-blocker.test.ts +459 -0
  982. package/src/readonly/write-blocker.ts +175 -0
  983. package/src/recovery/disaster-recovery.test.ts +618 -0
  984. package/src/recovery/disaster-recovery.ts +1181 -0
  985. package/src/recovery/index.ts +43 -0
  986. package/src/recovery/parquet-parser.ts +974 -0
  987. package/src/retention/index.ts +74 -0
  988. package/src/retention/policy.test.ts +571 -0
  989. package/src/retention/policy.ts +774 -0
  990. package/src/retention/purger.test.ts +465 -0
  991. package/src/retention/purger.ts +558 -0
  992. package/src/rls/auth-integration.test.ts +752 -0
  993. package/src/rls/auth-integration.ts +533 -0
  994. package/src/rls/generator.test.ts +829 -0
  995. package/src/rls/generator.ts +573 -0
  996. package/src/rls/index.ts +128 -0
  997. package/src/rls/policy.ts +208 -0
  998. package/src/rls/rls.test.ts +1071 -0
  999. package/src/rls/validator.test.ts +930 -0
  1000. package/src/rls/validator.ts +895 -0
  1001. package/src/routing/adaptive-router.test.ts +884 -0
  1002. package/src/routing/adaptive-router.ts +845 -0
  1003. package/src/routing/circuit-breaker.test.ts +1505 -0
  1004. package/src/routing/circuit-breaker.ts +852 -0
  1005. package/src/routing/cost-metrics.test.ts +565 -0
  1006. package/src/routing/cost-metrics.ts +408 -0
  1007. package/src/routing/do-connection-pool.test.ts +1109 -0
  1008. package/src/routing/do-connection-pool.ts +828 -0
  1009. package/src/routing/index.ts +158 -0
  1010. package/src/routing/query-complexity-estimator.test.ts +356 -0
  1011. package/src/routing/query-complexity-estimator.ts +444 -0
  1012. package/src/routing/request-coalescing.test.ts +738 -0
  1013. package/src/routing/request-coalescing.ts +475 -0
  1014. package/src/routing/runtime-router.test.ts +436 -0
  1015. package/src/routing/runtime-router.ts +357 -0
  1016. package/src/routing/tenant-router.test.ts +2493 -0
  1017. package/src/routing/tenant-router.ts +1908 -0
  1018. package/src/routing/websocket-pool.test.ts +551 -0
  1019. package/src/routing/websocket-pool.ts +577 -0
  1020. package/src/storage/access-pattern-tracker.test.ts +874 -0
  1021. package/src/storage/cache-layer.test.ts +560 -0
  1022. package/src/storage/cache-layer.ts +328 -0
  1023. package/src/storage/cost-aware-tiering.test.ts +652 -0
  1024. package/src/storage/cost-aware-tiering.ts +794 -0
  1025. package/src/storage/do-sqlite-blobs.test.ts +937 -0
  1026. package/src/storage/index.ts +272 -0
  1027. package/src/storage/interfaces.ts +974 -0
  1028. package/src/storage/r2-layer.test.ts +653 -0
  1029. package/src/storage/r2-layer.ts +434 -0
  1030. package/src/storage/r2-overflow.ts +920 -0
  1031. package/src/storage/r2-page-vfs.test.ts +2348 -0
  1032. package/src/storage/r2-page-vfs.ts +1054 -0
  1033. package/src/storage/swr-cache.test.ts +832 -0
  1034. package/src/storage/swr-cache.ts +398 -0
  1035. package/src/storage/swr-tiered-integration.test.ts +617 -0
  1036. package/src/storage/tiered-orchestrator.test.ts +2441 -0
  1037. package/src/storage/tiered-orchestrator.ts +2081 -0
  1038. package/src/storage/tiered-vfs-swr.test.ts +736 -0
  1039. package/src/storage/tiered-vfs-swr.ts +735 -0
  1040. package/src/storage/tiered-vfs.test.ts +793 -0
  1041. package/src/storage/tiered-vfs.ts +1082 -0
  1042. package/src/streaming/backpressure-controller.ts +452 -0
  1043. package/src/streaming/buffer-pool.ts +484 -0
  1044. package/src/streaming/cdc-iceberg-connector.ts +605 -0
  1045. package/src/streaming/index.ts +225 -0
  1046. package/src/streaming/live-cdc-stream.ts +985 -0
  1047. package/src/streaming/memory-bounded-stream.ts +443 -0
  1048. package/src/streaming/query-streamer.ts +662 -0
  1049. package/src/streaming/response-streaming.ts +557 -0
  1050. package/src/types/branded.ts +1075 -0
  1051. package/src/types/branded.ts.backup +273 -0
  1052. package/src/types/utilities.ts +1023 -0
  1053. package/src/types/wasm.d.ts +30 -0
  1054. package/src/validation/typed-errors.test.ts +420 -0
  1055. package/src/wal/replay-engine.ts +1264 -0
  1056. package/src/worker/__mocks__/capnweb.ts +15 -0
  1057. package/src/worker/__mocks__/pglite.data.ts +22 -0
  1058. package/src/worker/__mocks__/pglite.wasm.ts +33 -0
  1059. package/src/worker/auth-rate-limiter.test.ts +272 -0
  1060. package/src/worker/auth-rate-limiter.ts +448 -0
  1061. package/src/worker/auth.security-red.test.ts +1236 -0
  1062. package/src/worker/auth.security.test.ts +822 -0
  1063. package/src/worker/auth.test.ts +469 -0
  1064. package/src/worker/auth.ts +1104 -0
  1065. package/src/worker/cdc-backpressure.test.ts +726 -0
  1066. package/src/worker/cdc-backpressure.ts +866 -0
  1067. package/src/worker/cdc-sse.test.ts +780 -0
  1068. package/src/worker/cdc-sse.ts +728 -0
  1069. package/src/worker/cdc-websocket.ts +1229 -0
  1070. package/src/worker/cdc-ws.test.ts +1009 -0
  1071. package/src/worker/cdc.test.ts +327 -0
  1072. package/src/worker/cdc.ts +289 -0
  1073. package/src/worker/concerns/auth-concern.ts +179 -0
  1074. package/src/worker/concerns/cdc-concern.ts +247 -0
  1075. package/src/worker/concerns/index.ts +58 -0
  1076. package/src/worker/concerns/query-execution-concern.ts +194 -0
  1077. package/src/worker/concerns/storage-orchestration-concern.ts +373 -0
  1078. package/src/worker/discriminated-types.test.ts +280 -0
  1079. package/src/worker/do-auth-manager.ts +257 -0
  1080. package/src/worker/do-decomposition.test.ts +1236 -0
  1081. package/src/worker/do-pglite-manager.ts +302 -0
  1082. package/src/worker/do.test.ts +2254 -0
  1083. package/src/worker/do.ts +1878 -0
  1084. package/src/worker/entry.ts +417 -0
  1085. package/src/worker/errors.ts +285 -0
  1086. package/src/worker/health-check-manager.test.ts +261 -0
  1087. package/src/worker/health-check-manager.ts +231 -0
  1088. package/src/worker/index.ts +389 -0
  1089. package/src/worker/memory-pressure.test.ts +1460 -0
  1090. package/src/worker/memory-pressure.ts +2650 -0
  1091. package/src/worker/migration-manager.ts +582 -0
  1092. package/src/worker/neon-compat.test.ts +332 -0
  1093. package/src/worker/plugin-manager.ts +485 -0
  1094. package/src/worker/postgres.do-rpc.d.ts +76 -0
  1095. package/src/worker/proxy.ts +694 -0
  1096. package/src/worker/query-execution-manager.test.ts +303 -0
  1097. package/src/worker/query-execution-manager.ts +219 -0
  1098. package/src/worker/query-executor.test.ts +282 -0
  1099. package/src/worker/query-executor.ts +560 -0
  1100. package/src/worker/query-stats-manager.ts +229 -0
  1101. package/src/worker/result-handler.test.ts +364 -0
  1102. package/src/worker/result-handler.ts +510 -0
  1103. package/src/worker/routes.test.ts +795 -0
  1104. package/src/worker/routes.ts +650 -0
  1105. package/src/worker/rpc-methods-manager.test.ts +326 -0
  1106. package/src/worker/rpc-methods-manager.ts +276 -0
  1107. package/src/worker/rpc.ts +524 -0
  1108. package/src/worker/schema-version.ts +605 -0
  1109. package/src/worker/session-manager.test.ts +506 -0
  1110. package/src/worker/session-manager.ts +732 -0
  1111. package/src/worker/shutdown-manager.ts +469 -0
  1112. package/src/worker/sql-transform.test.ts +286 -0
  1113. package/src/worker/sql-transform.ts +368 -0
  1114. package/src/worker/supabase-compat.test.ts +621 -0
  1115. package/src/worker/types.test.ts +292 -0
  1116. package/src/worker/types.ts +873 -0
  1117. package/src/worker/user-routes.test.ts +703 -0
  1118. package/src/worker/user-routes.ts +303 -0
  1119. package/src/worker/wal-facade.ts +235 -0
  1120. package/src/worker/wal-r2.test.ts +570 -0
  1121. package/src/worker/wal-r2.ts +930 -0
  1122. package/src/worker/wal-replay.test.ts +845 -0
  1123. package/src/worker/wal-replay.ts +897 -0
  1124. package/src/worker/wal-retention.test.ts +758 -0
  1125. package/src/worker/wal-retention.ts +1075 -0
  1126. package/src/worker/wal.test.ts +618 -0
  1127. package/src/worker/wal.ts +697 -0
  1128. package/src/worker/websocket.test.ts +296 -0
  1129. package/src/worker/websocket.ts +284 -0
@@ -0,0 +1,1908 @@
1
+ /**
2
+ * Hostname-based Multi-tenant DO Routing
3
+ *
4
+ * Purpose: Route incoming requests to the correct Durable Object based on
5
+ * tenant identification extracted from hostname, path, or headers.
6
+ *
7
+ * This module provides a flexible tenant routing system that supports:
8
+ * - Subdomain-based tenancy (tenant1.app.com)
9
+ * - Path-based tenancy (/tenant1/api/...)
10
+ * - Header-based tenancy (X-Tenant-ID)
11
+ * - Custom extraction functions
12
+ * - Blocked tenant lists
13
+ * - Tenant isolation guarantees
14
+ */
15
+
16
+ // =============================================================================
17
+ // Constants
18
+ // =============================================================================
19
+
20
+ /**
21
+ * Standard HTTP header names used by the tenant router
22
+ */
23
+ export const HEADERS = {
24
+ /** Header containing the tenant identifier */
25
+ TENANT_ID: 'X-Tenant-ID',
26
+ /** Header containing the correlation/request ID */
27
+ CORRELATION_ID: 'X-Correlation-ID',
28
+ /** Header containing the trace ID for distributed tracing */
29
+ TRACE_ID: 'X-Trace-ID',
30
+ /** Header containing the span ID for distributed tracing */
31
+ SPAN_ID: 'X-Span-ID',
32
+ /** Header containing the parent span ID for distributed tracing */
33
+ PARENT_SPAN_ID: 'X-Parent-Span-ID',
34
+ /** Header indicating whether the trace is sampled */
35
+ TRACE_SAMPLED: 'X-Trace-Sampled',
36
+ } as const
37
+
38
+ export type HeaderName = (typeof HEADERS)[keyof typeof HEADERS]
39
+
40
+ export const DEFAULT_HEADER_NAME = HEADERS.TENANT_ID
41
+ export const DEFAULT_PATH_PREFIX = '/'
42
+ export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000
43
+
44
+ // Tenant ID validation constants
45
+ const TENANT_ID_MAX_LENGTH = 128
46
+ // Must start with alphanumeric, followed by alphanumeric, underscore, or hyphen
47
+ const TENANT_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/
48
+
49
+ /**
50
+ * Normalize a path prefix to ensure consistent format.
51
+ * - Ensures path starts with '/'
52
+ * - Removes trailing '/' (except for root '/')
53
+ *
54
+ * @param prefix The path prefix to normalize
55
+ * @returns Normalized path prefix
56
+ */
57
+ function normalizePathPrefix(prefix: string): string {
58
+ let normalized = prefix
59
+ if (!normalized.startsWith('/')) {
60
+ normalized = '/' + normalized
61
+ }
62
+ if (normalized.endsWith('/') && normalized.length > 1) {
63
+ normalized = normalized.slice(0, -1)
64
+ }
65
+ return normalized
66
+ }
67
+
68
+ /**
69
+ * Validate that a tenant ID is safe and well-formed.
70
+ *
71
+ * Security requirements:
72
+ * - Non-empty and within length limits
73
+ * - Starts with alphanumeric character
74
+ * - Contains only alphanumeric, underscore, or hyphen
75
+ * - No control characters (including null bytes)
76
+ * - No path traversal sequences
77
+ *
78
+ * @param tenantId The tenant ID to validate
79
+ * @returns true if the tenant ID is valid, false otherwise
80
+ */
81
+ export function isValidTenantId(tenantId: string): boolean {
82
+ // Check length bounds
83
+ if (tenantId.length === 0 || tenantId.length > TENANT_ID_MAX_LENGTH) {
84
+ return false
85
+ }
86
+
87
+ // Check pattern (alphanumeric start, alphanumeric/underscore/hyphen continuation)
88
+ if (!TENANT_ID_PATTERN.test(tenantId)) {
89
+ return false
90
+ }
91
+
92
+ // Check for control characters (ASCII 0-31 and 127)
93
+ for (let i = 0; i < tenantId.length; i++) {
94
+ const charCode = tenantId.charCodeAt(i)
95
+ if (charCode < 32 || charCode === 127) {
96
+ return false
97
+ }
98
+ }
99
+
100
+ return true
101
+ }
102
+
103
+ // =============================================================================
104
+ // Core Types
105
+ // =============================================================================
106
+
107
+ /**
108
+ * Source of tenant identification
109
+ */
110
+ export type TenantExtractionSource = 'subdomain' | 'path' | 'header' | 'custom'
111
+
112
+ /**
113
+ * Result of tenant extraction from a request
114
+ */
115
+ export interface TenantExtractionResult {
116
+ /** The extracted tenant identifier, or null if extraction failed */
117
+ tenantId: string | null
118
+ /** How the tenant was identified */
119
+ source: TenantExtractionSource
120
+ /** Original hostname from the request */
121
+ originalHostname: string
122
+ /** Modified path (with tenant prefix stripped for path-based extraction) */
123
+ modifiedPath?: string | undefined
124
+ /** Additional metadata from custom extractors */
125
+ metadata?: Record<string, unknown> | undefined
126
+ }
127
+
128
+ /**
129
+ * Routing metadata about how the tenant was resolved
130
+ */
131
+ export interface RoutingMetadata {
132
+ /** Which extractor type was used */
133
+ extractorType: TenantExtractionSource
134
+ /** Whether the tenant was found in cache (if caching is enabled) */
135
+ cached?: boolean
136
+ /** The DO ID that was generated */
137
+ doId: string
138
+ }
139
+
140
+ /**
141
+ * Timing information for the routing operation
142
+ */
143
+ export interface RoutingTiming {
144
+ /** Time taken to extract tenant from request (ms) */
145
+ extractionTimeMs: number
146
+ /** Time taken to route to DO (ms) */
147
+ routingTimeMs: number
148
+ /** Total time for the entire operation (ms) */
149
+ totalTimeMs: number
150
+ /** Timestamp when routing started */
151
+ startTimestamp: number
152
+ }
153
+
154
+ /**
155
+ * Tenant context information
156
+ */
157
+ export interface TenantContext {
158
+ /** The tenant identifier */
159
+ tenantId: string
160
+ /** How the tenant was identified */
161
+ source: TenantExtractionSource
162
+ /** Additional metadata from extraction */
163
+ metadata?: Record<string, unknown> | undefined
164
+ }
165
+
166
+ /**
167
+ * Result of routing a request to a tenant's DO
168
+ */
169
+ export interface TenantRoutingResult {
170
+ /** The response from the DO */
171
+ response: Response
172
+ /** Routing metadata about how tenant was resolved */
173
+ routingMetadata: RoutingMetadata
174
+ /** Timing information for the operation */
175
+ timing: RoutingTiming
176
+ /** Tenant context information */
177
+ tenantContext: TenantContext
178
+ }
179
+
180
+ /**
181
+ * Function signature for custom tenant extraction
182
+ */
183
+ export type TenantExtractor = (
184
+ request: Request
185
+ ) => TenantExtractionResult | Promise<TenantExtractionResult>
186
+
187
+ /**
188
+ * Simplified extractor that just returns tenant ID or null
189
+ */
190
+ export type SimpleTenantExtractor = (request: Request) => string | null | Promise<string | null>
191
+
192
+ /**
193
+ * Preset extractor types
194
+ */
195
+ export type PresetExtractorType = 'subdomain' | 'path' | 'header'
196
+
197
+ /**
198
+ * Callback for dynamic blocked tenant checks
199
+ */
200
+ export type BlockedTenantCallback = (
201
+ tenantId: string,
202
+ request: Request
203
+ ) => boolean | Promise<boolean>
204
+
205
+ /**
206
+ * Function to transform tenant IDs before routing
207
+ */
208
+ export type TenantTransformer = (tenantId: string) => string
209
+
210
+ /**
211
+ * Custom error response formatter
212
+ */
213
+ export type ErrorResponseFormatter = (
214
+ status: number,
215
+ message: string,
216
+ correlationId: string,
217
+ tenantId?: string
218
+ ) => Response
219
+
220
+ // =============================================================================
221
+ // Observability Types
222
+ // =============================================================================
223
+
224
+ /**
225
+ * Log levels for structured logging
226
+ */
227
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
228
+
229
+ /**
230
+ * Structured log entry
231
+ */
232
+ export interface LogEntry {
233
+ level: LogLevel
234
+ message: string
235
+ tenantId?: string
236
+ correlationId?: string
237
+ timestamp: number
238
+ data?: Record<string, unknown>
239
+ }
240
+
241
+ /**
242
+ * Logger interface for observability
243
+ */
244
+ export interface TenantRouterLogger {
245
+ log(entry: LogEntry): void
246
+ debug(message: string, data?: Record<string, unknown>): void
247
+ info(message: string, data?: Record<string, unknown>): void
248
+ warn(message: string, data?: Record<string, unknown>): void
249
+ error(message: string, data?: Record<string, unknown>): void
250
+ }
251
+
252
+ /**
253
+ * Metrics data for a single request
254
+ */
255
+ export interface RequestMetrics {
256
+ tenantId: string
257
+ method: string
258
+ path: string
259
+ status: number
260
+ routingLatencyMs: number
261
+ doResponseTimeMs: number
262
+ timestamp: number
263
+ correlationId: string
264
+ traceId?: string | undefined
265
+ spanId?: string | undefined
266
+ }
267
+
268
+ /**
269
+ * Metrics collector interface for observability
270
+ */
271
+ export interface MetricsCollector {
272
+ /**
273
+ * Record metrics for a routed request
274
+ */
275
+ recordRequest(metrics: RequestMetrics): void
276
+
277
+ /**
278
+ * Increment request count for a tenant
279
+ */
280
+ incrementRequestCount(tenantId: string): void
281
+
282
+ /**
283
+ * Record routing latency
284
+ */
285
+ recordLatency(tenantId: string, latencyMs: number): void
286
+
287
+ /**
288
+ * Record DO response time
289
+ */
290
+ recordDoResponseTime(tenantId: string, responseTimeMs: number): void
291
+ }
292
+
293
+ /**
294
+ * Trace context for distributed tracing
295
+ */
296
+ export interface TraceContext {
297
+ traceId: string
298
+ spanId: string
299
+ parentSpanId?: string
300
+ sampled?: boolean
301
+ }
302
+
303
+ /**
304
+ * Function to extract trace context from request
305
+ */
306
+ export type TraceContextExtractor = (request: Request) => TraceContext | null
307
+
308
+ /**
309
+ * Function to generate a new span ID
310
+ */
311
+ export type SpanIdGenerator = () => string
312
+
313
+ // =============================================================================
314
+ // Configuration Sub-interfaces
315
+ // =============================================================================
316
+
317
+ /**
318
+ * Core routing configuration
319
+ */
320
+ export interface RoutingConfig {
321
+ /** The Durable Object namespace to route requests to */
322
+ doNamespace: DurableObjectNamespace
323
+
324
+ /**
325
+ * How to extract tenant from requests:
326
+ * - 'subdomain': Extract from hostname subdomain
327
+ * - 'path': Extract from URL path
328
+ * - 'header': Extract from HTTP header
329
+ * - TenantExtractor: Custom extraction function
330
+ */
331
+ extractTenant: TenantExtractor | SimpleTenantExtractor | PresetExtractorType
332
+
333
+ /**
334
+ * Base domain for subdomain extraction (e.g., 'app.com')
335
+ * Required for proper subdomain extraction with multi-level TLDs
336
+ */
337
+ baseDomain?: string
338
+
339
+ /**
340
+ * Path prefix for path-based extraction
341
+ * e.g., '/orgs' would extract from /orgs/{tenant}/...
342
+ * @default '/'
343
+ */
344
+ pathPrefix?: string
345
+
346
+ /**
347
+ * Header name for header-based extraction
348
+ * @default 'X-Tenant-ID'
349
+ */
350
+ headerName?: string
351
+
352
+ /**
353
+ * Request timeout in milliseconds
354
+ * @default 30000
355
+ */
356
+ requestTimeoutMs?: number
357
+
358
+ /**
359
+ * List of blocked tenant identifiers
360
+ * Supports exact matches and wildcard patterns (e.g., 'admin*')
361
+ */
362
+ blockedTenants?: string[]
363
+
364
+ /**
365
+ * Dynamic callback for checking if a tenant is blocked
366
+ */
367
+ blockedTenantCallback?: BlockedTenantCallback
368
+
369
+ /**
370
+ * Custom error response formatter
371
+ */
372
+ errorResponseFormatter?: ErrorResponseFormatter
373
+ }
374
+
375
+ /**
376
+ * Observability configuration
377
+ */
378
+ export interface ObservabilityConfig {
379
+ /** Metrics collector for recording request metrics */
380
+ metricsCollector?: MetricsCollector
381
+
382
+ /** Logger for structured logging */
383
+ logger?: TenantRouterLogger
384
+
385
+ /** Function to extract trace context from incoming requests */
386
+ traceContextExtractor?: TraceContextExtractor
387
+
388
+ /**
389
+ * Whether to propagate trace context to forwarded requests
390
+ * @default true
391
+ */
392
+ propagateTraceContext?: boolean
393
+ }
394
+
395
+ /**
396
+ * Rate limiting configuration
397
+ */
398
+ export interface RateLimitingConfig {
399
+ /**
400
+ * Enable rate limiting
401
+ * @default false
402
+ */
403
+ enabled?: boolean
404
+
405
+ /**
406
+ * Maximum requests allowed within the window
407
+ */
408
+ maxRequests?: number
409
+
410
+ /**
411
+ * Time window in milliseconds
412
+ * @default 60000 (1 minute)
413
+ */
414
+ windowMs?: number
415
+
416
+ /**
417
+ * List of tenant IDs that bypass rate limiting
418
+ */
419
+ bypassTenants?: string[]
420
+
421
+ /**
422
+ * Dynamic callback for checking if a tenant should bypass rate limiting
423
+ */
424
+ bypassCallback?: (tenantId: string, request: Request) => boolean | Promise<boolean>
425
+ }
426
+
427
+ /**
428
+ * Domain caching configuration
429
+ */
430
+ export interface DomainCachingConfig {
431
+ /**
432
+ * Enable domain mapping cache
433
+ * @default false
434
+ */
435
+ enabled?: boolean
436
+
437
+ /**
438
+ * Cache TTL in milliseconds
439
+ * @default 300000 (5 minutes)
440
+ */
441
+ ttlMs?: number
442
+
443
+ /**
444
+ * Maximum number of cache entries
445
+ * @default 10000
446
+ */
447
+ maxEntries?: number
448
+ }
449
+
450
+ /**
451
+ * Complete tenant router configuration
452
+ * Extends RoutingConfig with nested sub-configs for better organization
453
+ */
454
+ export interface TenantRouterConfig extends RoutingConfig {
455
+ /**
456
+ * Whether to strip the tenant segment from the forwarded path
457
+ * @default true
458
+ */
459
+ stripTenantFromPath?: boolean
460
+
461
+ /**
462
+ * Transform tenant ID before routing (e.g., lowercase normalization)
463
+ */
464
+ transformTenant?: TenantTransformer
465
+
466
+ /**
467
+ * Ordered list of extraction strategies to try
468
+ * First successful extraction wins
469
+ */
470
+ extractionOrder?: PresetExtractorType[]
471
+
472
+ /** Observability settings */
473
+ observability?: ObservabilityConfig
474
+
475
+ /** Rate limiting settings */
476
+ rateLimit?: RateLimitingConfig
477
+
478
+ /** Domain caching settings */
479
+ domainCache?: DomainCachingConfig
480
+
481
+ // =============================================================================
482
+ // Legacy flat config options (deprecated, use nested configs instead)
483
+ // =============================================================================
484
+
485
+ /**
486
+ * @deprecated Use blockedTenantCallback instead
487
+ * Dynamic callback for checking if a tenant is blocked
488
+ */
489
+ isBlocked?: BlockedTenantCallback
490
+
491
+ /**
492
+ * @deprecated Use errorResponseFormatter instead
493
+ * Custom error response formatter
494
+ */
495
+ formatError?: ErrorResponseFormatter
496
+
497
+ /**
498
+ * @deprecated Use domainCache.ttlMs instead
499
+ * Cache TTL for custom domain mappings in milliseconds
500
+ * @default 300000 (5 minutes)
501
+ */
502
+ domainCacheTtlMs?: number
503
+
504
+ /**
505
+ * @deprecated Use rateLimit.maxRequests instead (with windowMs: 60000)
506
+ * Per-tenant rate limit (requests per minute)
507
+ * Set to 0 to disable rate limiting
508
+ * @default 0
509
+ */
510
+ rateLimitPerMinute?: number
511
+
512
+ /**
513
+ * @deprecated Use observability.metricsCollector instead
514
+ * Metrics collector for recording request metrics
515
+ */
516
+ metricsCollector?: MetricsCollector
517
+
518
+ /**
519
+ * @deprecated Use observability.logger instead
520
+ * Logger for structured logging
521
+ */
522
+ logger?: TenantRouterLogger
523
+
524
+ /**
525
+ * @deprecated Use observability.traceContextExtractor instead
526
+ * Function to extract trace context from incoming requests
527
+ */
528
+ traceContextExtractor?: TraceContextExtractor
529
+
530
+ /**
531
+ * @deprecated Use observability.propagateTraceContext instead
532
+ * Whether to propagate trace context to forwarded requests
533
+ * @default true
534
+ */
535
+ propagateTraceContext?: boolean
536
+ }
537
+
538
+ // =============================================================================
539
+ // Router Interface
540
+ // =============================================================================
541
+
542
+ /**
543
+ * The TenantRouter interface for routing requests to tenant-specific DOs
544
+ */
545
+ export interface TenantRouter {
546
+ /**
547
+ * Route a request to the appropriate tenant's Durable Object
548
+ * @param request The incoming request
549
+ * @returns Response from the tenant's DO
550
+ *
551
+ * @example
552
+ * ```typescript
553
+ * export default {
554
+ * async fetch(request: Request, env: Env) {
555
+ * const router = createTenantRouter({
556
+ * doNamespace: env.TENANT_DO,
557
+ * extractTenant: 'subdomain',
558
+ * baseDomain: 'myapp.com',
559
+ * })
560
+ *
561
+ * // Route request to tenant's DO
562
+ * return router.route(request)
563
+ * }
564
+ * }
565
+ * ```
566
+ */
567
+ route(request: Request): Promise<Response>
568
+
569
+ /**
570
+ * Route a request and return detailed routing result
571
+ * @param request The incoming request
572
+ * @returns Full routing result with response, metadata, timing, and context
573
+ *
574
+ * @example
575
+ * ```typescript
576
+ * const result = await router.routeWithResult(request)
577
+ *
578
+ * console.log(`Tenant: ${result.tenantContext.tenantId}`)
579
+ * console.log(`Source: ${result.routingMetadata.extractorType}`)
580
+ * console.log(`Total time: ${result.timing.totalTimeMs}ms`)
581
+ *
582
+ * return result.response
583
+ * ```
584
+ */
585
+ routeWithResult(request: Request): Promise<TenantRoutingResult>
586
+
587
+ /**
588
+ * Extract tenant ID from a request without routing
589
+ * @param request The incoming request
590
+ * @returns The tenant ID or null if not found
591
+ *
592
+ * @example
593
+ * ```typescript
594
+ * const tenantId = await router.getTenantId(request)
595
+ * if (!tenantId) {
596
+ * return new Response('Tenant not found', { status: 400 })
597
+ * }
598
+ * console.log(`Processing request for tenant: ${tenantId}`)
599
+ * ```
600
+ */
601
+ getTenantId(request: Request): Promise<string | null>
602
+
603
+ /**
604
+ * Get full extraction result from a request
605
+ * @param request The incoming request
606
+ * @returns The extraction result with metadata
607
+ */
608
+ extractTenant(request: Request): Promise<TenantExtractionResult>
609
+
610
+ /**
611
+ * Check if a tenant is blocked
612
+ * @param tenantId The tenant identifier
613
+ * @param request The original request (for context in dynamic checks)
614
+ * @returns True if the tenant is blocked
615
+ */
616
+ isBlocked(tenantId: string, request: Request): Promise<boolean>
617
+
618
+ /**
619
+ * Get the DO stub for a specific tenant
620
+ * @param tenantId The tenant identifier
621
+ * @returns The Durable Object stub
622
+ *
623
+ * @example
624
+ * ```typescript
625
+ * const stub = router.getStub('acme-corp')
626
+ *
627
+ * // Make a custom request to the tenant's DO
628
+ * const response = await stub.fetch('https://internal/api/health')
629
+ * ```
630
+ */
631
+ getStub(tenantId: string): DurableObjectStub
632
+ }
633
+
634
+ // =============================================================================
635
+ // Preset Extractors
636
+ // =============================================================================
637
+
638
+ /**
639
+ * Preset tenant extraction functions
640
+ *
641
+ * @example
642
+ * ```typescript
643
+ * // Use preset extractors with createTenantRouter
644
+ * const router = createTenantRouter({
645
+ * doNamespace: env.TENANT_DO,
646
+ * extractTenant: 'subdomain', // Uses extractors.subdomain internally
647
+ * baseDomain: 'myapp.com',
648
+ * })
649
+ *
650
+ * // Or use extractors directly
651
+ * const subdomainExtractor = extractors.subdomain('myapp.com')
652
+ * const tenantId = subdomainExtractor(request) // 'acme' from acme.myapp.com
653
+ * ```
654
+ */
655
+ export const extractors = {
656
+ /**
657
+ * Extract tenant from subdomain
658
+ * e.g., tenant1.app.com -> tenant1
659
+ *
660
+ * @param baseDomain Optional base domain for proper TLD handling
661
+ *
662
+ * @example
663
+ * ```typescript
664
+ * const extractor = extractors.subdomain('myapp.com')
665
+ *
666
+ * // acme.myapp.com -> 'acme'
667
+ * // www.myapp.com -> null (apex domain)
668
+ * // myapp.com -> null (apex domain)
669
+ * ```
670
+ */
671
+ subdomain:
672
+ (baseDomain?: string): SimpleTenantExtractor =>
673
+ (request: Request): string | null => {
674
+ const url = new URL(request.url)
675
+ let hostname = url.hostname.toLowerCase()
676
+
677
+ // Handle IP addresses - return null
678
+ if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname)) {
679
+ return null
680
+ }
681
+
682
+ // Remove port if present (for localhost:8787 cases)
683
+ hostname = hostname.split(':')[0] ?? hostname
684
+
685
+ // If baseDomain is provided, use it to determine subdomain
686
+ if (baseDomain) {
687
+ const normalizedBaseDomain = baseDomain.toLowerCase()
688
+ // Check if hostname ends with the base domain
689
+ if (hostname === normalizedBaseDomain) {
690
+ // This is the apex domain
691
+ return null
692
+ }
693
+ if (hostname.endsWith('.' + normalizedBaseDomain)) {
694
+ // Extract subdomain part
695
+ const subdomainPart = hostname.slice(0, -(normalizedBaseDomain.length + 1))
696
+ const segments = subdomainPart.split('.')
697
+ // Handle www prefix
698
+ let firstSegment = segments[0]
699
+ if (firstSegment === 'www' && segments.length > 1) {
700
+ firstSegment = segments[1]
701
+ }
702
+ return firstSegment || null
703
+ }
704
+ // Hostname doesn't match base domain
705
+ return null
706
+ }
707
+
708
+ // Without baseDomain, parse generically
709
+ const parts = hostname.split('.')
710
+
711
+ // Handle localhost
712
+ if (parts.length === 1) {
713
+ return null // Just "localhost" or similar
714
+ }
715
+
716
+ // Handle common patterns
717
+ // For two-part domains like "app.com", return null (apex)
718
+ if (parts.length === 2) {
719
+ return null
720
+ }
721
+
722
+ // For three or more parts, first segment is tenant (unless it's www)
723
+ let tenantSegment = parts[0]
724
+ if (tenantSegment === 'www' && parts.length > 3) {
725
+ tenantSegment = parts[1]
726
+ } else if (tenantSegment === 'www') {
727
+ return null // www.app.com is still apex
728
+ }
729
+
730
+ return tenantSegment || null
731
+ },
732
+
733
+ /**
734
+ * Extract tenant from URL path
735
+ * e.g., /tenant1/api/users -> tenant1
736
+ *
737
+ * @param prefix Path prefix before tenant segment
738
+ *
739
+ * @example
740
+ * ```typescript
741
+ * const extractor = extractors.path('/orgs')
742
+ *
743
+ * // /orgs/acme/users -> 'acme'
744
+ * // /orgs/bigcorp/api/data -> 'bigcorp'
745
+ * // /api/users -> null (no match)
746
+ * ```
747
+ */
748
+ path:
749
+ (prefix: string = '/'): SimpleTenantExtractor =>
750
+ (request: Request): string | null => {
751
+ const url = new URL(request.url)
752
+ let pathname = url.pathname
753
+
754
+ const normalizedPrefix = normalizePathPrefix(prefix)
755
+
756
+ // Check if pathname starts with the prefix
757
+ if (normalizedPrefix !== '/' && !pathname.startsWith(normalizedPrefix)) {
758
+ return null
759
+ }
760
+
761
+ // Get the path after the prefix
762
+ let pathAfterPrefix: string
763
+ if (normalizedPrefix === '/') {
764
+ pathAfterPrefix = pathname
765
+ } else {
766
+ pathAfterPrefix = pathname.slice(normalizedPrefix.length)
767
+ }
768
+
769
+ // pathAfterPrefix should now start with / or be empty
770
+ if (!pathAfterPrefix || pathAfterPrefix === '/') {
771
+ return null
772
+ }
773
+
774
+ // Remove leading slash
775
+ if (pathAfterPrefix.startsWith('/')) {
776
+ pathAfterPrefix = pathAfterPrefix.slice(1)
777
+ }
778
+
779
+ // Get the first segment (tenant)
780
+ const slashIndex = pathAfterPrefix.indexOf('/')
781
+ let tenantSegment: string
782
+ if (slashIndex === -1) {
783
+ tenantSegment = pathAfterPrefix
784
+ } else {
785
+ tenantSegment = pathAfterPrefix.slice(0, slashIndex)
786
+ }
787
+
788
+ // Decode URL encoding
789
+ try {
790
+ tenantSegment = decodeURIComponent(tenantSegment)
791
+ } catch {
792
+ // If decoding fails, use as-is
793
+ }
794
+
795
+ // Return null for empty string
796
+ if (!tenantSegment) {
797
+ return null
798
+ }
799
+
800
+ return tenantSegment
801
+ },
802
+
803
+ /**
804
+ * Extract tenant from HTTP header
805
+ * e.g., X-Tenant-ID: tenant1 -> tenant1
806
+ *
807
+ * @param headerName The header to extract from
808
+ *
809
+ * @example
810
+ * ```typescript
811
+ * const extractor = extractors.header('X-Org-ID')
812
+ *
813
+ * // Request with header 'X-Org-ID: acme' -> 'acme'
814
+ * // Request without header -> null
815
+ * ```
816
+ */
817
+ header:
818
+ (headerName: string = DEFAULT_HEADER_NAME): SimpleTenantExtractor =>
819
+ (request: Request): string | null => {
820
+ // Headers.get() is case-insensitive per HTTP spec
821
+ const value = request.headers.get(headerName)
822
+
823
+ if (value === null) {
824
+ return null
825
+ }
826
+
827
+ // Trim whitespace
828
+ const trimmed = value.trim()
829
+
830
+ // Return null for empty string
831
+ if (!trimmed) {
832
+ return null
833
+ }
834
+
835
+ return trimmed
836
+ },
837
+
838
+ /**
839
+ * Try multiple extractors in order
840
+ * Returns first successful extraction
841
+ *
842
+ * @param extractorFns Array of extractors to try
843
+ *
844
+ * @example
845
+ * ```typescript
846
+ * // Try subdomain first, then fall back to header
847
+ * const extractor = extractors.combined([
848
+ * extractors.subdomain('myapp.com'),
849
+ * extractors.header('X-Tenant-ID'),
850
+ * ])
851
+ *
852
+ * // acme.myapp.com -> 'acme' (from subdomain)
853
+ * // myapp.com with X-Tenant-ID: bigcorp -> 'bigcorp' (from header)
854
+ * ```
855
+ */
856
+ combined:
857
+ (extractorFns: SimpleTenantExtractor[]): SimpleTenantExtractor =>
858
+ async (request: Request): Promise<string | null> => {
859
+ for (const extractor of extractorFns) {
860
+ const result = await extractor(request)
861
+ if (result !== null) {
862
+ return result
863
+ }
864
+ }
865
+ return null
866
+ },
867
+ }
868
+
869
+ // =============================================================================
870
+ // Factory Function
871
+ // =============================================================================
872
+
873
+ /**
874
+ * Create a new TenantRouter instance
875
+ *
876
+ * @param config Router configuration
877
+ * @returns A configured TenantRouter
878
+ *
879
+ * @example
880
+ * ```typescript
881
+ * // Subdomain-based routing
882
+ * const router = createTenantRouter({
883
+ * doNamespace: env.TENANT_DO,
884
+ * extractTenant: 'subdomain',
885
+ * baseDomain: 'myapp.com',
886
+ * })
887
+ *
888
+ * // Path-based routing
889
+ * const router = createTenantRouter({
890
+ * doNamespace: env.TENANT_DO,
891
+ * extractTenant: 'path',
892
+ * pathPrefix: '/api/tenants',
893
+ * })
894
+ *
895
+ * // Header-based routing
896
+ * const router = createTenantRouter({
897
+ * doNamespace: env.TENANT_DO,
898
+ * extractTenant: 'header',
899
+ * headerName: 'X-Org-ID',
900
+ * })
901
+ *
902
+ * // Custom extractor
903
+ * const router = createTenantRouter({
904
+ * doNamespace: env.TENANT_DO,
905
+ * extractTenant: (req) => {
906
+ * const url = new URL(req.url)
907
+ * return url.searchParams.get('tenant')
908
+ * },
909
+ * })
910
+ * ```
911
+ */
912
+ export function createTenantRouter(config: TenantRouterConfig): TenantRouter {
913
+ const {
914
+ doNamespace,
915
+ extractTenant,
916
+ headerName = DEFAULT_HEADER_NAME,
917
+ pathPrefix = DEFAULT_PATH_PREFIX,
918
+ stripTenantFromPath = true,
919
+ blockedTenants = [],
920
+ blockedTenantCallback,
921
+ isBlocked: legacyIsBlockedCallback, // deprecated
922
+ transformTenant,
923
+ requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,
924
+ errorResponseFormatter,
925
+ formatError: legacyFormatError, // deprecated
926
+ baseDomain,
927
+ // Nested sub-configs
928
+ observability,
929
+ rateLimit: _rateLimit, // reserved for future use
930
+ domainCache: _domainCache, // reserved for future use
931
+ // Legacy flat observability options (deprecated)
932
+ metricsCollector: legacyMetricsCollector,
933
+ logger: legacyLogger,
934
+ traceContextExtractor: legacyTraceContextExtractor,
935
+ propagateTraceContext: legacyPropagateTraceContext,
936
+ } = config
937
+
938
+ // Resolve observability config: prefer nested config, fall back to legacy flat options
939
+ const metricsCollector = observability?.metricsCollector ?? legacyMetricsCollector
940
+ const logger = observability?.logger ?? legacyLogger
941
+ const traceContextExtractor = observability?.traceContextExtractor ?? legacyTraceContextExtractor
942
+ const propagateTraceContext = observability?.propagateTraceContext ?? legacyPropagateTraceContext ?? true
943
+
944
+ // Resolve blocked tenant callback: prefer new name, fall back to legacy
945
+ const isBlockedCallback = blockedTenantCallback ?? legacyIsBlockedCallback
946
+
947
+ // Resolve error formatter: prefer new name, fall back to legacy
948
+ const formatError = errorResponseFormatter ?? legacyFormatError
949
+
950
+ // Build the tenant extractor function
951
+ let tenantExtractor: TenantExtractor | SimpleTenantExtractor
952
+
953
+ if (typeof extractTenant === 'string') {
954
+ // Preset extractor type
955
+ switch (extractTenant) {
956
+ case 'subdomain':
957
+ tenantExtractor = extractors.subdomain(baseDomain)
958
+ break
959
+ case 'path':
960
+ tenantExtractor = extractors.path(pathPrefix)
961
+ break
962
+ case 'header':
963
+ tenantExtractor = extractors.header(headerName)
964
+ break
965
+ default:
966
+ throw new Error(`Invalid extractTenant value: ${extractTenant}`)
967
+ }
968
+ } else if (typeof extractTenant === 'function') {
969
+ tenantExtractor = extractTenant
970
+ } else {
971
+ throw new Error('extractTenant must be a string or function')
972
+ }
973
+
974
+ // Helper to generate correlation ID
975
+ function generateCorrelationId(): string {
976
+ return crypto.randomUUID()
977
+ }
978
+
979
+ // Helper to check if tenant matches a blocked pattern
980
+ function matchesBlockedPattern(tenantId: string, pattern: string): boolean {
981
+ if (pattern.endsWith('*')) {
982
+ const prefix = pattern.slice(0, -1)
983
+ return tenantId.startsWith(prefix)
984
+ }
985
+ return tenantId === pattern
986
+ }
987
+
988
+ // Default error formatter
989
+ function defaultFormatError(
990
+ status: number,
991
+ message: string,
992
+ correlationId: string,
993
+ _tenantId?: string
994
+ ): Response {
995
+ return new Response(
996
+ JSON.stringify({
997
+ error: message,
998
+ correlationId,
999
+ }),
1000
+ {
1001
+ status,
1002
+ headers: {
1003
+ 'Content-Type': 'application/json',
1004
+ [HEADERS.CORRELATION_ID]: correlationId,
1005
+ },
1006
+ }
1007
+ )
1008
+ }
1009
+
1010
+ const errorFormatter = formatError || defaultFormatError
1011
+
1012
+ // Implementation of the TenantRouter interface
1013
+ const router: TenantRouter = {
1014
+ async extractTenant(request: Request): Promise<TenantExtractionResult> {
1015
+ const url = new URL(request.url)
1016
+ const originalHostname = url.hostname
1017
+
1018
+ // Determine extraction source based on config
1019
+ let source: TenantExtractionResult['source'] = 'custom'
1020
+ if (typeof extractTenant === 'string') {
1021
+ source = extractTenant as TenantExtractionResult['source']
1022
+ }
1023
+
1024
+ // Call the extractor
1025
+ let extractionResult = await tenantExtractor(request)
1026
+
1027
+ // Check if it's a full TenantExtractionResult or just a string/null
1028
+ if (
1029
+ extractionResult !== null &&
1030
+ typeof extractionResult === 'object' &&
1031
+ 'tenantId' in extractionResult
1032
+ ) {
1033
+ // It's a full TenantExtractionResult
1034
+ return extractionResult as TenantExtractionResult
1035
+ }
1036
+
1037
+ // It's a SimpleTenantExtractor result (string | null)
1038
+ let tenantId = extractionResult as string | null
1039
+
1040
+ // Apply transformation if configured
1041
+ if (tenantId !== null && transformTenant) {
1042
+ tenantId = transformTenant(tenantId)
1043
+ }
1044
+
1045
+ // Build modified path for path-based extraction
1046
+ let modifiedPath: string | undefined
1047
+ if (source === 'path' && tenantId !== null && stripTenantFromPath) {
1048
+ let pathname = url.pathname
1049
+ const normalizedPrefix = normalizePathPrefix(pathPrefix)
1050
+
1051
+ // Remove prefix and tenant segment from path
1052
+ let pathAfterPrefix: string
1053
+ if (normalizedPrefix === '/') {
1054
+ pathAfterPrefix = pathname
1055
+ } else {
1056
+ pathAfterPrefix = pathname.slice(normalizedPrefix.length)
1057
+ }
1058
+
1059
+ // Remove leading slash and tenant segment
1060
+ if (pathAfterPrefix.startsWith('/')) {
1061
+ pathAfterPrefix = pathAfterPrefix.slice(1)
1062
+ }
1063
+ const slashIndex = pathAfterPrefix.indexOf('/')
1064
+ if (slashIndex === -1) {
1065
+ modifiedPath = '/'
1066
+ } else {
1067
+ modifiedPath = pathAfterPrefix.slice(slashIndex)
1068
+ }
1069
+ }
1070
+
1071
+ return {
1072
+ tenantId,
1073
+ source,
1074
+ originalHostname,
1075
+ modifiedPath,
1076
+ }
1077
+ },
1078
+
1079
+ async getTenantId(request: Request): Promise<string | null> {
1080
+ const result = await router.extractTenant(request)
1081
+ return result.tenantId
1082
+ },
1083
+
1084
+ async isBlocked(tenantId: string, request: Request): Promise<boolean> {
1085
+ // Check static blocked list
1086
+ for (const pattern of blockedTenants) {
1087
+ if (matchesBlockedPattern(tenantId, pattern)) {
1088
+ return true
1089
+ }
1090
+ }
1091
+
1092
+ // Check dynamic callback
1093
+ if (isBlockedCallback) {
1094
+ return await isBlockedCallback(tenantId, request)
1095
+ }
1096
+
1097
+ return false
1098
+ },
1099
+
1100
+ getStub(tenantId: string): DurableObjectStub {
1101
+ const doId = doNamespace.idFromName(tenantId)
1102
+ return doNamespace.get(doId)
1103
+ },
1104
+
1105
+ async route(request: Request): Promise<Response> {
1106
+ const correlationId = generateCorrelationId()
1107
+ const startTime = Date.now()
1108
+ const requestUrl = new URL(request.url)
1109
+
1110
+ // Extract trace context from incoming request
1111
+ let traceContext: TraceContext | null = null
1112
+ if (traceContextExtractor) {
1113
+ traceContext = traceContextExtractor(request)
1114
+ }
1115
+
1116
+ // Log debug: routing decision starting
1117
+ if (logger) {
1118
+ logger.debug('Starting tenant routing', {
1119
+ correlationId,
1120
+ path: requestUrl.pathname,
1121
+ method: request.method,
1122
+ traceId: traceContext?.traceId,
1123
+ })
1124
+ }
1125
+
1126
+ let tenantId: string | null = null
1127
+ let status = 500
1128
+
1129
+ try {
1130
+ // Extract tenant
1131
+ const extraction = await router.extractTenant(request)
1132
+
1133
+ if (extraction.tenantId === null) {
1134
+ // Log extraction failure
1135
+ if (logger) {
1136
+ logger.warn('Tenant extraction failed', {
1137
+ correlationId,
1138
+ path: requestUrl.pathname,
1139
+ method: request.method,
1140
+ source: extraction.source,
1141
+ hostname: extraction.originalHostname,
1142
+ })
1143
+ }
1144
+ status = 400
1145
+ return errorFormatter(400, 'Tenant identifier not found in request', correlationId)
1146
+ }
1147
+
1148
+ tenantId = extraction.tenantId
1149
+
1150
+ // Log debug: tenant extracted
1151
+ if (logger) {
1152
+ logger.debug('Tenant routing decision', {
1153
+ correlationId,
1154
+ tenantId,
1155
+ source: extraction.source,
1156
+ path: requestUrl.pathname,
1157
+ })
1158
+ }
1159
+
1160
+ // Validate tenant ID format - comprehensive security validation
1161
+ if (!isValidTenantId(tenantId)) {
1162
+ status = 400
1163
+ return errorFormatter(400, 'Invalid tenant identifier format', correlationId)
1164
+ }
1165
+
1166
+ // Check if blocked
1167
+ if (await router.isBlocked(tenantId, request)) {
1168
+ status = 404
1169
+ return errorFormatter(404, 'Not Found', correlationId, tenantId)
1170
+ }
1171
+
1172
+ // Get DO stub
1173
+ const stub = router.getStub(tenantId)
1174
+
1175
+ // If path-based and stripping tenant, use modified path
1176
+ if (extraction.source === 'path' && stripTenantFromPath && extraction.modifiedPath) {
1177
+ requestUrl.pathname = extraction.modifiedPath
1178
+ }
1179
+
1180
+ // Clone headers and add tenant ID
1181
+ const headers = new Headers(request.headers)
1182
+ headers.set(HEADERS.TENANT_ID, tenantId)
1183
+ headers.set(HEADERS.CORRELATION_ID, correlationId)
1184
+
1185
+ // Handle trace context propagation
1186
+ if (propagateTraceContext && traceContext) {
1187
+ // Propagate trace context headers
1188
+ headers.set(HEADERS.TRACE_ID, traceContext.traceId)
1189
+ headers.set(HEADERS.SPAN_ID, traceContext.spanId)
1190
+ if (traceContext.parentSpanId) {
1191
+ headers.set(HEADERS.PARENT_SPAN_ID, traceContext.parentSpanId)
1192
+ }
1193
+ } else if (!propagateTraceContext) {
1194
+ // Remove trace context headers when propagation is disabled
1195
+ headers.delete(HEADERS.TRACE_ID)
1196
+ headers.delete(HEADERS.SPAN_ID)
1197
+ headers.delete(HEADERS.PARENT_SPAN_ID)
1198
+ headers.delete(HEADERS.TRACE_SAMPLED)
1199
+ }
1200
+
1201
+ // Create new request with modified URL and headers
1202
+ const forwardedRequest = new Request(requestUrl.toString(), {
1203
+ method: request.method,
1204
+ headers,
1205
+ body: request.body,
1206
+ // @ts-expect-error - duplex is needed for streaming bodies
1207
+ duplex: request.body ? 'half' : undefined,
1208
+ })
1209
+
1210
+ // Forward to DO with timeout
1211
+ const controller = new AbortController()
1212
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs)
1213
+ const doStartTime = Date.now()
1214
+
1215
+ try {
1216
+ const response = await stub.fetch(forwardedRequest, {
1217
+ signal: controller.signal,
1218
+ })
1219
+ clearTimeout(timeoutId)
1220
+
1221
+ const doResponseTimeMs = Date.now() - doStartTime
1222
+ const routingLatencyMs = Date.now() - startTime
1223
+ status = response.status
1224
+
1225
+ // Emit metrics
1226
+ if (metricsCollector) {
1227
+ metricsCollector.incrementRequestCount(tenantId)
1228
+ metricsCollector.recordLatency(tenantId, routingLatencyMs)
1229
+ metricsCollector.recordDoResponseTime(tenantId, doResponseTimeMs)
1230
+ metricsCollector.recordRequest({
1231
+ tenantId,
1232
+ method: request.method,
1233
+ path: requestUrl.pathname,
1234
+ status: response.status,
1235
+ routingLatencyMs,
1236
+ doResponseTimeMs,
1237
+ timestamp: startTime,
1238
+ correlationId,
1239
+ traceId: traceContext?.traceId,
1240
+ spanId: traceContext?.spanId,
1241
+ })
1242
+ }
1243
+
1244
+ return response
1245
+ } catch (error) {
1246
+ clearTimeout(timeoutId)
1247
+
1248
+ if (error instanceof Error) {
1249
+ if (error.name === 'AbortError') {
1250
+ throw new TenantTimeoutError(tenantId, requestTimeoutMs)
1251
+ }
1252
+ throw new TenantUnavailableError(tenantId, error)
1253
+ }
1254
+ throw error
1255
+ }
1256
+ } catch (error) {
1257
+ const routingLatencyMs = Date.now() - startTime
1258
+
1259
+ // Emit error metrics
1260
+ if (metricsCollector && tenantId) {
1261
+ metricsCollector.incrementRequestCount(tenantId)
1262
+ metricsCollector.recordLatency(tenantId, routingLatencyMs)
1263
+ metricsCollector.recordRequest({
1264
+ tenantId,
1265
+ method: request.method,
1266
+ path: requestUrl.pathname,
1267
+ status,
1268
+ routingLatencyMs,
1269
+ doResponseTimeMs: 0,
1270
+ timestamp: startTime,
1271
+ correlationId,
1272
+ traceId: traceContext?.traceId,
1273
+ spanId: traceContext?.spanId,
1274
+ })
1275
+ }
1276
+
1277
+ if (error instanceof TenantNotFoundError) {
1278
+ return errorFormatter(400, 'Tenant identifier not found in request', correlationId)
1279
+ }
1280
+
1281
+ if (error instanceof TenantBlockedError) {
1282
+ return errorFormatter(404, 'Not Found', correlationId, error.tenantId)
1283
+ }
1284
+
1285
+ if (error instanceof InvalidTenantIdError) {
1286
+ return errorFormatter(400, 'Invalid tenant identifier format', correlationId)
1287
+ }
1288
+
1289
+ if (error instanceof TenantTimeoutError) {
1290
+ return errorFormatter(504, 'Gateway Timeout', correlationId, error.tenantId)
1291
+ }
1292
+
1293
+ if (error instanceof TenantUnavailableError) {
1294
+ return errorFormatter(502, 'Bad Gateway', correlationId, error.tenantId)
1295
+ }
1296
+
1297
+ // Generic error - don't expose details
1298
+ return errorFormatter(500, 'Internal Server Error', correlationId)
1299
+ }
1300
+ },
1301
+
1302
+ async routeWithResult(request: Request): Promise<TenantRoutingResult> {
1303
+ const startTimestamp = Date.now()
1304
+ const correlationId = generateCorrelationId()
1305
+
1306
+ // Extract tenant with timing
1307
+ const extractionStartTime = Date.now()
1308
+ const extraction = await router.extractTenant(request)
1309
+ const extractionTimeMs = Date.now() - extractionStartTime
1310
+
1311
+ if (extraction.tenantId === null) {
1312
+ throw new TenantNotFoundError('Tenant identifier not found in request', request)
1313
+ }
1314
+
1315
+ const tenantId = extraction.tenantId
1316
+
1317
+ // Validate tenant ID format - comprehensive security validation
1318
+ if (!isValidTenantId(tenantId)) {
1319
+ throw new InvalidTenantIdError(tenantId)
1320
+ }
1321
+
1322
+ // Check if blocked
1323
+ if (await router.isBlocked(tenantId, request)) {
1324
+ throw new TenantBlockedError(tenantId)
1325
+ }
1326
+
1327
+ // Get DO stub and ID
1328
+ const doId = doNamespace.idFromName(tenantId)
1329
+ const stub = doNamespace.get(doId)
1330
+
1331
+ // Build forwarded request
1332
+ const url = new URL(request.url)
1333
+
1334
+ // If path-based and stripping tenant, use modified path
1335
+ if (extraction.source === 'path' && stripTenantFromPath && extraction.modifiedPath) {
1336
+ url.pathname = extraction.modifiedPath
1337
+ }
1338
+
1339
+ // Clone headers and add tenant ID
1340
+ const headers = new Headers(request.headers)
1341
+ headers.set(HEADERS.TENANT_ID, tenantId)
1342
+ headers.set(HEADERS.CORRELATION_ID, correlationId)
1343
+
1344
+ // Create new request with modified URL and headers
1345
+ const forwardedRequest = new Request(url.toString(), {
1346
+ method: request.method,
1347
+ headers,
1348
+ body: request.body,
1349
+ // @ts-expect-error - duplex is needed for streaming bodies
1350
+ duplex: request.body ? 'half' : undefined,
1351
+ })
1352
+
1353
+ // Forward to DO with timeout
1354
+ const controller = new AbortController()
1355
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs)
1356
+ const routingStartTime = Date.now()
1357
+
1358
+ try {
1359
+ const response = await stub.fetch(forwardedRequest, {
1360
+ signal: controller.signal,
1361
+ })
1362
+ clearTimeout(timeoutId)
1363
+
1364
+ const routingTimeMs = Date.now() - routingStartTime
1365
+ const totalTimeMs = Date.now() - startTimestamp
1366
+
1367
+ return {
1368
+ response,
1369
+ routingMetadata: {
1370
+ extractorType: extraction.source,
1371
+ doId: doId.toString(),
1372
+ },
1373
+ timing: {
1374
+ extractionTimeMs,
1375
+ routingTimeMs,
1376
+ totalTimeMs,
1377
+ startTimestamp,
1378
+ },
1379
+ tenantContext: {
1380
+ tenantId,
1381
+ source: extraction.source,
1382
+ metadata: extraction.metadata,
1383
+ },
1384
+ }
1385
+ } catch (error) {
1386
+ clearTimeout(timeoutId)
1387
+
1388
+ if (error instanceof Error) {
1389
+ if (error.name === 'AbortError') {
1390
+ throw new TenantTimeoutError(tenantId, requestTimeoutMs)
1391
+ }
1392
+ throw new TenantUnavailableError(tenantId, error)
1393
+ }
1394
+ throw error
1395
+ }
1396
+ },
1397
+ }
1398
+
1399
+ return router
1400
+ }
1401
+
1402
+ // =============================================================================
1403
+ // Helper Types
1404
+ // =============================================================================
1405
+
1406
+ /**
1407
+ * Request with tenant context attached
1408
+ */
1409
+ export interface TenantRequest extends Request {
1410
+ tenantId: string
1411
+ tenantMetadata?: Record<string, unknown>
1412
+ }
1413
+
1414
+ /**
1415
+ * Tenant routing metrics
1416
+ */
1417
+ export interface TenantRoutingMetrics {
1418
+ requestCount: number
1419
+ successCount: number
1420
+ errorCount: number
1421
+ blockedCount: number
1422
+ avgRoutingLatencyMs: number
1423
+ avgDoResponseTimeMs: number
1424
+ }
1425
+
1426
+ /**
1427
+ * Per-tenant metrics
1428
+ */
1429
+ export interface PerTenantMetrics {
1430
+ tenantId: string
1431
+ requestCount: number
1432
+ lastRequestAt: number
1433
+ avgResponseTimeMs: number
1434
+ }
1435
+
1436
+ // =============================================================================
1437
+ // Error Types
1438
+ // =============================================================================
1439
+
1440
+ /**
1441
+ * Error thrown when tenant cannot be identified
1442
+ */
1443
+ export class TenantNotFoundError extends Error {
1444
+ constructor(
1445
+ message: string = 'Tenant identifier not found in request',
1446
+ public readonly request?: Request
1447
+ ) {
1448
+ super(message)
1449
+ this.name = 'TenantNotFoundError'
1450
+ }
1451
+ }
1452
+
1453
+ /**
1454
+ * Error thrown when tenant is blocked
1455
+ */
1456
+ export class TenantBlockedError extends Error {
1457
+ constructor(
1458
+ public readonly tenantId: string,
1459
+ message: string = 'Tenant is blocked'
1460
+ ) {
1461
+ super(message)
1462
+ this.name = 'TenantBlockedError'
1463
+ }
1464
+ }
1465
+
1466
+ /**
1467
+ * Error thrown when tenant ID format is invalid
1468
+ */
1469
+ export class InvalidTenantIdError extends Error {
1470
+ constructor(
1471
+ public readonly tenantId: string,
1472
+ message: string = 'Invalid tenant identifier format'
1473
+ ) {
1474
+ super(message)
1475
+ this.name = 'InvalidTenantIdError'
1476
+ }
1477
+ }
1478
+
1479
+ /**
1480
+ * Error thrown when DO request times out
1481
+ */
1482
+ export class TenantTimeoutError extends Error {
1483
+ constructor(
1484
+ public readonly tenantId: string,
1485
+ public readonly timeoutMs: number,
1486
+ message: string = 'Tenant DO request timed out'
1487
+ ) {
1488
+ super(message)
1489
+ this.name = 'TenantTimeoutError'
1490
+ }
1491
+ }
1492
+
1493
+ /**
1494
+ * Error thrown when DO is unreachable
1495
+ */
1496
+ export class TenantUnavailableError extends Error {
1497
+ public readonly tenantId: string
1498
+ public override readonly cause?: Error | undefined
1499
+
1500
+ constructor(
1501
+ tenantId: string,
1502
+ cause?: Error,
1503
+ message: string = 'Tenant DO is unavailable'
1504
+ ) {
1505
+ super(message)
1506
+ this.tenantId = tenantId
1507
+ this.cause = cause
1508
+ this.name = 'TenantUnavailableError'
1509
+ }
1510
+ }
1511
+
1512
+ // =============================================================================
1513
+ // Rate Limiting
1514
+ // =============================================================================
1515
+
1516
+ /**
1517
+ * Rate limit configuration
1518
+ */
1519
+ export interface RateLimitConfig {
1520
+ /**
1521
+ * Maximum requests allowed within the window
1522
+ */
1523
+ maxRequests: number
1524
+
1525
+ /**
1526
+ * Time window in milliseconds
1527
+ * @default 60000 (1 minute)
1528
+ */
1529
+ windowMs?: number
1530
+
1531
+ /**
1532
+ * List of tenant IDs that bypass rate limiting
1533
+ */
1534
+ bypassTenants?: string[]
1535
+
1536
+ /**
1537
+ * Custom function to determine if a tenant should bypass rate limiting
1538
+ */
1539
+ bypassCallback?: (tenantId: string, request: Request) => boolean | Promise<boolean>
1540
+ }
1541
+
1542
+ /**
1543
+ * Rate limit state for a single tenant
1544
+ */
1545
+ export interface RateLimitState {
1546
+ /** Number of requests in the current window */
1547
+ requestCount: number
1548
+ /** Timestamp when the window started */
1549
+ windowStart: number
1550
+ }
1551
+
1552
+ /**
1553
+ * Rate limit check result
1554
+ */
1555
+ export interface RateLimitResult {
1556
+ /** Whether the request is allowed */
1557
+ allowed: boolean
1558
+ /** Current request count in the window */
1559
+ currentCount: number
1560
+ /** Maximum requests allowed */
1561
+ maxRequests: number
1562
+ /** Seconds until the rate limit resets */
1563
+ retryAfterSeconds: number
1564
+ /** Whether this tenant bypasses rate limiting */
1565
+ bypassed: boolean
1566
+ }
1567
+
1568
+ /**
1569
+ * In-memory rate limiter for per-tenant request tracking
1570
+ */
1571
+ export class TenantRateLimiter {
1572
+ private state: Map<string, RateLimitState> = new Map()
1573
+ private readonly maxRequests: number
1574
+ private readonly windowMs: number
1575
+ private readonly bypassTenants: Set<string>
1576
+ private readonly bypassCallback?: ((tenantId: string, request: Request) => boolean | Promise<boolean>) | undefined
1577
+
1578
+ constructor(config: RateLimitConfig) {
1579
+ this.maxRequests = config.maxRequests
1580
+ this.windowMs = config.windowMs ?? 60_000
1581
+ this.bypassTenants = new Set(config.bypassTenants ?? [])
1582
+ this.bypassCallback = config.bypassCallback
1583
+ }
1584
+
1585
+ /**
1586
+ * Check if a request should be allowed for the given tenant
1587
+ */
1588
+ async check(tenantId: string, request: Request): Promise<RateLimitResult> {
1589
+ // Periodic cleanup of expired entries (1% chance per request)
1590
+ if (Math.random() < 0.01) {
1591
+ this.cleanupExpiredEntries()
1592
+ }
1593
+
1594
+ // Check bypass list first
1595
+ if (this.bypassTenants.has(tenantId)) {
1596
+ return {
1597
+ allowed: true,
1598
+ currentCount: 0,
1599
+ maxRequests: this.maxRequests,
1600
+ retryAfterSeconds: 0,
1601
+ bypassed: true,
1602
+ }
1603
+ }
1604
+
1605
+ // Check bypass callback
1606
+ if (this.bypassCallback) {
1607
+ const shouldBypass = await this.bypassCallback(tenantId, request)
1608
+ if (shouldBypass) {
1609
+ return {
1610
+ allowed: true,
1611
+ currentCount: 0,
1612
+ maxRequests: this.maxRequests,
1613
+ retryAfterSeconds: 0,
1614
+ bypassed: true,
1615
+ }
1616
+ }
1617
+ }
1618
+
1619
+ const now = Date.now()
1620
+ let tenantState = this.state.get(tenantId)
1621
+
1622
+ // Initialize or reset window if expired
1623
+ if (!tenantState || now - tenantState.windowStart >= this.windowMs) {
1624
+ tenantState = {
1625
+ requestCount: 0,
1626
+ windowStart: now,
1627
+ }
1628
+ this.state.set(tenantId, tenantState)
1629
+ }
1630
+
1631
+ // Calculate retry-after
1632
+ const windowEnd = tenantState.windowStart + this.windowMs
1633
+ const retryAfterSeconds = Math.ceil((windowEnd - now) / 1000)
1634
+
1635
+ // Check if limit exceeded
1636
+ if (tenantState.requestCount >= this.maxRequests) {
1637
+ return {
1638
+ allowed: false,
1639
+ currentCount: tenantState.requestCount,
1640
+ maxRequests: this.maxRequests,
1641
+ retryAfterSeconds,
1642
+ bypassed: false,
1643
+ }
1644
+ }
1645
+
1646
+ // Increment and allow
1647
+ tenantState.requestCount++
1648
+
1649
+ return {
1650
+ allowed: true,
1651
+ currentCount: tenantState.requestCount,
1652
+ maxRequests: this.maxRequests,
1653
+ retryAfterSeconds,
1654
+ bypassed: false,
1655
+ }
1656
+ }
1657
+
1658
+ /**
1659
+ * Get the current state for a tenant (for testing/debugging)
1660
+ */
1661
+ getState(tenantId: string): RateLimitState | undefined {
1662
+ return this.state.get(tenantId)
1663
+ }
1664
+
1665
+ /**
1666
+ * Reset rate limit state for a tenant
1667
+ */
1668
+ reset(tenantId: string): void {
1669
+ this.state.delete(tenantId)
1670
+ }
1671
+
1672
+ /**
1673
+ * Reset all rate limit state
1674
+ */
1675
+ resetAll(): void {
1676
+ this.state.clear()
1677
+ }
1678
+
1679
+ /**
1680
+ * Clean up expired entries from the rate limit state map
1681
+ * Removes entries that are 2x past their window to prevent unbounded memory growth
1682
+ */
1683
+ private cleanupExpiredEntries(): void {
1684
+ const now = Date.now()
1685
+ for (const [tenantId, state] of this.state.entries()) {
1686
+ // Remove entries that are 2x past their window
1687
+ if (now - state.windowStart >= this.windowMs * 2) {
1688
+ this.state.delete(tenantId)
1689
+ }
1690
+ }
1691
+ }
1692
+
1693
+ /**
1694
+ * Get the current number of tracked tenants (for monitoring/debugging)
1695
+ */
1696
+ getTrackedTenantCount(): number {
1697
+ return this.state.size
1698
+ }
1699
+ }
1700
+
1701
+ /**
1702
+ * Error thrown when rate limit is exceeded
1703
+ */
1704
+ export class TenantRateLimitError extends Error {
1705
+ constructor(
1706
+ public readonly tenantId: string,
1707
+ public readonly retryAfterSeconds: number,
1708
+ message: string = 'Rate limit exceeded'
1709
+ ) {
1710
+ super(message)
1711
+ this.name = 'TenantRateLimitError'
1712
+ }
1713
+ }
1714
+
1715
+ // =============================================================================
1716
+ // Caching Types and Implementation
1717
+ // =============================================================================
1718
+
1719
+ /**
1720
+ * Cache entry for domain-to-tenant mappings
1721
+ */
1722
+ export interface DomainCacheEntry {
1723
+ /** The tenant ID mapped to this domain */
1724
+ tenantId: string
1725
+ /** Timestamp when this entry was cached */
1726
+ cachedAt: number
1727
+ /** Additional metadata */
1728
+ metadata?: Record<string, unknown> | undefined
1729
+ }
1730
+
1731
+ /**
1732
+ * Configuration for the domain mapping cache
1733
+ */
1734
+ export interface DomainCacheConfig {
1735
+ /** TTL for cache entries in milliseconds */
1736
+ ttlMs: number
1737
+ /** Maximum number of entries to cache */
1738
+ maxEntries?: number
1739
+ }
1740
+
1741
+ /**
1742
+ * Cache statistics
1743
+ */
1744
+ export interface CacheStats {
1745
+ hits: number
1746
+ misses: number
1747
+ size: number
1748
+ invalidations: number
1749
+ }
1750
+
1751
+ /**
1752
+ * Interface for domain-to-tenant mapping cache
1753
+ */
1754
+ export interface DomainMappingCache {
1755
+ /** Get a cached tenant ID for a domain */
1756
+ get(domain: string): DomainCacheEntry | null
1757
+ /** Set a domain-to-tenant mapping in the cache */
1758
+ set(domain: string, tenantId: string, metadata?: Record<string, unknown>): void
1759
+ /** Invalidate a specific domain mapping */
1760
+ invalidate(domain: string): void
1761
+ /** Invalidate all cache entries */
1762
+ invalidateAll(): void
1763
+ /** Check if cache has a valid (non-expired) entry for a domain */
1764
+ has(domain: string): boolean
1765
+ /** Warm the cache with known domain-to-tenant mappings */
1766
+ warm(mappings: Record<string, string>): void
1767
+ /** Get the current cache size */
1768
+ size(): number
1769
+ /** Get cache statistics */
1770
+ stats(): CacheStats
1771
+ }
1772
+
1773
+ /**
1774
+ * Create a new domain mapping cache
1775
+ *
1776
+ * @param config Cache configuration
1777
+ * @returns A DomainMappingCache instance
1778
+ *
1779
+ * @example
1780
+ * ```typescript
1781
+ * const cache = createDomainMappingCache({ ttlMs: 300000 }) // 5 minute TTL
1782
+ *
1783
+ * // Set a mapping
1784
+ * cache.set('custom.example.com', 'tenant1')
1785
+ *
1786
+ * // Get a mapping (returns null if expired or not found)
1787
+ * const entry = cache.get('custom.example.com')
1788
+ * if (entry) {
1789
+ * console.log(entry.tenantId) // 'tenant1'
1790
+ * }
1791
+ *
1792
+ * // Warm cache on startup
1793
+ * cache.warm({
1794
+ * 'acme.com': 'acme-inc',
1795
+ * 'bigcorp.io': 'bigcorp',
1796
+ * })
1797
+ *
1798
+ * // Invalidate on config change
1799
+ * cache.invalidate('acme.com')
1800
+ * // or invalidate all
1801
+ * cache.invalidateAll()
1802
+ * ```
1803
+ */
1804
+ export function createDomainMappingCache(config: DomainCacheConfig): DomainMappingCache {
1805
+ const { ttlMs, maxEntries = 10000 } = config
1806
+ const cache = new Map<string, DomainCacheEntry>()
1807
+ let hits = 0
1808
+ let misses = 0
1809
+ let invalidations = 0
1810
+
1811
+ // Helper to check if an entry is expired
1812
+ function isExpired(entry: DomainCacheEntry): boolean {
1813
+ return Date.now() - entry.cachedAt > ttlMs
1814
+ }
1815
+
1816
+ // Helper to evict oldest entries if over capacity
1817
+ function evictIfNeeded(): void {
1818
+ if (cache.size <= maxEntries) return
1819
+
1820
+ // Find and remove oldest entries
1821
+ const entries = Array.from(cache.entries())
1822
+ entries.sort((a, b) => a[1].cachedAt - b[1].cachedAt)
1823
+ const toRemove = entries.slice(0, cache.size - maxEntries)
1824
+ for (const [key] of toRemove) {
1825
+ cache.delete(key)
1826
+ }
1827
+ }
1828
+
1829
+ return {
1830
+ get(domain: string): DomainCacheEntry | null {
1831
+ const normalizedDomain = domain.toLowerCase()
1832
+ const entry = cache.get(normalizedDomain)
1833
+
1834
+ if (!entry) {
1835
+ misses++
1836
+ return null
1837
+ }
1838
+
1839
+ if (isExpired(entry)) {
1840
+ cache.delete(normalizedDomain)
1841
+ misses++
1842
+ return null
1843
+ }
1844
+
1845
+ hits++
1846
+ return entry
1847
+ },
1848
+
1849
+ set(domain: string, tenantId: string, metadata?: Record<string, unknown>): void {
1850
+ const normalizedDomain = domain.toLowerCase()
1851
+ cache.set(normalizedDomain, {
1852
+ tenantId,
1853
+ cachedAt: Date.now(),
1854
+ metadata,
1855
+ })
1856
+ evictIfNeeded()
1857
+ },
1858
+
1859
+ invalidate(domain: string): void {
1860
+ const normalizedDomain = domain.toLowerCase()
1861
+ if (cache.delete(normalizedDomain)) {
1862
+ invalidations++
1863
+ }
1864
+ },
1865
+
1866
+ invalidateAll(): void {
1867
+ const count = cache.size
1868
+ cache.clear()
1869
+ invalidations += count
1870
+ },
1871
+
1872
+ has(domain: string): boolean {
1873
+ const normalizedDomain = domain.toLowerCase()
1874
+ const entry = cache.get(normalizedDomain)
1875
+ if (!entry) return false
1876
+ if (isExpired(entry)) {
1877
+ cache.delete(normalizedDomain)
1878
+ return false
1879
+ }
1880
+ return true
1881
+ },
1882
+
1883
+ warm(mappings: Record<string, string>): void {
1884
+ const now = Date.now()
1885
+ for (const [domain, tenantId] of Object.entries(mappings)) {
1886
+ const normalizedDomain = domain.toLowerCase()
1887
+ cache.set(normalizedDomain, {
1888
+ tenantId,
1889
+ cachedAt: now,
1890
+ })
1891
+ }
1892
+ evictIfNeeded()
1893
+ },
1894
+
1895
+ size(): number {
1896
+ return cache.size
1897
+ },
1898
+
1899
+ stats(): CacheStats {
1900
+ return {
1901
+ hits,
1902
+ misses,
1903
+ size: cache.size,
1904
+ invalidations,
1905
+ }
1906
+ },
1907
+ }
1908
+ }