@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,346 +0,0 @@
1
- /**
2
- * Centralized telemetry for materialization builds.
3
- *
4
- * Operators need to answer "are builds completing, how long do they take, and
5
- * where are they failing?" without grepping logs. The run counter carries
6
- * `mode` (`auto`|`orchestrated`) and `outcome` (`success`|`failed`|`cancelled`);
7
- * the run-duration histogram carries `mode` so a dashboard can render auto-run
8
- * vs orchestrated build latency side by side.
9
- *
10
- * Instruments are created lazily for the same reason as {@link ./query_cap_metrics}:
11
- * one created before `setGlobalMeterProvider` binds to a NoOp meter
12
- * (https://github.com/open-telemetry/opentelemetry-js/issues/3505).
13
- */
14
-
15
- import { type Counter, type Histogram } from "@opentelemetry/api";
16
- import { publisherMeter } from "./telemetry";
17
-
18
- export type MaterializationMode = "auto" | "orchestrated";
19
- export type MaterializationOutcome = "success" | "failed" | "cancelled";
20
- /** Manifest bind outcome: timeout is split out from generic failure on purpose. */
21
- export type ManifestBindOutcome = "success" | "failure" | "timeout";
22
- /**
23
- * Which engine a source build / table drop ran against: the source's own
24
- * warehouse (colocated, in-warehouse CTAS) or a DuckDB/DuckLake `storage=`
25
- * destination (federated passthrough CTAS). The two have very different latency
26
- * and failure profiles, so build/drop metrics are labeled with it rather than
27
- * pooling the two into one series.
28
- */
29
- export type StorageBuildEngine = "storage" | "in_warehouse";
30
- /** Why a source was refused materialization into a storage destination. */
31
- export type EligibilityRefusalReason =
32
- | "free_parameter"
33
- | "given"
34
- | "authorize"
35
- | "not_duckdb_portable"
36
- | "public_surface_unknown";
37
- /**
38
- * How a chained `storage=` source (one that reads a storage-materialized
39
- * upstream) was built: `parent_reuse` = built by reading the upstream's stored
40
- * lake table ("stack on the parent" — reuses the parent's work and is
41
- * consistent-by-construction); `inline_fallback` = stacking was ineligible/failed
42
- * so the upstream was recomputed from raw against the warehouse (non-strict);
43
- * `strict_refused` = stacking was ineligible under `strictUpstreams`, so
44
- * the build failed loudly rather than silently recomputing. This is the headline
45
- * signal for how far the parent-reuse path gets us in practice.
46
- */
47
- export type ChainedStorageBuildOutcome =
48
- | "parent_reuse"
49
- | "inline_fallback"
50
- | "strict_refused"
51
- // The parent-reuse attempt failed on infrastructure (attach, CTAS, the
52
- // destination being unreachable) rather than on shape. Distinct from
53
- // `inline_fallback` because it is an outage, not a modelling limit: the build
54
- // fails rather than recomputing from raw against the same destination.
55
- | "infra_failure";
56
-
57
- const resetHooks: (() => void)[] = [];
58
-
59
- /**
60
- * A lazily-created instrument: built from the global meter on first use (so it
61
- * binds to the real MeterProvider, not the NoOp default) and memoized. Each
62
- * registers a reset hook so a test can swap in a fresh MeterProvider.
63
- */
64
- function lazyCounter(name: string, description: string): () => Counter {
65
- let instrument: Counter | null = null;
66
- resetHooks.push(() => (instrument = null));
67
- return () =>
68
- (instrument ??= publisherMeter().createCounter(name, { description }));
69
- }
70
-
71
- function lazyHistogram(
72
- name: string,
73
- description: string,
74
- unit: string,
75
- ): () => Histogram {
76
- let instrument: Histogram | null = null;
77
- resetHooks.push(() => (instrument = null));
78
- return () =>
79
- (instrument ??= publisherMeter().createHistogram(name, {
80
- description,
81
- unit,
82
- }));
83
- }
84
-
85
- const runCounter = lazyCounter(
86
- "publisher_materialization_runs_total",
87
- "Materialization builds completed. Labels: mode ('auto'|'orchestrated'), outcome ('success'|'failed'|'cancelled').",
88
- );
89
- const runDuration = lazyHistogram(
90
- "publisher_materialization_run_duration_ms",
91
- "Wall-clock duration of a materialization build. Label: mode ('auto'|'orchestrated').",
92
- "ms",
93
- );
94
- const sourcesCounter = lazyCounter(
95
- "publisher_materialization_sources_total",
96
- "Persist sources processed by a materialization run. Label: outcome ('built'|'reused').",
97
- );
98
- const buildPlanComputeDuration = lazyHistogram(
99
- "publisher_materialization_build_plan_compute_duration_ms",
100
- "Wall-clock duration of compiling a package's build plan (Package.buildPlan).",
101
- "ms",
102
- );
103
- const autoLoadCounter = lazyCounter(
104
- "publisher_materialization_auto_load_total",
105
- "Auto-run manifest auto-load attempts. Label: outcome ('success'|'failure').",
106
- );
107
- const connectionDigestSkipCounter = lazyCounter(
108
- "publisher_materialization_connection_digest_skipped_total",
109
- "Connection digests skipped during build-plan compile because the connection did not resolve.",
110
- );
111
- const manifestBindCounter = lazyCounter(
112
- "publisher_materialization_manifest_bind_total",
113
- "Manifest bind attempts. Label: outcome ('success'|'failure'|'timeout').",
114
- );
115
- const manifestBindDegradedCounter = lazyCounter(
116
- "publisher_materialization_manifest_bind_degraded_total",
117
- "Manifest entries bound with an UNQUOTED table path because their connection " +
118
- "could not be resolved (serve-side bind) or is absent from the build " +
119
- "(build-side seed). A misconfiguration that breaks the source on a " +
120
- "case-folding engine (Snowflake); alertable.",
121
- );
122
- const sourceBuildDuration = lazyHistogram(
123
- "publisher_materialization_source_build_duration_ms",
124
- "Wall-clock duration of building a single persist source.",
125
- "ms",
126
- );
127
- const dropTablesCounter = lazyCounter(
128
- "publisher_materialization_drop_tables_total",
129
- "Physical tables dropped on delete. Label: outcome ('success'|'failure').",
130
- );
131
- const scheduledFireCounter = lazyCounter(
132
- "publisher_materialization_scheduled_fires_total",
133
- "Standalone-scheduler attempts to fire a package's materialization.schedule. " +
134
- "Label: outcome ('fired'|'conflict'|'error').",
135
- );
136
- const storageServeRoutingCounter = lazyCounter(
137
- "publisher_storage_serve_routing_total",
138
- "storage= serve routing decisions. Label: outcome ('storage'|'live_fallback'|'runtime_live_fallback').",
139
- );
140
- const storageBuildFailureCounter = lazyCounter(
141
- "publisher_storage_build_failures_total",
142
- "storage= build failures (federation/passthrough/attach/CTAS), distinct from " +
143
- "in-warehouse build failures. Label: destination (connection name).",
144
- );
145
- const eligibilityRefusedCounter = lazyCounter(
146
- "publisher_materialization_eligibility_refused_total",
147
- "storage= materialization-eligibility refusals. Label: reason " +
148
- "('free_parameter'|'given'|'authorize'|'not_duckdb_portable'|" +
149
- "'public_surface_unknown').",
150
- );
151
- const serveShapeTierDropCounter = lazyCounter(
152
- "publisher_storage_serve_shape_tier_drop_total",
153
- "storage serve-shape compile escalations: a refinement tier failed to " +
154
- "compile and the riskiest category was dropped. Label: tier (the failed " +
155
- "tier index, 0=full).",
156
- );
157
- const serveShapeTypeFallbackCounter = lazyCounter(
158
- "publisher_storage_serve_shape_type_fallback_total",
159
- "Captured DuckDB column types mapped to json in the serve shape (type " +
160
- "fidelity loss). Label: kind ('array'|'unrecognized').",
161
- );
162
- const chainedStorageBuildCounter = lazyCounter(
163
- "publisher_storage_chained_build_total",
164
- "Chained storage= source builds (a source reading a storage-materialized " +
165
- "upstream). Label: outcome ('parent_reuse'|'inline_fallback'|" +
166
- "'strict_refused'). The parent_reuse share is the headline signal for how " +
167
- "far the stack-on-the-parent path gets us vs recompute-from-raw.",
168
- );
169
-
170
- /**
171
- * Record a standalone-scheduler fire attempt. `fired` = a SCHEDULER run started;
172
- * `conflict` = a materialization was already active (this occurrence coalesces
173
- * into it); `error` = the fire failed unexpectedly.
174
- */
175
- export function recordScheduledFire(
176
- outcome: "fired" | "conflict" | "error",
177
- ): void {
178
- scheduledFireCounter().add(1, { outcome });
179
- }
180
-
181
- /** Record the outcome and duration of a materialization build. */
182
- export function recordMaterializationRun(
183
- mode: MaterializationMode,
184
- outcome: MaterializationOutcome,
185
- durationMs: number,
186
- ): void {
187
- runCounter().add(1, { mode, outcome });
188
- runDuration().record(durationMs, { mode });
189
- }
190
-
191
- /**
192
- * Record how many persist sources a run built vs. reused (carried forward
193
- * unchanged via skip-if-unchanged). Lets a dashboard show the reuse ratio,
194
- * the main lever on materialization cost.
195
- */
196
- export function recordSourcesOutcome(
197
- outcome: "built" | "reused",
198
- count: number,
199
- ): void {
200
- if (count <= 0) return;
201
- sourcesCounter().add(count, { outcome });
202
- }
203
-
204
- /**
205
- * Record the wall-clock cost of compiling a package's build plan
206
- * (`Package.buildPlan`). This recompiles models at load, so it is worth
207
- * tracking as a discrete cost separate from the build itself.
208
- */
209
- export function recordBuildPlanComputeDuration(durationMs: number): void {
210
- buildPlanComputeDuration().record(durationMs);
211
- }
212
-
213
- /**
214
- * Record an auto-run manifest auto-load attempt. Auto-load is best-effort (a
215
- * failure does not fail the run), so a failure here is otherwise invisible:
216
- * the run is MANIFEST_FILE_READY but queries still resolve to the old tables.
217
- */
218
- export function recordAutoLoadOutcome(outcome: "success" | "failure"): void {
219
- autoLoadCounter().add(1, { outcome });
220
- }
221
-
222
- /**
223
- * Record that a connection digest could not be computed during build-plan
224
- * compilation because the connection failed to resolve. The resulting sourceEntityIds
225
- * are computed without a digest, so this is a correctness signal, not just noise.
226
- */
227
- export function recordConnectionDigestSkipped(): void {
228
- connectionDigestSkipCounter().add(1);
229
- }
230
-
231
- /**
232
- * Record a manifest-bind attempt (publisher binding a configured manifest to a
233
- * package at load). `timeout` is distinguished from `failure` so operators can
234
- * tell an unreachable/slow manifest store from a malformed manifest.
235
- */
236
- export function recordManifestBind(outcome: ManifestBindOutcome): void {
237
- manifestBindCounter().add(1, { outcome });
238
- }
239
-
240
- /**
241
- * A single manifest entry bound with an unquoted table path because its
242
- * connection could not be resolved for quoting. Per-entry degradation (the rest
243
- * of the bind succeeds), distinct from the whole-bind {@link recordManifestBind}
244
- * outcome — hence its own counter rather than a label on that one.
245
- */
246
- export function recordManifestBindDegraded(): void {
247
- manifestBindDegradedCounter().add(1);
248
- }
249
-
250
- /**
251
- * Record the wall-clock duration of building one persist source's table,
252
- * labeled by engine so the DuckDB passthrough path and the in-warehouse CTAS
253
- * path (very different latency profiles) don't pool into one series.
254
- */
255
- export function recordSourceBuildDuration(
256
- durationMs: number,
257
- engine: StorageBuildEngine,
258
- ): void {
259
- sourceBuildDuration().record(durationMs, { engine });
260
- }
261
-
262
- /** Record a best-effort physical-table drop on materialization delete. */
263
- export function recordDropTables(
264
- outcome: "success" | "failure",
265
- engine: StorageBuildEngine,
266
- ): void {
267
- dropTablesCounter().add(1, { outcome, engine });
268
- }
269
-
270
- /**
271
- * Record a `storage=` build failure (federation / passthrough / attach / CTAS),
272
- * counted separately from in-warehouse build failures because the storage path
273
- * has its own failure modes and destination axis.
274
- */
275
- export function recordStorageBuildFailure(destination: string): void {
276
- storageBuildFailureCounter().add(1, { destination });
277
- }
278
-
279
- /**
280
- * Record a materialization-eligibility refusal (a source that can't be safely
281
- * materialized into a storage destination). The `given` reason is a security
282
- * refusal (a frozen given-filtered table would leak rows across tenants); worth
283
- * tracking how often authors attempt it.
284
- */
285
- export function recordEligibilityRefused(
286
- reason: EligibilityRefusalReason,
287
- ): void {
288
- eligibilityRefusedCounter().add(1, { reason });
289
- }
290
-
291
- /**
292
- * Record a serve-shape compile escalation: the refinement tier at `failedTier`
293
- * did not compile, so the riskiest category was dropped and the shape retried.
294
- * A systematically-dropping source tells authors which refinements aren't
295
- * servable from storage.
296
- */
297
- export function recordServeShapeTierDrop(failedTier: number): void {
298
- serveShapeTierDropCounter().add(1, { tier: String(failedTier) });
299
- }
300
-
301
- /**
302
- * Record a captured DuckDB column type that could not map onto a Malloy basic
303
- * type and was carried as json in the serve shape — a type-fidelity loss
304
- * specific to the storage tier. `array` = a collection/array type; `unrecognized`
305
- * = an unknown/nested/struct/enum/blob type.
306
- */
307
- export function recordServeShapeTypeFallback(
308
- kind: "array" | "unrecognized",
309
- ): void {
310
- serveShapeTypeFallbackCounter().add(1, { kind });
311
- }
312
-
313
- /**
314
- * Record a `storage=` serve-routing decision: `storage` = the query was served
315
- * from the materialized table via the virtual-source transform; `live_fallback`
316
- * = the transform was ineligible for this query (a refinement it can't
317
- * reproduce, an unbound source, mode not `on`) so it was served live;
318
- * `runtime_live_fallback` = the query DID route to storage and the store then
319
- * failed underneath it, and every binding's `freshnessFallback=live` allowed it
320
- * to degrade. That last one is the operationally interesting label: it means the
321
- * tier is broken while queries still succeed, which is invisible in the hit rate
322
- * alone. This hit rate is the headline KPI of the storage tier — otherwise the
323
- * fallback side is only a DEBUG log.
324
- */
325
- export function recordStorageServeRouting(
326
- outcome: "storage" | "live_fallback" | "runtime_live_fallback",
327
- ): void {
328
- storageServeRoutingCounter().add(1, { outcome });
329
- }
330
-
331
- /**
332
- * Record how a chained `storage=` source was built (see
333
- * {@link ChainedStorageBuildOutcome}). Only fired for a source that actually
334
- * reads a storage-materialized upstream — a single-source build emits nothing
335
- * here. The parent_reuse : inline_fallback ratio is the spike's key learning.
336
- */
337
- export function recordChainedStorageBuild(
338
- outcome: ChainedStorageBuildOutcome,
339
- ): void {
340
- chainedStorageBuildCounter().add(1, { outcome });
341
- }
342
-
343
- /** Visible for tests. Drops cached instruments so a fresh MeterProvider can capture emissions. */
344
- export function resetMaterializationTelemetryForTesting(): void {
345
- for (const reset of resetHooks) reset();
346
- }
@@ -1,35 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { AccessDeniedError } from "../errors";
3
- import { getMalloyErrorDetails } from "./error_messages";
4
-
5
- describe("getMalloyErrorDetails — access-denied branch", () => {
6
- it("recognizes an authorize denial and gives access-relevant (not syntax) advice", () => {
7
- const details = getMalloyErrorDetails(
8
- "executeQuery",
9
- "env/pkg/model.malloy",
10
- new AccessDeniedError('Access denied for source "gated".'),
11
- );
12
-
13
- // Message carries the source name, never the gate expression.
14
- expect(details.message).toContain('Access denied for source "gated".');
15
-
16
- // The suggestion is about satisfying access (givens/role), and the
17
- // generic Malloy-syntax suggestions are replaced, not appended.
18
- expect(details.suggestions).toHaveLength(1);
19
- expect(details.suggestions[0]).toMatch(/given|authorize|restricted/i);
20
- // Not the generic "check the database connection / consult the language
21
- // docs" advice that an unrecognized error would yield.
22
- expect(details.suggestions.join(" ")).not.toMatch(
23
- /database connection configuration|language documentation/i,
24
- );
25
- });
26
-
27
- it("still falls back to generic suggestions for an unrecognized error", () => {
28
- const details = getMalloyErrorDetails(
29
- "executeQuery",
30
- "env/pkg/model.malloy",
31
- new Error("something unexpected"),
32
- );
33
- expect(details.suggestions.length).toBeGreaterThan(1);
34
- });
35
- });
@@ -1,183 +0,0 @@
1
- export interface ErrorDetails {
2
- message: string;
3
- suggestions: string[];
4
- }
5
-
6
- /**
7
- * Generates error details for a resource not found scenario.
8
- * @param resourceUriOrContext The URI that was not found, or a context string (e.g., "Package 'X'", "Model 'Y' in package 'X'").
9
- * @returns ErrorDetails object with message and suggestions.
10
- */
11
- export function getNotFoundError(resourceUriOrContext: string): ErrorDetails {
12
- const baseMessage = `Resource not found: ${resourceUriOrContext}`;
13
- const suggestions: string[] = [
14
- `Verify the identifier (${resourceUriOrContext}) is spelled correctly and exists. Check capitalization and path separators.`,
15
- "Check if the resource exists and is correctly named in your Malloy environment structure or the specific model file.",
16
- ];
17
-
18
- return {
19
- message: baseMessage,
20
- suggestions: suggestions,
21
- };
22
- }
23
-
24
- /**
25
- * Generates generic error details for internal server errors.
26
- * @param operation The operation that failed (e.g., 'executeQuery').
27
- * @param error Optional: The underlying error object or message.
28
- * @returns ErrorDetails object.
29
- */
30
- export function getInternalError(
31
- operation: string,
32
- error?: unknown,
33
- ): ErrorDetails {
34
- const baseMessage = `An unexpected internal error occurred during ${operation}.`;
35
- const errorMessage = error instanceof Error ? error.message : String(error);
36
- return {
37
- message: error ? `${baseMessage}: ${errorMessage}` : baseMessage,
38
- suggestions: [
39
- "Try the request again later.",
40
- "If the problem persists, check server logs or contact support.",
41
- ],
42
- };
43
- }
44
-
45
- /**
46
- * Generates detailed error information for Malloy compilation or query execution errors.
47
- * @param operation The operation that failed (e.g., 'executeQuery (load model)', 'executeQuery').
48
- * @param modelIdentifier A path or URI identifying the model/notebook involved.
49
- * @param error The underlying Malloy error object or other error.
50
- * @returns ErrorDetails object.
51
- */
52
- export function getMalloyErrorDetails(
53
- operation: string,
54
- modelIdentifier: string,
55
- error: unknown,
56
- ): ErrorDetails {
57
- let baseMessage = `Error during ${operation} for resource '${modelIdentifier}'.`;
58
- const initialSuggestions: string[] = [
59
- `Verify the structure and syntax within the Malloy file: ${modelIdentifier}. Check for typos in source, query, or view names (e.g., 'source: my_source is ...', 'query: my_query is ...', 'view: my_view is { ... }'). See Model Structure: https://docs.malloydata.dev/documentation/language/statement`,
60
- "Ensure all referenced sources, queries, or views are correctly defined within the model or imported files using expected keywords (e.g., 'source:', 'query:', 'view:'). See Sources: https://docs.malloydata.dev/documentation/language/source",
61
- "Check the database connection configuration used by the model, if applicable.",
62
- "Consult the Malloy language documentation for syntax rules: https://docs.malloydata.dev/documentation/",
63
- ];
64
-
65
- let suggestions: string[] = [...initialSuggestions]; // Clone initial suggestions
66
- let refined = false; // Flag to track if specific suggestions were added
67
-
68
- // Attempt to extract more specific info if it's a MalloyError or similar
69
- if (error instanceof Error) {
70
- // Prepend the specific error message
71
- baseMessage = `Error during ${operation} for resource '${modelIdentifier}': ${error.message}`;
72
-
73
- // --- Add more specific suggestions based on common Malloy error patterns ---
74
- // Note: Order matters, check for more specific patterns first.
75
-
76
- const viewNotFoundMatch = error.message.match(
77
- /View '([^']+)' not found in source '([^']+)'/i,
78
- );
79
- const sourceNotFoundMatch = error.message.match(
80
- /Source '([^']+)' not found/i,
81
- );
82
- const queryNotFoundMatch = error.message.match(
83
- /Query '([^']+)' not found/i,
84
- );
85
- const referenceErrorMatch = error.message.match(
86
- /Reference to undefined object(?: '([^']+)')?/i,
87
- );
88
- const syntaxErrorMatch = error.message.match(
89
- /no viable alternative at input/i,
90
- );
91
- const connectionErrorMatch = error.message.match(
92
- /Cannot connect to database/i,
93
- );
94
- const fieldNotFoundMatch = error.message.match(
95
- /Field '([^']+)' not found in (source|query|view) '([^']+)'/i,
96
- );
97
- const invalidRequestMatch = error.message.match(
98
- /Invalid query request\\. Query OR queryName must be defined/i,
99
- );
100
- // `#(authorize)` gate denial. The message names only the source (gate
101
- // logic is never leaked), so the suggestion is about satisfying access,
102
- // not fixing syntax — otherwise this falls through to generic Malloy
103
- // syntax advice that misleads the caller.
104
- const accessDeniedMatch = error.message.match(
105
- /Access denied for source "([^"]+)"/i,
106
- );
107
-
108
- if (accessDeniedMatch) {
109
- refined = true;
110
- const [, sourceName] = accessDeniedMatch;
111
- suggestions = [
112
- `Suggestion: Access to source '${sourceName}' is restricted by an #(authorize) gate. Supply the givens its authorize expression requires (e.g. a role/region given) and retry. This is an authorization denial, not a syntax error.`,
113
- ];
114
- } else if (viewNotFoundMatch) {
115
- refined = true;
116
- const [, viewName, sourceName] = viewNotFoundMatch;
117
- suggestions.unshift(
118
- `Suggestion: View '${viewName}' was not found in source '${sourceName}'. Check the view name spelling or call malloy_getContext with sourceName '${sourceName}' to see the list of available views. Views are defined within sources like 'source: ${sourceName} is ... extend { view: ${viewName} is { ... } }'.`,
119
- );
120
- } else if (sourceNotFoundMatch) {
121
- refined = true;
122
- const [, sourceName] = sourceNotFoundMatch;
123
- suggestions.unshift(
124
- `Suggestion: Source '${sourceName}' was not found or could not be accessed. Verify its definition (e.g., \`source: ${sourceName} is table('...')\` or \`duckdb.sql("...")\`) and ensure any associated connections are valid.`,
125
- `Suggestion: Check the spelling of '${sourceName}'. You can list the sources in the package using malloy_getContext.`,
126
- );
127
- } else if (queryNotFoundMatch) {
128
- refined = true;
129
- const [, queryName] = queryNotFoundMatch;
130
- suggestions.unshift(
131
- `Suggestion: Named query '${queryName}' was not found. Verify its definition (e.g., \`query: ${queryName} is source_name -> { ... }\`) within the model '${modelIdentifier}'.`,
132
- `Suggestion: Check the spelling of '${queryName}'. Ensure it's a named query (defined with \`query:\`), not a view. You can list named queries using malloy_getContext.`,
133
- );
134
- } else if (fieldNotFoundMatch) {
135
- refined = true;
136
- const [, fieldName, fieldContextType, fieldContextName] =
137
- fieldNotFoundMatch;
138
- suggestions.unshift(
139
- `Suggestion: Field '${fieldName}' was not found in ${fieldContextType} '${fieldContextName}'. Check the spelling of the field name within the definition of '${fieldContextName}'.`,
140
- `Suggestion: Ensure the field is defined directly or inherited correctly in the '${fieldContextName}' ${fieldContextType}. You can inspect the ${fieldContextType}'s fields using malloy_getContext.`,
141
- );
142
- } else if (referenceErrorMatch) {
143
- refined = true;
144
- const objectName = referenceErrorMatch[1] || "[unknown object]";
145
- suggestions.unshift(
146
- `Suggestion: Check if the referenced object '${objectName}' (source, query, view, field, dimension, measure) is defined correctly and spelled correctly (e.g., 'source: my_source is ...', 'query: my_query is ...'). Ensure it's defined *before* being used in the model file.`,
147
- );
148
- } else if (syntaxErrorMatch) {
149
- refined = true;
150
- suggestions.unshift(
151
- "Suggestion: There seems to be a syntax error near the location indicated in the error message. Check for misplaced/missing keywords (like 'is', '->', 'extend', 'where', 'aggregate', 'group_by', 'nest'), commas, brackets '{ }', or parentheses '( )'. See Query Syntax: https://docs.malloydata.dev/documentation/language/query",
152
- );
153
- } else if (connectionErrorMatch) {
154
- refined = true;
155
- suggestions.unshift(
156
- "Suggestion: Verify the database connection details (credentials, host, database name, connection name in Malloy) associated with this model.",
157
- );
158
- suggestions.push(
159
- "Ensure the database server is running and accessible from the publisher server.",
160
- );
161
- } else if (invalidRequestMatch) {
162
- refined = true;
163
- suggestions.unshift(
164
- "Suggestion: This error can occur when specifying a view using 'sourceName' and 'queryName'. Try providing the full query string directly in the 'query' parameter instead (e.g., 'query': 'run: your_source_name->your_view_name').",
165
- "Suggestion: Also verify you are providing either the 'query' parameter OR the correct combination of 'queryName' (and optionally 'sourceName' for views).",
166
- );
167
- }
168
-
169
- // If no specific pattern matched, keep the initial suggestions
170
- if (!refined) {
171
- suggestions = initialSuggestions;
172
- }
173
- } else {
174
- // Generic message if it's not a standard Error object
175
- baseMessage += `: ${String(error)}`;
176
- suggestions = initialSuggestions; // Use initial for non-errors too
177
- }
178
-
179
- return {
180
- message: baseMessage,
181
- suggestions: suggestions,
182
- };
183
- }
@@ -1,108 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { MalloyError } from "@malloydata/malloy";
3
- import { classifyToolError } from "./handler_utils";
4
- import {
5
- AccessDeniedError,
6
- BadRequestError,
7
- ModelCompilationError,
8
- PackageNotFoundError,
9
- ServiceUnavailableError,
10
- } from "../errors";
11
-
12
- /**
13
- * classifyToolError decides what an agent is told to do about a failure, so
14
- * every branch is pinned by the advice it produces, not just by "an error came
15
- * back". Asserting only that an error exists is what let every class funnel
16
- * through the Malloy helper unnoticed.
17
- *
18
- * The routing is by error CLASS on purpose. getMalloyErrorDetails refines on
19
- * message patterns the shipped engine does not emit, so "did it refine?" would
20
- * send real compile errors to the internal branch.
21
- */
22
- describe("classifyToolError", () => {
23
- const advice = (e: unknown) =>
24
- JSON.stringify(classifyToolError("op", "env/pkg", e).suggestions);
25
-
26
- it("homes a missing package as not-found, not as Malloy", () => {
27
- const details = classifyToolError(
28
- "op",
29
- "env/pkg",
30
- new PackageNotFoundError("Package 'nope' not found"),
31
- );
32
- expect(details.message).toContain("Resource not found");
33
- expect(JSON.stringify(details.suggestions)).not.toContain("Malloy file");
34
- });
35
-
36
- it("homes back-pressure as retryable, not as Malloy", () => {
37
- const details = classifyToolError(
38
- "op",
39
- "env/pkg",
40
- new ServiceUnavailableError("Memory limit reached"),
41
- );
42
- expect(details.message).toContain("Memory limit reached");
43
- expect(JSON.stringify(details.suggestions)).toContain("Retry");
44
- expect(JSON.stringify(details.suggestions)).not.toContain("Malloy file");
45
- });
46
-
47
- it("keeps Malloy advice for a raw engine error", () => {
48
- // The regression guard for executeQuery. A bad query throws MalloyError,
49
- // NOT ModelCompilationError: the engine's error reaches the tool's catch
50
- // unwrapped. Route it by the internal branch and a syntax error tells the
51
- // agent an "unexpected internal error occurred", which is worse than the
52
- // bug this classifier was written to fix.
53
- const details = classifyToolError(
54
- "executeQuery",
55
- "env/pkg/m.malloy",
56
- new MalloyError("unexpected '@'", []),
57
- );
58
- expect(details.message).not.toContain("unexpected internal error");
59
- expect(advice(new MalloyError("unexpected '@'", []))).toContain("Malloy");
60
- });
61
-
62
- it("keeps Malloy advice for a wrapped compile error", () => {
63
- // What a failed reload throws. #890's promise is that a failed reload
64
- // returns the compile errors, so this must not read as an internal fault.
65
- const details = classifyToolError(
66
- "reloadPackage",
67
- "env/pkg",
68
- new ModelCompilationError({
69
- message: "Error(s) compiling model: unexpected '@'",
70
- }),
71
- );
72
- expect(details.message).not.toContain("unexpected internal error");
73
- });
74
-
75
- it("keeps Malloy advice for an authorize denial and a malformed request", () => {
76
- expect(
77
- classifyToolError(
78
- "op",
79
- "env/pkg",
80
- new AccessDeniedError('Access denied for source "orders"'),
81
- ).message,
82
- ).not.toContain("unexpected internal error");
83
- expect(
84
- classifyToolError(
85
- "op",
86
- "env/pkg",
87
- new BadRequestError("Invalid query request."),
88
- ).message,
89
- ).not.toContain("unexpected internal error");
90
- });
91
-
92
- it("reports anything else as internal rather than blaming the Malloy", () => {
93
- // A bug in our own code, a filesystem failure, or a worker crash. Telling
94
- // the agent to check its syntax sends it to edit a model that is fine.
95
- for (const error of [
96
- new TypeError("cannot read properties of undefined"),
97
- Object.assign(new Error("EACCES: permission denied"), {
98
- code: "EACCES",
99
- }),
100
- ]) {
101
- const details = classifyToolError("op", "env/pkg", error);
102
- expect(details.message).toContain("unexpected internal error");
103
- expect(JSON.stringify(details.suggestions)).not.toContain(
104
- "Malloy file",
105
- );
106
- }
107
- });
108
- });