@malloy-publisher/server 0.0.234 → 0.0.236

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/dist/app/api-doc.yaml +47 -17
  2. package/dist/app/assets/{EnvironmentPage-DTZQ4Gxc.js → EnvironmentPage-BsAnavYN.js} +1 -1
  3. package/dist/app/assets/{HomePage-C5mlDPXK.js → HomePage-CADE138j.js} +1 -1
  4. package/dist/app/assets/{LightMode-DGNmhG0u.js → LightMode-Cfh7KzN8.js} +1 -1
  5. package/dist/app/assets/{MainPage-CVL_wmP4.js → MainPage-CO3pRlnV.js} +1 -1
  6. package/dist/app/assets/{MaterializationsPage-DmzMBCpy.js → MaterializationsPage-p9YjkRXZ.js} +1 -1
  7. package/dist/app/assets/{ModelPage-Dbvf4QbB.js → ModelPage-C1OSTv-x.js} +1 -1
  8. package/dist/app/assets/{PackagePage-DxdHc2Qs.js → PackagePage-e4kN75YR.js} +1 -1
  9. package/dist/app/assets/{RouteError-OJdT4tCd.js → RouteError-CzbfOkng.js} +1 -1
  10. package/dist/app/assets/{ThemeEditorPage-Bk7s0KXY.js → ThemeEditorPage-CciagFTq.js} +1 -1
  11. package/dist/app/assets/{WorkbookPage-j_vCWdN3.js → WorkbookPage-DNWmkCXa.js} +1 -1
  12. package/dist/app/assets/{core-Rj_4rRnA.es-DoIfLxDJ.js → core-Rj_4rRnA.es-BZyvITuO.js} +1 -1
  13. package/dist/app/assets/{index-B_jKMR35.js → index-CH2AcDzc.js} +4 -4
  14. package/dist/app/assets/{index-hw-xn0X7.js → index-DQa463gC.js} +2 -2
  15. package/dist/app/assets/{index-DWIe_hK0.js → index-DQpV7MyA.js} +1 -1
  16. package/dist/app/assets/{index-BabP-V-S.js → index-DzaYbhnD.js} +1 -1
  17. package/dist/app/assets/{index-D-rDyK11.js → index-VBbcc8s6.js} +1 -1
  18. package/dist/app/index.html +1 -1
  19. package/dist/package_load_worker.mjs +8 -1
  20. package/dist/server.mjs +471 -54
  21. package/package.json +16 -13
  22. package/.eslintrc.json +0 -44
  23. package/.prettierignore +0 -1
  24. package/.prettierrc +0 -4
  25. package/build.ts +0 -97
  26. package/eslint.config.mjs +0 -76
  27. package/k6-tests/README.md +0 -144
  28. package/k6-tests/bun.lock +0 -797
  29. package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
  30. package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
  31. package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
  32. package/k6-tests/load-test/load-test-crud.ts +0 -142
  33. package/k6-tests/load-test/load-test.ts +0 -784
  34. package/k6-tests/package.json +0 -27
  35. package/k6-tests/scripts/run-with-otel.sh +0 -50
  36. package/k6-tests/smoke-test/smoke-test.ts +0 -158
  37. package/k6-tests/tsconfig.json +0 -29
  38. package/k6-tests/utils/client_factory.ts +0 -128
  39. package/k6-tests/utils/common.ts +0 -489
  40. package/k6-tests/utils/logger.ts +0 -62
  41. package/k6-tests/utils/types.d.ts +0 -35
  42. package/malloy_mcp.dxt +0 -0
  43. package/publisher.config.example.bigquery.json +0 -33
  44. package/publisher.config.example.duckdb.json +0 -23
  45. package/publisher.config.json +0 -23
  46. package/scripts/bake-duckdb-extensions.js +0 -110
  47. package/src/config.spec.ts +0 -1535
  48. package/src/config.theme.spec.ts +0 -178
  49. package/src/config.ts +0 -1142
  50. package/src/constants.ts +0 -105
  51. package/src/controller/compile.controller.ts +0 -40
  52. package/src/controller/connection.controller.spec.ts +0 -849
  53. package/src/controller/connection.controller.ts +0 -901
  54. package/src/controller/database.controller.ts +0 -24
  55. package/src/controller/materialization.controller.spec.ts +0 -293
  56. package/src/controller/materialization.controller.ts +0 -319
  57. package/src/controller/model.controller.ts +0 -164
  58. package/src/controller/package.controller.spec.ts +0 -273
  59. package/src/controller/package.controller.ts +0 -306
  60. package/src/controller/query.controller.ts +0 -152
  61. package/src/controller/theme.controller.ts +0 -83
  62. package/src/controller/watch-mode.controller.ts +0 -243
  63. package/src/data_styles.ts +0 -71
  64. package/src/default-publisher.config.json +0 -23
  65. package/src/ducklake_version.spec.ts +0 -163
  66. package/src/ducklake_version.ts +0 -153
  67. package/src/errors.spec.ts +0 -119
  68. package/src/errors.ts +0 -237
  69. package/src/filter_deprecation.spec.ts +0 -64
  70. package/src/filter_deprecation.ts +0 -42
  71. package/src/health.spec.ts +0 -123
  72. package/src/health.ts +0 -233
  73. package/src/heap_check.spec.ts +0 -144
  74. package/src/heap_check.ts +0 -144
  75. package/src/instrumentation.ts +0 -214
  76. package/src/json_utils.spec.ts +0 -51
  77. package/src/json_utils.ts +0 -33
  78. package/src/logger.spec.ts +0 -210
  79. package/src/logger.ts +0 -251
  80. package/src/malloy_pin_prereqs.spec.ts +0 -25
  81. package/src/materialization_metrics.spec.ts +0 -169
  82. package/src/materialization_metrics.ts +0 -346
  83. package/src/mcp/error_messages.spec.ts +0 -35
  84. package/src/mcp/error_messages.ts +0 -183
  85. package/src/mcp/handler_utils.spec.ts +0 -108
  86. package/src/mcp/handler_utils.ts +0 -251
  87. package/src/mcp/mcp_constants.ts +0 -6
  88. package/src/mcp/query_envelope.spec.ts +0 -229
  89. package/src/mcp/query_envelope.ts +0 -240
  90. package/src/mcp/server.protocol.spec.ts +0 -250
  91. package/src/mcp/server.ts +0 -87
  92. package/src/mcp/skills/build_skills_bundle.spec.ts +0 -51
  93. package/src/mcp/skills/build_skills_bundle.ts +0 -176
  94. package/src/mcp/skills/skills_bundle.json +0 -1
  95. package/src/mcp/skills/skills_bundle.spec.ts +0 -171
  96. package/src/mcp/tool_response.spec.ts +0 -108
  97. package/src/mcp/tool_response.ts +0 -138
  98. package/src/mcp/tools/compile_tool.spec.ts +0 -315
  99. package/src/mcp/tools/compile_tool.ts +0 -208
  100. package/src/mcp/tools/docs_search/build_docs_index.ts +0 -132
  101. package/src/mcp/tools/docs_search/malloy_docs_index.json +0 -1
  102. package/src/mcp/tools/docs_search_tool.spec.ts +0 -32
  103. package/src/mcp/tools/docs_search_tool.ts +0 -128
  104. package/src/mcp/tools/embedding_index.spec.ts +0 -1236
  105. package/src/mcp/tools/embedding_index.ts +0 -808
  106. package/src/mcp/tools/execute_query_tool.spec.ts +0 -294
  107. package/src/mcp/tools/execute_query_tool.ts +0 -316
  108. package/src/mcp/tools/get_context_eval.ts +0 -275
  109. package/src/mcp/tools/get_context_tool.spec.ts +0 -592
  110. package/src/mcp/tools/get_context_tool.ts +0 -585
  111. package/src/mcp/tools/reload_package_tool.spec.ts +0 -232
  112. package/src/mcp/tools/reload_package_tool.ts +0 -132
  113. package/src/mcp_config.spec.ts +0 -919
  114. package/src/mcp_config.ts +0 -425
  115. package/src/oom_guards.integration.spec.ts +0 -269
  116. package/src/package_load/package_load_pool.spec.ts +0 -252
  117. package/src/package_load/package_load_pool.ts +0 -931
  118. package/src/package_load/package_load_worker.ts +0 -1034
  119. package/src/package_load/protocol.ts +0 -364
  120. package/src/package_load/rpc_wait_accountant.spec.ts +0 -109
  121. package/src/package_load/rpc_wait_accountant.ts +0 -76
  122. package/src/package_load_metrics.spec.ts +0 -114
  123. package/src/package_load_metrics.ts +0 -127
  124. package/src/path_safety.spec.ts +0 -158
  125. package/src/path_safety.ts +0 -146
  126. package/src/pg_helpers.spec.ts +0 -223
  127. package/src/pg_helpers.ts +0 -52
  128. package/src/query_cap_metrics.spec.ts +0 -89
  129. package/src/query_cap_metrics.ts +0 -116
  130. package/src/query_concurrency.spec.ts +0 -247
  131. package/src/query_concurrency.ts +0 -237
  132. package/src/query_metadata_metrics.ts +0 -49
  133. package/src/query_param_utils.ts +0 -29
  134. package/src/query_timeout.spec.ts +0 -224
  135. package/src/query_timeout.ts +0 -179
  136. package/src/runtime/publisher.js +0 -328
  137. package/src/server-old.ts +0 -988
  138. package/src/server.ts +0 -2059
  139. package/src/service/annotations.spec.ts +0 -118
  140. package/src/service/annotations.ts +0 -91
  141. package/src/service/authorize.spec.ts +0 -154
  142. package/src/service/authorize.ts +0 -498
  143. package/src/service/authorize_integration.spec.ts +0 -2041
  144. package/src/service/build_plan.spec.ts +0 -685
  145. package/src/service/build_plan.ts +0 -797
  146. package/src/service/compile_authorize.spec.ts +0 -85
  147. package/src/service/compile_fragment_techniques.spec.ts +0 -156
  148. package/src/service/connection.spec.ts +0 -2360
  149. package/src/service/connection.ts +0 -2187
  150. package/src/service/connection_config.spec.ts +0 -734
  151. package/src/service/connection_config.ts +0 -899
  152. package/src/service/connection_federation.spec.ts +0 -184
  153. package/src/service/connection_fingerprint.spec.ts +0 -102
  154. package/src/service/connection_service.spec.ts +0 -711
  155. package/src/service/connection_service.ts +0 -291
  156. package/src/service/cron_evaluator.spec.ts +0 -79
  157. package/src/service/cron_evaluator.ts +0 -105
  158. package/src/service/db_utils.spec.ts +0 -1085
  159. package/src/service/db_utils.ts +0 -1502
  160. package/src/service/duckdb_instance_isolation.spec.ts +0 -137
  161. package/src/service/ducklake_lazy_attach.spec.ts +0 -110
  162. package/src/service/embedding_provider.spec.ts +0 -329
  163. package/src/service/embedding_provider.ts +0 -236
  164. package/src/service/environment.ts +0 -2191
  165. package/src/service/environment_admission.spec.ts +0 -344
  166. package/src/service/environment_compile.spec.ts +0 -197
  167. package/src/service/environment_store.spec.ts +0 -2274
  168. package/src/service/environment_store.ts +0 -2434
  169. package/src/service/environment_store_anchoring.spec.ts +0 -107
  170. package/src/service/environment_store_clone.spec.ts +0 -350
  171. package/src/service/explore_visibility.spec.ts +0 -434
  172. package/src/service/exports_probe.spec.ts +0 -107
  173. package/src/service/extension_fetch_policy.spec.ts +0 -256
  174. package/src/service/filter.spec.ts +0 -458
  175. package/src/service/filter.ts +0 -341
  176. package/src/service/filter_bypass.spec.ts +0 -418
  177. package/src/service/filter_integration.spec.ts +0 -1004
  178. package/src/service/freshness.spec.ts +0 -183
  179. package/src/service/freshness.ts +0 -112
  180. package/src/service/gcs_s3_utils.ts +0 -427
  181. package/src/service/given.ts +0 -105
  182. package/src/service/givens_integration.spec.ts +0 -219
  183. package/src/service/manifest_loader.spec.ts +0 -189
  184. package/src/service/manifest_loader.ts +0 -156
  185. package/src/service/materialization_build_session.spec.ts +0 -435
  186. package/src/service/materialization_build_session.ts +0 -681
  187. package/src/service/materialization_config_validation.spec.ts +0 -99
  188. package/src/service/materialization_config_validation.ts +0 -120
  189. package/src/service/materialization_eligibility.spec.ts +0 -158
  190. package/src/service/materialization_eligibility.ts +0 -305
  191. package/src/service/materialization_schedule_surface.spec.ts +0 -256
  192. package/src/service/materialization_scheduler.spec.ts +0 -289
  193. package/src/service/materialization_scheduler.ts +0 -304
  194. package/src/service/materialization_serve_transform.spec.ts +0 -1003
  195. package/src/service/materialization_serve_transform.ts +0 -779
  196. package/src/service/materialization_service.spec.ts +0 -2373
  197. package/src/service/materialization_service.ts +0 -2342
  198. package/src/service/materialization_test_fixtures.ts +0 -249
  199. package/src/service/model.spec.ts +0 -1157
  200. package/src/service/model.ts +0 -3370
  201. package/src/service/model_limits.spec.ts +0 -209
  202. package/src/service/model_limits.ts +0 -131
  203. package/src/service/model_storage_serve.spec.ts +0 -193
  204. package/src/service/model_storage_serve_joins.spec.ts +0 -193
  205. package/src/service/package.spec.ts +0 -612
  206. package/src/service/package.ts +0 -1799
  207. package/src/service/package_manifest.spec.ts +0 -230
  208. package/src/service/package_manifest.ts +0 -241
  209. package/src/service/package_memory_governor.spec.ts +0 -173
  210. package/src/service/package_memory_governor.ts +0 -233
  211. package/src/service/package_quote_bound_tables.spec.ts +0 -171
  212. package/src/service/package_race.spec.ts +0 -208
  213. package/src/service/package_reload_safety.spec.ts +0 -193
  214. package/src/service/package_rollback.spec.ts +0 -190
  215. package/src/service/package_worker_path.spec.ts +0 -645
  216. package/src/service/path_injection.spec.ts +0 -39
  217. package/src/service/persist_annotation_validation.spec.ts +0 -89
  218. package/src/service/persist_annotation_validation.ts +0 -59
  219. package/src/service/persistence_policy.spec.ts +0 -374
  220. package/src/service/proxy.spec.ts +0 -414
  221. package/src/service/proxy.ts +0 -248
  222. package/src/service/query_boundary.spec.ts +0 -470
  223. package/src/service/query_metadata.spec.ts +0 -408
  224. package/src/service/query_metadata.ts +0 -492
  225. package/src/service/query_metadata_identity.spec.ts +0 -149
  226. package/src/service/query_text.spec.ts +0 -79
  227. package/src/service/query_text.ts +0 -43
  228. package/src/service/quoting.spec.ts +0 -135
  229. package/src/service/quoting.ts +0 -78
  230. package/src/service/resolve_environment.ts +0 -15
  231. package/src/service/restricted_mode.spec.ts +0 -299
  232. package/src/service/source_extraction.ts +0 -225
  233. package/src/service/theme_store.ts +0 -199
  234. package/src/storage/DatabaseInterface.ts +0 -198
  235. package/src/storage/StorageManager.ts +0 -113
  236. package/src/storage/duckdb/ConnectionRepository.ts +0 -143
  237. package/src/storage/duckdb/DuckDBConnection.ts +0 -123
  238. package/src/storage/duckdb/DuckDBRepository.ts +0 -208
  239. package/src/storage/duckdb/EnvironmentRepository.ts +0 -148
  240. package/src/storage/duckdb/MaterializationRepository.spec.ts +0 -191
  241. package/src/storage/duckdb/MaterializationRepository.ts +0 -336
  242. package/src/storage/duckdb/PackageRepository.ts +0 -135
  243. package/src/storage/duckdb/schema.ts +0 -229
  244. package/src/stream_helpers.spec.ts +0 -280
  245. package/src/stream_helpers.ts +0 -162
  246. package/src/telemetry.ts +0 -20
  247. package/src/test_helpers/metrics_harness.ts +0 -166
  248. package/src/theme_key_parity.spec.ts +0 -57
  249. package/src/utils.ts +0 -29
  250. package/tests/fixtures/authorize-compile/model.malloy +0 -9
  251. package/tests/fixtures/authorize-compile/publisher.json +0 -4
  252. package/tests/fixtures/html-pages-nopublic/model.malloy +0 -1
  253. package/tests/fixtures/html-pages-nopublic/publisher.json +0 -5
  254. package/tests/fixtures/html-pages-test/data.csv +0 -3
  255. package/tests/fixtures/html-pages-test/public/assets/app.css +0 -3
  256. package/tests/fixtures/html-pages-test/public/barehtml.html +0 -4
  257. package/tests/fixtures/html-pages-test/public/bodymeta.html +0 -8
  258. package/tests/fixtures/html-pages-test/public/data.json +0 -1
  259. package/tests/fixtures/html-pages-test/public/fitcomment.html +0 -11
  260. package/tests/fixtures/html-pages-test/public/index.html +0 -9
  261. package/tests/fixtures/html-pages-test/public/nohead.html +0 -8
  262. package/tests/fixtures/html-pages-test/public/notfit.html +0 -13
  263. package/tests/fixtures/html-pages-test/public/slides.html +0 -12
  264. package/tests/fixtures/html-pages-test/public/sub/page2.html +0 -9
  265. package/tests/fixtures/html-pages-test/public/unterminated.html +0 -10
  266. package/tests/fixtures/html-pages-test/publisher.json +0 -5
  267. package/tests/fixtures/html-pages-test/report.malloy +0 -1
  268. package/tests/fixtures/persist-multi-level/data/orders.csv +0 -5
  269. package/tests/fixtures/persist-multi-level/multi_level.malloy +0 -18
  270. package/tests/fixtures/persist-multi-level/publisher.json +0 -5
  271. package/tests/fixtures/persist-schedule-test/data/orders.csv +0 -5
  272. package/tests/fixtures/persist-schedule-test/persist_schedule_test.malloy +0 -11
  273. package/tests/fixtures/persist-schedule-test/publisher.json +0 -7
  274. package/tests/fixtures/persist-schedule-warn-test/data/orders.csv +0 -5
  275. package/tests/fixtures/persist-schedule-warn-test/persist_warn_test.malloy +0 -11
  276. package/tests/fixtures/persist-schedule-warn-test/publisher.json +0 -6
  277. package/tests/fixtures/persist-test/data/orders.csv +0 -5
  278. package/tests/fixtures/persist-test/persist_test.malloy +0 -11
  279. package/tests/fixtures/persist-test/publisher.json +0 -5
  280. package/tests/fixtures/publisher.config.json +0 -15
  281. package/tests/fixtures/query-givens/data/orders.csv +0 -7
  282. package/tests/fixtures/query-givens/model.malloy +0 -34
  283. package/tests/fixtures/query-givens/publisher.json +0 -5
  284. package/tests/fixtures/xlsx/database.xlsx +0 -0
  285. package/tests/harness/mcp_test_setup.ts +0 -332
  286. package/tests/harness/rest_e2e.ts +0 -68
  287. package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +0 -92
  288. package/tests/integration/concurrent_package/concurrent_package.integration.spec.ts +0 -280
  289. package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +0 -138
  290. package/tests/integration/first_boot/readiness_line.integration.spec.ts +0 -177
  291. package/tests/integration/html_pages/html_pages.integration.spec.ts +0 -440
  292. package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +0 -259
  293. package/tests/integration/materialization/environment_list.integration.spec.ts +0 -133
  294. package/tests/integration/materialization/freshness_gate.integration.spec.ts +0 -292
  295. package/tests/integration/materialization/manifest_binding.integration.spec.ts +0 -430
  296. package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -441
  297. package/tests/integration/materialization/orchestrated_rebind.integration.spec.ts +0 -150
  298. package/tests/integration/materialization/reference_manifest.integration.spec.ts +0 -251
  299. package/tests/integration/materialization/schedule_edit.integration.spec.ts +0 -126
  300. package/tests/integration/materialization/scheduler.integration.spec.ts +0 -200
  301. package/tests/integration/materialization/scheduler_recovery.integration.spec.ts +0 -172
  302. package/tests/integration/materialization/scheduler_transitions.integration.spec.ts +0 -256
  303. package/tests/integration/materialization/scheduler_wiring.integration.spec.ts +0 -143
  304. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -438
  305. package/tests/integration/mcp/mcp_get_context_semantic.integration.spec.ts +0 -235
  306. package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -98
  307. package/tests/integration/query_givens/query_givens.integration.spec.ts +0 -146
  308. package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +0 -121
  309. package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +0 -110
  310. package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -421
  311. package/tests/unit/controller/theme.controller.spec.ts +0 -141
  312. package/tests/unit/duckdb/attached_databases.test.ts +0 -1243
  313. package/tests/unit/duckdb/duckdb_connection.test.ts +0 -181
  314. package/tests/unit/duckdb/legacy_schema_migration.test.ts +0 -197
  315. package/tests/unit/duckdb/repositories.test.ts +0 -208
  316. package/tests/unit/ducklake/ducklake.test.ts +0 -952
  317. package/tests/unit/service/theme_store.spec.ts +0 -139
  318. package/tsconfig.json +0 -32
