@malloy-publisher/server 0.0.87 → 0.0.89
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/build.ts +26 -0
- package/dist/app/api-doc.yaml +126 -5
- package/dist/app/assets/RenderedResult-BAZuT25g-QakVAbYy.js +2 -0
- package/dist/app/assets/{index-BbW5TZg_.js → index-Bq29VQqL.js} +2 -2
- package/dist/app/assets/{index-2xWCh-ya.css → index-CcIq0aEZ.css} +1 -1
- package/dist/app/assets/index-DZMePHJ5.js +251 -0
- package/dist/app/assets/{index-CIfV3yj1.js → index-TslDWlxH.js} +6 -6
- package/dist/app/assets/{index.umd-x-naS8R7.js → index.umd-BN4_E5KD.js} +259 -259
- package/dist/app/assets/mui-BEbinrI-.js +161 -0
- package/dist/app/assets/vendor-c5ypKtDW.js +17 -0
- package/dist/app/index.html +4 -2
- package/dist/instrumentation.js +67818 -35196
- package/dist/server.js +80404 -82231
- package/package.json +11 -5
- package/publisher.config.json +1 -1
- package/src/config.ts +20 -0
- package/src/constants.ts +14 -0
- package/src/controller/connection.controller.ts +21 -4
- package/src/controller/package.controller.ts +52 -2
- package/src/controller/schedule.controller.ts +3 -3
- package/src/controller/watch-mode.controller.ts +83 -0
- package/src/errors.ts +2 -1
- package/src/logger.ts +9 -0
- package/src/server.ts +33 -19
- package/src/service/connection.ts +159 -161
- package/src/service/model.ts +6 -6
- package/src/service/package.spec.ts +12 -10
- package/src/service/package.ts +15 -8
- package/src/service/project.ts +77 -36
- package/src/service/project_store.spec.ts +83 -56
- package/src/service/project_store.ts +330 -50
- package/src/utils.ts +0 -18
- package/tests/harness/mcp_test_setup.ts +5 -5
- package/dist/app/assets/RenderedResult-BAZuT25g-BMU632YI.js +0 -2
- package/dist/app/assets/index-C7whj6wK.js +0 -432
|
@@ -1,24 +1,35 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { GetObjectCommand, S3 } from "@aws-sdk/client-s3";
|
|
2
|
+
import { Storage } from "@google-cloud/storage";
|
|
3
|
+
import * as fs from "fs";
|
|
2
4
|
import * as path from "path";
|
|
5
|
+
import AdmZip from "adm-zip";
|
|
6
|
+
import simpleGit from "simple-git";
|
|
7
|
+
import { Writable } from "stream";
|
|
3
8
|
import { components } from "../api";
|
|
4
|
-
import {
|
|
9
|
+
import { getPublisherConfig, isPublisherConfigFrozen } from "../config";
|
|
10
|
+
import { API_PREFIX, PUBLISHER_CONFIG_NAME, publisherPath } from "../constants";
|
|
5
11
|
import { FrozenConfigError, ProjectNotFoundError } from "../errors";
|
|
6
12
|
import { logger } from "../logger";
|
|
7
|
-
import { isPublisherConfigFrozen } from "../utils";
|
|
8
13
|
import { Project } from "./project";
|
|
9
14
|
type ApiProject = components["schemas"]["Project"];
|
|
10
15
|
|
|
11
16
|
export class ProjectStore {
|
|
12
|
-
|
|
17
|
+
public serverRootPath: string;
|
|
13
18
|
private projects: Map<string, Project> = new Map();
|
|
14
19
|
public publisherConfigIsFrozen: boolean;
|
|
20
|
+
public finishedInitialization: Promise<void>;
|
|
21
|
+
private s3Client = new S3({
|
|
22
|
+
followRegionRedirects: true,
|
|
23
|
+
});
|
|
24
|
+
private gcsClient = new Storage();
|
|
15
25
|
|
|
16
26
|
constructor(serverRootPath: string) {
|
|
17
27
|
this.serverRootPath = serverRootPath;
|
|
18
|
-
|
|
28
|
+
this.finishedInitialization = this.initialize();
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
private async initialize() {
|
|
32
|
+
const initialTime = performance.now();
|
|
22
33
|
try {
|
|
23
34
|
this.publisherConfigIsFrozen = isPublisherConfigFrozen(
|
|
24
35
|
this.serverRootPath,
|
|
@@ -26,46 +37,55 @@ export class ProjectStore {
|
|
|
26
37
|
const projectManifest = await ProjectStore.reloadProjectManifest(
|
|
27
38
|
this.serverRootPath,
|
|
28
39
|
);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
logger.info(`Initializing project store.`);
|
|
41
|
+
await Promise.all(
|
|
42
|
+
Object.keys(projectManifest.projects).map(async (projectName) => {
|
|
43
|
+
logger.info(`Adding project "${projectName}"`);
|
|
44
|
+
const project = await this.addProject(
|
|
45
|
+
{
|
|
46
|
+
name: projectName,
|
|
47
|
+
resource: `${API_PREFIX}/projects/${projectName}`,
|
|
48
|
+
location: projectManifest.projects[projectName],
|
|
49
|
+
},
|
|
50
|
+
true,
|
|
51
|
+
);
|
|
52
|
+
return project.listPackages();
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
logger.info(
|
|
56
|
+
`Project store successfully initialized in ${performance.now() - initialTime}ms`,
|
|
57
|
+
);
|
|
42
58
|
} catch (error) {
|
|
43
59
|
logger.error("Error initializing project store", { error });
|
|
60
|
+
console.error(error);
|
|
44
61
|
process.exit(1);
|
|
45
62
|
}
|
|
46
63
|
}
|
|
47
64
|
|
|
48
|
-
public listProjects() {
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
public async listProjects() {
|
|
66
|
+
await this.finishedInitialization;
|
|
67
|
+
return Promise.all(
|
|
68
|
+
Array.from(this.projects.values()).map((project) =>
|
|
69
|
+
project.serialize(),
|
|
70
|
+
),
|
|
51
71
|
);
|
|
52
72
|
}
|
|
53
73
|
|
|
54
74
|
public async getProject(
|
|
55
75
|
projectName: string,
|
|
56
|
-
reload: boolean,
|
|
76
|
+
reload: boolean = false,
|
|
57
77
|
): Promise<Project> {
|
|
78
|
+
await this.finishedInitialization;
|
|
58
79
|
let project = this.projects.get(projectName);
|
|
59
80
|
if (project === undefined || reload) {
|
|
60
81
|
const projectManifest = await ProjectStore.reloadProjectManifest(
|
|
61
82
|
this.serverRootPath,
|
|
62
83
|
);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
) {
|
|
84
|
+
const projectPath =
|
|
85
|
+
project?.metadata.location || projectManifest.projects[projectName];
|
|
86
|
+
if (!projectPath) {
|
|
67
87
|
throw new ProjectNotFoundError(
|
|
68
|
-
`Project "${projectName}" not
|
|
88
|
+
`Project "${projectName}" could not be resolved to a path.`,
|
|
69
89
|
);
|
|
70
90
|
}
|
|
71
91
|
project = await this.addProject({
|
|
@@ -76,10 +96,17 @@ export class ProjectStore {
|
|
|
76
96
|
return project;
|
|
77
97
|
}
|
|
78
98
|
|
|
79
|
-
public async addProject(
|
|
80
|
-
|
|
99
|
+
public async addProject(
|
|
100
|
+
project: ApiProject,
|
|
101
|
+
skipInitialization: boolean = false,
|
|
102
|
+
) {
|
|
103
|
+
if (!skipInitialization) {
|
|
104
|
+
await this.finishedInitialization;
|
|
105
|
+
}
|
|
106
|
+
if (!skipInitialization && this.publisherConfigIsFrozen) {
|
|
81
107
|
throw new FrozenConfigError();
|
|
82
108
|
}
|
|
109
|
+
|
|
83
110
|
const projectName = project.name;
|
|
84
111
|
if (!projectName) {
|
|
85
112
|
throw new Error("Project name is required");
|
|
@@ -87,24 +114,48 @@ export class ProjectStore {
|
|
|
87
114
|
const projectManifest = await ProjectStore.reloadProjectManifest(
|
|
88
115
|
this.serverRootPath,
|
|
89
116
|
);
|
|
90
|
-
const projectPath =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!(await fs.stat(absoluteProjectPath)).isDirectory()) {
|
|
98
|
-
throw new ProjectNotFoundError(
|
|
99
|
-
`Project ${projectName} not found in ${absoluteProjectPath}`,
|
|
117
|
+
const projectPath =
|
|
118
|
+
project.location || projectManifest.projects[projectName];
|
|
119
|
+
let absoluteProjectPath: string;
|
|
120
|
+
if (projectPath) {
|
|
121
|
+
absoluteProjectPath = await this.loadProjectIntoDisk(
|
|
122
|
+
projectName,
|
|
123
|
+
projectPath,
|
|
100
124
|
);
|
|
125
|
+
if (absoluteProjectPath.endsWith(".zip")) {
|
|
126
|
+
absoluteProjectPath = await this.unzipProject(absoluteProjectPath);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
absoluteProjectPath = await this.scaffoldProject(project);
|
|
101
130
|
}
|
|
102
|
-
const newProject = await Project.create(
|
|
131
|
+
const newProject = await Project.create(
|
|
132
|
+
projectName,
|
|
133
|
+
absoluteProjectPath,
|
|
134
|
+
project.connections || [],
|
|
135
|
+
);
|
|
103
136
|
this.projects.set(projectName, newProject);
|
|
104
137
|
return newProject;
|
|
105
138
|
}
|
|
106
139
|
|
|
140
|
+
public async unzipProject(absoluteProjectPath: string) {
|
|
141
|
+
logger.info(
|
|
142
|
+
`Detected zip file at "${absoluteProjectPath}". Unzipping...`,
|
|
143
|
+
);
|
|
144
|
+
const unzippedProjectPath = absoluteProjectPath.replace(".zip", "");
|
|
145
|
+
await fs.promises.rm(unzippedProjectPath, {
|
|
146
|
+
recursive: true,
|
|
147
|
+
force: true,
|
|
148
|
+
});
|
|
149
|
+
await fs.promises.mkdir(unzippedProjectPath, { recursive: true });
|
|
150
|
+
|
|
151
|
+
const zip = new AdmZip(absoluteProjectPath);
|
|
152
|
+
zip.extractAllTo(unzippedProjectPath, true);
|
|
153
|
+
|
|
154
|
+
return unzippedProjectPath;
|
|
155
|
+
}
|
|
156
|
+
|
|
107
157
|
public async updateProject(project: ApiProject) {
|
|
158
|
+
await this.finishedInitialization;
|
|
108
159
|
if (this.publisherConfigIsFrozen) {
|
|
109
160
|
throw new FrozenConfigError();
|
|
110
161
|
}
|
|
@@ -122,6 +173,7 @@ export class ProjectStore {
|
|
|
122
173
|
}
|
|
123
174
|
|
|
124
175
|
public async deleteProject(projectName: string) {
|
|
176
|
+
await this.finishedInitialization;
|
|
125
177
|
if (this.publisherConfigIsFrozen) {
|
|
126
178
|
throw new FrozenConfigError();
|
|
127
179
|
}
|
|
@@ -133,26 +185,20 @@ export class ProjectStore {
|
|
|
133
185
|
return project;
|
|
134
186
|
}
|
|
135
187
|
|
|
136
|
-
|
|
137
|
-
serverRootPath: string,
|
|
138
|
-
): Promise<{ projects: { [key: string]: string } }> {
|
|
188
|
+
public static async reloadProjectManifest(serverRootPath: string) {
|
|
139
189
|
try {
|
|
140
|
-
|
|
141
|
-
path.join(serverRootPath, "publisher.config.json"),
|
|
142
|
-
"utf8",
|
|
143
|
-
);
|
|
144
|
-
return JSON.parse(projectManifestContent);
|
|
190
|
+
return getPublisherConfig(serverRootPath);
|
|
145
191
|
} catch (error) {
|
|
146
192
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
147
193
|
logger.error(
|
|
148
|
-
`Error reading
|
|
194
|
+
`Error reading ${PUBLISHER_CONFIG_NAME}. Generating from directory`,
|
|
149
195
|
{ error },
|
|
150
196
|
);
|
|
151
197
|
return { projects: {} };
|
|
152
198
|
} else {
|
|
153
199
|
// If publisher.config.json is missing, generate the manifest from directories
|
|
154
200
|
try {
|
|
155
|
-
const entries = await fs.readdir(serverRootPath, {
|
|
201
|
+
const entries = await fs.promises.readdir(serverRootPath, {
|
|
156
202
|
withFileTypes: true,
|
|
157
203
|
});
|
|
158
204
|
const projects: { [key: string]: string } = {};
|
|
@@ -171,4 +217,238 @@ export class ProjectStore {
|
|
|
171
217
|
}
|
|
172
218
|
}
|
|
173
219
|
}
|
|
220
|
+
|
|
221
|
+
private async scaffoldProject(project: ApiProject) {
|
|
222
|
+
const projectName = project.name;
|
|
223
|
+
if (!projectName) {
|
|
224
|
+
throw new Error("Project name is required");
|
|
225
|
+
}
|
|
226
|
+
const absoluteProjectPath = `${publisherPath}/${projectName}`;
|
|
227
|
+
await fs.promises.mkdir(absoluteProjectPath, { recursive: true });
|
|
228
|
+
if (project.readme) {
|
|
229
|
+
await fs.promises.writeFile(
|
|
230
|
+
path.join(absoluteProjectPath, "README.md"),
|
|
231
|
+
project.readme,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return absoluteProjectPath;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private async loadProjectIntoDisk(projectName: string, projectPath: string) {
|
|
238
|
+
const absoluteTargetPath = `${publisherPath}/${projectName}`;
|
|
239
|
+
// Handle absolute paths
|
|
240
|
+
if (projectPath.startsWith("/")) {
|
|
241
|
+
try {
|
|
242
|
+
logger.info(`Mounting local directory at "${projectPath}"`);
|
|
243
|
+
await this.mountLocalDirectory(
|
|
244
|
+
projectPath,
|
|
245
|
+
absoluteTargetPath,
|
|
246
|
+
projectName,
|
|
247
|
+
);
|
|
248
|
+
return absoluteTargetPath;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
logger.error(`Failed to mount local directory "${projectPath}"`, {
|
|
251
|
+
error,
|
|
252
|
+
});
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Handle GCS URIs
|
|
258
|
+
if (projectPath.startsWith("gs://")) {
|
|
259
|
+
// Download from GCS
|
|
260
|
+
try {
|
|
261
|
+
logger.info(
|
|
262
|
+
`Downloading GCS path "${projectPath}" to "${absoluteTargetPath}"`,
|
|
263
|
+
);
|
|
264
|
+
await this.downloadGcsDirectory(
|
|
265
|
+
projectPath,
|
|
266
|
+
projectName,
|
|
267
|
+
absoluteTargetPath,
|
|
268
|
+
);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
logger.error(`Failed to download GCS path "${projectPath}"`, {
|
|
271
|
+
error,
|
|
272
|
+
});
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
return absoluteTargetPath;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Handle S3 URIs
|
|
279
|
+
if (projectPath.startsWith("s3://")) {
|
|
280
|
+
try {
|
|
281
|
+
logger.info(`Mounting S3 path "${projectPath}"`);
|
|
282
|
+
await this.downloadS3Directory(
|
|
283
|
+
projectPath,
|
|
284
|
+
projectName,
|
|
285
|
+
absoluteTargetPath,
|
|
286
|
+
);
|
|
287
|
+
return absoluteTargetPath;
|
|
288
|
+
} catch (error) {
|
|
289
|
+
logger.error(`Failed to mount S3 path "${projectPath}"`, { error });
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Handle GitHub URIs
|
|
295
|
+
if (
|
|
296
|
+
projectPath.startsWith("https://github.com/") ||
|
|
297
|
+
projectPath.startsWith("git@")
|
|
298
|
+
) {
|
|
299
|
+
try {
|
|
300
|
+
logger.info(`Mounting GitHub path "${projectPath}"`);
|
|
301
|
+
await this.downloadGitHubDirectory(projectPath, absoluteTargetPath);
|
|
302
|
+
return absoluteTargetPath;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
logger.error(`Failed to mount GitHub path "${projectPath}"`, {
|
|
305
|
+
error,
|
|
306
|
+
});
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const errorMsg = `Invalid project path: "${projectPath}". Must be an absolute mounted path or a GCS/S3/GitHub URI.`;
|
|
312
|
+
logger.error(errorMsg, { projectName, projectPath });
|
|
313
|
+
throw new ProjectNotFoundError(errorMsg);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
public async mountLocalDirectory(
|
|
317
|
+
projectPath: string,
|
|
318
|
+
absoluteTargetPath: string,
|
|
319
|
+
projectName: string,
|
|
320
|
+
) {
|
|
321
|
+
const projectDirExists = (
|
|
322
|
+
await fs.promises.stat(projectPath)
|
|
323
|
+
).isDirectory();
|
|
324
|
+
if (projectDirExists) {
|
|
325
|
+
await fs.promises.rm(absoluteTargetPath, {
|
|
326
|
+
recursive: true,
|
|
327
|
+
force: true,
|
|
328
|
+
});
|
|
329
|
+
await fs.promises.mkdir(absoluteTargetPath, { recursive: true });
|
|
330
|
+
await fs.promises.cp(projectPath, absoluteTargetPath, {
|
|
331
|
+
recursive: true,
|
|
332
|
+
});
|
|
333
|
+
} else {
|
|
334
|
+
throw new ProjectNotFoundError(
|
|
335
|
+
`Project ${projectName} not found in "${projectPath}"`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async downloadGcsDirectory(
|
|
341
|
+
gcsPath: string,
|
|
342
|
+
projectName: string,
|
|
343
|
+
absoluteDirPath: string,
|
|
344
|
+
) {
|
|
345
|
+
const trimmedPath = gcsPath.slice(5);
|
|
346
|
+
const [bucketName, ...prefixParts] = trimmedPath.split("/");
|
|
347
|
+
const prefix = prefixParts.join("/");
|
|
348
|
+
const [files] = await this.gcsClient.bucket(bucketName).getFiles({
|
|
349
|
+
prefix,
|
|
350
|
+
});
|
|
351
|
+
if (files.length === 0) {
|
|
352
|
+
throw new ProjectNotFoundError(
|
|
353
|
+
`Project ${projectName} not found in ${gcsPath}`,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
await fs.promises.rm(absoluteDirPath, { recursive: true, force: true });
|
|
357
|
+
await fs.promises.mkdir(absoluteDirPath, { recursive: true });
|
|
358
|
+
await Promise.all(
|
|
359
|
+
files.map(async (file) => {
|
|
360
|
+
const relativeFilePath = file.name.replace(prefix, "");
|
|
361
|
+
const absoluteFilePath = path.join(
|
|
362
|
+
absoluteDirPath,
|
|
363
|
+
relativeFilePath,
|
|
364
|
+
);
|
|
365
|
+
if (file.name.endsWith("/")) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
await fs.promises.mkdir(path.dirname(absoluteFilePath), {
|
|
369
|
+
recursive: true,
|
|
370
|
+
});
|
|
371
|
+
return fs.promises.writeFile(
|
|
372
|
+
absoluteFilePath,
|
|
373
|
+
await file.download(),
|
|
374
|
+
);
|
|
375
|
+
}),
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async downloadS3Directory(
|
|
380
|
+
s3Path: string,
|
|
381
|
+
projectName: string,
|
|
382
|
+
absoluteDirPath: string,
|
|
383
|
+
) {
|
|
384
|
+
const trimmedPath = s3Path.slice(5);
|
|
385
|
+
const [bucketName, ...prefixParts] = trimmedPath.split("/");
|
|
386
|
+
const prefix = prefixParts.join("/");
|
|
387
|
+
const objects = await this.s3Client.listObjectsV2({
|
|
388
|
+
Bucket: bucketName,
|
|
389
|
+
Prefix: prefix,
|
|
390
|
+
});
|
|
391
|
+
await fs.promises.rm(absoluteDirPath, { recursive: true, force: true });
|
|
392
|
+
await fs.promises.mkdir(absoluteDirPath, { recursive: true });
|
|
393
|
+
|
|
394
|
+
if (!objects.Contents || objects.Contents.length === 0) {
|
|
395
|
+
throw new ProjectNotFoundError(
|
|
396
|
+
`Project ${projectName} not found in ${s3Path}`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
await Promise.all(
|
|
400
|
+
objects.Contents?.map(async (object) => {
|
|
401
|
+
const key = object.Key;
|
|
402
|
+
if (!key) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const relativeFilePath = key.replace(prefix, "");
|
|
406
|
+
if (!relativeFilePath || relativeFilePath.endsWith("/")) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const absoluteFilePath = path.join(
|
|
410
|
+
absoluteDirPath,
|
|
411
|
+
relativeFilePath,
|
|
412
|
+
);
|
|
413
|
+
await fs.promises.mkdir(path.dirname(absoluteFilePath), {
|
|
414
|
+
recursive: true,
|
|
415
|
+
});
|
|
416
|
+
const command = new GetObjectCommand({
|
|
417
|
+
Bucket: bucketName,
|
|
418
|
+
Key: key,
|
|
419
|
+
});
|
|
420
|
+
const item = await this.s3Client.send(command);
|
|
421
|
+
if (!item.Body) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const file = fs.createWriteStream(absoluteFilePath);
|
|
425
|
+
item.Body.transformToWebStream().pipeTo(Writable.toWeb(file));
|
|
426
|
+
await new Promise<void>((resolve, reject) => {
|
|
427
|
+
file.on("error", reject);
|
|
428
|
+
file.on("finish", resolve);
|
|
429
|
+
});
|
|
430
|
+
}),
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async downloadGitHubDirectory(githubUrl: string, absoluteDirPath: string) {
|
|
435
|
+
await fs.promises.rm(absoluteDirPath, { recursive: true, force: true });
|
|
436
|
+
await fs.promises.mkdir(absoluteDirPath, { recursive: true });
|
|
437
|
+
|
|
438
|
+
await new Promise<void>((resolve, reject) => {
|
|
439
|
+
simpleGit().clone(githubUrl, absoluteDirPath, {}, (err) => {
|
|
440
|
+
if (err) {
|
|
441
|
+
console.error(err);
|
|
442
|
+
logger.error(
|
|
443
|
+
`Failed to clone GitHub repository "${githubUrl}"`,
|
|
444
|
+
{
|
|
445
|
+
error: err,
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
reject(err);
|
|
449
|
+
}
|
|
450
|
+
resolve();
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
}
|
|
174
454
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { URLReader } from "@malloydata/malloy";
|
|
2
2
|
import * as fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
3
|
import { fileURLToPath } from "url";
|
|
5
4
|
|
|
6
|
-
export const PACKAGE_MANIFEST_NAME = "publisher.json";
|
|
7
|
-
export const CONNECTIONS_MANIFEST_NAME = "publisher.connections.json";
|
|
8
|
-
export const MODEL_FILE_SUFFIX = ".malloy";
|
|
9
|
-
export const NOTEBOOK_FILE_SUFFIX = ".malloynb";
|
|
10
|
-
// TODO: Move this to server config.
|
|
11
|
-
export const ROW_LIMIT = 1000;
|
|
12
|
-
|
|
13
5
|
export const URL_READER: URLReader = {
|
|
14
6
|
readURL: (url: URL) => {
|
|
15
7
|
let path = url.toString();
|
|
@@ -20,13 +12,3 @@ export const URL_READER: URLReader = {
|
|
|
20
12
|
},
|
|
21
13
|
};
|
|
22
14
|
|
|
23
|
-
export const isPublisherConfigFrozen = (serverRoot: string) => {
|
|
24
|
-
const publisherConfigPath = path.join(serverRoot, "publisher.config.json");
|
|
25
|
-
if (!fs.existsSync(publisherConfigPath)) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
const publisherConfig = JSON.parse(
|
|
29
|
-
fs.readFileSync(publisherConfigPath, "utf8"),
|
|
30
|
-
);
|
|
31
|
-
return Boolean(publisherConfig.frozenConfig);
|
|
32
|
-
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import http from "http";
|
|
2
|
-
import { AddressInfo } from "net";
|
|
3
|
-
import { URL } from "url";
|
|
4
|
-
import path from "path";
|
|
5
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
2
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
3
|
import {
|
|
8
|
-
Request,
|
|
9
4
|
Notification,
|
|
5
|
+
Request,
|
|
10
6
|
Result,
|
|
11
7
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import http from "http";
|
|
9
|
+
import { AddressInfo } from "net";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { URL } from "url";
|
|
12
12
|
|
|
13
13
|
// --- Real Server Import ---
|
|
14
14
|
// Import the actual configured Express app instance
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index.umd-x-naS8R7.js","assets/index-C7whj6wK.js","assets/index-2xWCh-ya.css"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{j as a,r as c,_ as w}from"./index-C7whj6wK.js";const x=async e=>{if(typeof window>"u")throw new Error("MalloyRenderer can only be used in browser environment");const{MalloyRenderer:d}=await w(async()=>{const{MalloyRenderer:i}=await import("./index.umd-x-naS8R7.js").then(l=>l.i);return{MalloyRenderer:i}},__vite__mapDeps([0,1,2]));return new d({onClick:e}).createViz()};function m({result:e,height:d,isFillElement:i,onSizeChange:l,onDrill:y}){const u=c.useRef(null),[g,f]=c.useState(!1);return c.useLayoutEffect(()=>{if(!u.current||!e)return;let t=!0;const r=u.current;for(;r.firstChild;)r.removeChild(r.firstChild);return x(y).then(o=>{if(!t)return;const n=new MutationObserver(s=>{for(const h of s)if(h.type==="childList"&&h.addedNodes.length>0&&Array.from(h.addedNodes).some(p=>p.nodeType===Node.ELEMENT_NODE)){n.disconnect(),setTimeout(()=>{t&&f(!0)},50);break}});n.observe(r,{childList:!0,subtree:!0,characterData:!0});try{o.setResult(JSON.parse(e)),o.render(r)}catch(s){console.error("Error rendering visualization:",s),n.disconnect(),t&&f(!0)}}).catch(o=>{console.error("Failed to create renderer:",o),t&&f(!0)}),()=>{t=!1}},[e,y]),c.useEffect(()=>{if(!u.current||!g)return;const t=u.current,r=()=>{if(t){const s=t.offsetHeight;s>0?l&&l(s):i&&t.firstChild&&(t.firstChild.offsetHeight==0?i(!0):i(!1))}},o=setTimeout(r,100);let n=null;return n=new MutationObserver(r),n.observe(t,{childList:!0,subtree:!0,attributes:!0}),()=>{clearTimeout(o),n?.disconnect()}},[l,e,i,g]),a.jsx("div",{ref:u,style:{width:"100%",height:d?`${d}px`:"100%"}})}function E(e){return typeof window>"u"?a.jsx("div",{style:{width:"100%",height:e.height?`${e.height}px`:"100%",display:"flex",alignItems:"center",justifyContent:"center",color:"#666"},children:"Loading..."}):a.jsx(c.Suspense,{fallback:a.jsx("div",{style:{width:"100%",height:e.height?`${e.height}px`:"100%",display:"flex",alignItems:"center",justifyContent:"center",color:"#666"},children:"Loading visualization..."}),children:a.jsx(m,{...e})})}export{E as default};
|