@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
package/src/config.ts CHANGED
@@ -18,7 +18,7 @@ export type Connection = {
18
18
  type: string;
19
19
  };
20
20
 
21
- export type Project = {
21
+ export type Environment = {
22
22
  name: string;
23
23
  packages: Package[];
24
24
  connections?: Connection[];
@@ -26,10 +26,10 @@ export type Project = {
26
26
 
27
27
  export type PublisherConfig = {
28
28
  frozenConfig: boolean;
29
- projects: Project[];
29
+ environments: Environment[];
30
30
  };
31
31
 
32
- export type ProcessedProject = {
32
+ export type ProcessedEnvironment = {
33
33
  name: string;
34
34
  packages: Package[];
35
35
  connections: ApiConnection[];
@@ -37,7 +37,7 @@ export type ProcessedProject = {
37
37
 
38
38
  export type ProcessedPublisherConfig = {
39
39
  frozenConfig: boolean;
40
- projects: ProcessedProject[];
40
+ environments: ProcessedEnvironment[];
41
41
  };
42
42
 
43
43
  function substituteEnvVars(value: string): string {
@@ -81,7 +81,7 @@ export const getPublisherConfig = (serverRoot: string): PublisherConfig => {
81
81
  if (!fs.existsSync(publisherConfigPath)) {
82
82
  return {
83
83
  frozenConfig: false,
84
- projects: [],
84
+ environments: [],
85
85
  };
86
86
  }
87
87
 
@@ -101,7 +101,7 @@ export const getPublisherConfig = (serverRoot: string): PublisherConfig => {
101
101
  );
102
102
  return {
103
103
  frozenConfig: false,
104
- projects: [],
104
+ environments: [],
105
105
  };
106
106
  }
107
107
 
@@ -111,29 +111,30 @@ export const getPublisherConfig = (serverRoot: string): PublisherConfig => {
111
111
  if (
112
112
  processedConfig &&
113
113
  typeof processedConfig === "object" &&
114
- "projects" in processedConfig &&
115
- processedConfig.projects &&
116
- typeof processedConfig.projects === "object" &&
117
- !Array.isArray(processedConfig.projects)
114
+ "environments" in processedConfig &&
115
+ processedConfig.environments &&
116
+ typeof processedConfig.environments === "object" &&
117
+ !Array.isArray(processedConfig.environments)
118
118
  ) {
119
119
  logger.error(
120
- `Invalid ${PUBLISHER_CONFIG_NAME}: projects must be an array. Using default empty config.`,
120
+ `Invalid ${PUBLISHER_CONFIG_NAME}: the "environments" field must be a JSON array. Using default empty config.`,
121
121
  );
122
122
  return {
123
123
  frozenConfig: false,
124
- projects: [],
124
+ environments: [],
125
125
  };
126
126
  }
127
127
 
128
- // Ensure projects is an array
129
- let projects: unknown[] = [];
128
+ // Ensure environments is an array
129
+ let environments: unknown[] = [];
130
130
  if (
131
131
  processedConfig &&
132
132
  typeof processedConfig === "object" &&
133
- "projects" in processedConfig &&
134
- Array.isArray((processedConfig as { projects: unknown }).projects)
133
+ "environments" in processedConfig &&
134
+ Array.isArray((processedConfig as { environments: unknown }).environments)
135
135
  ) {
136
- projects = (processedConfig as { projects: unknown[] }).projects;
136
+ environments = (processedConfig as { environments: unknown[] })
137
+ .environments;
137
138
  }
138
139
 
139
140
  let frozenConfig = false;
@@ -149,7 +150,7 @@ export const getPublisherConfig = (serverRoot: string): PublisherConfig => {
149
150
 
150
151
  return {
151
152
  frozenConfig,
152
- projects,
153
+ environments,
153
154
  } as PublisherConfig;
154
155
  };
155
156
 
@@ -168,20 +169,22 @@ export const isPublisherConfigFrozen = (serverRoot: string) => {
168
169
 
169
170
  export const getConnectionsFromPublisherConfig = (
170
171
  serverRoot: string,
171
- projectName: string,
172
+ environmentName: string,
172
173
  ): Connection[] => {
173
174
  try {
174
175
  const publisherConfig = getPublisherConfig(serverRoot);
175
- if (!Array.isArray(publisherConfig.projects)) {
176
+ if (!Array.isArray(publisherConfig.environments)) {
176
177
  return [];
177
178
  }
178
- const project = publisherConfig.projects.find(
179
- (p) => p && p.name === projectName,
179
+ const environment = publisherConfig.environments.find(
180
+ (e) => e && e.name === environmentName,
180
181
  );
181
- return Array.isArray(project?.connections) ? project.connections : [];
182
+ return Array.isArray(environment?.connections)
183
+ ? environment.connections
184
+ : [];
182
185
  } catch (error) {
183
186
  logger.error(
184
- `Error getting connections for project "${projectName}" from ${PUBLISHER_CONFIG_NAME}`,
187
+ `Error getting connections for environment "${environmentName}" from ${PUBLISHER_CONFIG_NAME}`,
185
188
  { error },
186
189
  );
187
190
  return [];
@@ -228,59 +231,59 @@ export const getProcessedPublisherConfig = (
228
231
  ): ProcessedPublisherConfig => {
229
232
  const rawConfig = getPublisherConfig(serverRoot);
230
233
 
231
- // Ensure projects is an array
232
- if (!Array.isArray(rawConfig.projects)) {
234
+ // Ensure environments is an array
235
+ if (!Array.isArray(rawConfig.environments)) {
233
236
  logger.warn(
234
- `Invalid ${PUBLISHER_CONFIG_NAME}: projects must be an array. Using empty array.`,
237
+ `Invalid ${PUBLISHER_CONFIG_NAME}: the "environments" field must be a JSON array. Using empty array.`,
235
238
  );
236
239
  return {
237
240
  frozenConfig: rawConfig.frozenConfig ?? false,
238
- projects: [],
241
+ environments: [],
239
242
  };
240
243
  }
241
244
 
242
- // Filter and validate projects, skipping invalid ones
243
- const validProjects: ProcessedProject[] = [];
244
- for (const project of rawConfig.projects) {
245
- if (!project || typeof project !== "object") {
245
+ // Filter and validate environments, skipping invalid ones
246
+ const validEnvironments: ProcessedEnvironment[] = [];
247
+ for (const environment of rawConfig.environments) {
248
+ if (!environment || typeof environment !== "object") {
246
249
  logger.warn(
247
- `Invalid project in ${PUBLISHER_CONFIG_NAME}: project must be an object. Skipping.`,
250
+ `Invalid environment in ${PUBLISHER_CONFIG_NAME}: entry must be an object. Skipping.`,
248
251
  );
249
252
  continue;
250
253
  }
251
254
 
252
- if (!project.name || typeof project.name !== "string") {
255
+ if (!environment.name || typeof environment.name !== "string") {
253
256
  logger.warn(
254
- `Invalid project in ${PUBLISHER_CONFIG_NAME}: missing or invalid "name" field. Skipping project.`,
255
- { project },
257
+ `Invalid environment in ${PUBLISHER_CONFIG_NAME}: missing or invalid "name" field. Skipping entry.`,
258
+ { environment },
256
259
  );
257
260
  continue;
258
261
  }
259
262
 
260
- if (!Array.isArray(project.packages)) {
263
+ if (!Array.isArray(environment.packages)) {
261
264
  logger.warn(
262
- `Invalid project "${project.name}" in ${PUBLISHER_CONFIG_NAME}: missing or invalid "packages" field (must be an array). Skipping project.`,
265
+ `Invalid environment "${environment.name}" in ${PUBLISHER_CONFIG_NAME}: missing or invalid "packages" field (must be an array). Skipping entry.`,
263
266
  );
264
267
  continue;
265
268
  }
266
269
 
267
270
  // Validate packages have required fields
268
- const validPackages = project.packages.filter((pkg) => {
271
+ const validPackages = environment.packages.filter((pkg) => {
269
272
  if (!pkg || typeof pkg !== "object") {
270
273
  logger.warn(
271
- `Invalid package in project "${project.name}": package must be an object. Skipping.`,
274
+ `Invalid package in environment "${environment.name}": package must be an object. Skipping.`,
272
275
  );
273
276
  return false;
274
277
  }
275
278
  if (!pkg.name || typeof pkg.name !== "string") {
276
279
  logger.warn(
277
- `Invalid package in project "${project.name}": missing or invalid "name" field. Skipping.`,
280
+ `Invalid package in environment "${environment.name}": missing or invalid "name" field. Skipping.`,
278
281
  );
279
282
  return false;
280
283
  }
281
284
  if (!pkg.location || typeof pkg.location !== "string") {
282
285
  logger.warn(
283
- `Invalid package "${pkg.name}" in project "${project.name}": missing or invalid "location" field. Skipping.`,
286
+ `Invalid package "${pkg.name}" in environment "${environment.name}": missing or invalid "location" field. Skipping.`,
284
287
  );
285
288
  return false;
286
289
  }
@@ -289,22 +292,22 @@ export const getProcessedPublisherConfig = (
289
292
 
290
293
  if (validPackages.length === 0) {
291
294
  logger.warn(
292
- `Project "${project.name}" has no valid packages. Skipping project.`,
295
+ `Environment "${environment.name}" has no valid packages. Skipping entry.`,
293
296
  );
294
297
  continue;
295
298
  }
296
299
 
297
- validProjects.push({
298
- name: project.name,
300
+ validEnvironments.push({
301
+ name: environment.name,
299
302
  packages: validPackages,
300
303
  connections: convertConnectionsToApiConnections(
301
- project.connections || [],
304
+ environment.connections || [],
302
305
  ),
303
306
  });
304
307
  }
305
308
 
306
309
  return {
307
310
  frozenConfig: rawConfig.frozenConfig ?? false,
308
- projects: validProjects,
311
+ environments: validEnvironments,
309
312
  };
310
313
  };
@@ -1,22 +1,25 @@
1
1
  import type { LogMessage } from "@malloydata/malloy";
2
- import { ProjectStore } from "../service/project_store";
2
+ import { EnvironmentStore } from "../service/environment_store";
3
3
 
4
4
  export class CompileController {
5
- private projectStore: ProjectStore;
5
+ private environmentStore: EnvironmentStore;
6
6
 
7
- constructor(projectStore: ProjectStore) {
8
- this.projectStore = projectStore;
7
+ constructor(environmentStore: EnvironmentStore) {
8
+ this.environmentStore = environmentStore;
9
9
  }
10
10
 
11
11
  public async compile(
12
- projectName: string,
12
+ environmentName: string,
13
13
  packageName: string,
14
14
  modelName: string,
15
15
  source: string,
16
16
  includeSql: boolean = false,
17
17
  ): Promise<{ status: string; problems: LogMessage[]; sql?: string }> {
18
- const project = await this.projectStore.getProject(projectName, false);
19
- const { problems, sql } = await project.compileSource(
18
+ const environment = await this.environmentStore.getEnvironment(
19
+ environmentName,
20
+ false,
21
+ );
22
+ const { problems, sql } = await environment.compileSource(
20
23
  packageName,
21
24
  modelName,
22
25
  source,
@@ -10,8 +10,8 @@ import {
10
10
  getSchemasForConnection,
11
11
  listTablesForSchema,
12
12
  } from "../service/db_utils";
13
- import type { Project } from "../service/project";
14
- import { ProjectStore } from "../service/project_store";
13
+ import type { Environment } from "../service/environment";
14
+ import { EnvironmentStore } from "../service/environment_store";
15
15
 
16
16
  type ApiConnection = components["schemas"]["Connection"];
17
17
  type ApiConnectionStatus = components["schemas"]["ConnectionStatus"];
@@ -103,19 +103,19 @@ function validateAdminAuthoredConnection(
103
103
  }
104
104
 
105
105
  export class ConnectionController {
106
- private projectStore: ProjectStore;
106
+ private environmentStore: EnvironmentStore;
107
107
  private connectionService: ConnectionService;
108
- constructor(projectStore: ProjectStore) {
109
- this.projectStore = projectStore;
110
- this.connectionService = new ConnectionService(projectStore);
108
+ constructor(environmentStore: EnvironmentStore) {
109
+ this.environmentStore = environmentStore;
110
+ this.connectionService = new ConnectionService(environmentStore);
111
111
  }
112
112
 
113
113
  /**
114
114
  * Gets the appropriate Malloy connection for a given connection name.
115
- * For DuckDB connections, retrieves from package level; for others, from project level.
115
+ * For DuckDB connections, retrieves from package level; for others, from environment level.
116
116
  */
117
117
  private getApiConnectionForLookup(
118
- project: Project,
118
+ environment: Environment,
119
119
  connectionName: string,
120
120
  ): ApiConnection {
121
121
  if (connectionName === "duckdb") {
@@ -125,35 +125,38 @@ export class ConnectionController {
125
125
  duckdbConnection: { attachedDatabases: [] },
126
126
  };
127
127
  }
128
- return project.getApiConnection(connectionName);
128
+ return environment.getApiConnection(connectionName);
129
129
  }
130
130
 
131
131
  private async getMalloyConnection(
132
- projectName: string,
132
+ environmentName: string,
133
133
  connectionName: string,
134
134
  packageName?: string,
135
135
  ): Promise<Connection> {
136
- const project = await this.projectStore.getProject(projectName, false);
136
+ const environment = await this.environmentStore.getEnvironment(
137
+ environmentName,
138
+ false,
139
+ );
137
140
 
138
141
  // "duckdb" is the per-package sandbox; its rootDirectory is the
139
- // package's directory. There is no project-level "duckdb" — the name is
142
+ // package's directory. There is no environment-level "duckdb" — the name is
140
143
  // reserved at config time. So the lookup is intrinsically per-package
141
144
  // and the caller must say which package to use.
142
145
  if (connectionName === "duckdb") {
143
- const packages = await project.listPackages();
146
+ const packages = await environment.listPackages();
144
147
  if (packages.length === 0) {
145
- // Fall through to project; this will surface the standard
148
+ // Fall through to environment; this will surface the standard
146
149
  // "connection not found" rather than silently inventing one.
147
- return await project.getMalloyConnection(connectionName);
150
+ return await environment.getMalloyConnection(connectionName);
148
151
  }
149
152
  if (packageName) {
150
153
  const known = packages.some((p) => p.name === packageName);
151
154
  if (!known) {
152
155
  throw new BadRequestError(
153
- `Package "${packageName}" not found in project "${projectName}"`,
156
+ `Package "${packageName}" not found in environment "${environmentName}"`,
154
157
  );
155
158
  }
156
- const pkg = await project.getPackage(packageName);
159
+ const pkg = await environment.getPackage(packageName);
157
160
  return await pkg.getMalloyConnection(connectionName);
158
161
  }
159
162
  if (packages.length === 1) {
@@ -161,15 +164,15 @@ export class ConnectionController {
161
164
  if (!onlyPackage) {
162
165
  throw new ConnectionError("Package name is undefined");
163
166
  }
164
- const pkg = await project.getPackage(onlyPackage);
167
+ const pkg = await environment.getPackage(onlyPackage);
165
168
  return await pkg.getMalloyConnection(connectionName);
166
169
  }
167
170
  throw new BadRequestError(
168
- `Ambiguous "duckdb" connection lookup: project "${projectName}" has multiple packages. ` +
169
- `Use /projects/${projectName}/packages/{packageName}/connections/duckdb/... to disambiguate.`,
171
+ `Ambiguous "duckdb" connection lookup: environment "${environmentName}" has multiple packages. ` +
172
+ `Use /environments/${environmentName}/packages/{packageName}/connections/duckdb/... to disambiguate.`,
170
173
  );
171
174
  } else {
172
- return await project.getMalloyConnection(connectionName);
175
+ return await environment.getMalloyConnection(connectionName);
173
176
  }
174
177
  }
175
178
 
@@ -188,7 +191,7 @@ export class ConnectionController {
188
191
  fetchTableSchema: (
189
192
  tableKey: string,
190
193
  tablePath: string,
191
- ) => Promise<TableSourceDef | undefined>;
194
+ ) => Promise<TableSourceDef | string | undefined>;
192
195
  }
193
196
  ).fetchTableSchema(tableKey, tablePath);
194
197
  if (!source) {
@@ -198,6 +201,7 @@ export class ConnectionController {
198
201
  if (typeof source === "string") {
199
202
  throw new ConnectionError(source);
200
203
  }
204
+
201
205
  return {
202
206
  source: JSON.stringify(source),
203
207
  resource: tablePath,
@@ -223,39 +227,50 @@ export class ConnectionController {
223
227
  }
224
228
 
225
229
  public async getConnection(
226
- projectName: string,
230
+ environmentName: string,
227
231
  connectionName: string,
228
232
  ): Promise<ApiConnection> {
229
- if (!projectName || !connectionName) {
233
+ if (!environmentName || !connectionName) {
230
234
  throw new BadRequestError("Connection payload is required");
231
235
  }
232
236
  // Prefer the in-memory API connection (which was materialized by the
233
- // project on load and carries `attributes`). The DB row stores the
237
+ // environment on load and carries `attributes`). The DB row stores the
234
238
  // raw config and FK columns, which aren't the ApiConnection shape.
235
- const project = await this.projectStore.getProject(projectName, false);
236
- return project.getApiConnection(connectionName);
239
+ const environment = await this.environmentStore.getEnvironment(
240
+ environmentName,
241
+ false,
242
+ );
243
+ return environment.getApiConnection(connectionName);
237
244
  }
238
245
 
239
- public async listConnections(projectName: string): Promise<ApiConnection[]> {
240
- const project = await this.projectStore.getProject(projectName, false);
241
- return project.listApiConnections();
246
+ public async listConnections(
247
+ environmentName: string,
248
+ ): Promise<ApiConnection[]> {
249
+ const environment = await this.environmentStore.getEnvironment(
250
+ environmentName,
251
+ false,
252
+ );
253
+ return environment.listApiConnections();
242
254
  }
243
255
 
244
256
  // Lists schemas (namespaces) available in a connection.
245
257
  // For "duckdb", the per-package sandbox, packageName disambiguates which
246
- // package's DuckDB to browse in a multi-package project.
258
+ // package's DuckDB to browse in a multi-package environment.
247
259
  public async listSchemas(
248
- projectName: string,
260
+ environmentName: string,
249
261
  connectionName: string,
250
262
  packageName?: string,
251
263
  ): Promise<ApiSchema[]> {
252
- const project = await this.projectStore.getProject(projectName, false);
264
+ const environment = await this.environmentStore.getEnvironment(
265
+ environmentName,
266
+ false,
267
+ );
253
268
  const connection = this.getApiConnectionForLookup(
254
- project,
269
+ environment,
255
270
  connectionName,
256
271
  );
257
272
  const malloyConnection = await this.getMalloyConnection(
258
- projectName,
273
+ environmentName,
259
274
  connectionName,
260
275
  packageName,
261
276
  );
@@ -266,19 +281,22 @@ export class ConnectionController {
266
281
  // Lists tables available in a schema. For postgres the schema is usually "public".
267
282
  // packageName disambiguates per-package "duckdb" lookups (see listSchemas).
268
283
  public async listTables(
269
- projectName: string,
284
+ environmentName: string,
270
285
  connectionName: string,
271
286
  schemaName: string,
272
287
  tableNames?: string[],
273
288
  packageName?: string,
274
289
  ): Promise<ApiTable[]> {
275
- const project = await this.projectStore.getProject(projectName, false);
290
+ const environment = await this.environmentStore.getEnvironment(
291
+ environmentName,
292
+ false,
293
+ );
276
294
  const connection = this.getApiConnectionForLookup(
277
- project,
295
+ environment,
278
296
  connectionName,
279
297
  );
280
298
  const malloyConnection = await this.getMalloyConnection(
281
- projectName,
299
+ environmentName,
282
300
  connectionName,
283
301
  packageName,
284
302
  );
@@ -292,13 +310,13 @@ export class ConnectionController {
292
310
  }
293
311
 
294
312
  public async getConnectionSqlSource(
295
- projectName: string,
313
+ environmentName: string,
296
314
  connectionName: string,
297
315
  sqlStatement: string,
298
316
  packageName?: string,
299
317
  ): Promise<ApiSqlSource> {
300
318
  const malloyConnection = await this.getMalloyConnection(
301
- projectName,
319
+ environmentName,
302
320
  connectionName,
303
321
  packageName,
304
322
  );
@@ -329,21 +347,24 @@ export class ConnectionController {
329
347
  }
330
348
 
331
349
  public async getTable(
332
- projectName: string,
350
+ environmentName: string,
333
351
  connectionName: string,
334
352
  schemaName: string,
335
353
  tablePath: string,
336
354
  packageName?: string,
337
355
  ): Promise<ApiTable> {
338
356
  const malloyConnection = await this.getMalloyConnection(
339
- projectName,
357
+ environmentName,
340
358
  connectionName,
341
359
  packageName,
342
360
  );
343
361
  // Use getApiConnection to get the unwrapped ApiConnection config, consistent with listSchemas and listTables.
344
- const project = await this.projectStore.getProject(projectName, false);
362
+ const environment = await this.environmentStore.getEnvironment(
363
+ environmentName,
364
+ false,
365
+ );
345
366
  const connection = this.getApiConnectionForLookup(
346
- project,
367
+ environment,
347
368
  connectionName,
348
369
  );
349
370
 
@@ -415,14 +436,14 @@ export class ConnectionController {
415
436
  }
416
437
 
417
438
  public async getConnectionQueryData(
418
- projectName: string,
439
+ environmentName: string,
419
440
  connectionName: string,
420
441
  sqlStatement: string,
421
442
  options: string,
422
443
  packageName?: string,
423
444
  ): Promise<ApiQueryData> {
424
445
  const malloyConnection = await this.getMalloyConnection(
425
- projectName,
446
+ environmentName,
426
447
  connectionName,
427
448
  packageName,
428
449
  );
@@ -449,13 +470,13 @@ export class ConnectionController {
449
470
  }
450
471
 
451
472
  public async getConnectionTemporaryTable(
452
- projectName: string,
473
+ environmentName: string,
453
474
  connectionName: string,
454
475
  sqlStatement: string,
455
476
  packageName?: string,
456
477
  ): Promise<ApiTemporaryTable> {
457
478
  const malloyConnection = await this.getMalloyConnection(
458
- projectName,
479
+ environmentName,
459
480
  connectionName,
460
481
  packageName,
461
482
  );
@@ -509,7 +530,7 @@ export class ConnectionController {
509
530
  }
510
531
 
511
532
  public async addConnection(
512
- projectName: string,
533
+ environmentName: string,
513
534
  connectionName: string,
514
535
  connectionConfig: ApiConnection,
515
536
  ): Promise<{ message: string }> {
@@ -529,11 +550,11 @@ export class ConnectionController {
529
550
  validateAdminAuthoredConnection(connectionName, connectionConfig);
530
551
 
531
552
  logger.info(
532
- `Creating connection "${connectionName}" in project "${projectName}"`,
553
+ `Creating connection "${connectionName}" in environment "${environmentName}"`,
533
554
  );
534
555
 
535
556
  await this.connectionService.addConnection(
536
- projectName,
557
+ environmentName,
537
558
  connectionName,
538
559
  connectionConfig,
539
560
  );
@@ -544,7 +565,7 @@ export class ConnectionController {
544
565
  }
545
566
 
546
567
  public async updateConnection(
547
- projectName: string,
568
+ environmentName: string,
548
569
  connectionName: string,
549
570
  connection: Partial<ApiConnection>,
550
571
  ): Promise<{ message: string }> {
@@ -556,11 +577,11 @@ export class ConnectionController {
556
577
  validateAdminAuthoredConnection(connectionName, connection);
557
578
 
558
579
  logger.info(
559
- `Updating connection "${connectionName}" in project "${projectName}"`,
580
+ `Updating connection "${connectionName}" in environment "${environmentName}"`,
560
581
  );
561
582
 
562
583
  await this.connectionService.updateConnection(
563
- projectName,
584
+ environmentName,
564
585
  connectionName,
565
586
  connection,
566
587
  );
@@ -571,15 +592,15 @@ export class ConnectionController {
571
592
  }
572
593
 
573
594
  public async deleteConnection(
574
- projectName: string,
595
+ environmentName: string,
575
596
  connectionName: string,
576
597
  ): Promise<{ message: string }> {
577
598
  logger.info(
578
- `Deleting connection "${connectionName}" from project "${projectName}"`,
599
+ `Deleting connection "${connectionName}" from environment "${environmentName}"`,
579
600
  );
580
601
 
581
602
  await this.connectionService.deleteConnection(
582
- projectName,
603
+ environmentName,
583
604
  connectionName,
584
605
  );
585
606
 
@@ -1,21 +1,24 @@
1
1
  import { components } from "../api";
2
- import { ProjectStore } from "../service/project_store";
2
+ import { EnvironmentStore } from "../service/environment_store";
3
3
 
4
4
  type ApiDatabase = components["schemas"]["Database"];
5
5
 
6
6
  export class DatabaseController {
7
- private projectStore: ProjectStore;
7
+ private environmentStore: EnvironmentStore;
8
8
 
9
- constructor(projectStore: ProjectStore) {
10
- this.projectStore = projectStore;
9
+ constructor(environmentStore: EnvironmentStore) {
10
+ this.environmentStore = environmentStore;
11
11
  }
12
12
 
13
13
  public async listDatabases(
14
- projectName: string,
14
+ environmentName: string,
15
15
  packageName: string,
16
16
  ): Promise<ApiDatabase[]> {
17
- const project = await this.projectStore.getProject(projectName, false);
18
- const p = await project.getPackage(packageName, false);
17
+ const environment = await this.environmentStore.getEnvironment(
18
+ environmentName,
19
+ false,
20
+ );
21
+ const p = await environment.getPackage(packageName, false);
19
22
  return p.listDatabases();
20
23
  }
21
24
  }