@malloy-publisher/server 0.0.196-dev → 0.0.198-dev

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 (100) hide show
  1. package/dist/app/api-doc.yaml +213 -214
  2. package/dist/app/assets/EnvironmentPage-1j6QDWAy.js +1 -0
  3. package/dist/app/assets/HomePage-DMop21VG.js +1 -0
  4. package/dist/app/assets/MainPage-BbE8ETz1.js +2 -0
  5. package/dist/app/assets/ModelPage-D2jvfe3t.js +1 -0
  6. package/dist/app/assets/PackagePage-BbnhGoD3.js +1 -0
  7. package/dist/app/assets/{RouteError-DefbDO7F.js → RouteError-D3LGEZ3i.js} +1 -1
  8. package/dist/app/assets/WorkbookPage-DttVIj4u.js +1 -0
  9. package/dist/app/assets/{core-BrfQApxh.es-DnvCX4oH.js → core-w79IMXAG.es-Bd0UlzOL.js} +1 -1
  10. package/dist/app/assets/{index-Bu0ub036.js → index-5K9YjIxF.js} +117 -117
  11. package/dist/app/assets/{index-CkzK3JIl.js → index-C513UodQ.js} +1 -1
  12. package/dist/app/assets/{index-CoA6HIGS.js → index-DIgzgp69.js} +1 -1
  13. package/dist/app/assets/{index.umd-B6Ms2PpL.js → index.umd-BMeMPq_9.js} +1 -1
  14. package/dist/app/index.html +1 -1
  15. package/dist/server.mjs +1947 -1317
  16. package/package.json +1 -1
  17. package/publisher.config.json +2 -2
  18. package/src/config.spec.ts +74 -66
  19. package/src/config.ts +50 -47
  20. package/src/controller/compile.controller.ts +10 -7
  21. package/src/controller/connection.controller.ts +79 -58
  22. package/src/controller/database.controller.ts +10 -7
  23. package/src/controller/manifest.controller.ts +23 -14
  24. package/src/controller/materialization.controller.ts +14 -14
  25. package/src/controller/model.controller.ts +35 -20
  26. package/src/controller/package.controller.ts +83 -49
  27. package/src/controller/query.controller.ts +11 -8
  28. package/src/controller/watch-mode.controller.ts +35 -29
  29. package/src/errors.ts +2 -2
  30. package/src/mcp/error_messages.ts +2 -2
  31. package/src/mcp/handler_utils.ts +23 -20
  32. package/src/mcp/mcp_constants.ts +1 -1
  33. package/src/mcp/prompts/handlers.ts +3 -3
  34. package/src/mcp/prompts/prompt_service.ts +5 -5
  35. package/src/mcp/prompts/utils.ts +12 -12
  36. package/src/mcp/resource_metadata.ts +3 -3
  37. package/src/mcp/resources/environment_resource.ts +187 -0
  38. package/src/mcp/resources/model_resource.ts +19 -17
  39. package/src/mcp/resources/notebook_resource.ts +13 -13
  40. package/src/mcp/resources/package_resource.ts +30 -27
  41. package/src/mcp/resources/query_resource.ts +15 -10
  42. package/src/mcp/resources/source_resource.ts +10 -10
  43. package/src/mcp/resources/view_resource.ts +11 -11
  44. package/src/mcp/server.ts +16 -14
  45. package/src/mcp/tools/discovery_tools.ts +67 -49
  46. package/src/mcp/tools/execute_query_tool.ts +14 -14
  47. package/src/server-old.ts +1139 -0
  48. package/src/server.ts +191 -159
  49. package/src/service/connection.spec.ts +158 -133
  50. package/src/service/connection.ts +42 -39
  51. package/src/service/connection_config.spec.ts +13 -11
  52. package/src/service/connection_config.ts +28 -19
  53. package/src/service/connection_service.spec.ts +63 -43
  54. package/src/service/connection_service.ts +106 -89
  55. package/src/service/{project.ts → environment.ts} +92 -77
  56. package/src/service/{project_compile.spec.ts → environment_compile.spec.ts} +1 -1
  57. package/src/service/{project_store.spec.ts → environment_store.spec.ts} +99 -85
  58. package/src/service/{project_store.ts → environment_store.ts} +368 -326
  59. package/src/service/manifest_service.spec.ts +15 -15
  60. package/src/service/manifest_service.ts +26 -21
  61. package/src/service/materialization_service.spec.ts +93 -59
  62. package/src/service/materialization_service.ts +71 -62
  63. package/src/service/materialized_table_gc.spec.ts +15 -15
  64. package/src/service/materialized_table_gc.ts +3 -3
  65. package/src/service/model.ts +2 -2
  66. package/src/service/package.spec.ts +2 -2
  67. package/src/service/package.ts +23 -21
  68. package/src/service/resolve_environment.ts +15 -0
  69. package/src/storage/DatabaseInterface.ts +34 -25
  70. package/src/storage/StorageManager.mock.ts +3 -3
  71. package/src/storage/StorageManager.ts +24 -23
  72. package/src/storage/duckdb/ConnectionRepository.ts +13 -11
  73. package/src/storage/duckdb/DuckDBConnection.ts +1 -1
  74. package/src/storage/duckdb/DuckDBManifestStore.ts +6 -6
  75. package/src/storage/duckdb/DuckDBRepository.ts +47 -47
  76. package/src/storage/duckdb/{ProjectRepository.ts → EnvironmentRepository.ts} +35 -35
  77. package/src/storage/duckdb/ManifestRepository.ts +21 -20
  78. package/src/storage/duckdb/MaterializationRepository.ts +31 -28
  79. package/src/storage/duckdb/PackageRepository.ts +11 -11
  80. package/src/storage/duckdb/manifest_store.spec.ts +2 -2
  81. package/src/storage/duckdb/schema.ts +20 -20
  82. package/src/storage/ducklake/DuckLakeManifestStore.ts +14 -14
  83. package/tests/fixtures/publisher.config.json +1 -1
  84. package/tests/harness/e2e.ts +1 -1
  85. package/tests/harness/mcp_test_setup.ts +1 -1
  86. package/tests/harness/mocks.ts +10 -8
  87. package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +4 -4
  88. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +27 -48
  89. package/tests/integration/mcp/mcp_resource.integration.spec.ts +26 -35
  90. package/tests/unit/duckdb/attached_databases.test.ts +51 -33
  91. package/tests/unit/ducklake/ducklake.test.ts +24 -22
  92. package/tests/unit/mcp/prompt_happy.test.ts +8 -8
  93. package/dist/app/assets/HomePage-DbZS0N7G.js +0 -1
  94. package/dist/app/assets/MainPage-CBuWkbmr.js +0 -2
  95. package/dist/app/assets/ModelPage-Bt37smot.js +0 -1
  96. package/dist/app/assets/PackagePage-DLZe50WG.js +0 -1
  97. package/dist/app/assets/ProjectPage-FQTEPXP4.js +0 -1
  98. package/dist/app/assets/WorkbookPage-CkAo16ar.js +0 -1
  99. package/src/mcp/resources/project_resource.ts +0 -184
  100. package/src/service/resolve_project.ts +0 -13
