@malloy-publisher/server 0.0.234 → 0.0.236

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 (318) hide show
  1. package/dist/app/api-doc.yaml +47 -17
  2. package/dist/app/assets/{EnvironmentPage-DTZQ4Gxc.js → EnvironmentPage-BsAnavYN.js} +1 -1
  3. package/dist/app/assets/{HomePage-C5mlDPXK.js → HomePage-CADE138j.js} +1 -1
  4. package/dist/app/assets/{LightMode-DGNmhG0u.js → LightMode-Cfh7KzN8.js} +1 -1
  5. package/dist/app/assets/{MainPage-CVL_wmP4.js → MainPage-CO3pRlnV.js} +1 -1
  6. package/dist/app/assets/{MaterializationsPage-DmzMBCpy.js → MaterializationsPage-p9YjkRXZ.js} +1 -1
  7. package/dist/app/assets/{ModelPage-Dbvf4QbB.js → ModelPage-C1OSTv-x.js} +1 -1
  8. package/dist/app/assets/{PackagePage-DxdHc2Qs.js → PackagePage-e4kN75YR.js} +1 -1
  9. package/dist/app/assets/{RouteError-OJdT4tCd.js → RouteError-CzbfOkng.js} +1 -1
  10. package/dist/app/assets/{ThemeEditorPage-Bk7s0KXY.js → ThemeEditorPage-CciagFTq.js} +1 -1
  11. package/dist/app/assets/{WorkbookPage-j_vCWdN3.js → WorkbookPage-DNWmkCXa.js} +1 -1
  12. package/dist/app/assets/{core-Rj_4rRnA.es-DoIfLxDJ.js → core-Rj_4rRnA.es-BZyvITuO.js} +1 -1
  13. package/dist/app/assets/{index-B_jKMR35.js → index-CH2AcDzc.js} +4 -4
  14. package/dist/app/assets/{index-hw-xn0X7.js → index-DQa463gC.js} +2 -2
  15. package/dist/app/assets/{index-DWIe_hK0.js → index-DQpV7MyA.js} +1 -1
  16. package/dist/app/assets/{index-BabP-V-S.js → index-DzaYbhnD.js} +1 -1
  17. package/dist/app/assets/{index-D-rDyK11.js → index-VBbcc8s6.js} +1 -1
  18. package/dist/app/index.html +1 -1
  19. package/dist/package_load_worker.mjs +8 -1
  20. package/dist/server.mjs +471 -54
  21. package/package.json +16 -13
  22. package/.eslintrc.json +0 -44
  23. package/.prettierignore +0 -1
  24. package/.prettierrc +0 -4
  25. package/build.ts +0 -97
  26. package/eslint.config.mjs +0 -76
  27. package/k6-tests/README.md +0 -144
  28. package/k6-tests/bun.lock +0 -797
  29. package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
  30. package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
  31. package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
  32. package/k6-tests/load-test/load-test-crud.ts +0 -142
  33. package/k6-tests/load-test/load-test.ts +0 -784
  34. package/k6-tests/package.json +0 -27
  35. package/k6-tests/scripts/run-with-otel.sh +0 -50
  36. package/k6-tests/smoke-test/smoke-test.ts +0 -158
  37. package/k6-tests/tsconfig.json +0 -29
  38. package/k6-tests/utils/client_factory.ts +0 -128
  39. package/k6-tests/utils/common.ts +0 -489
  40. package/k6-tests/utils/logger.ts +0 -62
  41. package/k6-tests/utils/types.d.ts +0 -35
  42. package/malloy_mcp.dxt +0 -0
  43. package/publisher.config.example.bigquery.json +0 -33
  44. package/publisher.config.example.duckdb.json +0 -23
  45. package/publisher.config.json +0 -23
  46. package/scripts/bake-duckdb-extensions.js +0 -110
  47. package/src/config.spec.ts +0 -1535
  48. package/src/config.theme.spec.ts +0 -178
  49. package/src/config.ts +0 -1142
  50. package/src/constants.ts +0 -105
  51. package/src/controller/compile.controller.ts +0 -40
  52. package/src/controller/connection.controller.spec.ts +0 -849
  53. package/src/controller/connection.controller.ts +0 -901
  54. package/src/controller/database.controller.ts +0 -24
  55. package/src/controller/materialization.controller.spec.ts +0 -293
  56. package/src/controller/materialization.controller.ts +0 -319
  57. package/src/controller/model.controller.ts +0 -164
  58. package/src/controller/package.controller.spec.ts +0 -273
  59. package/src/controller/package.controller.ts +0 -306
  60. package/src/controller/query.controller.ts +0 -152
  61. package/src/controller/theme.controller.ts +0 -83
  62. package/src/controller/watch-mode.controller.ts +0 -243
  63. package/src/data_styles.ts +0 -71
  64. package/src/default-publisher.config.json +0 -23
  65. package/src/ducklake_version.spec.ts +0 -163
  66. package/src/ducklake_version.ts +0 -153
  67. package/src/errors.spec.ts +0 -119
  68. package/src/errors.ts +0 -237
  69. package/src/filter_deprecation.spec.ts +0 -64
  70. package/src/filter_deprecation.ts +0 -42
  71. package/src/health.spec.ts +0 -123
  72. package/src/health.ts +0 -233
  73. package/src/heap_check.spec.ts +0 -144
  74. package/src/heap_check.ts +0 -144
  75. package/src/instrumentation.ts +0 -214
  76. package/src/json_utils.spec.ts +0 -51
  77. package/src/json_utils.ts +0 -33
  78. package/src/logger.spec.ts +0 -210
  79. package/src/logger.ts +0 -251
  80. package/src/malloy_pin_prereqs.spec.ts +0 -25
  81. package/src/materialization_metrics.spec.ts +0 -169
  82. package/src/materialization_metrics.ts +0 -346
  83. package/src/mcp/error_messages.spec.ts +0 -35
  84. package/src/mcp/error_messages.ts +0 -183
  85. package/src/mcp/handler_utils.spec.ts +0 -108
  86. package/src/mcp/handler_utils.ts +0 -251
  87. package/src/mcp/mcp_constants.ts +0 -6
  88. package/src/mcp/query_envelope.spec.ts +0 -229
  89. package/src/mcp/query_envelope.ts +0 -240
  90. package/src/mcp/server.protocol.spec.ts +0 -250
  91. package/src/mcp/server.ts +0 -87
  92. package/src/mcp/skills/build_skills_bundle.spec.ts +0 -51
  93. package/src/mcp/skills/build_skills_bundle.ts +0 -176
  94. package/src/mcp/skills/skills_bundle.json +0 -1
  95. package/src/mcp/skills/skills_bundle.spec.ts +0 -171
  96. package/src/mcp/tool_response.spec.ts +0 -108
  97. package/src/mcp/tool_response.ts +0 -138
  98. package/src/mcp/tools/compile_tool.spec.ts +0 -315
  99. package/src/mcp/tools/compile_tool.ts +0 -208
  100. package/src/mcp/tools/docs_search/build_docs_index.ts +0 -132
  101. package/src/mcp/tools/docs_search/malloy_docs_index.json +0 -1
  102. package/src/mcp/tools/docs_search_tool.spec.ts +0 -32
  103. package/src/mcp/tools/docs_search_tool.ts +0 -128
  104. package/src/mcp/tools/embedding_index.spec.ts +0 -1236
  105. package/src/mcp/tools/embedding_index.ts +0 -808
  106. package/src/mcp/tools/execute_query_tool.spec.ts +0 -294
  107. package/src/mcp/tools/execute_query_tool.ts +0 -316
  108. package/src/mcp/tools/get_context_eval.ts +0 -275
  109. package/src/mcp/tools/get_context_tool.spec.ts +0 -592
  110. package/src/mcp/tools/get_context_tool.ts +0 -585
  111. package/src/mcp/tools/reload_package_tool.spec.ts +0 -232
  112. package/src/mcp/tools/reload_package_tool.ts +0 -132
  113. package/src/mcp_config.spec.ts +0 -919
  114. package/src/mcp_config.ts +0 -425
  115. package/src/oom_guards.integration.spec.ts +0 -269
  116. package/src/package_load/package_load_pool.spec.ts +0 -252
  117. package/src/package_load/package_load_pool.ts +0 -931
  118. package/src/package_load/package_load_worker.ts +0 -1034
  119. package/src/package_load/protocol.ts +0 -364
  120. package/src/package_load/rpc_wait_accountant.spec.ts +0 -109
  121. package/src/package_load/rpc_wait_accountant.ts +0 -76
  122. package/src/package_load_metrics.spec.ts +0 -114
  123. package/src/package_load_metrics.ts +0 -127
  124. package/src/path_safety.spec.ts +0 -158
  125. package/src/path_safety.ts +0 -146
  126. package/src/pg_helpers.spec.ts +0 -223
  127. package/src/pg_helpers.ts +0 -52
  128. package/src/query_cap_metrics.spec.ts +0 -89
  129. package/src/query_cap_metrics.ts +0 -116
  130. package/src/query_concurrency.spec.ts +0 -247
  131. package/src/query_concurrency.ts +0 -237
  132. package/src/query_metadata_metrics.ts +0 -49
  133. package/src/query_param_utils.ts +0 -29
  134. package/src/query_timeout.spec.ts +0 -224
  135. package/src/query_timeout.ts +0 -179
  136. package/src/runtime/publisher.js +0 -328
  137. package/src/server-old.ts +0 -988
  138. package/src/server.ts +0 -2059
  139. package/src/service/annotations.spec.ts +0 -118
  140. package/src/service/annotations.ts +0 -91
  141. package/src/service/authorize.spec.ts +0 -154
  142. package/src/service/authorize.ts +0 -498
  143. package/src/service/authorize_integration.spec.ts +0 -2041
  144. package/src/service/build_plan.spec.ts +0 -685
  145. package/src/service/build_plan.ts +0 -797
  146. package/src/service/compile_authorize.spec.ts +0 -85
  147. package/src/service/compile_fragment_techniques.spec.ts +0 -156
  148. package/src/service/connection.spec.ts +0 -2360
  149. package/src/service/connection.ts +0 -2187
  150. package/src/service/connection_config.spec.ts +0 -734
  151. package/src/service/connection_config.ts +0 -899
  152. package/src/service/connection_federation.spec.ts +0 -184
  153. package/src/service/connection_fingerprint.spec.ts +0 -102
  154. package/src/service/connection_service.spec.ts +0 -711
  155. package/src/service/connection_service.ts +0 -291
  156. package/src/service/cron_evaluator.spec.ts +0 -79
  157. package/src/service/cron_evaluator.ts +0 -105
  158. package/src/service/db_utils.spec.ts +0 -1085
  159. package/src/service/db_utils.ts +0 -1502
  160. package/src/service/duckdb_instance_isolation.spec.ts +0 -137
  161. package/src/service/ducklake_lazy_attach.spec.ts +0 -110
  162. package/src/service/embedding_provider.spec.ts +0 -329
  163. package/src/service/embedding_provider.ts +0 -236
  164. package/src/service/environment.ts +0 -2191
  165. package/src/service/environment_admission.spec.ts +0 -344
  166. package/src/service/environment_compile.spec.ts +0 -197
  167. package/src/service/environment_store.spec.ts +0 -2274
  168. package/src/service/environment_store.ts +0 -2434
  169. package/src/service/environment_store_anchoring.spec.ts +0 -107
  170. package/src/service/environment_store_clone.spec.ts +0 -350
  171. package/src/service/explore_visibility.spec.ts +0 -434
  172. package/src/service/exports_probe.spec.ts +0 -107
  173. package/src/service/extension_fetch_policy.spec.ts +0 -256
  174. package/src/service/filter.spec.ts +0 -458
  175. package/src/service/filter.ts +0 -341
  176. package/src/service/filter_bypass.spec.ts +0 -418
  177. package/src/service/filter_integration.spec.ts +0 -1004
  178. package/src/service/freshness.spec.ts +0 -183
  179. package/src/service/freshness.ts +0 -112
  180. package/src/service/gcs_s3_utils.ts +0 -427
  181. package/src/service/given.ts +0 -105
  182. package/src/service/givens_integration.spec.ts +0 -219
  183. package/src/service/manifest_loader.spec.ts +0 -189
  184. package/src/service/manifest_loader.ts +0 -156
  185. package/src/service/materialization_build_session.spec.ts +0 -435
  186. package/src/service/materialization_build_session.ts +0 -681
  187. package/src/service/materialization_config_validation.spec.ts +0 -99
  188. package/src/service/materialization_config_validation.ts +0 -120
  189. package/src/service/materialization_eligibility.spec.ts +0 -158
  190. package/src/service/materialization_eligibility.ts +0 -305
  191. package/src/service/materialization_schedule_surface.spec.ts +0 -256
  192. package/src/service/materialization_scheduler.spec.ts +0 -289
  193. package/src/service/materialization_scheduler.ts +0 -304
  194. package/src/service/materialization_serve_transform.spec.ts +0 -1003
  195. package/src/service/materialization_serve_transform.ts +0 -779
  196. package/src/service/materialization_service.spec.ts +0 -2373
  197. package/src/service/materialization_service.ts +0 -2342
  198. package/src/service/materialization_test_fixtures.ts +0 -249
  199. package/src/service/model.spec.ts +0 -1157
  200. package/src/service/model.ts +0 -3370
  201. package/src/service/model_limits.spec.ts +0 -209
  202. package/src/service/model_limits.ts +0 -131
  203. package/src/service/model_storage_serve.spec.ts +0 -193
  204. package/src/service/model_storage_serve_joins.spec.ts +0 -193
  205. package/src/service/package.spec.ts +0 -612
  206. package/src/service/package.ts +0 -1799
  207. package/src/service/package_manifest.spec.ts +0 -230
  208. package/src/service/package_manifest.ts +0 -241
  209. package/src/service/package_memory_governor.spec.ts +0 -173
  210. package/src/service/package_memory_governor.ts +0 -233
  211. package/src/service/package_quote_bound_tables.spec.ts +0 -171
  212. package/src/service/package_race.spec.ts +0 -208
  213. package/src/service/package_reload_safety.spec.ts +0 -193
  214. package/src/service/package_rollback.spec.ts +0 -190
  215. package/src/service/package_worker_path.spec.ts +0 -645
  216. package/src/service/path_injection.spec.ts +0 -39
  217. package/src/service/persist_annotation_validation.spec.ts +0 -89
  218. package/src/service/persist_annotation_validation.ts +0 -59
  219. package/src/service/persistence_policy.spec.ts +0 -374
  220. package/src/service/proxy.spec.ts +0 -414
  221. package/src/service/proxy.ts +0 -248
  222. package/src/service/query_boundary.spec.ts +0 -470
  223. package/src/service/query_metadata.spec.ts +0 -408
  224. package/src/service/query_metadata.ts +0 -492
  225. package/src/service/query_metadata_identity.spec.ts +0 -149
  226. package/src/service/query_text.spec.ts +0 -79
  227. package/src/service/query_text.ts +0 -43
  228. package/src/service/quoting.spec.ts +0 -135
  229. package/src/service/quoting.ts +0 -78
  230. package/src/service/resolve_environment.ts +0 -15
  231. package/src/service/restricted_mode.spec.ts +0 -299
  232. package/src/service/source_extraction.ts +0 -225
  233. package/src/service/theme_store.ts +0 -199
  234. package/src/storage/DatabaseInterface.ts +0 -198
  235. package/src/storage/StorageManager.ts +0 -113
  236. package/src/storage/duckdb/ConnectionRepository.ts +0 -143
  237. package/src/storage/duckdb/DuckDBConnection.ts +0 -123
  238. package/src/storage/duckdb/DuckDBRepository.ts +0 -208
  239. package/src/storage/duckdb/EnvironmentRepository.ts +0 -148
  240. package/src/storage/duckdb/MaterializationRepository.spec.ts +0 -191
  241. package/src/storage/duckdb/MaterializationRepository.ts +0 -336
  242. package/src/storage/duckdb/PackageRepository.ts +0 -135
  243. package/src/storage/duckdb/schema.ts +0 -229
  244. package/src/stream_helpers.spec.ts +0 -280
  245. package/src/stream_helpers.ts +0 -162
  246. package/src/telemetry.ts +0 -20
  247. package/src/test_helpers/metrics_harness.ts +0 -166
  248. package/src/theme_key_parity.spec.ts +0 -57
  249. package/src/utils.ts +0 -29
  250. package/tests/fixtures/authorize-compile/model.malloy +0 -9
  251. package/tests/fixtures/authorize-compile/publisher.json +0 -4
  252. package/tests/fixtures/html-pages-nopublic/model.malloy +0 -1
  253. package/tests/fixtures/html-pages-nopublic/publisher.json +0 -5
  254. package/tests/fixtures/html-pages-test/data.csv +0 -3
  255. package/tests/fixtures/html-pages-test/public/assets/app.css +0 -3
  256. package/tests/fixtures/html-pages-test/public/barehtml.html +0 -4
  257. package/tests/fixtures/html-pages-test/public/bodymeta.html +0 -8
  258. package/tests/fixtures/html-pages-test/public/data.json +0 -1
  259. package/tests/fixtures/html-pages-test/public/fitcomment.html +0 -11
  260. package/tests/fixtures/html-pages-test/public/index.html +0 -9
  261. package/tests/fixtures/html-pages-test/public/nohead.html +0 -8
  262. package/tests/fixtures/html-pages-test/public/notfit.html +0 -13
  263. package/tests/fixtures/html-pages-test/public/slides.html +0 -12
  264. package/tests/fixtures/html-pages-test/public/sub/page2.html +0 -9
  265. package/tests/fixtures/html-pages-test/public/unterminated.html +0 -10
  266. package/tests/fixtures/html-pages-test/publisher.json +0 -5
  267. package/tests/fixtures/html-pages-test/report.malloy +0 -1
  268. package/tests/fixtures/persist-multi-level/data/orders.csv +0 -5
  269. package/tests/fixtures/persist-multi-level/multi_level.malloy +0 -18
  270. package/tests/fixtures/persist-multi-level/publisher.json +0 -5
  271. package/tests/fixtures/persist-schedule-test/data/orders.csv +0 -5
  272. package/tests/fixtures/persist-schedule-test/persist_schedule_test.malloy +0 -11
  273. package/tests/fixtures/persist-schedule-test/publisher.json +0 -7
  274. package/tests/fixtures/persist-schedule-warn-test/data/orders.csv +0 -5
  275. package/tests/fixtures/persist-schedule-warn-test/persist_warn_test.malloy +0 -11
  276. package/tests/fixtures/persist-schedule-warn-test/publisher.json +0 -6
  277. package/tests/fixtures/persist-test/data/orders.csv +0 -5
  278. package/tests/fixtures/persist-test/persist_test.malloy +0 -11
  279. package/tests/fixtures/persist-test/publisher.json +0 -5
  280. package/tests/fixtures/publisher.config.json +0 -15
  281. package/tests/fixtures/query-givens/data/orders.csv +0 -7
  282. package/tests/fixtures/query-givens/model.malloy +0 -34
  283. package/tests/fixtures/query-givens/publisher.json +0 -5
  284. package/tests/fixtures/xlsx/database.xlsx +0 -0
  285. package/tests/harness/mcp_test_setup.ts +0 -332
  286. package/tests/harness/rest_e2e.ts +0 -68
  287. package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +0 -92
  288. package/tests/integration/concurrent_package/concurrent_package.integration.spec.ts +0 -280
  289. package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +0 -138
  290. package/tests/integration/first_boot/readiness_line.integration.spec.ts +0 -177
  291. package/tests/integration/html_pages/html_pages.integration.spec.ts +0 -440
  292. package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +0 -259
  293. package/tests/integration/materialization/environment_list.integration.spec.ts +0 -133
  294. package/tests/integration/materialization/freshness_gate.integration.spec.ts +0 -292
  295. package/tests/integration/materialization/manifest_binding.integration.spec.ts +0 -430
  296. package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -441
  297. package/tests/integration/materialization/orchestrated_rebind.integration.spec.ts +0 -150
  298. package/tests/integration/materialization/reference_manifest.integration.spec.ts +0 -251
  299. package/tests/integration/materialization/schedule_edit.integration.spec.ts +0 -126
  300. package/tests/integration/materialization/scheduler.integration.spec.ts +0 -200
  301. package/tests/integration/materialization/scheduler_recovery.integration.spec.ts +0 -172
  302. package/tests/integration/materialization/scheduler_transitions.integration.spec.ts +0 -256
  303. package/tests/integration/materialization/scheduler_wiring.integration.spec.ts +0 -143
  304. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -438
  305. package/tests/integration/mcp/mcp_get_context_semantic.integration.spec.ts +0 -235
  306. package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -98
  307. package/tests/integration/query_givens/query_givens.integration.spec.ts +0 -146
  308. package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +0 -121
  309. package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +0 -110
  310. package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -421
  311. package/tests/unit/controller/theme.controller.spec.ts +0 -141
  312. package/tests/unit/duckdb/attached_databases.test.ts +0 -1243
  313. package/tests/unit/duckdb/duckdb_connection.test.ts +0 -181
  314. package/tests/unit/duckdb/legacy_schema_migration.test.ts +0 -197
  315. package/tests/unit/duckdb/repositories.test.ts +0 -208
  316. package/tests/unit/ducklake/ducklake.test.ts +0 -952
  317. package/tests/unit/service/theme_store.spec.ts +0 -139
  318. package/tsconfig.json +0 -32