@@ -1,135 +0,0 @@
1
- import { Package } from "../DatabaseInterface";
2
- import { DuckDBConnection } from "./DuckDBConnection";
3
-
4
- export class PackageRepository {
5
- constructor(private db: DuckDBConnection) {}
6
-
7
- private generateId(): string {
8
- return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
9
- }
10
-
11
- private now(): Date {
12
- return new Date();
13
- }
14
-
15
- async listPackages(environmentId: string): Promise<Package[]> {
16
- const rows = await this.db.all<Record<string, unknown>>(
17
- "SELECT * FROM packages WHERE environment_id = ? ORDER BY name",
18
- [environmentId],
19
- );
20
- return rows.map(this.mapToPackage);
21
- }
22
-
23
- async getPackageById(id: string): Promise<Package | null> {
24
- const row = await this.db.get<Record<string, unknown>>(
25
- "SELECT * FROM packages WHERE id = ?",
26
- [id],
27
- );
28
- return row ? this.mapToPackage(row) : null;
29
- }
30
-
31
- async getPackageByName(
32
- environmentId: string,
33
- name: string,
34
- ): Promise<Package | null> {
35
- const row = await this.db.get<Record<string, unknown>>(
36
- "SELECT * FROM packages WHERE environment_id = ? AND name = ?",
37
- [environmentId, name],
38
- );
39
- return row ? this.mapToPackage(row) : null;
40
- }
41
-
42
- async createPackage(
43
- pkg: Omit<Package, "id" | "createdAt" | "updatedAt">,
44
- ): Promise<Package> {
45
- const id = this.generateId();
46
- const now = this.now();
47
-
48
- await this.db.run(
49
- `INSERT INTO packages (id, environment_id, name, description, manifest_path, metadata, created_at, updated_at)
50
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
51
- [
52
- id,
53
- pkg.environmentId,
54
- pkg.name,
55
- pkg.description || null,
56
- pkg.manifestPath,
57
- pkg.metadata ? JSON.stringify(pkg.metadata) : null,
58
- now.toISOString(),
59
- now.toISOString(),
60
- ],
61
- );
62
-
63
- return {
64
- id,
65
- ...pkg,
66
- createdAt: now,
67
- updatedAt: now,
68
- };
69
- }
70
-
71
- async updatePackage(
72
- id: string,
73
- updates: Partial<Package>,
74
- ): Promise<Package> {
75
- const existing = await this.getPackageById(id);
76
- if (!existing) {
77
- throw new Error(`Package with id ${id} not found`);
78
- }
79
-
80
- const now = this.now();
81
- const setClauses: string[] = [];
82
- const params: unknown[] = [];
83
-
84
- if (updates.name !== undefined) {
85
- setClauses.push(`name = ?`);
86
- params.push(updates.name);
87
- }
88
- if (updates.description !== undefined) {
89
- setClauses.push(`description = ?`);
90
- params.push(updates.description);
91
- }
92
- if (updates.manifestPath !== undefined) {
93
- setClauses.push(`manifest_path = ?`);
94
- params.push(updates.manifestPath);
95
- }
96
- if (updates.metadata !== undefined) {
97
- setClauses.push(`metadata = ?`);
98
- params.push(JSON.stringify(updates.metadata));
99
- }
100
-
101
- setClauses.push(`updated_at = ?`);
102
- params.push(now.toISOString());
103
- params.push(id);
104
-
105
- await this.db.run(
106
- `UPDATE packages SET ${setClauses.join(", ")} WHERE id = ?`,
107
- params,
108
- );
109
-
110
- return this.getPackageById(id) as Promise<Package>;
111
- }
112
-
113
- async deletePackage(id: string): Promise<void> {
114
- await this.db.run("DELETE FROM packages WHERE id = ?", [id]);
115
- }
116
-
117
- async deletePackagesByEnvironmentId(id: string): Promise<void> {
118
- await this.db.run("DELETE FROM packages WHERE environment_id = ?", [id]);
119
- }
120
-
121
- private mapToPackage(row: Record<string, unknown>): Package {
122
- return {
123
- id: row.id as string,
124
- environmentId: row.environment_id as string,
125
- name: row.name as string,
126
- description: row.description as string | undefined,
127
- manifestPath: row.manifest_path as string,
128
- metadata: row.metadata
129
- ? JSON.parse(row.metadata as string)
130
- : undefined,
131
- createdAt: new Date(row.created_at as string),
132
- updatedAt: new Date(row.updated_at as string),
133
- };
134
- }
135
- }
@@ -1,229 +0,0 @@
1
- import { logger } from "../../logger";
2
- import { DuckDBConnection } from "./DuckDBConnection";
3
-
4
- export async function initializeSchema(
5
- db: DuckDBConnection,
6
- force: boolean = false,
7
- ): Promise<void> {
8
- const initialized = await db.isInitialized();
9
-
10
- if (force) {
11
- logger.info(
12
- "Reinitializing database schema dropping and recreating all tables",
13
- );
14
- await dropAllTables(db);
15
- } else if (!initialized) {
16
- // TODO: Remove this during projects cleanup
17
- // If a pre-rename `projects` schema is on disk, the new
18
- // CREATE TABLE IF NOT EXISTS pass below would silently leave child
19
- // tables on the old `project_id` column and the first query against
20
- // `environment_id` would crash. Drop the legacy tables (with a loud
21
- // warning) so the fresh schema can be created cleanly. This is
22
- // destructive: operators upgrading should re-create their environments
23
- // and packages via the API after the upgrade.
24
- await dropLegacyProjectSchema(db);
25
- logger.info("Creating database schema for the first time...");
26
- }
27
- // Always fall through to the CREATE TABLE IF NOT EXISTS pass below.
28
- // The statements are idempotent and let an already-initialized DB pick
29
- // up new tables added in later builds (e.g., the `themes` table for
30
- // the in-app Theme Editor) without forcing operators to run --init
31
- // and lose their existing environments / packages / materializations.
32
-
33
- // Environments table
34
- await db.run(`
35
- CREATE TABLE IF NOT EXISTS environments (
36
- id VARCHAR PRIMARY KEY,
37
- name VARCHAR NOT NULL UNIQUE,
38
- path VARCHAR NOT NULL,
39
- description VARCHAR,
40
- metadata JSON,
41
- created_at TIMESTAMP NOT NULL,
42
- updated_at TIMESTAMP NOT NULL
43
- )
44
- `);
45
-
46
- // Packages table
47
- await db.run(`
48
- CREATE TABLE IF NOT EXISTS packages (
49
- id VARCHAR PRIMARY KEY,
50
- environment_id VARCHAR NOT NULL,
51
- name VARCHAR NOT NULL,
52
- description VARCHAR,
53
- manifest_path VARCHAR NOT NULL,
54
- metadata JSON,
55
- created_at TIMESTAMP NOT NULL,
56
- updated_at TIMESTAMP NOT NULL,
57
- FOREIGN KEY (environment_id) REFERENCES environments(id),
58
- UNIQUE (environment_id, name)
59
- )
60
- `);
61
-
62
- // Connections table
63
- await db.run(`
64
- CREATE TABLE IF NOT EXISTS connections (
65
- id VARCHAR PRIMARY KEY,
66
- environment_id VARCHAR NOT NULL,
67
- name VARCHAR NOT NULL,
68
- type VARCHAR NOT NULL,
69
- config JSON NOT NULL,
70
- created_at TIMESTAMP NOT NULL,
71
- updated_at TIMESTAMP NOT NULL,
72
- FOREIGN KEY (environment_id) REFERENCES environments(id),
73
- UNIQUE (environment_id, name)
74
- )
75
- `);
76
-
77
- // Materializations table.
78
- //
79
- // `active_key` enforces at-most-one active (non-terminal) materialization
80
- // per (environment, package) at the DB layer. It is set to
81
- // `{environment_id}|{package_name}` while the row is active and cleared
82
- // to NULL on transition to any terminal state. A unique index on
83
- // `active_key` (see below) makes the insert-then-check race impossible —
84
- // a second concurrent create fails with a constraint violation, which the
85
- // service layer translates to `MaterializationConflictError`.
86
- // `manifest` is a JSON blob holding the build output returned inline on the
87
- // resource.
88
- await db.run(`
89
- CREATE TABLE IF NOT EXISTS materializations (
90
- id VARCHAR PRIMARY KEY,
91
- environment_id VARCHAR NOT NULL,
92
- package_name VARCHAR NOT NULL,
93
- status VARCHAR NOT NULL,
94
- active_key VARCHAR,
95
- started_at TIMESTAMP,
96
- completed_at TIMESTAMP,
97
- error TEXT,
98
- metadata JSON,
99
- manifest JSON,
100
- created_at TIMESTAMP NOT NULL,
101
- updated_at TIMESTAMP NOT NULL,
102
- FOREIGN KEY (environment_id) REFERENCES environments(id)
103
- )
104
- `);
105
-
106
- // Themes table.
107
- //
108
- // Singleton storage for the instance-wide theme edited by the in-app
109
- // Theme Editor. At most one row (id = "default"); we use INSERT ON
110
- // CONFLICT to keep the table effectively a key/value cell.
111
- //
112
- // publisher.config.json's optional `theme` block is a BOOT SEED only —
113
- // on first run, if this table is empty, ThemeStore writes the seed
114
- // here. Subsequent reads come from this table so the editor's saves
115
- // win over the file.
116
- await db.run(`
117
- CREATE TABLE IF NOT EXISTS themes (
118
- id VARCHAR PRIMARY KEY,
119
- payload JSON NOT NULL,
120
- created_at TIMESTAMP NOT NULL,
121
- updated_at TIMESTAMP NOT NULL
122
- )
123
- `);
124
-
125
- await createEntityEmbeddingsTable(db);
126
-
127
- // Create indexes for better query performance
128
- await db.run(
129
- "CREATE INDEX IF NOT EXISTS idx_packages_environment_id ON packages(environment_id)",
130
- );
131
- await db.run(
132
- "CREATE INDEX IF NOT EXISTS idx_connections_environment_id ON connections(environment_id)",
133
- );
134
- await db.run(
135
- "CREATE INDEX IF NOT EXISTS idx_materializations_environment_package ON materializations(environment_id, package_name)",
136
- );
137
- await db.run(
138
- "CREATE UNIQUE INDEX IF NOT EXISTS idx_materializations_active_key ON materializations(active_key)",
139
- );
140
- }
141
-
142
- /**
143
- * Vector cache for semantic `malloy_getContext` retrieval (see
144
- * mcp/tools/embedding_index.ts). One row per discoverable model entity;
145
- * the primary key mirrors the tool's in-memory dedup key. Rows are
146
- * content-addressed: the index sync compares `content_hash` (over the
147
- * embedded text) plus `embedding_model` and re-embeds on mismatch, while
148
- * a dimensionality change is detected at query time by the stale-row
149
- * heal, so staleness always self-corrects and wiping the table only
150
- * ever costs re-embedding. `embedding` is a LIST column searched with
151
- * list_cosine_similarity; at the entity counts a single Publisher
152
- * serves, a brute-force scan is faster and simpler than a vector-index
153
- * extension.
154
- */
155
- export async function createEntityEmbeddingsTable(
156
- db: DuckDBConnection,
157
- ): Promise<void> {
158
- await db.run(`
159
- CREATE TABLE IF NOT EXISTS entity_embeddings (
160
- environment_name VARCHAR NOT NULL,
161
- package_name VARCHAR NOT NULL,
162
- entity_kind VARCHAR NOT NULL,
163
- entity_source VARCHAR NOT NULL,
164
- entity_name VARCHAR NOT NULL,
165
- model_path VARCHAR NOT NULL,
166
- content_hash VARCHAR NOT NULL,
167
- embedding_model VARCHAR NOT NULL,
168
- dims INTEGER NOT NULL,
169
- embedding FLOAT[] NOT NULL,
170
- updated_at TIMESTAMP NOT NULL,
171
- PRIMARY KEY (environment_name, package_name, entity_kind, entity_source, entity_name)
172
- )
173
- `);
174
- }
175
-
176
- // TODO: Remove this during projects cleanup
177
- // Tables in the pre-rename schema, listed children-first so DROP order
178
- // satisfies foreign-key dependencies on the legacy `projects` table.
179
- const LEGACY_TABLES_DROP_ORDER = [
180
- "build_manifests",
181
- "materializations",
182
- "packages",
183
- "connections",
184
- "projects",
185
- ] as const;
186
-
187
- async function dropLegacyProjectSchema(db: DuckDBConnection): Promise<void> {
188
- const legacy = await db.all<{ name: string }>(
189
- "SELECT name FROM sqlite_master WHERE type='table' AND name='projects'",
190
- );
191
- if (!legacy || legacy.length === 0) {
192
- return;
193
- }
194
-
195
- logger.warn(
196
- "Detected legacy 'projects' schema. Dropping legacy tables; existing environments/packages/connections/materializations data will be lost. Re-create them via the API after upgrade.",
197
- );
198
-
199
- for (const table of LEGACY_TABLES_DROP_ORDER) {
200
- try {
201
- await db.run(`DROP TABLE IF EXISTS ${table}`);
202
- } catch (err) {
203
- logger.warn(`Failed to drop legacy table ${table}:`, err);
204
- }
205
- }
206
- }
207
-
208
- async function dropAllTables(db: DuckDBConnection): Promise<void> {
209
- const tables = [
210
- "build_manifests",
211
- "materializations",
212
- "packages",
213
- "connections",
214
- "environments",
215
- "themes",
216
- "entity_embeddings",
217
- ];
218
-
219
- logger.info("Dropping tables:", tables.join(", "));
220
-
221
- for (const table of tables) {
222
- try {
223
- await db.run(`DROP TABLE IF EXISTS ${table} `);
224
- logger.info(`Dropped table: ${table}`);
225
- } catch (err) {
226
- logger.warn(` Warning: Could not drop table ${table}:`, err);
227
- }
228
- }
229
- }
@@ -1,280 +0,0 @@
1
- import type {
2
- QueryRecord,
3
- RunSQLOptions,
4
- StreamingConnection,
5
- } from "@malloydata/malloy";
6
- import { describe, expect, it } from "bun:test";
7
-
8
- import { PayloadTooLargeError } from "./errors";
9
- import { isStreamingConnection, streamSqlWithBudget } from "./stream_helpers";
10
-
11
- /**
12
- * Build a fake StreamingConnection backed by a fixed row array. The
13
- * fake records the abort signal so tests can confirm
14
- * `streamSqlWithBudget` actually signaled the driver to stop —
15
- * client-side counting alone is not enough; the whole point of the
16
- * helper is to terminate fetching early.
17
- */
18
- function fakeStreamingConnection(opts: {
19
- rows: QueryRecord[];
20
- /**
21
- * When true (the default), the fake honors `RunSQLOptions.rowLimit`
22
- * by slicing — same as real Postgres/DuckDB. Set false to drive
23
- * the helper's own overflow detection (the (cap+1)-th row check).
24
- */
25
- honorRowLimit?: boolean;
26
- /**
27
- * Test hook: set to `true` when the fake observes `signal.aborted`
28
- * flip via the listener it installed at the start of streaming.
29
- */
30
- abortObserved?: { value: boolean };
31
- /**
32
- * Test hook: captures the options the helper passed to
33
- * `runSQLStream`, so tests can assert it preserved caller-supplied
34
- * `rowLimit` (and the abortSignal it wired in).
35
- */
36
- capturedOptions?: { value: RunSQLOptions | undefined };
37
- }): StreamingConnection {
38
- const { rows, honorRowLimit = true, abortObserved, capturedOptions } = opts;
39
- return {
40
- canStream(): true {
41
- return true;
42
- },
43
- async *runSQLStream(
44
- _sql: string,
45
- options?: RunSQLOptions,
46
- ): AsyncIterableIterator<QueryRecord> {
47
- if (capturedOptions) capturedOptions.value = options;
48
- if (abortObserved) {
49
- options?.abortSignal?.addEventListener("abort", () => {
50
- abortObserved.value = true;
51
- });
52
- }
53
- const limit =
54
- honorRowLimit && typeof options?.rowLimit === "number"
55
- ? options.rowLimit
56
- : rows.length;
57
- for (let i = 0; i < Math.min(rows.length, limit); i += 1) {
58
- yield rows[i];
59
- }
60
- },
61
- } as unknown as StreamingConnection;
62
- }
63
-
64
- describe("isStreamingConnection", () => {
65
- it("returns true for a connection whose canStream() returns true", () => {
66
- const conn = fakeStreamingConnection({ rows: [] });
67
- expect(isStreamingConnection(conn)).toBe(true);
68
- });
69
-
70
- it("returns false for a connection without canStream", () => {
71
- expect(isStreamingConnection({} as never)).toBe(false);
72
- });
73
-
74
- it("returns false for a connection whose canStream() returns false", () => {
75
- const conn = {
76
- canStream() {
77
- return false;
78
- },
79
- } as never;
80
- expect(isStreamingConnection(conn)).toBe(false);
81
- });
82
- });
83
-
84
- describe("streamSqlWithBudget", () => {
85
- it("returns all rows when both budgets are comfortably above the stream", async () => {
86
- const rows: QueryRecord[] = [{ a: 1 }, { a: 2 }, { a: 3 }];
87
- const conn = fakeStreamingConnection({ rows });
88
- const result = await streamSqlWithBudget(
89
- conn,
90
- "SELECT a FROM t",
91
- { rowLimit: 10 },
92
- { maxRows: 10, maxBytes: 1_000_000 },
93
- );
94
- expect(result.rows).toEqual(rows);
95
- expect(result.totalRows).toBe(3);
96
- });
97
-
98
- it("forwards caller-supplied runSQLOptions (rowLimit) to the driver", async () => {
99
- const captured = { value: undefined as RunSQLOptions | undefined };
100
- const conn = fakeStreamingConnection({
101
- rows: [{ a: 1 }, { a: 2 }],
102
- capturedOptions: captured,
103
- });
104
- await streamSqlWithBudget(
105
- conn,
106
- "SELECT 1",
107
- { rowLimit: 6 },
108
- { maxRows: 5, maxBytes: 0 },
109
- );
110
- expect(captured.value?.rowLimit).toBe(6);
111
- // abortSignal must be wired in so the helper can abort the
112
- // iterator on overflow.
113
- expect(captured.value?.abortSignal).toBeDefined();
114
- });
115
-
116
- it("composes the caller-supplied abortSignal with its internal cap-abort signal", async () => {
117
- // Step 5: the caller's signal is the query timeout. Composing
118
- // both sources means EITHER an external timeout OR an internal
119
- // cap overflow terminates the iterator. The combined signal
120
- // must be a fresh AbortSignal (not either input by reference);
121
- // aborting either input must mark the composed signal aborted.
122
- const captured = { value: undefined as RunSQLOptions | undefined };
123
- const callerAc = new AbortController();
124
- const conn = fakeStreamingConnection({
125
- rows: [{ a: 1 }],
126
- capturedOptions: captured,
127
- });
128
- await streamSqlWithBudget(
129
- conn,
130
- "SELECT 1",
131
- { rowLimit: 10, abortSignal: callerAc.signal },
132
- { maxRows: 5, maxBytes: 0 },
133
- );
134
- const observed = captured.value?.abortSignal;
135
- expect(observed).toBeInstanceOf(AbortSignal);
136
- // Composed signal is a new object (`AbortSignal.any` returns a
137
- // fresh signal), not the caller's signal by reference.
138
- expect(observed).not.toBe(callerAc.signal);
139
- expect(observed?.aborted).toBe(false);
140
- });
141
-
142
- it("composed signal aborts when the caller's signal aborts", async () => {
143
- // Drive the external (caller) signal manually and confirm the
144
- // composed signal that reached the driver tracks it. This is the
145
- // half of composition that runWithQueryTimeout depends on for
146
- // 504 to actually cancel an in-flight query.
147
- if (typeof AbortSignal.any !== "function") return;
148
- const captured = { value: undefined as RunSQLOptions | undefined };
149
- const callerAc = new AbortController();
150
- const conn = fakeStreamingConnection({
151
- rows: [{ a: 1 }],
152
- capturedOptions: captured,
153
- });
154
- await streamSqlWithBudget(
155
- conn,
156
- "SELECT 1",
157
- { rowLimit: 10, abortSignal: callerAc.signal },
158
- { maxRows: 5, maxBytes: 0 },
159
- );
160
- const observed = captured.value?.abortSignal;
161
- expect(observed?.aborted).toBe(false);
162
- callerAc.abort();
163
- expect(observed?.aborted).toBe(true);
164
- });
165
-
166
- it("throws PayloadTooLargeError and aborts the iterator on the (cap+1)-th row", async () => {
167
- const rows: QueryRecord[] = [
168
- { a: 1 },
169
- { a: 2 },
170
- { a: 3 },
171
- { a: 4 },
172
- { a: 5 },
173
- ];
174
- const abortObserved = { value: false };
175
- const conn = fakeStreamingConnection({
176
- rows,
177
- abortObserved,
178
- honorRowLimit: false,
179
- });
180
- await expect(
181
- streamSqlWithBudget(
182
- conn,
183
- "SELECT a FROM t",
184
- {},
185
- { maxRows: 2, maxBytes: 1_000_000 },
186
- ),
187
- ).rejects.toBeInstanceOf(PayloadTooLargeError);
188
- await expect(
189
- streamSqlWithBudget(
190
- conn,
191
- "SELECT a FROM t",
192
- {},
193
- { maxRows: 2, maxBytes: 1_000_000 },
194
- ),
195
- ).rejects.toThrow("more than 2 rows");
196
- // The helper must have fired the abort signal so a real driver
197
- // (pg-query-stream / duckdb) would stop producing rows server-
198
- // side, not just be discarded client-side.
199
- expect(abortObserved.value).toBe(true);
200
- });
201
-
202
- it("throws PayloadTooLargeError when summed JSON byte size exceeds the cap", async () => {
203
- const big = "x".repeat(40);
204
- const rows: QueryRecord[] = [{ s: big }, { s: big }, { s: big }];
205
- const abortObserved = { value: false };
206
- const conn = fakeStreamingConnection({
207
- rows,
208
- abortObserved,
209
- honorRowLimit: false,
210
- });
211
- await expect(
212
- streamSqlWithBudget(
213
- conn,
214
- "SELECT s FROM t",
215
- {},
216
- { maxRows: 100, maxBytes: 60 },
217
- ),
218
- ).rejects.toThrow("exceeded 60 bytes");
219
- expect(abortObserved.value).toBe(true);
220
- });
221
-
222
- it("returns all rows when the byte cap is disabled (maxBytes = 0)", async () => {
223
- const big = "x".repeat(10_000);
224
- const rows: QueryRecord[] = Array.from({ length: 3 }, () => ({ s: big }));
225
- const conn = fakeStreamingConnection({ rows, honorRowLimit: false });
226
- const result = await streamSqlWithBudget(
227
- conn,
228
- "SELECT s FROM t",
229
- {},
230
- { maxRows: 100, maxBytes: 0 },
231
- );
232
- expect(result.rows.length).toBe(3);
233
- });
234
-
235
- it("returns all rows when the row cap is disabled (maxRows = 0)", async () => {
236
- const rows: QueryRecord[] = Array.from({ length: 50 }, (_, i) => ({
237
- a: i,
238
- }));
239
- const conn = fakeStreamingConnection({ rows, honorRowLimit: false });
240
- const result = await streamSqlWithBudget(
241
- conn,
242
- "SELECT a FROM t",
243
- {},
244
- { maxRows: 0, maxBytes: 1_000_000 },
245
- );
246
- expect(result.rows.length).toBe(50);
247
- });
248
-
249
- it("returns rows when count equals the cap exactly (not an overflow)", async () => {
250
- const rows: QueryRecord[] = [{ a: 1 }, { a: 2 }, { a: 3 }];
251
- const conn = fakeStreamingConnection({ rows, honorRowLimit: false });
252
- const result = await streamSqlWithBudget(
253
- conn,
254
- "SELECT a FROM t",
255
- {},
256
- { maxRows: 3, maxBytes: 1_000_000 },
257
- );
258
- expect(result.rows.length).toBe(3);
259
- });
260
-
261
- it("re-throws non-overflow errors from the driver", async () => {
262
- const conn = {
263
- canStream(): true {
264
- return true;
265
- },
266
- // eslint-disable-next-line require-yield
267
- async *runSQLStream(): AsyncIterableIterator<QueryRecord> {
268
- throw new Error("connection reset");
269
- },
270
- } as unknown as StreamingConnection;
271
- await expect(
272
- streamSqlWithBudget(
273
- conn,
274
- "SELECT 1",
275
- {},
276
- { maxRows: 10, maxBytes: 1_000 },
277
- ),
278
- ).rejects.toThrow("connection reset");
279
- });
280
- });