@malloy-publisher/server 0.0.83 → 0.0.85
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/assets/{RenderedResult-BAZuT25g-DNQaQGnw.js → RenderedResult-BAZuT25g-9oy_tMLH.js} +2 -2
- package/dist/app/assets/{index-BLifO_E1.js → index-C3s9KcRM.js} +1 -1
- package/dist/app/assets/{index-AckIUuWg.js → index-DQ2nRzzZ.js} +1 -1
- package/dist/app/assets/index-Da-HoCBr.js +432 -0
- package/dist/app/assets/{index.umd-BBC0Ow3c.js → index.umd-CeT9AycY.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/server.js +294 -20
- package/package.json +11 -10
- package/src/controller/database.controller.ts +2 -2
- package/src/logger.ts +5 -6
- package/src/server.ts +12 -0
- package/src/service/connection.ts +1 -1
- package/src/service/package.ts +57 -10
- package/src/service/project.ts +26 -11
- package/dist/app/assets/index-xWJJKafF.js +0 -432
package/dist/server.js
CHANGED
|
@@ -235636,8 +235636,8 @@ class DatabaseController {
|
|
|
235636
235636
|
this.projectStore = projectStore;
|
|
235637
235637
|
}
|
|
235638
235638
|
async listDatabases(projectName, packageName) {
|
|
235639
|
-
const project = await this.projectStore.getProject(projectName);
|
|
235640
|
-
const p = await project.getPackage(packageName);
|
|
235639
|
+
const project = await this.projectStore.getProject(projectName, false);
|
|
235640
|
+
const p = await project.getPackage(packageName, false);
|
|
235641
235641
|
return p.listDatabases();
|
|
235642
235642
|
}
|
|
235643
235643
|
}
|
|
@@ -243482,6 +243482,214 @@ function initializeMcpServer(projectStore) {
|
|
|
243482
243482
|
var fs7 = __toESM(require("fs/promises"));
|
|
243483
243483
|
var path6 = __toESM(require("path"));
|
|
243484
243484
|
|
|
243485
|
+
// ../../node_modules/async-mutex/index.mjs
|
|
243486
|
+
var E_TIMEOUT = new Error("timeout while waiting for mutex to become available");
|
|
243487
|
+
var E_ALREADY_LOCKED = new Error("mutex already locked");
|
|
243488
|
+
var E_CANCELED = new Error("request for lock canceled");
|
|
243489
|
+
var __awaiter$2 = function(thisArg, _arguments, P, generator) {
|
|
243490
|
+
function adopt(value) {
|
|
243491
|
+
return value instanceof P ? value : new P(function(resolve) {
|
|
243492
|
+
resolve(value);
|
|
243493
|
+
});
|
|
243494
|
+
}
|
|
243495
|
+
return new (P || (P = Promise))(function(resolve, reject) {
|
|
243496
|
+
function fulfilled(value) {
|
|
243497
|
+
try {
|
|
243498
|
+
step(generator.next(value));
|
|
243499
|
+
} catch (e) {
|
|
243500
|
+
reject(e);
|
|
243501
|
+
}
|
|
243502
|
+
}
|
|
243503
|
+
function rejected(value) {
|
|
243504
|
+
try {
|
|
243505
|
+
step(generator["throw"](value));
|
|
243506
|
+
} catch (e) {
|
|
243507
|
+
reject(e);
|
|
243508
|
+
}
|
|
243509
|
+
}
|
|
243510
|
+
function step(result) {
|
|
243511
|
+
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
243512
|
+
}
|
|
243513
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
243514
|
+
});
|
|
243515
|
+
};
|
|
243516
|
+
|
|
243517
|
+
class Semaphore {
|
|
243518
|
+
constructor(_value, _cancelError = E_CANCELED) {
|
|
243519
|
+
this._value = _value;
|
|
243520
|
+
this._cancelError = _cancelError;
|
|
243521
|
+
this._queue = [];
|
|
243522
|
+
this._weightedWaiters = [];
|
|
243523
|
+
}
|
|
243524
|
+
acquire(weight = 1, priority = 0) {
|
|
243525
|
+
if (weight <= 0)
|
|
243526
|
+
throw new Error(`invalid weight ${weight}: must be positive`);
|
|
243527
|
+
return new Promise((resolve, reject) => {
|
|
243528
|
+
const task = { resolve, reject, weight, priority };
|
|
243529
|
+
const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);
|
|
243530
|
+
if (i === -1 && weight <= this._value) {
|
|
243531
|
+
this._dispatchItem(task);
|
|
243532
|
+
} else {
|
|
243533
|
+
this._queue.splice(i + 1, 0, task);
|
|
243534
|
+
}
|
|
243535
|
+
});
|
|
243536
|
+
}
|
|
243537
|
+
runExclusive(callback_1) {
|
|
243538
|
+
return __awaiter$2(this, arguments, undefined, function* (callback, weight = 1, priority = 0) {
|
|
243539
|
+
const [value, release] = yield this.acquire(weight, priority);
|
|
243540
|
+
try {
|
|
243541
|
+
return yield callback(value);
|
|
243542
|
+
} finally {
|
|
243543
|
+
release();
|
|
243544
|
+
}
|
|
243545
|
+
});
|
|
243546
|
+
}
|
|
243547
|
+
waitForUnlock(weight = 1, priority = 0) {
|
|
243548
|
+
if (weight <= 0)
|
|
243549
|
+
throw new Error(`invalid weight ${weight}: must be positive`);
|
|
243550
|
+
if (this._couldLockImmediately(weight, priority)) {
|
|
243551
|
+
return Promise.resolve();
|
|
243552
|
+
} else {
|
|
243553
|
+
return new Promise((resolve) => {
|
|
243554
|
+
if (!this._weightedWaiters[weight - 1])
|
|
243555
|
+
this._weightedWaiters[weight - 1] = [];
|
|
243556
|
+
insertSorted(this._weightedWaiters[weight - 1], { resolve, priority });
|
|
243557
|
+
});
|
|
243558
|
+
}
|
|
243559
|
+
}
|
|
243560
|
+
isLocked() {
|
|
243561
|
+
return this._value <= 0;
|
|
243562
|
+
}
|
|
243563
|
+
getValue() {
|
|
243564
|
+
return this._value;
|
|
243565
|
+
}
|
|
243566
|
+
setValue(value) {
|
|
243567
|
+
this._value = value;
|
|
243568
|
+
this._dispatchQueue();
|
|
243569
|
+
}
|
|
243570
|
+
release(weight = 1) {
|
|
243571
|
+
if (weight <= 0)
|
|
243572
|
+
throw new Error(`invalid weight ${weight}: must be positive`);
|
|
243573
|
+
this._value += weight;
|
|
243574
|
+
this._dispatchQueue();
|
|
243575
|
+
}
|
|
243576
|
+
cancel() {
|
|
243577
|
+
this._queue.forEach((entry) => entry.reject(this._cancelError));
|
|
243578
|
+
this._queue = [];
|
|
243579
|
+
}
|
|
243580
|
+
_dispatchQueue() {
|
|
243581
|
+
this._drainUnlockWaiters();
|
|
243582
|
+
while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
|
|
243583
|
+
this._dispatchItem(this._queue.shift());
|
|
243584
|
+
this._drainUnlockWaiters();
|
|
243585
|
+
}
|
|
243586
|
+
}
|
|
243587
|
+
_dispatchItem(item) {
|
|
243588
|
+
const previousValue = this._value;
|
|
243589
|
+
this._value -= item.weight;
|
|
243590
|
+
item.resolve([previousValue, this._newReleaser(item.weight)]);
|
|
243591
|
+
}
|
|
243592
|
+
_newReleaser(weight) {
|
|
243593
|
+
let called = false;
|
|
243594
|
+
return () => {
|
|
243595
|
+
if (called)
|
|
243596
|
+
return;
|
|
243597
|
+
called = true;
|
|
243598
|
+
this.release(weight);
|
|
243599
|
+
};
|
|
243600
|
+
}
|
|
243601
|
+
_drainUnlockWaiters() {
|
|
243602
|
+
if (this._queue.length === 0) {
|
|
243603
|
+
for (let weight = this._value;weight > 0; weight--) {
|
|
243604
|
+
const waiters = this._weightedWaiters[weight - 1];
|
|
243605
|
+
if (!waiters)
|
|
243606
|
+
continue;
|
|
243607
|
+
waiters.forEach((waiter) => waiter.resolve());
|
|
243608
|
+
this._weightedWaiters[weight - 1] = [];
|
|
243609
|
+
}
|
|
243610
|
+
} else {
|
|
243611
|
+
const queuedPriority = this._queue[0].priority;
|
|
243612
|
+
for (let weight = this._value;weight > 0; weight--) {
|
|
243613
|
+
const waiters = this._weightedWaiters[weight - 1];
|
|
243614
|
+
if (!waiters)
|
|
243615
|
+
continue;
|
|
243616
|
+
const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);
|
|
243617
|
+
(i === -1 ? waiters : waiters.splice(0, i)).forEach((waiter) => waiter.resolve());
|
|
243618
|
+
}
|
|
243619
|
+
}
|
|
243620
|
+
}
|
|
243621
|
+
_couldLockImmediately(weight, priority) {
|
|
243622
|
+
return (this._queue.length === 0 || this._queue[0].priority < priority) && weight <= this._value;
|
|
243623
|
+
}
|
|
243624
|
+
}
|
|
243625
|
+
function insertSorted(a, v) {
|
|
243626
|
+
const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);
|
|
243627
|
+
a.splice(i + 1, 0, v);
|
|
243628
|
+
}
|
|
243629
|
+
function findIndexFromEnd(a, predicate) {
|
|
243630
|
+
for (let i = a.length - 1;i >= 0; i--) {
|
|
243631
|
+
if (predicate(a[i])) {
|
|
243632
|
+
return i;
|
|
243633
|
+
}
|
|
243634
|
+
}
|
|
243635
|
+
return -1;
|
|
243636
|
+
}
|
|
243637
|
+
var __awaiter$1 = function(thisArg, _arguments, P, generator) {
|
|
243638
|
+
function adopt(value) {
|
|
243639
|
+
return value instanceof P ? value : new P(function(resolve) {
|
|
243640
|
+
resolve(value);
|
|
243641
|
+
});
|
|
243642
|
+
}
|
|
243643
|
+
return new (P || (P = Promise))(function(resolve, reject) {
|
|
243644
|
+
function fulfilled(value) {
|
|
243645
|
+
try {
|
|
243646
|
+
step(generator.next(value));
|
|
243647
|
+
} catch (e) {
|
|
243648
|
+
reject(e);
|
|
243649
|
+
}
|
|
243650
|
+
}
|
|
243651
|
+
function rejected(value) {
|
|
243652
|
+
try {
|
|
243653
|
+
step(generator["throw"](value));
|
|
243654
|
+
} catch (e) {
|
|
243655
|
+
reject(e);
|
|
243656
|
+
}
|
|
243657
|
+
}
|
|
243658
|
+
function step(result) {
|
|
243659
|
+
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
243660
|
+
}
|
|
243661
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
243662
|
+
});
|
|
243663
|
+
};
|
|
243664
|
+
|
|
243665
|
+
class Mutex {
|
|
243666
|
+
constructor(cancelError) {
|
|
243667
|
+
this._semaphore = new Semaphore(1, cancelError);
|
|
243668
|
+
}
|
|
243669
|
+
acquire() {
|
|
243670
|
+
return __awaiter$1(this, arguments, undefined, function* (priority = 0) {
|
|
243671
|
+
const [, releaser] = yield this._semaphore.acquire(1, priority);
|
|
243672
|
+
return releaser;
|
|
243673
|
+
});
|
|
243674
|
+
}
|
|
243675
|
+
runExclusive(callback, priority = 0) {
|
|
243676
|
+
return this._semaphore.runExclusive(() => callback(), 1, priority);
|
|
243677
|
+
}
|
|
243678
|
+
isLocked() {
|
|
243679
|
+
return this._semaphore.isLocked();
|
|
243680
|
+
}
|
|
243681
|
+
waitForUnlock(priority = 0) {
|
|
243682
|
+
return this._semaphore.waitForUnlock(1, priority);
|
|
243683
|
+
}
|
|
243684
|
+
release() {
|
|
243685
|
+
if (this._semaphore.isLocked())
|
|
243686
|
+
this._semaphore.release();
|
|
243687
|
+
}
|
|
243688
|
+
cancel() {
|
|
243689
|
+
return this._semaphore.cancel();
|
|
243690
|
+
}
|
|
243691
|
+
}
|
|
243692
|
+
|
|
243485
243693
|
// src/service/project.ts
|
|
243486
243694
|
var fs6 = __toESM(require("fs/promises"));
|
|
243487
243695
|
var path5 = __toESM(require("path"));
|
|
@@ -243559,7 +243767,7 @@ async function createConnections(basePath) {
|
|
|
243559
243767
|
const config = {
|
|
243560
243768
|
host: connection.mysqlConnection.host,
|
|
243561
243769
|
port: connection.mysqlConnection.port,
|
|
243562
|
-
|
|
243770
|
+
user: connection.mysqlConnection.user,
|
|
243563
243771
|
password: connection.mysqlConnection.password,
|
|
243564
243772
|
database: connection.mysqlConnection.database
|
|
243565
243773
|
};
|
|
@@ -244198,6 +244406,8 @@ class Scheduler {
|
|
|
244198
244406
|
}
|
|
244199
244407
|
|
|
244200
244408
|
// src/service/package.ts
|
|
244409
|
+
var ENABLE_LIST_MODEL_COMPILATION = true;
|
|
244410
|
+
|
|
244201
244411
|
class Package {
|
|
244202
244412
|
projectName;
|
|
244203
244413
|
packageName;
|
|
@@ -244223,18 +244433,57 @@ class Package {
|
|
|
244223
244433
|
static async create(projectName, packageName, packagePath, projectConnections) {
|
|
244224
244434
|
const startTime = performance.now();
|
|
244225
244435
|
await Package.validatePackageManifestExistsOrThrowError(packagePath);
|
|
244436
|
+
const manifestValidationTime = performance.now();
|
|
244437
|
+
logger.info("Package manifest validation completed", {
|
|
244438
|
+
packageName,
|
|
244439
|
+
duration: manifestValidationTime - startTime,
|
|
244440
|
+
unit: "ms"
|
|
244441
|
+
});
|
|
244226
244442
|
try {
|
|
244227
244443
|
const packageConfig = await Package.readPackageConfig(packagePath);
|
|
244444
|
+
const packageConfigTime = performance.now();
|
|
244445
|
+
logger.info("Package config read completed", {
|
|
244446
|
+
packageName,
|
|
244447
|
+
duration: packageConfigTime - manifestValidationTime,
|
|
244448
|
+
unit: "ms"
|
|
244449
|
+
});
|
|
244228
244450
|
packageConfig.resource = `${API_PREFIX}/projects/${projectName}/packages/${packageName}`;
|
|
244229
244451
|
const databases = await Package.readDatabases(packagePath);
|
|
244452
|
+
const databasesTime = performance.now();
|
|
244453
|
+
logger.info("Databases read completed", {
|
|
244454
|
+
packageName,
|
|
244455
|
+
databaseCount: databases.length,
|
|
244456
|
+
duration: databasesTime - packageConfigTime,
|
|
244457
|
+
unit: "ms"
|
|
244458
|
+
});
|
|
244230
244459
|
const connections = new Map(projectConnections);
|
|
244231
244460
|
const { malloyConnections: packageConnections } = await createConnections(packagePath);
|
|
244461
|
+
const connectionsTime = performance.now();
|
|
244462
|
+
logger.info("Package connections created", {
|
|
244463
|
+
packageName,
|
|
244464
|
+
connectionCount: packageConnections.size,
|
|
244465
|
+
duration: connectionsTime - databasesTime,
|
|
244466
|
+
unit: "ms"
|
|
244467
|
+
});
|
|
244232
244468
|
packageConnections.forEach((connection) => {
|
|
244233
244469
|
connections.set(connection.name, connection);
|
|
244234
244470
|
});
|
|
244235
244471
|
connections.set("duckdb", new import_db_duckdb.DuckDBConnection("duckdb", ":memory:", packagePath));
|
|
244236
244472
|
const models = await Package.loadModels(packageName, packagePath, connections);
|
|
244473
|
+
const modelsTime = performance.now();
|
|
244474
|
+
logger.info("Models loaded", {
|
|
244475
|
+
packageName,
|
|
244476
|
+
modelCount: models.size,
|
|
244477
|
+
duration: modelsTime - connectionsTime,
|
|
244478
|
+
unit: "ms"
|
|
244479
|
+
});
|
|
244237
244480
|
const scheduler = Scheduler.create(models);
|
|
244481
|
+
const schedulerTime = performance.now();
|
|
244482
|
+
logger.info("Scheduler created", {
|
|
244483
|
+
packageName,
|
|
244484
|
+
duration: schedulerTime - modelsTime,
|
|
244485
|
+
unit: "ms"
|
|
244486
|
+
});
|
|
244238
244487
|
const endTime = performance.now();
|
|
244239
244488
|
const executionTime = endTime - startTime;
|
|
244240
244489
|
this.packageLoadHistogram.record(executionTime, {
|
|
@@ -244243,14 +244492,16 @@ class Package {
|
|
|
244243
244492
|
});
|
|
244244
244493
|
return new Package(projectName, packageName, packagePath, packageConfig, databases, models, scheduler);
|
|
244245
244494
|
} catch (error) {
|
|
244246
|
-
logger.error(
|
|
244495
|
+
logger.error(`Error loading package ${packageName}`, { error });
|
|
244247
244496
|
const endTime = performance.now();
|
|
244248
244497
|
const executionTime = endTime - startTime;
|
|
244249
244498
|
this.packageLoadHistogram.record(executionTime, {
|
|
244250
244499
|
malloy_package_name: packageName,
|
|
244251
244500
|
status: "error"
|
|
244252
244501
|
});
|
|
244253
|
-
throw new Error(
|
|
244502
|
+
throw new Error(`Error loading package ${packageName}`, {
|
|
244503
|
+
cause: error
|
|
244504
|
+
});
|
|
244254
244505
|
}
|
|
244255
244506
|
}
|
|
244256
244507
|
getPackageName() {
|
|
@@ -244280,10 +244531,12 @@ class Package {
|
|
|
244280
244531
|
return modelPath.endsWith(MODEL_FILE_SUFFIX);
|
|
244281
244532
|
}).map(async (modelPath) => {
|
|
244282
244533
|
let error;
|
|
244283
|
-
|
|
244284
|
-
|
|
244285
|
-
|
|
244286
|
-
|
|
244534
|
+
if (ENABLE_LIST_MODEL_COMPILATION) {
|
|
244535
|
+
try {
|
|
244536
|
+
await this.models.get(modelPath)?.getModel();
|
|
244537
|
+
} catch (modelError) {
|
|
244538
|
+
error = modelError instanceof Error ? modelError.message : undefined;
|
|
244539
|
+
}
|
|
244287
244540
|
}
|
|
244288
244541
|
return {
|
|
244289
244542
|
projectName: this.projectName,
|
|
@@ -244298,7 +244551,10 @@ class Package {
|
|
|
244298
244551
|
return await Promise.all(Array.from(this.models.keys()).filter((modelPath) => {
|
|
244299
244552
|
return modelPath.endsWith(NOTEBOOK_FILE_SUFFIX);
|
|
244300
244553
|
}).map(async (modelPath) => {
|
|
244301
|
-
|
|
244554
|
+
let error;
|
|
244555
|
+
if (ENABLE_LIST_MODEL_COMPILATION) {
|
|
244556
|
+
error = this.models.get(modelPath)?.getNotebookError();
|
|
244557
|
+
}
|
|
244302
244558
|
return {
|
|
244303
244559
|
projectName: this.projectName,
|
|
244304
244560
|
packageName: this.packageName,
|
|
@@ -244380,6 +244636,7 @@ class Package {
|
|
|
244380
244636
|
// src/service/project.ts
|
|
244381
244637
|
class Project {
|
|
244382
244638
|
packages = new Map;
|
|
244639
|
+
packageMutexes = new Map;
|
|
244383
244640
|
malloyConnections;
|
|
244384
244641
|
apiConnections;
|
|
244385
244642
|
internalConnections;
|
|
@@ -244448,10 +244705,9 @@ class Project {
|
|
|
244448
244705
|
});
|
|
244449
244706
|
const packageMetadata = await Promise.all(files.filter((file) => file.isDirectory()).map(async (directory) => {
|
|
244450
244707
|
try {
|
|
244451
|
-
|
|
244452
|
-
|
|
244453
|
-
|
|
244454
|
-
} catch {
|
|
244708
|
+
return (await this.getPackage(directory.name, false)).getPackageMetadata();
|
|
244709
|
+
} catch (error) {
|
|
244710
|
+
console.log(`Failed to load package: ${directory.name} due to : ${error}`);
|
|
244455
244711
|
return;
|
|
244456
244712
|
}
|
|
244457
244713
|
}));
|
|
@@ -244462,17 +244718,25 @@ class Project {
|
|
|
244462
244718
|
}
|
|
244463
244719
|
}
|
|
244464
244720
|
async getPackage(packageName, reload) {
|
|
244465
|
-
let
|
|
244466
|
-
if (
|
|
244721
|
+
let packageMutex = this.packageMutexes.get(packageName);
|
|
244722
|
+
if (!packageMutex) {
|
|
244723
|
+
packageMutex = new Mutex;
|
|
244724
|
+
this.packageMutexes.set(packageName, packageMutex);
|
|
244725
|
+
}
|
|
244726
|
+
return await packageMutex.runExclusive(async () => {
|
|
244727
|
+
const _package = this.packages.get(packageName);
|
|
244728
|
+
if (_package !== undefined && !reload) {
|
|
244729
|
+
return _package;
|
|
244730
|
+
}
|
|
244467
244731
|
try {
|
|
244468
|
-
|
|
244469
|
-
this.packages.set(packageName,
|
|
244732
|
+
const _package2 = await Package.create(this.projectName, packageName, path5.join(this.projectPath, packageName), this.malloyConnections);
|
|
244733
|
+
this.packages.set(packageName, _package2);
|
|
244734
|
+
return _package2;
|
|
244470
244735
|
} catch (error) {
|
|
244471
244736
|
this.packages.delete(packageName);
|
|
244472
244737
|
throw error;
|
|
244473
244738
|
}
|
|
244474
|
-
}
|
|
244475
|
-
return _package;
|
|
244739
|
+
});
|
|
244476
244740
|
}
|
|
244477
244741
|
}
|
|
244478
244742
|
|
|
@@ -244595,6 +244859,7 @@ var databaseController = new DatabaseController(projectStore);
|
|
|
244595
244859
|
var queryController = new QueryController(projectStore);
|
|
244596
244860
|
var scheduleController = new ScheduleController(projectStore);
|
|
244597
244861
|
var mcpApp = import_express.default();
|
|
244862
|
+
initProjects();
|
|
244598
244863
|
mcpApp.use(MCP_ENDPOINT, import_express.default.json());
|
|
244599
244864
|
mcpApp.use(MCP_ENDPOINT, import_cors.default());
|
|
244600
244865
|
mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
@@ -244914,3 +245179,12 @@ mainServer.listen(PUBLISHER_PORT, PUBLISHER_HOST, () => {
|
|
|
244914
245179
|
var mcpHttpServer = mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
|
|
244915
245180
|
logger.info(`MCP server listening at http://${PUBLISHER_HOST}:${MCP_PORT}`);
|
|
244916
245181
|
});
|
|
245182
|
+
function initProjects() {
|
|
245183
|
+
projectStore.listProjects().then((projects) => {
|
|
245184
|
+
projects.forEach((project) => {
|
|
245185
|
+
projectStore.getProject(project.name, false).then((project2) => {
|
|
245186
|
+
project2.listPackages();
|
|
245187
|
+
});
|
|
245188
|
+
});
|
|
245189
|
+
});
|
|
245190
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloy-publisher/server",
|
|
3
3
|
"description": "Malloy Publisher Server",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.85",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"malloy-publisher": "dist/server.js"
|
|
@@ -21,21 +21,22 @@
|
|
|
21
21
|
"generate-api-types": "bunx openapi-typescript ../../api-doc.yaml --output src/api.ts"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@malloydata/db-bigquery": "^0.0.
|
|
25
|
-
"@malloydata/db-duckdb": "^0.0.
|
|
26
|
-
"@malloydata/db-mysql": "^0.0.
|
|
27
|
-
"@malloydata/db-postgres": "^0.0.
|
|
28
|
-
"@malloydata/db-snowflake": "^0.0.
|
|
29
|
-
"@malloydata/db-trino": "^0.0.
|
|
30
|
-
"@malloydata/malloy": "^0.0.
|
|
31
|
-
"@malloydata/malloy-sql": "^0.0.
|
|
32
|
-
"@malloydata/render": "^0.0.
|
|
24
|
+
"@malloydata/db-bigquery": "^0.0.295",
|
|
25
|
+
"@malloydata/db-duckdb": "^0.0.295",
|
|
26
|
+
"@malloydata/db-mysql": "^0.0.295",
|
|
27
|
+
"@malloydata/db-postgres": "^0.0.295",
|
|
28
|
+
"@malloydata/db-snowflake": "^0.0.295",
|
|
29
|
+
"@malloydata/db-trino": "^0.0.295",
|
|
30
|
+
"@malloydata/malloy": "^0.0.295",
|
|
31
|
+
"@malloydata/malloy-sql": "^0.0.295",
|
|
32
|
+
"@malloydata/render": "^0.0.295",
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.13.2",
|
|
34
34
|
"@opentelemetry/api": "^1.9.0",
|
|
35
35
|
"@opentelemetry/auto-instrumentations-node": "^0.57.0",
|
|
36
36
|
"@opentelemetry/sdk-metrics": "^2.0.0",
|
|
37
37
|
"@opentelemetry/sdk-node": "^0.200.0",
|
|
38
38
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
39
|
+
"async-mutex": "^0.5.0",
|
|
39
40
|
"body-parser": "^1.20.2",
|
|
40
41
|
"class-transformer": "^0.5.1",
|
|
41
42
|
"class-validator": "^0.14.1",
|
|
@@ -14,8 +14,8 @@ export class DatabaseController {
|
|
|
14
14
|
projectName: string,
|
|
15
15
|
packageName: string,
|
|
16
16
|
): Promise<ApiDatabase[]> {
|
|
17
|
-
const project = await this.projectStore.getProject(projectName);
|
|
18
|
-
const p = await project.getPackage(packageName);
|
|
17
|
+
const project = await this.projectStore.getProject(projectName, false);
|
|
18
|
+
const p = await project.getPackage(packageName, false);
|
|
19
19
|
return p.listDatabases();
|
|
20
20
|
}
|
|
21
21
|
}
|
package/src/logger.ts
CHANGED
|
@@ -27,20 +27,19 @@ export const logAxiosError = (error: AxiosError) => {
|
|
|
27
27
|
if (error.response) {
|
|
28
28
|
// The request was made and the server responded with a status code
|
|
29
29
|
// that falls out of the range of 2xx
|
|
30
|
-
logger.error("Axios error", {
|
|
31
|
-
|
|
30
|
+
logger.error("Axios server-side error", {
|
|
31
|
+
url: error.response.config.url,
|
|
32
32
|
status: error.response.status,
|
|
33
33
|
headers: error.response.headers,
|
|
34
|
+
data: error.response.data,
|
|
34
35
|
});
|
|
35
36
|
} else if (error.request) {
|
|
36
37
|
// The request was made but no response was received
|
|
37
38
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
|
38
39
|
// http.ClientRequest in node.js
|
|
39
|
-
logger.error("
|
|
40
|
-
request: error.request,
|
|
41
|
-
});
|
|
40
|
+
logger.error("Axios client-side error", { error: error.request });
|
|
42
41
|
} else {
|
|
43
42
|
// Something happened in setting up the request that triggered an Error
|
|
44
|
-
logger.error("
|
|
43
|
+
logger.error("Axios unknown error", { error });
|
|
45
44
|
}
|
|
46
45
|
};
|
package/src/server.ts
CHANGED
|
@@ -92,6 +92,7 @@ const scheduleController = new ScheduleController(projectStore);
|
|
|
92
92
|
|
|
93
93
|
const mcpApp = express();
|
|
94
94
|
|
|
95
|
+
initProjects();
|
|
95
96
|
mcpApp.use(MCP_ENDPOINT, express.json());
|
|
96
97
|
mcpApp.use(MCP_ENDPOINT, cors());
|
|
97
98
|
|
|
@@ -634,3 +635,14 @@ const mcpHttpServer = mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
|
|
|
634
635
|
});
|
|
635
636
|
|
|
636
637
|
export { app, mainServer as httpServer, mcpApp, mcpHttpServer };
|
|
638
|
+
|
|
639
|
+
// Warm up the packages
|
|
640
|
+
function initProjects() {
|
|
641
|
+
projectStore.listProjects().then((projects) => {
|
|
642
|
+
projects.forEach((project) => {
|
|
643
|
+
projectStore.getProject(project.name!, false).then((project) => {
|
|
644
|
+
project.listPackages();
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
}
|
|
@@ -91,7 +91,7 @@ export async function createConnections(basePath: string): Promise<{
|
|
|
91
91
|
const config = {
|
|
92
92
|
host: connection.mysqlConnection.host,
|
|
93
93
|
port: connection.mysqlConnection.port,
|
|
94
|
-
|
|
94
|
+
user: connection.mysqlConnection.user,
|
|
95
95
|
password: connection.mysqlConnection.password,
|
|
96
96
|
database: connection.mysqlConnection.database,
|
|
97
97
|
};
|
package/src/service/package.ts
CHANGED
|
@@ -31,6 +31,7 @@ type ApiSchedule = components["schemas"]["Schedule"];
|
|
|
31
31
|
type ApiColumn = components["schemas"]["Column"];
|
|
32
32
|
type ApiTableDescription = components["schemas"]["TableDescription"];
|
|
33
33
|
|
|
34
|
+
const ENABLE_LIST_MODEL_COMPILATION = true;
|
|
34
35
|
export class Package {
|
|
35
36
|
private projectName: string;
|
|
36
37
|
private packageName: string;
|
|
@@ -74,17 +75,43 @@ export class Package {
|
|
|
74
75
|
): Promise<Package> {
|
|
75
76
|
const startTime = performance.now();
|
|
76
77
|
await Package.validatePackageManifestExistsOrThrowError(packagePath);
|
|
78
|
+
const manifestValidationTime = performance.now();
|
|
79
|
+
logger.info("Package manifest validation completed", {
|
|
80
|
+
packageName,
|
|
81
|
+
duration: manifestValidationTime - startTime,
|
|
82
|
+
unit: "ms",
|
|
83
|
+
});
|
|
77
84
|
|
|
78
85
|
try {
|
|
79
86
|
const packageConfig = await Package.readPackageConfig(packagePath);
|
|
87
|
+
const packageConfigTime = performance.now();
|
|
88
|
+
logger.info("Package config read completed", {
|
|
89
|
+
packageName,
|
|
90
|
+
duration: packageConfigTime - manifestValidationTime,
|
|
91
|
+
unit: "ms",
|
|
92
|
+
});
|
|
80
93
|
packageConfig.resource = `${API_PREFIX}/projects/${projectName}/packages/${packageName}`;
|
|
81
94
|
|
|
82
95
|
const databases = await Package.readDatabases(packagePath);
|
|
96
|
+
const databasesTime = performance.now();
|
|
97
|
+
logger.info("Databases read completed", {
|
|
98
|
+
packageName,
|
|
99
|
+
databaseCount: databases.length,
|
|
100
|
+
duration: databasesTime - packageConfigTime,
|
|
101
|
+
unit: "ms",
|
|
102
|
+
});
|
|
83
103
|
const connections = new Map<string, Connection>(projectConnections);
|
|
84
104
|
|
|
85
105
|
// Package connections override project connections.
|
|
86
106
|
const { malloyConnections: packageConnections } =
|
|
87
107
|
await createConnections(packagePath);
|
|
108
|
+
const connectionsTime = performance.now();
|
|
109
|
+
logger.info("Package connections created", {
|
|
110
|
+
packageName,
|
|
111
|
+
connectionCount: packageConnections.size,
|
|
112
|
+
duration: connectionsTime - databasesTime,
|
|
113
|
+
unit: "ms",
|
|
114
|
+
});
|
|
88
115
|
packageConnections.forEach((connection) => {
|
|
89
116
|
connections.set(connection.name, connection);
|
|
90
117
|
});
|
|
@@ -100,7 +127,20 @@ export class Package {
|
|
|
100
127
|
packagePath,
|
|
101
128
|
connections,
|
|
102
129
|
);
|
|
130
|
+
const modelsTime = performance.now();
|
|
131
|
+
logger.info("Models loaded", {
|
|
132
|
+
packageName,
|
|
133
|
+
modelCount: models.size,
|
|
134
|
+
duration: modelsTime - connectionsTime,
|
|
135
|
+
unit: "ms",
|
|
136
|
+
});
|
|
103
137
|
const scheduler = Scheduler.create(models);
|
|
138
|
+
const schedulerTime = performance.now();
|
|
139
|
+
logger.info("Scheduler created", {
|
|
140
|
+
packageName,
|
|
141
|
+
duration: schedulerTime - modelsTime,
|
|
142
|
+
unit: "ms",
|
|
143
|
+
});
|
|
104
144
|
const endTime = performance.now();
|
|
105
145
|
const executionTime = endTime - startTime;
|
|
106
146
|
this.packageLoadHistogram.record(executionTime, {
|
|
@@ -117,14 +157,16 @@ export class Package {
|
|
|
117
157
|
scheduler,
|
|
118
158
|
);
|
|
119
159
|
} catch (error) {
|
|
120
|
-
logger.error(
|
|
160
|
+
logger.error(`Error loading package ${packageName}`, { error });
|
|
121
161
|
const endTime = performance.now();
|
|
122
162
|
const executionTime = endTime - startTime;
|
|
123
163
|
this.packageLoadHistogram.record(executionTime, {
|
|
124
164
|
malloy_package_name: packageName,
|
|
125
165
|
status: "error",
|
|
126
166
|
});
|
|
127
|
-
throw new Error(
|
|
167
|
+
throw new Error(`Error loading package ${packageName}`, {
|
|
168
|
+
cause: error,
|
|
169
|
+
});
|
|
128
170
|
}
|
|
129
171
|
}
|
|
130
172
|
|
|
@@ -164,13 +206,15 @@ export class Package {
|
|
|
164
206
|
})
|
|
165
207
|
.map(async (modelPath) => {
|
|
166
208
|
let error: string | undefined;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
209
|
+
if (ENABLE_LIST_MODEL_COMPILATION) {
|
|
210
|
+
try {
|
|
211
|
+
await this.models.get(modelPath)?.getModel();
|
|
212
|
+
} catch (modelError) {
|
|
213
|
+
error =
|
|
214
|
+
modelError instanceof Error
|
|
215
|
+
? modelError.message
|
|
216
|
+
: undefined;
|
|
217
|
+
}
|
|
174
218
|
}
|
|
175
219
|
return {
|
|
176
220
|
projectName: this.projectName,
|
|
@@ -190,7 +234,10 @@ export class Package {
|
|
|
190
234
|
return modelPath.endsWith(NOTEBOOK_FILE_SUFFIX);
|
|
191
235
|
})
|
|
192
236
|
.map(async (modelPath) => {
|
|
193
|
-
|
|
237
|
+
let error: Error | undefined;
|
|
238
|
+
if (ENABLE_LIST_MODEL_COMPILATION) {
|
|
239
|
+
error = this.models.get(modelPath)?.getNotebookError();
|
|
240
|
+
}
|
|
194
241
|
return {
|
|
195
242
|
projectName: this.projectName,
|
|
196
243
|
packageName: this.packageName,
|