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