@malloy-publisher/server 0.0.196-dev → 0.0.197-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.
- package/dist/app/api-doc.yaml +213 -214
- package/dist/app/assets/EnvironmentPage-1j6QDWAy.js +1 -0
- package/dist/app/assets/HomePage-DMop21VG.js +1 -0
- package/dist/app/assets/MainPage-BbE8ETz1.js +2 -0
- package/dist/app/assets/ModelPage-D2jvfe3t.js +1 -0
- package/dist/app/assets/PackagePage-BbnhGoD3.js +1 -0
- package/dist/app/assets/{RouteError-DefbDO7F.js → RouteError-D3LGEZ3i.js} +1 -1
- package/dist/app/assets/WorkbookPage-DttVIj4u.js +1 -0
- package/dist/app/assets/{core-BrfQApxh.es-DnvCX4oH.js → core-w79IMXAG.es-Bd0UlzOL.js} +1 -1
- package/dist/app/assets/{index-Bu0ub036.js → index-5K9YjIxF.js} +117 -117
- package/dist/app/assets/{index-CkzK3JIl.js → index-C513UodQ.js} +1 -1
- package/dist/app/assets/{index-CoA6HIGS.js → index-DIgzgp69.js} +1 -1
- package/dist/app/assets/{index.umd-B6Ms2PpL.js → index.umd-BMeMPq_9.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/server.mjs +1328 -1304
- package/package.json +1 -1
- package/publisher.config.json +2 -2
- package/src/config.spec.ts +74 -66
- package/src/config.ts +50 -47
- package/src/controller/compile.controller.ts +10 -7
- package/src/controller/connection.controller.ts +79 -58
- package/src/controller/database.controller.ts +10 -7
- package/src/controller/manifest.controller.ts +23 -14
- package/src/controller/materialization.controller.ts +14 -14
- package/src/controller/model.controller.ts +35 -20
- package/src/controller/package.controller.ts +83 -49
- package/src/controller/query.controller.ts +11 -8
- package/src/controller/watch-mode.controller.ts +35 -29
- package/src/errors.ts +2 -2
- package/src/mcp/error_messages.ts +2 -2
- package/src/mcp/handler_utils.ts +23 -20
- package/src/mcp/mcp_constants.ts +1 -1
- package/src/mcp/prompts/handlers.ts +3 -3
- package/src/mcp/prompts/prompt_service.ts +5 -5
- package/src/mcp/prompts/utils.ts +12 -12
- package/src/mcp/resource_metadata.ts +3 -3
- package/src/mcp/resources/environment_resource.ts +187 -0
- package/src/mcp/resources/model_resource.ts +19 -17
- package/src/mcp/resources/notebook_resource.ts +13 -13
- package/src/mcp/resources/package_resource.ts +30 -27
- package/src/mcp/resources/query_resource.ts +15 -10
- package/src/mcp/resources/source_resource.ts +10 -10
- package/src/mcp/resources/view_resource.ts +11 -11
- package/src/mcp/server.ts +16 -14
- package/src/mcp/tools/discovery_tools.ts +67 -49
- package/src/mcp/tools/execute_query_tool.ts +14 -14
- package/src/server.ts +175 -159
- package/src/service/connection.spec.ts +158 -133
- package/src/service/connection.ts +42 -39
- package/src/service/connection_config.spec.ts +13 -11
- package/src/service/connection_config.ts +28 -19
- package/src/service/connection_service.spec.ts +63 -43
- package/src/service/connection_service.ts +106 -89
- package/src/service/{project.ts → environment.ts} +92 -77
- package/src/service/{project_compile.spec.ts → environment_compile.spec.ts} +1 -1
- package/src/service/{project_store.spec.ts → environment_store.spec.ts} +99 -85
- package/src/service/{project_store.ts → environment_store.ts} +368 -326
- package/src/service/manifest_service.spec.ts +15 -15
- package/src/service/manifest_service.ts +26 -21
- package/src/service/materialization_service.spec.ts +93 -59
- package/src/service/materialization_service.ts +71 -62
- package/src/service/materialized_table_gc.spec.ts +15 -15
- package/src/service/materialized_table_gc.ts +3 -3
- package/src/service/model.ts +2 -2
- package/src/service/package.spec.ts +2 -2
- package/src/service/package.ts +23 -21
- package/src/service/resolve_environment.ts +15 -0
- package/src/storage/DatabaseInterface.ts +34 -25
- package/src/storage/StorageManager.mock.ts +3 -3
- package/src/storage/StorageManager.ts +24 -23
- package/src/storage/duckdb/ConnectionRepository.ts +13 -11
- package/src/storage/duckdb/DuckDBConnection.ts +1 -1
- package/src/storage/duckdb/DuckDBManifestStore.ts +6 -6
- package/src/storage/duckdb/DuckDBRepository.ts +47 -47
- package/src/storage/duckdb/{ProjectRepository.ts → EnvironmentRepository.ts} +35 -35
- package/src/storage/duckdb/ManifestRepository.ts +21 -20
- package/src/storage/duckdb/MaterializationRepository.ts +31 -28
- package/src/storage/duckdb/PackageRepository.ts +11 -11
- package/src/storage/duckdb/manifest_store.spec.ts +2 -2
- package/src/storage/duckdb/schema.ts +20 -20
- package/src/storage/ducklake/DuckLakeManifestStore.ts +14 -14
- package/tests/fixtures/publisher.config.json +1 -1
- package/tests/harness/e2e.ts +1 -1
- package/tests/harness/mcp_test_setup.ts +1 -1
- package/tests/harness/mocks.ts +10 -8
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +4 -4
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +27 -48
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +26 -35
- package/tests/unit/duckdb/attached_databases.test.ts +51 -33
- package/tests/unit/ducklake/ducklake.test.ts +24 -22
- package/tests/unit/mcp/prompt_happy.test.ts +8 -8
- package/dist/app/assets/HomePage-DbZS0N7G.js +0 -1
- package/dist/app/assets/MainPage-CBuWkbmr.js +0 -2
- package/dist/app/assets/ModelPage-Bt37smot.js +0 -1
- package/dist/app/assets/PackagePage-DLZe50WG.js +0 -1
- package/dist/app/assets/ProjectPage-FQTEPXP4.js +0 -1
- package/dist/app/assets/WorkbookPage-CkAo16ar.js +0 -1
- package/src/mcp/resources/project_resource.ts +0 -184
- package/src/service/resolve_project.ts +0 -13
|
@@ -7,16 +7,16 @@ import { components } from "../api";
|
|
|
7
7
|
import { API_PREFIX, README_NAME } from "../constants";
|
|
8
8
|
import {
|
|
9
9
|
ConnectionNotFoundError,
|
|
10
|
+
EnvironmentNotFoundError,
|
|
10
11
|
PackageNotFoundError,
|
|
11
|
-
ProjectNotFoundError,
|
|
12
12
|
} from "../errors";
|
|
13
13
|
import { logger } from "../logger";
|
|
14
14
|
import { URL_READER } from "../utils";
|
|
15
15
|
import {
|
|
16
|
-
|
|
16
|
+
buildEnvironmentMalloyConfig,
|
|
17
17
|
deleteDuckLakeConnectionFile,
|
|
18
|
+
EnvironmentMalloyConfig,
|
|
18
19
|
InternalConnection,
|
|
19
|
-
ProjectMalloyConfig,
|
|
20
20
|
} from "./connection";
|
|
21
21
|
import { ApiConnection } from "./model";
|
|
22
22
|
import { Package } from "./package";
|
|
@@ -34,7 +34,7 @@ interface PackageInfo {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
type ApiPackage = components["schemas"]["Package"];
|
|
37
|
-
type
|
|
37
|
+
type ApiEnvironment = components["schemas"]["Environment"];
|
|
38
38
|
type RetiredConnectionGeneration = {
|
|
39
39
|
label: string;
|
|
40
40
|
releaseConnections: () => Promise<void>;
|
|
@@ -43,60 +43,62 @@ type RetiredConnectionGeneration = {
|
|
|
43
43
|
|
|
44
44
|
const RETIRED_CONNECTION_DRAIN_MS = 30_000;
|
|
45
45
|
|
|
46
|
-
export class
|
|
46
|
+
export class Environment {
|
|
47
47
|
private packages: Map<string, Package> = new Map();
|
|
48
|
-
// Lock ordering: connectionMutex (
|
|
48
|
+
// Lock ordering: connectionMutex (environment) MUST be acquired before any
|
|
49
49
|
// packageMutex. Connection updates may invalidate cached package
|
|
50
|
-
// MalloyConfigs and force reloads, so the
|
|
50
|
+
// MalloyConfigs and force reloads, so the environment lock is the outer one.
|
|
51
51
|
// Never acquire connectionMutex while holding a packageMutex — that's the
|
|
52
52
|
// AB/BA deadlock path.
|
|
53
53
|
private packageMutexes = new Map<string, Mutex>();
|
|
54
54
|
private packageStatuses: Map<string, PackageInfo> = new Map();
|
|
55
|
-
private malloyConfig:
|
|
55
|
+
private malloyConfig: EnvironmentMalloyConfig;
|
|
56
56
|
private connectionMutex = new Mutex();
|
|
57
57
|
private retiredConnectionGenerations =
|
|
58
58
|
new Set<RetiredConnectionGeneration>();
|
|
59
59
|
private apiConnections: ApiConnection[];
|
|
60
|
-
private
|
|
61
|
-
private
|
|
62
|
-
public metadata:
|
|
60
|
+
private environmentPath: string;
|
|
61
|
+
private environmentName: string;
|
|
62
|
+
public metadata: ApiEnvironment;
|
|
63
63
|
|
|
64
64
|
constructor(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
malloyConfig:
|
|
65
|
+
environmentName: string,
|
|
66
|
+
environmentPath: string,
|
|
67
|
+
malloyConfig: EnvironmentMalloyConfig,
|
|
68
68
|
apiConnections: InternalConnection[],
|
|
69
69
|
) {
|
|
70
|
-
this.
|
|
71
|
-
this.
|
|
70
|
+
this.environmentName = environmentName;
|
|
71
|
+
this.environmentPath = environmentPath;
|
|
72
72
|
this.malloyConfig = malloyConfig;
|
|
73
73
|
this.apiConnections = apiConnections;
|
|
74
74
|
this.metadata = {
|
|
75
|
-
resource: `${API_PREFIX}/
|
|
76
|
-
name: this.
|
|
77
|
-
location: this.
|
|
75
|
+
resource: `${API_PREFIX}/environments/${this.environmentName}`,
|
|
76
|
+
name: this.environmentName,
|
|
77
|
+
location: this.environmentPath,
|
|
78
78
|
};
|
|
79
|
-
void this.
|
|
79
|
+
void this.reloadEnvironmentMetadata();
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
private async
|
|
82
|
+
private async writeEnvironmentReadme(readme?: string): Promise<void> {
|
|
83
83
|
if (readme === undefined) return;
|
|
84
84
|
|
|
85
|
-
const readmePath = path.join(this.
|
|
85
|
+
const readmePath = path.join(this.environmentPath, "README.md");
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
88
|
await fs.promises.writeFile(readmePath, readme, "utf-8");
|
|
89
|
-
logger.info(
|
|
89
|
+
logger.info(
|
|
90
|
+
`Updated README.md for environment ${this.environmentName}`,
|
|
91
|
+
);
|
|
90
92
|
} catch (err) {
|
|
91
93
|
logger.error(`Failed to write README.md`, { error: err });
|
|
92
|
-
throw new Error(`Failed to update
|
|
94
|
+
throw new Error(`Failed to update environment README`);
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
public async update(payload:
|
|
98
|
+
public async update(payload: ApiEnvironment) {
|
|
97
99
|
if (payload.readme !== undefined) {
|
|
98
100
|
this.metadata.readme = payload.readme;
|
|
99
|
-
await this.
|
|
101
|
+
await this.writeEnvironmentReadme(payload.readme);
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
if (payload.materializationStorage !== undefined) {
|
|
@@ -104,24 +106,24 @@ export class Project {
|
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
// Handle connections update
|
|
107
|
-
// TODO: Update
|
|
109
|
+
// TODO: Update environment connections should have its own API endpoint
|
|
108
110
|
if (payload.connections) {
|
|
109
111
|
const payloadConnections = payload.connections;
|
|
110
112
|
await this.runConnectionUpdateExclusive(async () => {
|
|
111
113
|
logger.info(
|
|
112
|
-
`Updating ${payloadConnections.length} connections for
|
|
114
|
+
`Updating ${payloadConnections.length} connections for environment ${this.environmentName}`,
|
|
113
115
|
);
|
|
114
116
|
const isUpdateConnectionRequest = true;
|
|
115
|
-
const nextMalloyConfig =
|
|
117
|
+
const nextMalloyConfig = buildEnvironmentMalloyConfig(
|
|
116
118
|
payloadConnections,
|
|
117
|
-
this.
|
|
119
|
+
this.environmentPath,
|
|
118
120
|
isUpdateConnectionRequest,
|
|
119
121
|
);
|
|
120
122
|
|
|
121
123
|
this.updateConnections(nextMalloyConfig);
|
|
122
124
|
|
|
123
125
|
logger.info(
|
|
124
|
-
`Successfully updated connections for
|
|
126
|
+
`Successfully updated connections for environment ${this.environmentName}`,
|
|
125
127
|
{
|
|
126
128
|
apiConnections: this.apiConnections.length,
|
|
127
129
|
internalConnections: this.apiConnections.length,
|
|
@@ -134,49 +136,54 @@ export class Project {
|
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
static async create(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
environmentName: string,
|
|
140
|
+
environmentPath: string,
|
|
139
141
|
connections: ApiConnection[],
|
|
140
|
-
): Promise<
|
|
141
|
-
if (!(await fs.promises.stat(
|
|
142
|
-
throw new
|
|
143
|
-
`
|
|
142
|
+
): Promise<Environment> {
|
|
143
|
+
if (!(await fs.promises.stat(environmentPath))?.isDirectory()) {
|
|
144
|
+
throw new EnvironmentNotFoundError(
|
|
145
|
+
`Environment path ${environmentPath} not found`,
|
|
144
146
|
);
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
logger.info(`Creating
|
|
148
|
-
const malloyConfig =
|
|
149
|
+
logger.info(`Creating environment with connection configuration`);
|
|
150
|
+
const malloyConfig = buildEnvironmentMalloyConfig(
|
|
151
|
+
connections,
|
|
152
|
+
environmentPath,
|
|
153
|
+
);
|
|
149
154
|
|
|
150
155
|
logger.info(
|
|
151
|
-
`Loaded ${malloyConfig.apiConnections.length} connections for
|
|
156
|
+
`Loaded ${malloyConfig.apiConnections.length} connections for environment ${environmentName}`,
|
|
152
157
|
{
|
|
153
158
|
apiConnections: malloyConfig.apiConnections,
|
|
154
159
|
},
|
|
155
160
|
);
|
|
156
161
|
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
162
|
+
const environment = new Environment(
|
|
163
|
+
environmentName,
|
|
164
|
+
environmentPath,
|
|
160
165
|
malloyConfig,
|
|
161
166
|
malloyConfig.apiConnections,
|
|
162
167
|
);
|
|
163
168
|
|
|
164
|
-
return
|
|
169
|
+
return environment;
|
|
165
170
|
}
|
|
166
171
|
|
|
167
|
-
public async
|
|
172
|
+
public async reloadEnvironmentMetadata(): Promise<ApiEnvironment> {
|
|
168
173
|
let readme = "";
|
|
169
174
|
try {
|
|
170
175
|
readme = (
|
|
171
|
-
await fs.promises.readFile(
|
|
176
|
+
await fs.promises.readFile(
|
|
177
|
+
path.join(this.environmentPath, README_NAME),
|
|
178
|
+
)
|
|
172
179
|
).toString();
|
|
173
180
|
} catch {
|
|
174
181
|
// Readme not found, so we'll just return an empty string
|
|
175
182
|
}
|
|
176
183
|
this.metadata = {
|
|
177
184
|
...this.metadata,
|
|
178
|
-
resource: `${API_PREFIX}/
|
|
179
|
-
name: this.
|
|
185
|
+
resource: `${API_PREFIX}/environments/${this.environmentName}`,
|
|
186
|
+
name: this.environmentName,
|
|
180
187
|
readme,
|
|
181
188
|
};
|
|
182
189
|
return this.metadata;
|
|
@@ -190,14 +197,14 @@ export class Project {
|
|
|
190
197
|
): Promise<{ problems: LogMessage[]; sql?: string }> {
|
|
191
198
|
// Place the virtual file in the model's directory so relative imports resolve correctly.
|
|
192
199
|
const modelDir = path.dirname(
|
|
193
|
-
path.join(this.
|
|
200
|
+
path.join(this.environmentPath, packageName, modelName),
|
|
194
201
|
);
|
|
195
202
|
const virtualUri = `file://${path.join(modelDir, "__compile_check.malloy")}`;
|
|
196
203
|
const virtualUrl = new URL(virtualUri);
|
|
197
204
|
|
|
198
205
|
// Read the full model file so the submitted source inherits the model's
|
|
199
206
|
// complete namespace — imports, source definitions, queries, etc.
|
|
200
|
-
const modelPath = path.join(this.
|
|
207
|
+
const modelPath = path.join(this.environmentPath, packageName, modelName);
|
|
201
208
|
let modelContent = "";
|
|
202
209
|
try {
|
|
203
210
|
modelContent = await fs.promises.readFile(modelPath, "utf8");
|
|
@@ -222,7 +229,7 @@ export class Project {
|
|
|
222
229
|
|
|
223
230
|
// Initialize Runtime with the package's active MalloyConfig so compile
|
|
224
231
|
// checks see the same package-scoped duckdb as execution. This runtime
|
|
225
|
-
// borrows the package config; the package/
|
|
232
|
+
// borrows the package config; the package/environment lifecycle owns release.
|
|
226
233
|
const runtime = new Runtime({
|
|
227
234
|
urlReader: interceptingReader,
|
|
228
235
|
config: pkg.getMalloyConfig(),
|
|
@@ -279,7 +286,7 @@ export class Project {
|
|
|
279
286
|
);
|
|
280
287
|
}
|
|
281
288
|
|
|
282
|
-
public
|
|
289
|
+
public getEnvironmentMalloyConfig() {
|
|
283
290
|
return this.malloyConfig.malloyConfig;
|
|
284
291
|
}
|
|
285
292
|
|
|
@@ -336,7 +343,9 @@ export class Project {
|
|
|
336
343
|
}
|
|
337
344
|
|
|
338
345
|
public async listPackages(): Promise<ApiPackage[]> {
|
|
339
|
-
logger.debug("Listing packages", {
|
|
346
|
+
logger.debug("Listing packages", {
|
|
347
|
+
environmentPath: this.environmentPath,
|
|
348
|
+
});
|
|
340
349
|
try {
|
|
341
350
|
const packageMetadata = await Promise.all(
|
|
342
351
|
Array.from(this.packageStatuses.keys()).map(async (packageName) => {
|
|
@@ -422,9 +431,9 @@ export class Project {
|
|
|
422
431
|
|
|
423
432
|
try {
|
|
424
433
|
logger.debug(`Loading package ${packageName}...`);
|
|
425
|
-
const packagePath = path.join(this.
|
|
434
|
+
const packagePath = path.join(this.environmentPath, packageName);
|
|
426
435
|
const _package = await Package.create(
|
|
427
|
-
this.
|
|
436
|
+
this.environmentName,
|
|
428
437
|
packageName,
|
|
429
438
|
packagePath,
|
|
430
439
|
() => this.malloyConfig.malloyConfig,
|
|
@@ -453,7 +462,7 @@ export class Project {
|
|
|
453
462
|
}
|
|
454
463
|
|
|
455
464
|
public async addPackage(packageName: string) {
|
|
456
|
-
const packagePath = path.join(this.
|
|
465
|
+
const packagePath = path.join(this.environmentPath, packageName);
|
|
457
466
|
if (
|
|
458
467
|
!(await fs.promises
|
|
459
468
|
.access(packagePath)
|
|
@@ -464,7 +473,7 @@ export class Project {
|
|
|
464
473
|
throw new PackageNotFoundError(`Package ${packageName} not found`);
|
|
465
474
|
}
|
|
466
475
|
logger.info(
|
|
467
|
-
`Adding package ${packageName} to
|
|
476
|
+
`Adding package ${packageName} to environment ${this.environmentName}`,
|
|
468
477
|
{
|
|
469
478
|
packagePath,
|
|
470
479
|
malloyConfig: this.malloyConfig.malloyConfig,
|
|
@@ -475,7 +484,7 @@ export class Project {
|
|
|
475
484
|
this.packages.set(
|
|
476
485
|
packageName,
|
|
477
486
|
await Package.create(
|
|
478
|
-
this.
|
|
487
|
+
this.environmentName,
|
|
479
488
|
packageName,
|
|
480
489
|
packagePath,
|
|
481
490
|
() => this.malloyConfig.malloyConfig,
|
|
@@ -494,7 +503,7 @@ export class Project {
|
|
|
494
503
|
packageName: string,
|
|
495
504
|
metadata: { name: string; description?: string },
|
|
496
505
|
): Promise<void> {
|
|
497
|
-
const packagePath = path.join(this.
|
|
506
|
+
const packagePath = path.join(this.environmentPath, packageName);
|
|
498
507
|
const manifestPath = path.join(packagePath, "publisher.json");
|
|
499
508
|
|
|
500
509
|
try {
|
|
@@ -577,12 +586,12 @@ export class Project {
|
|
|
577
586
|
|
|
578
587
|
if (packageStatus?.status === PackageStatus.LOADING) {
|
|
579
588
|
logger.error("Package loading. Can't unload.", {
|
|
580
|
-
|
|
589
|
+
environmentName: this.environmentName,
|
|
581
590
|
packageName,
|
|
582
591
|
});
|
|
583
592
|
throw new Error(
|
|
584
593
|
"Package loading. Can't unload. " +
|
|
585
|
-
this.
|
|
594
|
+
this.environmentName +
|
|
586
595
|
" " +
|
|
587
596
|
packageName,
|
|
588
597
|
);
|
|
@@ -593,14 +602,18 @@ export class Project {
|
|
|
593
602
|
await _package.getMalloyConfig().releaseConnections();
|
|
594
603
|
|
|
595
604
|
try {
|
|
596
|
-
await fs.promises.rm(path.join(this.
|
|
605
|
+
await fs.promises.rm(path.join(this.environmentPath, packageName), {
|
|
597
606
|
recursive: true,
|
|
598
607
|
force: true,
|
|
599
608
|
});
|
|
600
609
|
} catch (err) {
|
|
601
610
|
logger.error(
|
|
602
611
|
"Error removing package directory while unloading package",
|
|
603
|
-
{
|
|
612
|
+
{
|
|
613
|
+
error: err,
|
|
614
|
+
environmentName: this.environmentName,
|
|
615
|
+
packageName,
|
|
616
|
+
},
|
|
604
617
|
);
|
|
605
618
|
}
|
|
606
619
|
|
|
@@ -610,7 +623,7 @@ export class Project {
|
|
|
610
623
|
}
|
|
611
624
|
|
|
612
625
|
public updateConnections(
|
|
613
|
-
malloyConfig:
|
|
626
|
+
malloyConfig: EnvironmentMalloyConfig,
|
|
614
627
|
_apiConnections?: ApiConnection[],
|
|
615
628
|
afterPreviousRelease?: () => Promise<void>,
|
|
616
629
|
): void {
|
|
@@ -620,7 +633,7 @@ export class Project {
|
|
|
620
633
|
|
|
621
634
|
if (previousMalloyConfig !== malloyConfig) {
|
|
622
635
|
this.retireConnectionGeneration(
|
|
623
|
-
`
|
|
636
|
+
`environment ${this.environmentName}`,
|
|
624
637
|
async () => {
|
|
625
638
|
await previousMalloyConfig.releaseConnections();
|
|
626
639
|
await afterPreviousRelease?.();
|
|
@@ -642,18 +655,18 @@ export class Project {
|
|
|
642
655
|
|
|
643
656
|
if (index !== -1) {
|
|
644
657
|
logger.info(
|
|
645
|
-
`Removed connection ${connectionName} from
|
|
658
|
+
`Removed connection ${connectionName} from environment ${this.environmentName}`,
|
|
646
659
|
);
|
|
647
660
|
} else {
|
|
648
661
|
logger.warn(
|
|
649
|
-
`Connection ${connectionName} not found in
|
|
662
|
+
`Connection ${connectionName} not found in environment ${this.environmentName}`,
|
|
650
663
|
);
|
|
651
664
|
}
|
|
652
665
|
}
|
|
653
666
|
|
|
654
667
|
public async closeAllConnections(): Promise<void> {
|
|
655
668
|
// Release the package-scoped MalloyConfigs (each holds the package's own
|
|
656
|
-
// sandbox `duckdb` connection) before tearing down the
|
|
669
|
+
// sandbox `duckdb` connection) before tearing down the environment config
|
|
657
670
|
// they wrap. Without this, hard unload leaks per-package DuckDB handles.
|
|
658
671
|
const packageReleases = await Promise.allSettled(
|
|
659
672
|
Array.from(this.packages.values(), (pkg) =>
|
|
@@ -663,7 +676,7 @@ export class Project {
|
|
|
663
676
|
for (const result of packageReleases) {
|
|
664
677
|
if (result.status === "rejected") {
|
|
665
678
|
logger.error(
|
|
666
|
-
`Error closing package connections for
|
|
679
|
+
`Error closing package connections for environment ${this.environmentName}`,
|
|
667
680
|
{ error: result.reason },
|
|
668
681
|
);
|
|
669
682
|
}
|
|
@@ -675,7 +688,7 @@ export class Project {
|
|
|
675
688
|
await this.malloyConfig.releaseConnections();
|
|
676
689
|
} catch (error) {
|
|
677
690
|
logger.error(
|
|
678
|
-
`Error closing connections for
|
|
691
|
+
`Error closing connections for environment ${this.environmentName}`,
|
|
679
692
|
{ error },
|
|
680
693
|
);
|
|
681
694
|
}
|
|
@@ -683,10 +696,12 @@ export class Project {
|
|
|
683
696
|
|
|
684
697
|
this.apiConnections = [];
|
|
685
698
|
|
|
686
|
-
logger.info(
|
|
699
|
+
logger.info(
|
|
700
|
+
`Closed all connections for environment ${this.environmentName}`,
|
|
701
|
+
);
|
|
687
702
|
}
|
|
688
703
|
|
|
689
|
-
public async serialize(): Promise<
|
|
704
|
+
public async serialize(): Promise<ApiEnvironment> {
|
|
690
705
|
return {
|
|
691
706
|
...this.metadata,
|
|
692
707
|
connections: this.listApiConnections(),
|
|
@@ -696,17 +711,17 @@ export class Project {
|
|
|
696
711
|
|
|
697
712
|
public async deleteDuckDBConnection(connectionName: string): Promise<void> {
|
|
698
713
|
const duckdbPath = path.join(
|
|
699
|
-
this.
|
|
714
|
+
this.environmentPath,
|
|
700
715
|
`${connectionName}.duckdb`,
|
|
701
716
|
);
|
|
702
717
|
try {
|
|
703
718
|
await fs.promises.rm(duckdbPath, { force: true });
|
|
704
719
|
logger.info(
|
|
705
|
-
`Removed DuckDB connection file ${connectionName} from
|
|
720
|
+
`Removed DuckDB connection file ${connectionName} from environment ${this.environmentName}`,
|
|
706
721
|
);
|
|
707
722
|
} catch (error) {
|
|
708
723
|
logger.error(
|
|
709
|
-
`Failed to remove DuckDB connection file ${connectionName} from
|
|
724
|
+
`Failed to remove DuckDB connection file ${connectionName} from environment ${this.environmentName}`,
|
|
710
725
|
{ error },
|
|
711
726
|
);
|
|
712
727
|
}
|
|
@@ -715,9 +730,9 @@ export class Project {
|
|
|
715
730
|
public async deleteDuckLakeConnection(
|
|
716
731
|
connectionName: string,
|
|
717
732
|
): Promise<void> {
|
|
718
|
-
await deleteDuckLakeConnectionFile(connectionName, this.
|
|
733
|
+
await deleteDuckLakeConnectionFile(connectionName, this.environmentPath);
|
|
719
734
|
logger.info(
|
|
720
|
-
`Removed DuckLake connection ${connectionName} from
|
|
735
|
+
`Removed DuckLake connection ${connectionName} from environment ${this.environmentName}`,
|
|
721
736
|
);
|
|
722
737
|
}
|
|
723
738
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { extractPreamble, extractPreambleFromSource } from "./
|
|
4
|
+
import { extractPreamble, extractPreambleFromSource } from "./environment";
|
|
5
5
|
|
|
6
6
|
describe("extractPreambleFromSource", () => {
|
|
7
7
|
it("should extract pragmas and imports before a source definition", () => {
|