@@ -1,11 +1,14 @@
1
+ import { DuckDBConnection } from "@malloydata/db-duckdb";
1
2
  import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
3
  import fs from "fs/promises";
3
4
  import path from "path";
4
5
  import sinon from "sinon";
5
- import { DuckDBConnection } from "@malloydata/db-duckdb";
6
- import { createProjectConnections, testConnectionConfig } from "./connection";
7
- import { assembleProjectConnections } from "./connection_config";
8
6
  import { components } from "../api";
7
+ import {
8
+ createEnvironmentConnections,
9
+ testConnectionConfig,
10
+ } from "./connection";
11
+ import { assembleEnvironmentConnections } from "./connection_config";
9
12
 
10
13
  type ApiConnection = components["schemas"]["Connection"];
11
14
  type AttachedDatabase = components["schemas"]["AttachedDatabase"];
@@ -43,11 +46,14 @@ const readBigQueryServiceAccountJson = async (): Promise<string> =>
43
46
  fs.readFile(process.env.GOOGLE_APPLICATION_CREDENTIALS!, "utf-8");
44
47
 
45
48
  describe("connection integration tests", () => {
46
- const testProjectPath = path.join(process.cwd(), "test-project-connections");
49
+ const testEnvironmentPath = path.join(
50
+ process.cwd(),
51
+ "test-environment-connections",
52
+ );
47
53
  let createdConnections: DuckDBConnection[] = [];
48
54
 
49
55
  beforeEach(async () => {
50
- await fs.mkdir(testProjectPath, { recursive: true });
56
+ await fs.mkdir(testEnvironmentPath, { recursive: true });
51
57
  });
52
58
 
53
59
  afterEach(async () => {
@@ -68,7 +74,7 @@ describe("connection integration tests", () => {
68
74
 
69
75
  for (let attempt = 0; attempt < maxRetries; attempt++) {
70
76
  try {
71
- await fs.rm(testProjectPath, { recursive: true, force: true });
77
+ await fs.rm(testEnvironmentPath, { recursive: true, force: true });
72
78
  return;
73
79
  } catch (error) {
74
80
  lastError = error;
@@ -87,7 +93,7 @@ describe("connection integration tests", () => {
87
93
  }
88
94
  });
89
95
 
90
- describe("createProjectConnections", () => {
96
+ describe("createEnvironmentConnections", () => {
91
97
  describe("DuckDB with PostgreSQL attachment", () => {
92
98
  it(
93
99
  "should create DuckDB connection with attached PostgreSQL database",
@@ -118,9 +124,9 @@ describe("connection integration tests", () => {
118
124
  };
119
125
 
120
126
  const { malloyConnections, apiConnections } =
121
- await createProjectConnections(
127
+ await createEnvironmentConnections(
122
128
  [duckdbConnection],
123
- testProjectPath,
129
+ testEnvironmentPath,
124
130
  );
125
131
 
126
132
  expect(malloyConnections.size).toBe(1);
@@ -178,9 +184,9 @@ describe("connection integration tests", () => {
178
184
  },
179
185
  };
180
186
 
181
- const { malloyConnections } = await createProjectConnections(
187
+ const { malloyConnections } = await createEnvironmentConnections(
182
188
  [duckdbConnection],
183
- testProjectPath,
189
+ testEnvironmentPath,
184
190
  );
185
191
 
186
192
  const connection = malloyConnections.get(
@@ -233,9 +239,9 @@ describe("connection integration tests", () => {
233
239
  },
234
240
  };
235
241
 
236
- const { malloyConnections } = await createProjectConnections(
242
+ const { malloyConnections } = await createEnvironmentConnections(
237
243
  [duckdbConnection],
238
- testProjectPath,
244
+ testEnvironmentPath,
239
245
  );
240
246
 
241
247
  const connection = malloyConnections.get(
@@ -287,9 +293,9 @@ describe("connection integration tests", () => {
287
293
  },
288
294
  };
289
295
 
290
- const { malloyConnections } = await createProjectConnections(
296
+ const { malloyConnections } = await createEnvironmentConnections(
291
297
  [duckdbConnection],
292
- testProjectPath,
298
+ testEnvironmentPath,
293
299
  );
294
300
 
295
301
  const connection = malloyConnections.get(
@@ -340,9 +346,9 @@ describe("connection integration tests", () => {
340
346
  },
341
347
  };
342
348
 
343
- const { malloyConnections } = await createProjectConnections(
349
+ const { malloyConnections } = await createEnvironmentConnections(
344
350
  [duckdbConnection],
345
- testProjectPath,
351
+ testEnvironmentPath,
346
352
  );
347
353
 
348
354
  const connection = malloyConnections.get(
@@ -390,9 +396,9 @@ describe("connection integration tests", () => {
390
396
  },
391
397
  };
392
398
 
393
- const { malloyConnections } = await createProjectConnections(
399
+ const { malloyConnections } = await createEnvironmentConnections(
394
400
  [duckdbConnection],
395
- testProjectPath,
401
+ testEnvironmentPath,
396
402
  );
397
403
 
398
404
  const connection = malloyConnections.get(
@@ -444,9 +450,9 @@ describe("connection integration tests", () => {
444
450
  },
445
451
  };
446
452
 
447
- const { malloyConnections } = await createProjectConnections(
453
+ const { malloyConnections } = await createEnvironmentConnections(
448
454
  [duckdbConnection],
449
- testProjectPath,
455
+ testEnvironmentPath,
450
456
  );
451
457
 
452
458
  const connection = malloyConnections.get(
@@ -502,9 +508,9 @@ describe("connection integration tests", () => {
502
508
  },
503
509
  };
504
510
 
505
- const { malloyConnections } = await createProjectConnections(
511
+ const { malloyConnections } = await createEnvironmentConnections(
506
512
  [duckdbConnection],
507
- testProjectPath,
513
+ testEnvironmentPath,
508
514
  );
509
515
 
510
516
  const connection = malloyConnections.get(
@@ -558,9 +564,9 @@ describe("connection integration tests", () => {
558
564
  },
559
565
  };
560
566
 
561
- const { malloyConnections } = await createProjectConnections(
567
+ const { malloyConnections } = await createEnvironmentConnections(
562
568
  [duckdbConnection],
563
- testProjectPath,
569
+ testEnvironmentPath,
564
570
  );
565
571
 
566
572
  const connection = malloyConnections.get(
@@ -614,9 +620,9 @@ describe("connection integration tests", () => {
614
620
  },
615
621
  };
616
622
 
617
- const { malloyConnections } = await createProjectConnections(
623
+ const { malloyConnections } = await createEnvironmentConnections(
618
624
  [duckdbConnection],
619
- testProjectPath,
625
+ testEnvironmentPath,
620
626
  );
621
627
 
622
628
  const connection = malloyConnections.get(
@@ -637,7 +643,7 @@ describe("connection integration tests", () => {
637
643
  "should validate BigQuery service account key format",
638
644
  async () => {
639
645
  await expect(
640
- createProjectConnections(
646
+ createEnvironmentConnections(
641
647
  [
642
648
  {
643
649
  name: "duckdb_bq_invalid",
@@ -648,7 +654,7 @@ describe("connection integration tests", () => {
648
654
  name: "bq_invalid",
649
655
  type: "bigquery",
650
656
  bigqueryConnection: {
651
- defaultProjectId: "test-project",
657
+ defaultProjectId: "test-environment",
652
658
  serviceAccountKeyJson: JSON.stringify({
653
659
  invalid: "key",
654
660
  }),
@@ -658,7 +664,7 @@ describe("connection integration tests", () => {
658
664
  },
659
665
  },
660
666
  ],
661
- testProjectPath,
667
+ testEnvironmentPath,
662
668
  ),
663
669
  ).rejects.toThrow(/Invalid service account key/);
664
670
  },
@@ -696,9 +702,9 @@ describe("connection integration tests", () => {
696
702
  },
697
703
  };
698
704
 
699
- const { malloyConnections } = await createProjectConnections(
705
+ const { malloyConnections } = await createEnvironmentConnections(
700
706
  [duckdbConnection],
701
- testProjectPath,
707
+ testEnvironmentPath,
702
708
  );
703
709
 
704
710
  const connection = malloyConnections.get(
@@ -723,7 +729,7 @@ describe("connection integration tests", () => {
723
729
 
724
730
  it("should validate required Snowflake fields", async () => {
725
731
  await expect(
726
- createProjectConnections(
732
+ createEnvironmentConnections(
727
733
  [
728
734
  {
729
735
  name: "duckdb_sf_incomplete",
@@ -742,7 +748,7 @@ describe("connection integration tests", () => {
742
748
  },
743
749
  },
744
750
  ],
745
- testProjectPath,
751
+ testEnvironmentPath,
746
752
  ),
747
753
  ).rejects.toThrow(/required/);
748
754
  });
@@ -776,9 +782,9 @@ describe("connection integration tests", () => {
776
782
  },
777
783
  };
778
784
 
779
- const { malloyConnections } = await createProjectConnections(
785
+ const { malloyConnections } = await createEnvironmentConnections(
780
786
  [duckdbConnection],
781
- testProjectPath,
787
+ testEnvironmentPath,
782
788
  );
783
789
 
784
790
  const connection = malloyConnections.get(
@@ -828,9 +834,9 @@ describe("connection integration tests", () => {
828
834
  },
829
835
  };
830
836
 
831
- const { malloyConnections } = await createProjectConnections(
837
+ const { malloyConnections } = await createEnvironmentConnections(
832
838
  [duckdbConnection],
833
- testProjectPath,
839
+ testEnvironmentPath,
834
840
  );
835
841
 
836
842
  const connection = malloyConnections.get(
@@ -869,9 +875,9 @@ describe("connection integration tests", () => {
869
875
  },
870
876
  };
871
877
 
872
- const { malloyConnections } = await createProjectConnections(
878
+ const { malloyConnections } = await createEnvironmentConnections(
873
879
  [duckdbConnection],
874
- testProjectPath,
880
+ testEnvironmentPath,
875
881
  );
876
882
 
877
883
  const connection = malloyConnections.get(
@@ -946,7 +952,7 @@ describe("connection integration tests", () => {
946
952
  return;
947
953
  }
948
954
 
949
- const { malloyConnections } = await createProjectConnections(
955
+ const { malloyConnections } = await createEnvironmentConnections(
950
956
  [
951
957
  {
952
958
  name: "duckdb_multi",
@@ -954,7 +960,7 @@ describe("connection integration tests", () => {
954
960
  duckdbConnection: { attachedDatabases: attachments },
955
961
  },
956
962
  ],
957
- testProjectPath,
963
+ testEnvironmentPath,
958
964
  );
959
965
 
960
966
  const connection = malloyConnections.get(
@@ -987,41 +993,45 @@ describe("connection integration tests", () => {
987
993
  return;
988
994
  }
989
995
 
990
- const { malloyConnections } = await createProjectConnections(
991
- [
992
- {
993
- name: "ducklake_test",
994
- type: "ducklake",
995
- ducklakeConnection: {
996
- catalog: {
997
- postgresConnection: {
998
- host: process.env.POSTGRES_TEST_HOST,
999
- port: parseInt(
1000
- process.env.POSTGRES_TEST_PORT || "5432",
1001
- ),
1002
- userName: process.env.POSTGRES_TEST_USER!,
1003
- password:
1004
- process.env.POSTGRES_TEST_PASSWORD!,
1005
- databaseName:
1006
- process.env.POSTGRES_TEST_DATABASE,
996
+ const { malloyConnections } =
997
+ await createEnvironmentConnections(
998
+ [
999
+ {
1000
+ name: "ducklake_test",
1001
+ type: "ducklake",
1002
+ ducklakeConnection: {
1003
+ catalog: {
1004
+ postgresConnection: {
1005
+ host: process.env.POSTGRES_TEST_HOST,
1006
+ port: parseInt(
1007
+ process.env.POSTGRES_TEST_PORT ||
1008
+ "5432",
1009
+ ),
1010
+ userName:
1011
+ process.env.POSTGRES_TEST_USER!,
1012
+ password:
1013
+ process.env.POSTGRES_TEST_PASSWORD!,
1014
+ databaseName:
1015
+ process.env.POSTGRES_TEST_DATABASE,
1016
+ },
1007
1017
  },
1008
- },
1009
- storage: {
1010
- bucketUrl:
1011
- process.env.S3_TEST_BUCKET_URL ||
1012
- "s3://test-bucket",
1013
- s3Connection: {
1014
- accessKeyId:
1015
- process.env.S3_TEST_ACCESS_KEY_ID!,
1016
- secretAccessKey:
1017
- process.env.S3_TEST_SECRET_ACCESS_KEY!,
1018
+ storage: {
1019
+ bucketUrl:
1020
+ process.env.S3_TEST_BUCKET_URL ||
1021
+ "s3://test-bucket",
1022
+ s3Connection: {
1023
+ accessKeyId:
1024
+ process.env.S3_TEST_ACCESS_KEY_ID!,
1025
+ secretAccessKey:
1026
+ process.env
1027
+ .S3_TEST_SECRET_ACCESS_KEY!,
1028
+ },
1018
1029
  },
1019
1030
  },
1020
1031
  },
1021
- },
1022
- ],
1023
- testProjectPath,
1024
- );
1032
+ ],
1033
+ testEnvironmentPath,
1034
+ );
1025
1035
 
1026
1036
  const connection = malloyConnections.get(
1027
1037
  "ducklake_test",
@@ -1041,7 +1051,7 @@ describe("connection integration tests", () => {
1041
1051
 
1042
1052
  it("should throw error if DuckLake catalog connection is missing", async () => {
1043
1053
  await expect(
1044
- createProjectConnections(
1054
+ createEnvironmentConnections(
1045
1055
  [
1046
1056
  {
1047
1057
  name: "ducklake_no_catalog",
@@ -1057,7 +1067,7 @@ describe("connection integration tests", () => {
1057
1067
  },
1058
1068
  } as ApiConnection,
1059
1069
  ],
1060
- testProjectPath,
1070
+ testEnvironmentPath,
1061
1071
  ),
1062
1072
  ).rejects.toThrow(
1063
1073
  /PostgreSQL connection configuration is required/,
@@ -1066,14 +1076,14 @@ describe("connection integration tests", () => {
1066
1076
 
1067
1077
  it("should throw error if DuckLake connection config is missing", async () => {
1068
1078
  await expect(
1069
- createProjectConnections(
1079
+ createEnvironmentConnections(
1070
1080
  [
1071
1081
  {
1072
1082
  name: "ducklake_missing_config",
1073
1083
  type: "ducklake",
1074
1084
  },
1075
1085
  ],
1076
- testProjectPath,
1086
+ testEnvironmentPath,
1077
1087
  ),
1078
1088
  ).rejects.toThrow(
1079
1089
  /DuckLake connection configuration is missing/,
@@ -1083,7 +1093,7 @@ describe("connection integration tests", () => {
1083
1093
 
1084
1094
  it("should throw error if DuckDB connection name conflicts with attached database", async () => {
1085
1095
  await expect(
1086
- createProjectConnections(
1096
+ createEnvironmentConnections(
1087
1097
  [
1088
1098
  {
1089
1099
  name: "conflict_db",
@@ -1102,14 +1112,14 @@ describe("connection integration tests", () => {
1102
1112
  },
1103
1113
  },
1104
1114
  ],
1105
- testProjectPath,
1115
+ testEnvironmentPath,
1106
1116
  ),
1107
1117
  ).rejects.toThrow(/cannot conflict/);
1108
1118
  });
1109
1119
 
1110
1120
  it("should throw error if connection name is 'duckdb'", async () => {
1111
1121
  await expect(
1112
- createProjectConnections(
1122
+ createEnvironmentConnections(
1113
1123
  [
1114
1124
  {
1115
1125
  name: "duckdb",
@@ -1117,30 +1127,31 @@ describe("connection integration tests", () => {
1117
1127
  duckdbConnection: { attachedDatabases: [] },
1118
1128
  },
1119
1129
  ],
1120
- testProjectPath,
1130
+ testEnvironmentPath,
1121
1131
  ),
1122
1132
  ).rejects.toThrow(/cannot be 'duckdb'/);
1123
1133
  });
1124
1134
 
1125
- it("should allow DuckDB connections with no attachments", async () => {
1126
- const { malloyConnections } = await createProjectConnections(
1127
- [
1128
- {
1129
- name: "empty_duckdb",
1130
- type: "duckdb",
1131
- duckdbConnection: { attachedDatabases: [] },
1132
- },
1133
- ],
1134
- testProjectPath,
1135
+ it("should reject DuckDB connections with no attachments", async () => {
1136
+ await expect(
1137
+ createEnvironmentConnections(
1138
+ [
1139
+ {
1140
+ name: "empty_duckdb",
1141
+ type: "duckdb",
1142
+ duckdbConnection: { attachedDatabases: [] },
1143
+ },
1144
+ ],
1145
+ testEnvironmentPath,
1146
+ ),
1147
+ ).rejects.toThrow(
1148
+ "DuckDB connection must have at least one attached database",
1135
1149
  );
1136
-
1137
- const connection = malloyConnections.get("empty_duckdb");
1138
- expect(connection).toBeDefined();
1139
1150
  });
1140
1151
 
1141
1152
  it("should reject unsupported DuckDB connector fields", async () => {
1142
1153
  await expect(
1143
- createProjectConnections(
1154
+ createEnvironmentConnections(
1144
1155
  [
1145
1156
  {
1146
1157
  name: "duckdb_with_setup_sql",
@@ -1151,14 +1162,14 @@ describe("connection integration tests", () => {
1151
1162
  },
1152
1163
  } as unknown as ApiConnection,
1153
1164
  ],
1154
- testProjectPath,
1165
+ testEnvironmentPath,
1155
1166
  ),
1156
1167
  ).rejects.toThrow(/Unsupported DuckDB connection field/);
1157
1168
  });
1158
1169
 
1159
- it("should reject project-authored DuckDB policy fields", async () => {
1170
+ it("should reject environment-authored DuckDB policy fields", async () => {
1160
1171
  await expect(
1161
- createProjectConnections(
1172
+ createEnvironmentConnections(
1162
1173
  [
1163
1174
  {
1164
1175
  name: "duckdb_with_policy",
@@ -1169,14 +1180,14 @@ describe("connection integration tests", () => {
1169
1180
  },
1170
1181
  } as unknown as ApiConnection,
1171
1182
  ],
1172
- testProjectPath,
1183
+ testEnvironmentPath,
1173
1184
  ),
1174
1185
  ).rejects.toThrow(/Unsupported DuckDB connection field/);
1175
1186
  });
1176
1187
 
1177
1188
  it("should preserve Snowflake private-key auth options", async () => {
1178
1189
  const { malloyConnections, releaseConnections } =
1179
- await createProjectConnections(
1190
+ await createEnvironmentConnections(
1180
1191
  [
1181
1192
  {
1182
1193
  name: "sf_private_key",
@@ -1190,7 +1201,7 @@ describe("connection integration tests", () => {
1190
1201
  },
1191
1202
  },
1192
1203
  ],
1193
- testProjectPath,
1204
+ testEnvironmentPath,
1194
1205
  );
1195
1206
 
1196
1207
  try {
@@ -1209,7 +1220,7 @@ describe("connection integration tests", () => {
1209
1220
  });
1210
1221
 
1211
1222
  it("should translate Trino Peaka credentials to core extraCredential", () => {
1212
- const assembled = assembleProjectConnections(
1223
+ const assembled = assembleEnvironmentConnections(
1213
1224
  [
1214
1225
  {
1215
1226
  name: "trino_peaka",
@@ -1224,7 +1235,7 @@ describe("connection integration tests", () => {
1224
1235
  },
1225
1236
  },
1226
1237
  ],
1227
- testProjectPath,
1238
+ testEnvironmentPath,
1228
1239
  );
1229
1240
 
1230
1241
  expect(
@@ -1235,29 +1246,29 @@ describe("connection integration tests", () => {
1235
1246
  ).toBeUndefined();
1236
1247
  });
1237
1248
 
1238
- it("should validate project-level BigQuery service account keys", () => {
1249
+ it("should validate environment-level BigQuery service account keys", () => {
1239
1250
  expect(() =>
1240
- assembleProjectConnections(
1251
+ assembleEnvironmentConnections(
1241
1252
  [
1242
1253
  {
1243
1254
  name: "bq_invalid",
1244
1255
  type: "bigquery",
1245
1256
  bigqueryConnection: {
1246
- defaultProjectId: "test-project",
1257
+ defaultProjectId: "test-environment",
1247
1258
  serviceAccountKeyJson: '{"invalid":"key"}',
1248
1259
  },
1249
1260
  },
1250
1261
  ],
1251
- testProjectPath,
1262
+ testEnvironmentPath,
1252
1263
  ),
1253
1264
  ).toThrow(/missing "type" field/);
1254
1265
  });
1255
1266
 
1256
- it("should preserve PGSSLMODE for project-level Postgres", () => {
1267
+ it("should preserve PGSSLMODE for environment-level Postgres", () => {
1257
1268
  const previousPgSslMode = process.env.PGSSLMODE;
1258
1269
  process.env.PGSSLMODE = "require";
1259
1270
  try {
1260
- const assembled = assembleProjectConnections(
1271
+ const assembled = assembleEnvironmentConnections(
1261
1272
  [
1262
1273
  {
1263
1274
  name: "pg_ssl",
@@ -1271,7 +1282,7 @@ describe("connection integration tests", () => {
1271
1282
  },
1272
1283
  },
1273
1284
  ],
1274
- testProjectPath,
1285
+ testEnvironmentPath,
1275
1286
  );
1276
1287
 
1277
1288
  expect(
@@ -1286,42 +1297,56 @@ describe("connection integration tests", () => {
1286
1297
  }
1287
1298
  });
1288
1299
 
1289
- it("should use project-root-relative file paths for project-level DuckDB", async () => {
1290
- const insideCsvPath = path.join(testProjectPath, "inside.csv");
1300
+ it("should use environment-root-relative file paths for environment-level DuckDB", async () => {
1301
+ const insideCsvPath = path.join(testEnvironmentPath, "inside.csv");
1291
1302
  await fs.writeFile(insideCsvPath, "id\n1\n");
1292
1303
 
1293
- const { malloyConnections } = await createProjectConnections(
1304
+ const minimalGcsAttachment = {
1305
+ name: "test_gcs",
1306
+ type: "gcs" as const,
1307
+ gcsConnection: {
1308
+ keyId: "test-key-id",
1309
+ secret: "test-secret",
1310
+ },
1311
+ };
1312
+
1313
+ const { malloyConnections } = await createEnvironmentConnections(
1294
1314
  [
1295
1315
  {
1296
- name: "project_scoped_duckdb",
1316
+ name: "environment_scoped_duckdb",
1297
1317
  type: "duckdb",
1298
- duckdbConnection: { attachedDatabases: [] },
1318
+ duckdbConnection: {
1319
+ attachedDatabases: [minimalGcsAttachment],
1320
+ },
1299
1321
  },
1300
1322
  ],
1301
- testProjectPath,
1323
+ testEnvironmentPath,
1302
1324
  );
1303
1325
 
1304
1326
  const connection = malloyConnections.get(
1305
- "project_scoped_duckdb",
1327
+ "environment_scoped_duckdb",
1306
1328
  ) as DuckDBConnection;
1307
1329
  createdConnections.push(connection);
1308
1330
 
1309
- const assembled = assembleProjectConnections(
1331
+ const assembled = assembleEnvironmentConnections(
1310
1332
  [
1311
1333
  {
1312
- name: "project_scoped_duckdb",
1334
+ name: "environment_scoped_duckdb",
1313
1335
  type: "duckdb",
1314
- duckdbConnection: { attachedDatabases: [] },
1336
+ duckdbConnection: {
1337
+ attachedDatabases: [minimalGcsAttachment],
1338
+ },
1315
1339
  },
1316
1340
  ],
1317
- testProjectPath,
1341
+ testEnvironmentPath,
1318
1342
  );
1319
1343
  expect(
1320
- assembled.pojo.connections.project_scoped_duckdb
1344
+ assembled.pojo.connections.environment_scoped_duckdb
1321
1345
  .workingDirectory,
1322
1346
  ).toBeUndefined();
1323
1347
  expect(
1324
- assembled.pojo.connections.project_scoped_duckdb.securityPolicy,
1348
+ assembled.pojo.connections.environment_scoped_duckdb
1349
+ .securityPolicy,
1325
1350
  ).toBeUndefined();
1326
1351
 
1327
1352
  await expect(
@@ -1330,7 +1355,7 @@ describe("connection integration tests", () => {
1330
1355
  });
1331
1356
 
1332
1357
  it("should keep external access available for federated DuckDB entries", () => {
1333
- const assembled = assembleProjectConnections(
1358
+ const assembled = assembleEnvironmentConnections(
1334
1359
  [
1335
1360
  {
1336
1361
  name: "federated_duckdb",
@@ -1352,7 +1377,7 @@ describe("connection integration tests", () => {
1352
1377
  },
1353
1378
  },
1354
1379
  ],
1355
- testProjectPath,
1380
+ testEnvironmentPath,
1356
1381
  );
1357
1382
 
1358
1383
  const entry = assembled.pojo.connections.federated_duckdb;
@@ -1362,7 +1387,7 @@ describe("connection integration tests", () => {
1362
1387
  });
1363
1388
 
1364
1389
  it("should keep external access available for MotherDuck entries", () => {
1365
- const assembled = assembleProjectConnections(
1390
+ const assembled = assembleEnvironmentConnections(
1366
1391
  [
1367
1392
  {
1368
1393
  name: "md",
@@ -1373,7 +1398,7 @@ describe("connection integration tests", () => {
1373
1398
  },
1374
1399
  },
1375
1400
  ],
1376
- testProjectPath,
1401
+ testEnvironmentPath,
1377
1402
  );
1378
1403
 
1379
1404
  const entry = assembled.pojo.connections.md;
@@ -1400,7 +1425,7 @@ describe("connection integration tests", () => {
1400
1425
  },
1401
1426
  };
1402
1427
 
1403
- const { malloyConnections } = await createProjectConnections(
1428
+ const { malloyConnections } = await createEnvironmentConnections(
1404
1429
  [
1405
1430
  {
1406
1431
  name: "duckdb_duplicate_test",
@@ -1413,7 +1438,7 @@ describe("connection integration tests", () => {
1413
1438
  },
1414
1439
  },
1415
1440
  ],
1416
- testProjectPath,
1441
+ testEnvironmentPath,
1417
1442
  );
1418
1443
 
1419
1444
  const connection = malloyConnections.get(
@@ -1492,7 +1517,7 @@ describe("connection integration tests", () => {
1492
1517
  return;
1493
1518
  }
1494
1519
 
1495
- const { malloyConnections } = await createProjectConnections(
1520
+ const { malloyConnections } = await createEnvironmentConnections(
1496
1521
  [
1497
1522
  {
1498
1523
  name: "duckdb_special_chars",
@@ -1516,7 +1541,7 @@ describe("connection integration tests", () => {
1516
1541
  },
1517
1542
  },
1518
1543
  ],
1519
- testProjectPath,
1544
+ testEnvironmentPath,
1520
1545
  );
1521
1546
 
1522
1547
  const connection = malloyConnections.get(
@@ -1536,7 +1561,7 @@ describe("connection integration tests", () => {
1536
1561
  return;
1537
1562
  }
1538
1563
 
1539
- const { apiConnections } = await createProjectConnections(
1564
+ const { apiConnections } = await createEnvironmentConnections(
1540
1565
  [
1541
1566
  {
1542
1567
  name: "duckdb_attrs",
@@ -1561,7 +1586,7 @@ describe("connection integration tests", () => {
1561
1586
  },
1562
1587
  },
1563
1588
  ],
1564
- testProjectPath,
1589
+ testEnvironmentPath,
1565
1590
  );
1566
1591
 
1567
1592
  const connection = apiConnections[0];