@@ -1,119 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import {
3
- AccessDeniedError,
4
- BadRequestError,
5
- ConnectionAuthError,
6
- ConnectionError,
7
- internalErrorToHttpError,
8
- ModelCompilationError,
9
- NotQueryableError,
10
- PayloadTooLargeError,
11
- QueryTimeoutError,
12
- ServiceUnavailableError,
13
- } from "./errors";
14
-
15
- describe("internalErrorToHttpError", () => {
16
- it("maps ConnectionAuthError to 422", () => {
17
- const { status, json } = internalErrorToHttpError(
18
- new ConnectionAuthError("creds rejected for db_x"),
19
- );
20
- expect(status).toBe(422);
21
- expect(json).toEqual({
22
- code: 422,
23
- message: "creds rejected for db_x",
24
- });
25
- });
26
-
27
- it("maps BadRequestError to 400", () => {
28
- const { status, json } = internalErrorToHttpError(
29
- new BadRequestError("bad input"),
30
- );
31
- expect(status).toBe(400);
32
- expect(json).toEqual({ code: 400, message: "bad input" });
33
- });
34
-
35
- it("maps AccessDeniedError to 403 (authorize gate)", () => {
36
- const { status, json } = internalErrorToHttpError(
37
- new AccessDeniedError('Access denied for source "gated".'),
38
- );
39
- expect(status).toBe(403);
40
- expect(json).toEqual({
41
- code: 403,
42
- message: 'Access denied for source "gated".',
43
- });
44
- });
45
-
46
- it("maps NotQueryableError to 404 (explore boundary)", () => {
47
- const { status, json } = internalErrorToHttpError(
48
- new NotQueryableError('No queryable source "hidden".'),
49
- );
50
- expect(status).toBe(404);
51
- expect(json).toEqual({
52
- code: 404,
53
- message: 'No queryable source "hidden".',
54
- });
55
- });
56
-
57
- it("maps ModelCompilationError to 424", () => {
58
- const { status, json } = internalErrorToHttpError(
59
- new ModelCompilationError({ message: "compile failed" }),
60
- );
61
- expect(status).toBe(424);
62
- expect(json).toEqual({ code: 424, message: "compile failed" });
63
- });
64
-
65
- it("maps ConnectionError to 502 (distinct from auth, still retryable)", () => {
66
- const { status, json } = internalErrorToHttpError(
67
- new ConnectionError("upstream broken"),
68
- );
69
- expect(status).toBe(502);
70
- expect(json).toEqual({ code: 502, message: "upstream broken" });
71
- });
72
-
73
- it("falls through to 500 for unrecognized errors", () => {
74
- const { status, json } = internalErrorToHttpError(new Error("boom"));
75
- expect(status).toBe(500);
76
- expect(json.message).toBe("boom");
77
- });
78
-
79
- it("maps PayloadTooLargeError to 413", () => {
80
- const { status, json } = internalErrorToHttpError(
81
- new PayloadTooLargeError(
82
- "Query returned more than 100000 rows; refine the query or raise PUBLISHER_MAX_QUERY_ROWS.",
83
- ),
84
- );
85
- expect(status).toBe(413);
86
- expect(json).toEqual({
87
- code: 413,
88
- message:
89
- "Query returned more than 100000 rows; refine the query or raise PUBLISHER_MAX_QUERY_ROWS.",
90
- });
91
- });
92
-
93
- it("maps ServiceUnavailableError to 503 (load shedding / back-pressure)", () => {
94
- const { status, json } = internalErrorToHttpError(
95
- new ServiceUnavailableError(
96
- "Pod at max concurrent queries (32); retry later.",
97
- ),
98
- );
99
- expect(status).toBe(503);
100
- expect(json).toEqual({
101
- code: 503,
102
- message: "Pod at max concurrent queries (32); retry later.",
103
- });
104
- });
105
-
106
- it("maps QueryTimeoutError to 504 (gateway timeout, distinct from 503 back-pressure)", () => {
107
- const { status, json } = internalErrorToHttpError(
108
- new QueryTimeoutError(
109
- "Query exceeded PUBLISHER_QUERY_TIMEOUT_MS (300000ms) and was aborted.",
110
- ),
111
- );
112
- expect(status).toBe(504);
113
- expect(json).toEqual({
114
- code: 504,
115
- message:
116
- "Query exceeded PUBLISHER_QUERY_TIMEOUT_MS (300000ms) and was aborted.",
117
- });
118
- });
119
- });
package/src/errors.ts DELETED
@@ -1,237 +0,0 @@
1
- import { MalloyError } from "@malloydata/malloy";
2
- import { PUBLISHER_CONFIG_NAME } from "./constants";
3
-
4
- export function internalErrorToHttpError(error: Error) {
5
- if (error instanceof BadRequestError) {
6
- return httpError(400, error.message);
7
- } else if (error instanceof FrozenConfigError) {
8
- return httpError(403, error.message);
9
- } else if (error instanceof AccessDeniedError) {
10
- return httpError(403, error.message);
11
- } else if (error instanceof EnvironmentNotFoundError) {
12
- return httpError(404, error.message);
13
- } else if (error instanceof PackageNotFoundError) {
14
- return httpError(404, error.message);
15
- } else if (error instanceof ModelNotFoundError) {
16
- return httpError(404, error.message);
17
- } else if (error instanceof NotQueryableError) {
18
- return httpError(404, error.message);
19
- } else if (error instanceof MalloyError) {
20
- return httpError(400, error.message);
21
- } else if (error instanceof ConnectionNotFoundError) {
22
- return httpError(404, error.message);
23
- } else if (error instanceof ConnectionAuthError) {
24
- return httpError(422, error.message);
25
- } else if (error instanceof UnsupportedCatalogFormatError) {
26
- return httpError(422, error.message);
27
- } else if (error instanceof MaterializationEligibilityError) {
28
- return httpError(422, error.message);
29
- } else if (error instanceof ModelCompilationError) {
30
- return httpError(424, error.message);
31
- } else if (error instanceof ConnectionError) {
32
- return httpError(502, error.message);
33
- } else if (error instanceof MaterializationNotFoundError) {
34
- return httpError(404, error.message);
35
- } else if (error instanceof MaterializationConflictError) {
36
- return httpError(409, error.message);
37
- } else if (error instanceof InvalidStateTransitionError) {
38
- return httpError(409, error.message);
39
- } else if (error instanceof ServiceUnavailableError) {
40
- return httpError(503, error.message);
41
- } else if (error instanceof PayloadTooLargeError) {
42
- return httpError(413, error.message);
43
- } else if (error instanceof QueryTimeoutError) {
44
- return httpError(504, error.message);
45
- } else {
46
- return httpError(500, error.message);
47
- }
48
- }
49
-
50
- function httpError(code: number, message: string) {
51
- return {
52
- status: code,
53
- json: {
54
- code,
55
- message: message,
56
- },
57
- };
58
- }
59
-
60
- export class NotImplementedError extends Error {
61
- constructor(message: string) {
62
- super(message);
63
- }
64
- }
65
-
66
- export class BadRequestError extends Error {
67
- constructor(message: string) {
68
- super(message);
69
- }
70
- }
71
-
72
- export class EnvironmentNotFoundError extends Error {
73
- constructor(message: string) {
74
- super(message);
75
- }
76
- }
77
-
78
- export class PackageNotFoundError extends Error {
79
- constructor(message: string) {
80
- super(message);
81
- }
82
- }
83
-
84
- export class ModelNotFoundError extends Error {
85
- constructor(message: string) {
86
- super(message);
87
- }
88
- }
89
-
90
- export class ConnectionNotFoundError extends Error {
91
- constructor(message: string) {
92
- super(message);
93
- }
94
- }
95
-
96
- export class ConnectionError extends Error {
97
- constructor(message: string) {
98
- super(message);
99
- }
100
- }
101
-
102
- export class ConnectionAuthError extends Error {
103
- constructor(message: string) {
104
- super(message);
105
- }
106
- }
107
-
108
- // A catalog was reached and authenticated fine, but its on-disk format is
109
- // outside the range the pinned engine's extension can attach (see
110
- // ducklake_version.ts). Distinct from ConnectionAuthError so the 422 doesn't
111
- // read as a credentials problem. Maps to HTTP 422.
112
- export class UnsupportedCatalogFormatError extends Error {
113
- constructor(message: string) {
114
- super(message);
115
- }
116
- }
117
-
118
- export class ModelCompilationError extends Error {
119
- // Accepts a MalloyError or any message-bearing object, so callers that add
120
- // context around a compile failure (e.g. naming the source whose authorize
121
- // annotation failed) can reuse this 424 mapping without a separate class.
122
- constructor(error: { message: string }) {
123
- super(error.message);
124
- }
125
- }
126
-
127
- /**
128
- * A persist source was asked to materialize into a `storage=` destination (the
129
- * DuckDB/DuckLake tier) but is ineligible: it has an unbound free parameter, it
130
- * references a given (an RLAC/tenant-isolation refusal), or its served shape
131
- * does not compile in DuckDB. Mapped to HTTP **422** (the request is
132
- * well-formed, but the source cannot be processed into a materialized artifact)
133
- * — a hard refuse, never a silent fallback. Kept a distinct class so the
134
- * givens/RLAC refusal is greppable for security review. Accepts a
135
- * message-bearing object to match {@link ModelCompilationError}'s ergonomics.
136
- */
137
- export class MaterializationEligibilityError extends Error {
138
- constructor(error: { message: string }) {
139
- super(error.message);
140
- this.name = "MaterializationEligibilityError";
141
- }
142
- }
143
-
144
- export class FrozenConfigError extends Error {
145
- constructor(
146
- message = `Publisher config can't be updated when ${PUBLISHER_CONFIG_NAME} has { "frozenConfig": true }`,
147
- ) {
148
- super(message);
149
- }
150
- }
151
-
152
- /**
153
- * A request was denied by a source's `#(authorize)` gate (HTTP 403). Thrown by
154
- * the runtime authorize check when no in-scope expression evaluates true for
155
- * the supplied givens (including when a referenced given has no value).
156
- */
157
- export class AccessDeniedError extends Error {
158
- constructor(message: string) {
159
- super(message);
160
- this.name = "AccessDeniedError";
161
- }
162
- }
163
-
164
- /**
165
- * A query targeted a source/model that is not part of the package's queryable
166
- * surface under `queryableSources: "declared"` (a non-`explores` model file, or
167
- * a source not in a model's `export {}` closure). Mapped to HTTP **404**, not
168
- * 403, and with a deliberately generic message: unlike `#(authorize)` (which is
169
- * identity-scoped and answers "who"), the explore boundary is identity-free and
170
- * answers "what is queryable" — so a hidden target should be indistinguishable
171
- * from a non-existent one (no enumeration / existence oracle).
172
- */
173
- export class NotQueryableError extends Error {
174
- constructor(message: string) {
175
- super(message);
176
- this.name = "NotQueryableError";
177
- }
178
- }
179
-
180
- export class MaterializationNotFoundError extends Error {
181
- constructor(message: string) {
182
- super(message);
183
- }
184
- }
185
-
186
- export class MaterializationConflictError extends Error {
187
- constructor(message: string) {
188
- super(message);
189
- }
190
- }
191
-
192
- export class InvalidStateTransitionError extends Error {
193
- constructor(message: string) {
194
- super(message);
195
- }
196
- }
197
-
198
- /**
199
- * Thrown when the publisher is temporarily refusing a request to keep
200
- * RSS under the configured `PUBLISHER_MAX_MEMORY_BYTES` cap. Mapped to
201
- * HTTP 503 so an upstream proxy / client can retry with back-off.
202
- */
203
- export class ServiceUnavailableError extends Error {
204
- constructor(message: string) {
205
- super(message);
206
- }
207
- }
208
-
209
- /**
210
- * Thrown when a response would exceed a server-side size cap (e.g. an
211
- * ad-hoc connection SQL query that returned more than
212
- * `PUBLISHER_MAX_QUERY_ROWS` rows). Mapped to HTTP 413 so callers know
213
- * the request was well-formed but the result is too large for the
214
- * publisher to materialize; the remediation is "refine the query" or
215
- * "raise the cap", not "retry".
216
- */
217
- export class PayloadTooLargeError extends Error {
218
- constructor(message: string) {
219
- super(message);
220
- }
221
- }
222
-
223
- /**
224
- * Thrown when a query exceeded the configured wall-clock budget
225
- * (`PUBLISHER_QUERY_TIMEOUT_MS`) and the publisher aborted it
226
- * mid-execution. Mapped to HTTP 504 (`Gateway Timeout`) because the
227
- * publisher acts as a gateway to the underlying database — the
228
- * upstream caller did nothing wrong, but the downstream query took
229
- * too long. Distinct from {@link ServiceUnavailableError} so clients
230
- * can distinguish "back off, the pod is loaded" (503, retryable)
231
- * from "this specific query is too expensive" (504, refine it).
232
- */
233
- export class QueryTimeoutError extends Error {
234
- constructor(message: string) {
235
- super(message);
236
- }
237
- }
@@ -1,64 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { setFilterDeprecationHeaders } from "./filter_deprecation";
3
-
4
- const makeRes = () => {
5
- const headers: Record<string, string> = {};
6
- const res = {
7
- headers,
8
- setHeader(name: string, value: string) {
9
- headers[name] = value;
10
- return res;
11
- },
12
- };
13
- return res;
14
- };
15
-
16
- const DEPRECATION_LINK =
17
- '<https://github.com/malloydata/publisher/blob/main/docs/givens.md>; rel="deprecation"; type="text/markdown"';
18
-
19
- describe("setFilterDeprecationHeaders", () => {
20
- it("does not set headers when neither filterParams nor bypassFilters is supplied", () => {
21
- const res = makeRes();
22
- setFilterDeprecationHeaders(res, {});
23
- expect(res.headers.Deprecation).toBeUndefined();
24
- expect(res.headers.Link).toBeUndefined();
25
- });
26
-
27
- it("does not set headers for an explicit empty filterParams object (no-op opt-out)", () => {
28
- const res = makeRes();
29
- setFilterDeprecationHeaders(res, { filterParams: {} });
30
- expect(res.headers.Deprecation).toBeUndefined();
31
- expect(res.headers.Link).toBeUndefined();
32
- });
33
-
34
- it("sets RFC 8594 headers when filterParams carries values", () => {
35
- const res = makeRes();
36
- setFilterDeprecationHeaders(res, {
37
- filterParams: { region: ["US"] },
38
- });
39
- expect(res.headers.Deprecation).toBe("true");
40
- expect(res.headers.Link).toBe(DEPRECATION_LINK);
41
- });
42
-
43
- it("sets RFC 8594 headers when bypassFilters is true", () => {
44
- const res = makeRes();
45
- setFilterDeprecationHeaders(res, { bypassFilters: true });
46
- expect(res.headers.Deprecation).toBe("true");
47
- expect(res.headers.Link).toBe(DEPRECATION_LINK);
48
- });
49
-
50
- it("does not set headers when bypassFilters is undefined and filterParams is undefined", () => {
51
- const res = makeRes();
52
- setFilterDeprecationHeaders(res, {
53
- filterParams: undefined,
54
- bypassFilters: undefined,
55
- });
56
- expect(res.headers.Deprecation).toBeUndefined();
57
- });
58
-
59
- it("does not set headers when filterParams is null", () => {
60
- const res = makeRes();
61
- setFilterDeprecationHeaders(res, { filterParams: null });
62
- expect(res.headers.Deprecation).toBeUndefined();
63
- });
64
- });
@@ -1,42 +0,0 @@
1
- /**
2
- * Minimal structural type the helper needs from an HTTP response. Kept
3
- * narrower than `express.Response` so tests can pass a tiny stub instead
4
- * of constructing a full Express response.
5
- */
6
- export interface HeaderSetter {
7
- setHeader(name: string, value: string): unknown;
8
- }
9
-
10
- /**
11
- * Attach RFC 8594 deprecation headers when the request carries any of the
12
- * legacy `#(filter)` API surface (`filterParams` / `bypassFilters` on POST,
13
- * `filter_params` / `bypass_filters` on the notebook-cell GET). The
14
- * complementary operator-facing notice for legacy *models* (independent of
15
- * whether the caller used the deprecated request fields) ships as a
16
- * one-time warn log in `Model`'s constructor — see service/model.ts.
17
- *
18
- * `filterParams` here is the *parsed* value (after JSON.parse for the GET
19
- * notebook-cell path), so an empty `{}` is treated as a no-op opt-out — the
20
- * header only fires when the caller actually supplied filter values.
21
- */
22
- export const setFilterDeprecationHeaders = (
23
- res: HeaderSetter,
24
- options: { filterParams?: unknown; bypassFilters?: unknown },
25
- ): void => {
26
- const hasFilterParams =
27
- options.filterParams !== undefined &&
28
- options.filterParams !== null &&
29
- !(
30
- typeof options.filterParams === "object" &&
31
- !Array.isArray(options.filterParams) &&
32
- Object.keys(options.filterParams as Record<string, unknown>).length ===
33
- 0
34
- );
35
- if (hasFilterParams || options.bypassFilters !== undefined) {
36
- res.setHeader("Deprecation", "true");
37
- res.setHeader(
38
- "Link",
39
- '<https://github.com/malloydata/publisher/blob/main/docs/givens.md>; rel="deprecation"; type="text/markdown"',
40
- );
41
- }
42
- };
@@ -1,123 +0,0 @@
1
- import {
2
- afterAll,
3
- afterEach,
4
- beforeEach,
5
- describe,
6
- expect,
7
- it,
8
- spyOn,
9
- } from "bun:test";
10
- import { Server } from "http";
11
- import { performGracefulShutdownAfterDrain } from "./health";
12
- import { logger } from "./logger";
13
- import { __setPackageLoadPoolForTests } from "./package_load/package_load_pool";
14
-
15
- // Regression test for the graceful-shutdown ordering bug that caused
16
- // [winston] Attempt to write logs with no transports: {"message":"Waiting 50 seconds..."}
17
- // to appear in production logs. logger.close() must run after every
18
- // logger.* call, including the "Waiting ... seconds after server close
19
- // before exit..." message.
20
- //
21
- // Tests call performGracefulShutdownAfterDrain directly rather than
22
- // emitting SIGTERM, so module-level operationalState is not mutated
23
- // and the spec stays isolated from sibling tests in the same process.
24
- describe("performGracefulShutdownAfterDrain: shutdown ordering", () => {
25
- const originalExit = process.exit;
26
- let callOrder: string[];
27
-
28
- afterAll(async () => {
29
- // performGracefulShutdownAfterDrain drains the package-load worker pool
30
- // via getPackageLoadPool().shutdown() — which lazily CREATES the global
31
- // singleton and leaves it installed in its shut-down state. In prod the
32
- // process exits right after, so that's fine; in this shared test process
33
- // it poisons every later spec that touches the worker path
34
- // (Package.create → "PackageLoadPool is shutting down"), with failures
35
- // that come and go with bun's platform-dependent file order. Reset the
36
- // singleton so the next user lazily creates a fresh pool.
37
- await __setPackageLoadPoolForTests(null);
38
- });
39
-
40
- beforeEach(() => {
41
- callOrder = [];
42
-
43
- spyOn(logger, "info").mockImplementation(((msg: string) => {
44
- callOrder.push(`info:${msg}`);
45
- return logger;
46
- }) as never);
47
- spyOn(logger, "close").mockImplementation((() => {
48
- callOrder.push("close");
49
- return logger;
50
- }) as never);
51
- // Silence warn/error calls so spec output stays clean. They are
52
- // not load-bearing for these assertions.
53
- spyOn(logger, "warn").mockImplementation((() => logger) as never);
54
- spyOn(logger, "error").mockImplementation((() => logger) as never);
55
-
56
- process.exit = ((_code?: number) => {
57
- callOrder.push("exit");
58
- }) as never;
59
- });
60
-
61
- afterEach(() => {
62
- process.exit = originalExit;
63
- });
64
-
65
- // performGracefulShutdownAfterDrain lazily creates and shuts down the
66
- // module-level PackageLoadPool singleton. If we leave that shut-down
67
- // pool installed, sibling specs that run later in the same Bun process
68
- // (e.g. package.spec.ts, package_race.spec.ts) inherit a dead pool and
69
- // fail with "PackageLoadPool is shutting down". Reset the singleton so
70
- // the next getPackageLoadPool() spins up a fresh one. The leaked-pool
71
- // ordering is filesystem-readdir dependent, which is why it only
72
- // surfaced on Linux CI.
73
- afterAll(async () => {
74
- await __setPackageLoadPoolForTests(null);
75
- });
76
-
77
- const fakeServer = (): Server => ({ listening: false }) as unknown as Server;
78
-
79
- it("logs the 'Waiting ...' message before closing the logger", async () => {
80
- await performGracefulShutdownAfterDrain(fakeServer(), fakeServer(), 0.05);
81
-
82
- const waitingIdx = callOrder.findIndex((entry) =>
83
- entry.startsWith("info:Waiting"),
84
- );
85
- const closeIdx = callOrder.indexOf("close");
86
- const exitIdx = callOrder.indexOf("exit");
87
-
88
- expect(waitingIdx).toBeGreaterThanOrEqual(0);
89
- expect(closeIdx).toBeGreaterThanOrEqual(0);
90
- expect(exitIdx).toBeGreaterThanOrEqual(0);
91
- expect(waitingIdx).toBeLessThan(closeIdx);
92
- expect(closeIdx).toBeLessThan(exitIdx);
93
- });
94
-
95
- it("emits no logger.info calls after logger.close", async () => {
96
- await performGracefulShutdownAfterDrain(fakeServer(), fakeServer(), 0.05);
97
-
98
- const closeIdx = callOrder.indexOf("close");
99
- const lateInfoIdx = callOrder.findIndex(
100
- (entry, idx) => idx > closeIdx && entry.startsWith("info:"),
101
- );
102
- expect(closeIdx).toBeGreaterThanOrEqual(0);
103
- expect(lateInfoIdx).toBe(-1);
104
- });
105
-
106
- it("closes the logger exactly once", async () => {
107
- await performGracefulShutdownAfterDrain(fakeServer(), fakeServer(), 0.05);
108
-
109
- const closes = callOrder.filter((entry) => entry === "close").length;
110
- expect(closes).toBe(1);
111
- });
112
-
113
- it("skips the 'Waiting ...' message when gracefulCloseTimeoutSeconds is 0", async () => {
114
- await performGracefulShutdownAfterDrain(fakeServer(), fakeServer(), 0);
115
-
116
- const waitingCalls = callOrder.filter((entry) =>
117
- entry.startsWith("info:Waiting"),
118
- );
119
- expect(waitingCalls.length).toBe(0);
120
- expect(callOrder.indexOf("close")).toBeGreaterThanOrEqual(0);
121
- expect(callOrder.indexOf("exit")).toBeGreaterThanOrEqual(0);
122
- });
123
- });