@malloy-publisher/server 0.0.235 → 0.0.237

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 (319) hide show
  1. package/dist/app/api-doc.yaml +83 -27
  2. package/dist/app/assets/{EnvironmentPage-BsAnavYN.js → EnvironmentPage-DaP6CpgK.js} +1 -1
  3. package/dist/app/assets/{HomePage-CADE138j.js → HomePage-C9yNw3iu.js} +1 -1
  4. package/dist/app/assets/{LightMode-Cfh7KzN8.js → LightMode-Be4-G-dX.js} +1 -1
  5. package/dist/app/assets/{MainPage-CO3pRlnV.js → MainPage-J_rU2SgI.js} +2 -2
  6. package/dist/app/assets/{MaterializationsPage-p9YjkRXZ.js → MaterializationsPage-DHgPI4MO.js} +1 -1
  7. package/dist/app/assets/ModelPage-BzXfjTu5.js +1 -0
  8. package/dist/app/assets/{PackagePage-e4kN75YR.js → PackagePage-ByoSTLJ4.js} +1 -1
  9. package/dist/app/assets/RouteError-CrSeKN2r.js +1 -0
  10. package/dist/app/assets/{ThemeEditorPage-CciagFTq.js → ThemeEditorPage-Btxnmhtk.js} +1 -1
  11. package/dist/app/assets/{WorkbookPage-DNWmkCXa.js → WorkbookPage-D6JLPnWO.js} +1 -1
  12. package/dist/app/assets/{core-Rj_4rRnA.es-BZyvITuO.js → core-BSRgt1mG.es-Bc_D2Lq4.js} +1 -1
  13. package/dist/app/assets/{index-CH2AcDzc.js → index-BRijWj6K.js} +15 -15
  14. package/dist/app/assets/{index-DQa463gC.js → index-BX4D94xw.js} +1 -1
  15. package/dist/app/assets/{index-VBbcc8s6.js → index-Cvy0xtb7.js} +1 -1
  16. package/dist/app/assets/{index-DQpV7MyA.js → index-DMAhIJNC.js} +1 -1
  17. package/dist/app/index.html +1 -1
  18. package/dist/package_load_worker.mjs +59 -20
  19. package/dist/server.mjs +1342 -616
  20. package/package.json +8 -2
  21. package/.eslintrc.json +0 -44
  22. package/.prettierignore +0 -1
  23. package/.prettierrc +0 -4
  24. package/build.ts +0 -97
  25. package/dist/app/assets/ModelPage-C1OSTv-x.js +0 -1
  26. package/dist/app/assets/RouteError-CzbfOkng.js +0 -1
  27. package/eslint.config.mjs +0 -76
  28. package/k6-tests/README.md +0 -144
  29. package/k6-tests/bun.lock +0 -797
  30. package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
  31. package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
  32. package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
  33. package/k6-tests/load-test/load-test-crud.ts +0 -142
  34. package/k6-tests/load-test/load-test.ts +0 -784
  35. package/k6-tests/package.json +0 -27
  36. package/k6-tests/scripts/run-with-otel.sh +0 -50
  37. package/k6-tests/smoke-test/smoke-test.ts +0 -158
  38. package/k6-tests/tsconfig.json +0 -29
  39. package/k6-tests/utils/client_factory.ts +0 -128
  40. package/k6-tests/utils/common.ts +0 -489
  41. package/k6-tests/utils/logger.ts +0 -62
  42. package/k6-tests/utils/types.d.ts +0 -35
  43. package/malloy_mcp.dxt +0 -0
  44. package/publisher.config.example.bigquery.json +0 -33
  45. package/publisher.config.example.duckdb.json +0 -23
  46. package/publisher.config.json +0 -23
  47. package/scripts/bake-duckdb-extensions.js +0 -110
  48. package/src/config.spec.ts +0 -1535
  49. package/src/config.theme.spec.ts +0 -178
  50. package/src/config.ts +0 -1142
  51. package/src/constants.ts +0 -105
  52. package/src/controller/compile.controller.ts +0 -40
  53. package/src/controller/connection.controller.spec.ts +0 -849
  54. package/src/controller/connection.controller.ts +0 -901
  55. package/src/controller/database.controller.ts +0 -24
  56. package/src/controller/materialization.controller.spec.ts +0 -293
  57. package/src/controller/materialization.controller.ts +0 -319
  58. package/src/controller/model.controller.ts +0 -164
  59. package/src/controller/package.controller.spec.ts +0 -273
  60. package/src/controller/package.controller.ts +0 -306
  61. package/src/controller/query.controller.ts +0 -152
  62. package/src/controller/theme.controller.ts +0 -83
  63. package/src/controller/watch-mode.controller.ts +0 -243
  64. package/src/data_styles.ts +0 -71
  65. package/src/default-publisher.config.json +0 -23
  66. package/src/ducklake_version.spec.ts +0 -163
  67. package/src/ducklake_version.ts +0 -153
  68. package/src/errors.spec.ts +0 -119
  69. package/src/errors.ts +0 -237
  70. package/src/filter_deprecation.spec.ts +0 -64
  71. package/src/filter_deprecation.ts +0 -42
  72. package/src/health.spec.ts +0 -123
  73. package/src/health.ts +0 -233
  74. package/src/heap_check.spec.ts +0 -144
  75. package/src/heap_check.ts +0 -144
  76. package/src/instrumentation.ts +0 -214
  77. package/src/json_utils.spec.ts +0 -51
  78. package/src/json_utils.ts +0 -33
  79. package/src/logger.spec.ts +0 -210
  80. package/src/logger.ts +0 -251
  81. package/src/malloy_pin_prereqs.spec.ts +0 -25
  82. package/src/materialization_metrics.spec.ts +0 -169
  83. package/src/materialization_metrics.ts +0 -346
  84. package/src/mcp/error_messages.spec.ts +0 -35
  85. package/src/mcp/error_messages.ts +0 -183
  86. package/src/mcp/handler_utils.spec.ts +0 -108
  87. package/src/mcp/handler_utils.ts +0 -251
  88. package/src/mcp/mcp_constants.ts +0 -6
  89. package/src/mcp/query_envelope.spec.ts +0 -229
  90. package/src/mcp/query_envelope.ts +0 -240
  91. package/src/mcp/server.protocol.spec.ts +0 -250
  92. package/src/mcp/server.ts +0 -87
  93. package/src/mcp/skills/build_skills_bundle.spec.ts +0 -51
  94. package/src/mcp/skills/build_skills_bundle.ts +0 -176
  95. package/src/mcp/skills/skills_bundle.json +0 -1
  96. package/src/mcp/skills/skills_bundle.spec.ts +0 -171
  97. package/src/mcp/tool_response.spec.ts +0 -108
  98. package/src/mcp/tool_response.ts +0 -138
  99. package/src/mcp/tools/compile_tool.spec.ts +0 -315
  100. package/src/mcp/tools/compile_tool.ts +0 -208
  101. package/src/mcp/tools/docs_search/build_docs_index.ts +0 -132
  102. package/src/mcp/tools/docs_search/malloy_docs_index.json +0 -1
  103. package/src/mcp/tools/docs_search_tool.spec.ts +0 -32
  104. package/src/mcp/tools/docs_search_tool.ts +0 -128
  105. package/src/mcp/tools/embedding_index.spec.ts +0 -1236
  106. package/src/mcp/tools/embedding_index.ts +0 -808
  107. package/src/mcp/tools/execute_query_tool.spec.ts +0 -294
  108. package/src/mcp/tools/execute_query_tool.ts +0 -316
  109. package/src/mcp/tools/get_context_eval.ts +0 -275
  110. package/src/mcp/tools/get_context_tool.spec.ts +0 -592
  111. package/src/mcp/tools/get_context_tool.ts +0 -585
  112. package/src/mcp/tools/reload_package_tool.spec.ts +0 -232
  113. package/src/mcp/tools/reload_package_tool.ts +0 -132
  114. package/src/mcp_config.spec.ts +0 -919
  115. package/src/mcp_config.ts +0 -425
  116. package/src/oom_guards.integration.spec.ts +0 -269
  117. package/src/package_load/package_load_pool.spec.ts +0 -252
  118. package/src/package_load/package_load_pool.ts +0 -931
  119. package/src/package_load/package_load_worker.ts +0 -1034
  120. package/src/package_load/protocol.ts +0 -364
  121. package/src/package_load/rpc_wait_accountant.spec.ts +0 -109
  122. package/src/package_load/rpc_wait_accountant.ts +0 -76
  123. package/src/package_load_metrics.spec.ts +0 -114
  124. package/src/package_load_metrics.ts +0 -127
  125. package/src/path_safety.spec.ts +0 -158
  126. package/src/path_safety.ts +0 -146
  127. package/src/pg_helpers.spec.ts +0 -223
  128. package/src/pg_helpers.ts +0 -52
  129. package/src/query_cap_metrics.spec.ts +0 -89
  130. package/src/query_cap_metrics.ts +0 -116
  131. package/src/query_concurrency.spec.ts +0 -247
  132. package/src/query_concurrency.ts +0 -237
  133. package/src/query_metadata_metrics.ts +0 -49
  134. package/src/query_param_utils.ts +0 -29
  135. package/src/query_timeout.spec.ts +0 -224
  136. package/src/query_timeout.ts +0 -179
  137. package/src/runtime/publisher.js +0 -328
  138. package/src/server-old.ts +0 -988
  139. package/src/server.ts +0 -2059
  140. package/src/service/annotations.spec.ts +0 -118
  141. package/src/service/annotations.ts +0 -91
  142. package/src/service/authorize.spec.ts +0 -154
  143. package/src/service/authorize.ts +0 -498
  144. package/src/service/authorize_integration.spec.ts +0 -2041
  145. package/src/service/build_plan.spec.ts +0 -685
  146. package/src/service/build_plan.ts +0 -797
  147. package/src/service/compile_authorize.spec.ts +0 -85
  148. package/src/service/compile_fragment_techniques.spec.ts +0 -156
  149. package/src/service/connection.spec.ts +0 -2360
  150. package/src/service/connection.ts +0 -2187
  151. package/src/service/connection_config.spec.ts +0 -734
  152. package/src/service/connection_config.ts +0 -899
  153. package/src/service/connection_federation.spec.ts +0 -184
  154. package/src/service/connection_fingerprint.spec.ts +0 -102
  155. package/src/service/connection_service.spec.ts +0 -711
  156. package/src/service/connection_service.ts +0 -291
  157. package/src/service/cron_evaluator.spec.ts +0 -79
  158. package/src/service/cron_evaluator.ts +0 -105
  159. package/src/service/db_utils.spec.ts +0 -1085
  160. package/src/service/db_utils.ts +0 -1502
  161. package/src/service/duckdb_instance_isolation.spec.ts +0 -137
  162. package/src/service/ducklake_lazy_attach.spec.ts +0 -110
  163. package/src/service/embedding_provider.spec.ts +0 -329
  164. package/src/service/embedding_provider.ts +0 -236
  165. package/src/service/environment.ts +0 -2191
  166. package/src/service/environment_admission.spec.ts +0 -344
  167. package/src/service/environment_compile.spec.ts +0 -197
  168. package/src/service/environment_store.spec.ts +0 -2274
  169. package/src/service/environment_store.ts +0 -2434
  170. package/src/service/environment_store_anchoring.spec.ts +0 -107
  171. package/src/service/environment_store_clone.spec.ts +0 -350
  172. package/src/service/explore_visibility.spec.ts +0 -434
  173. package/src/service/exports_probe.spec.ts +0 -107
  174. package/src/service/extension_fetch_policy.spec.ts +0 -256
  175. package/src/service/filter.spec.ts +0 -458
  176. package/src/service/filter.ts +0 -341
  177. package/src/service/filter_bypass.spec.ts +0 -418
  178. package/src/service/filter_integration.spec.ts +0 -1004
  179. package/src/service/freshness.spec.ts +0 -183
  180. package/src/service/freshness.ts +0 -112
  181. package/src/service/gcs_s3_utils.ts +0 -427
  182. package/src/service/given.ts +0 -105
  183. package/src/service/givens_integration.spec.ts +0 -219
  184. package/src/service/manifest_loader.spec.ts +0 -189
  185. package/src/service/manifest_loader.ts +0 -156
  186. package/src/service/materialization_build_session.spec.ts +0 -435
  187. package/src/service/materialization_build_session.ts +0 -681
  188. package/src/service/materialization_config_validation.spec.ts +0 -99
  189. package/src/service/materialization_config_validation.ts +0 -120
  190. package/src/service/materialization_eligibility.spec.ts +0 -158
  191. package/src/service/materialization_eligibility.ts +0 -305
  192. package/src/service/materialization_schedule_surface.spec.ts +0 -256
  193. package/src/service/materialization_scheduler.spec.ts +0 -289
  194. package/src/service/materialization_scheduler.ts +0 -304
  195. package/src/service/materialization_serve_transform.spec.ts +0 -1003
  196. package/src/service/materialization_serve_transform.ts +0 -779
  197. package/src/service/materialization_service.spec.ts +0 -2373
  198. package/src/service/materialization_service.ts +0 -2342
  199. package/src/service/materialization_test_fixtures.ts +0 -249
  200. package/src/service/model.spec.ts +0 -1157
  201. package/src/service/model.ts +0 -3370
  202. package/src/service/model_limits.spec.ts +0 -209
  203. package/src/service/model_limits.ts +0 -131
  204. package/src/service/model_storage_serve.spec.ts +0 -193
  205. package/src/service/model_storage_serve_joins.spec.ts +0 -193
  206. package/src/service/package.spec.ts +0 -612
  207. package/src/service/package.ts +0 -1799
  208. package/src/service/package_manifest.spec.ts +0 -230
  209. package/src/service/package_manifest.ts +0 -241
  210. package/src/service/package_memory_governor.spec.ts +0 -173
  211. package/src/service/package_memory_governor.ts +0 -233
  212. package/src/service/package_quote_bound_tables.spec.ts +0 -171
  213. package/src/service/package_race.spec.ts +0 -208
  214. package/src/service/package_reload_safety.spec.ts +0 -193
  215. package/src/service/package_rollback.spec.ts +0 -190
  216. package/src/service/package_worker_path.spec.ts +0 -645
  217. package/src/service/path_injection.spec.ts +0 -39
  218. package/src/service/persist_annotation_validation.spec.ts +0 -89
  219. package/src/service/persist_annotation_validation.ts +0 -59
  220. package/src/service/persistence_policy.spec.ts +0 -374
  221. package/src/service/proxy.spec.ts +0 -414
  222. package/src/service/proxy.ts +0 -248
  223. package/src/service/query_boundary.spec.ts +0 -470
  224. package/src/service/query_metadata.spec.ts +0 -408
  225. package/src/service/query_metadata.ts +0 -492
  226. package/src/service/query_metadata_identity.spec.ts +0 -149
  227. package/src/service/query_text.spec.ts +0 -79
  228. package/src/service/query_text.ts +0 -43
  229. package/src/service/quoting.spec.ts +0 -135
  230. package/src/service/quoting.ts +0 -78
  231. package/src/service/resolve_environment.ts +0 -15
  232. package/src/service/restricted_mode.spec.ts +0 -299
  233. package/src/service/source_extraction.ts +0 -225
  234. package/src/service/theme_store.ts +0 -199
  235. package/src/storage/DatabaseInterface.ts +0 -198
  236. package/src/storage/StorageManager.ts +0 -113
  237. package/src/storage/duckdb/ConnectionRepository.ts +0 -143
  238. package/src/storage/duckdb/DuckDBConnection.ts +0 -123
  239. package/src/storage/duckdb/DuckDBRepository.ts +0 -208
  240. package/src/storage/duckdb/EnvironmentRepository.ts +0 -148
  241. package/src/storage/duckdb/MaterializationRepository.spec.ts +0 -191
  242. package/src/storage/duckdb/MaterializationRepository.ts +0 -336
  243. package/src/storage/duckdb/PackageRepository.ts +0 -135
  244. package/src/storage/duckdb/schema.ts +0 -229
  245. package/src/stream_helpers.spec.ts +0 -280
  246. package/src/stream_helpers.ts +0 -162
  247. package/src/telemetry.ts +0 -20
  248. package/src/test_helpers/metrics_harness.ts +0 -166
  249. package/src/theme_key_parity.spec.ts +0 -57
  250. package/src/utils.ts +0 -29
  251. package/tests/fixtures/authorize-compile/model.malloy +0 -9
  252. package/tests/fixtures/authorize-compile/publisher.json +0 -4
  253. package/tests/fixtures/html-pages-nopublic/model.malloy +0 -1
  254. package/tests/fixtures/html-pages-nopublic/publisher.json +0 -5
  255. package/tests/fixtures/html-pages-test/data.csv +0 -3
  256. package/tests/fixtures/html-pages-test/public/assets/app.css +0 -3
  257. package/tests/fixtures/html-pages-test/public/barehtml.html +0 -4
  258. package/tests/fixtures/html-pages-test/public/bodymeta.html +0 -8
  259. package/tests/fixtures/html-pages-test/public/data.json +0 -1
  260. package/tests/fixtures/html-pages-test/public/fitcomment.html +0 -11
  261. package/tests/fixtures/html-pages-test/public/index.html +0 -9
  262. package/tests/fixtures/html-pages-test/public/nohead.html +0 -8
  263. package/tests/fixtures/html-pages-test/public/notfit.html +0 -13
  264. package/tests/fixtures/html-pages-test/public/slides.html +0 -12
  265. package/tests/fixtures/html-pages-test/public/sub/page2.html +0 -9
  266. package/tests/fixtures/html-pages-test/public/unterminated.html +0 -10
  267. package/tests/fixtures/html-pages-test/publisher.json +0 -5
  268. package/tests/fixtures/html-pages-test/report.malloy +0 -1
  269. package/tests/fixtures/persist-multi-level/data/orders.csv +0 -5
  270. package/tests/fixtures/persist-multi-level/multi_level.malloy +0 -18
  271. package/tests/fixtures/persist-multi-level/publisher.json +0 -5
  272. package/tests/fixtures/persist-schedule-test/data/orders.csv +0 -5
  273. package/tests/fixtures/persist-schedule-test/persist_schedule_test.malloy +0 -11
  274. package/tests/fixtures/persist-schedule-test/publisher.json +0 -7
  275. package/tests/fixtures/persist-schedule-warn-test/data/orders.csv +0 -5
  276. package/tests/fixtures/persist-schedule-warn-test/persist_warn_test.malloy +0 -11
  277. package/tests/fixtures/persist-schedule-warn-test/publisher.json +0 -6
  278. package/tests/fixtures/persist-test/data/orders.csv +0 -5
  279. package/tests/fixtures/persist-test/persist_test.malloy +0 -11
  280. package/tests/fixtures/persist-test/publisher.json +0 -5
  281. package/tests/fixtures/publisher.config.json +0 -15
  282. package/tests/fixtures/query-givens/data/orders.csv +0 -7
  283. package/tests/fixtures/query-givens/model.malloy +0 -34
  284. package/tests/fixtures/query-givens/publisher.json +0 -5
  285. package/tests/fixtures/xlsx/database.xlsx +0 -0
  286. package/tests/harness/mcp_test_setup.ts +0 -332
  287. package/tests/harness/rest_e2e.ts +0 -68
  288. package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +0 -92
  289. package/tests/integration/concurrent_package/concurrent_package.integration.spec.ts +0 -280
  290. package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +0 -138
  291. package/tests/integration/first_boot/readiness_line.integration.spec.ts +0 -177
  292. package/tests/integration/html_pages/html_pages.integration.spec.ts +0 -440
  293. package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +0 -259
  294. package/tests/integration/materialization/environment_list.integration.spec.ts +0 -133
  295. package/tests/integration/materialization/freshness_gate.integration.spec.ts +0 -292
  296. package/tests/integration/materialization/manifest_binding.integration.spec.ts +0 -430
  297. package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -441
  298. package/tests/integration/materialization/orchestrated_rebind.integration.spec.ts +0 -150
  299. package/tests/integration/materialization/reference_manifest.integration.spec.ts +0 -251
  300. package/tests/integration/materialization/schedule_edit.integration.spec.ts +0 -126
  301. package/tests/integration/materialization/scheduler.integration.spec.ts +0 -200
  302. package/tests/integration/materialization/scheduler_recovery.integration.spec.ts +0 -172
  303. package/tests/integration/materialization/scheduler_transitions.integration.spec.ts +0 -256
  304. package/tests/integration/materialization/scheduler_wiring.integration.spec.ts +0 -143
  305. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -438
  306. package/tests/integration/mcp/mcp_get_context_semantic.integration.spec.ts +0 -235
  307. package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -98
  308. package/tests/integration/query_givens/query_givens.integration.spec.ts +0 -146
  309. package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +0 -121
  310. package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +0 -110
  311. package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -421
  312. package/tests/unit/controller/theme.controller.spec.ts +0 -141
  313. package/tests/unit/duckdb/attached_databases.test.ts +0 -1243
  314. package/tests/unit/duckdb/duckdb_connection.test.ts +0 -181
  315. package/tests/unit/duckdb/legacy_schema_migration.test.ts +0 -197
  316. package/tests/unit/duckdb/repositories.test.ts +0 -208
  317. package/tests/unit/ducklake/ducklake.test.ts +0 -952
  318. package/tests/unit/service/theme_store.spec.ts +0 -139
  319. 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
- });