@malloy-publisher/server 0.0.196 → 0.0.197
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/README.docker.md +88 -20
- package/README.md +15 -0
- package/build.ts +16 -0
- package/dist/app/api-doc.yaml +20 -3
- package/dist/app/assets/EnvironmentPage-BVkQH_xQ.js +1 -0
- package/dist/app/assets/HomePage-BgH9UkjK.js +1 -0
- package/dist/app/assets/MainPage-DiBxABem.js +2 -0
- package/dist/app/assets/ModelPage-oS70fj83.js +1 -0
- package/dist/app/assets/PackagePage-F_qLDAdv.js +1 -0
- package/dist/app/assets/RouteError-WqpffppN.js +1 -0
- package/dist/app/assets/WorkbookPage-_YmC-ebR.js +1 -0
- package/dist/app/assets/{core-w79IMXAG.es-Bd0UlzOL.js → core-B8L9xCYT.es-BcRLJTnC.js} +14 -14
- package/dist/app/assets/index-BMViiwtJ.js +451 -0
- package/dist/app/assets/{index-C513UodQ.js → index-C3XPaTaS.js} +15 -15
- package/dist/app/assets/index-rg8Ok8nl.js +1803 -0
- package/dist/app/assets/{index.umd-BMeMPq_9.js → index.umd-CCAfKkxY.js} +1 -1
- package/dist/app/index.html +2 -3
- package/dist/default-publisher.config.json +23 -0
- package/dist/instrumentation.mjs +1 -3
- package/dist/server.mjs +334 -165
- package/package.json +11 -12
- package/publisher.config.example.bigquery.json +33 -0
- package/publisher.config.example.duckdb.json +23 -0
- package/publisher.config.json +1 -11
- package/src/config.spec.ts +118 -0
- package/src/config.ts +78 -2
- package/src/controller/connection.controller.ts +1 -1
- package/src/default-publisher.config.json +23 -0
- package/src/errors.spec.ts +42 -0
- package/src/errors.ts +8 -0
- package/src/health.ts +26 -0
- package/src/logger.ts +1 -3
- package/src/pg_helpers.spec.ts +226 -0
- package/src/pg_helpers.ts +129 -0
- package/src/server.ts +20 -0
- package/src/service/connection.spec.ts +6 -4
- package/src/service/connection.ts +8 -3
- package/src/service/connection_config.ts +2 -2
- package/src/service/environment.ts +53 -25
- package/src/service/environment_store.spec.ts +19 -0
- package/src/service/environment_store.ts +21 -2
- package/src/service/package.ts +4 -3
- package/src/storage/StorageManager.ts +71 -11
- package/src/utils.ts +11 -0
- package/tests/unit/duckdb/attached_databases.test.ts +5 -5
- package/tests/unit/storage/StorageManager.test.ts +166 -0
- package/dist/app/assets/EnvironmentPage-1j6QDWAy.js +0 -1
- package/dist/app/assets/HomePage-DMop21VG.js +0 -1
- package/dist/app/assets/MainPage-BbE8ETz1.js +0 -2
- package/dist/app/assets/ModelPage-D2jvfe3t.js +0 -1
- package/dist/app/assets/PackagePage-BbnhGoD3.js +0 -1
- package/dist/app/assets/RouteError-D3LGEZ3i.js +0 -1
- package/dist/app/assets/WorkbookPage-DttVIj4u.js +0 -1
- package/dist/app/assets/index-5K9YjIxF.js +0 -456
- package/dist/app/assets/index-DIgzgp69.js +0 -1742
package/dist/server.mjs
CHANGED
|
@@ -199378,14 +199378,14 @@ var require_brace_expansion = __commonJS((exports, module) => {
|
|
|
199378
199378
|
var require_minimatch = __commonJS((exports, module) => {
|
|
199379
199379
|
module.exports = minimatch;
|
|
199380
199380
|
minimatch.Minimatch = Minimatch;
|
|
199381
|
-
var
|
|
199381
|
+
var path7 = function() {
|
|
199382
199382
|
try {
|
|
199383
199383
|
return __require("path");
|
|
199384
199384
|
} catch (e) {}
|
|
199385
199385
|
}() || {
|
|
199386
199386
|
sep: "/"
|
|
199387
199387
|
};
|
|
199388
|
-
minimatch.sep =
|
|
199388
|
+
minimatch.sep = path7.sep;
|
|
199389
199389
|
var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {};
|
|
199390
199390
|
var expand = require_brace_expansion();
|
|
199391
199391
|
var plTypes = {
|
|
@@ -199476,8 +199476,8 @@ var require_minimatch = __commonJS((exports, module) => {
|
|
|
199476
199476
|
if (!options)
|
|
199477
199477
|
options = {};
|
|
199478
199478
|
pattern = pattern.trim();
|
|
199479
|
-
if (!options.allowWindowsEscape &&
|
|
199480
|
-
pattern = pattern.split(
|
|
199479
|
+
if (!options.allowWindowsEscape && path7.sep !== "/") {
|
|
199480
|
+
pattern = pattern.split(path7.sep).join("/");
|
|
199481
199481
|
}
|
|
199482
199482
|
this.options = options;
|
|
199483
199483
|
this.set = [];
|
|
@@ -199854,8 +199854,8 @@ var require_minimatch = __commonJS((exports, module) => {
|
|
|
199854
199854
|
if (f === "/" && partial)
|
|
199855
199855
|
return true;
|
|
199856
199856
|
var options = this.options;
|
|
199857
|
-
if (
|
|
199858
|
-
f = f.split(
|
|
199857
|
+
if (path7.sep !== "/") {
|
|
199858
|
+
f = f.split(path7.sep).join("/");
|
|
199859
199859
|
}
|
|
199860
199860
|
f = f.split(slashSplit);
|
|
199861
199861
|
this.debug(this.pattern, "split", f);
|
|
@@ -199966,9 +199966,9 @@ var require_recursive_readdir = __commonJS((exports, module) => {
|
|
|
199966
199966
|
var p = __require("path");
|
|
199967
199967
|
var minimatch = require_minimatch();
|
|
199968
199968
|
function patternMatcher(pattern) {
|
|
199969
|
-
return function(
|
|
199969
|
+
return function(path7, stats) {
|
|
199970
199970
|
var minimatcher = new minimatch.Minimatch(pattern, { matchBase: true });
|
|
199971
|
-
return (!minimatcher.negate || stats.isFile()) && minimatcher.match(
|
|
199971
|
+
return (!minimatcher.negate || stats.isFile()) && minimatcher.match(path7);
|
|
199972
199972
|
};
|
|
199973
199973
|
}
|
|
199974
199974
|
function toMatcherFunction(ignoreEntry) {
|
|
@@ -199978,14 +199978,14 @@ var require_recursive_readdir = __commonJS((exports, module) => {
|
|
|
199978
199978
|
return patternMatcher(ignoreEntry);
|
|
199979
199979
|
}
|
|
199980
199980
|
}
|
|
199981
|
-
function readdir3(
|
|
199981
|
+
function readdir3(path7, ignores, callback) {
|
|
199982
199982
|
if (typeof ignores == "function") {
|
|
199983
199983
|
callback = ignores;
|
|
199984
199984
|
ignores = [];
|
|
199985
199985
|
}
|
|
199986
199986
|
if (!callback) {
|
|
199987
199987
|
return new Promise(function(resolve3, reject) {
|
|
199988
|
-
readdir3(
|
|
199988
|
+
readdir3(path7, ignores || [], function(err, data) {
|
|
199989
199989
|
if (err) {
|
|
199990
199990
|
reject(err);
|
|
199991
199991
|
} else {
|
|
@@ -199996,7 +199996,7 @@ var require_recursive_readdir = __commonJS((exports, module) => {
|
|
|
199996
199996
|
}
|
|
199997
199997
|
ignores = ignores.map(toMatcherFunction);
|
|
199998
199998
|
var list = [];
|
|
199999
|
-
fs4.readdir(
|
|
199999
|
+
fs4.readdir(path7, function(err, files) {
|
|
200000
200000
|
if (err) {
|
|
200001
200001
|
return callback(err);
|
|
200002
200002
|
}
|
|
@@ -200005,7 +200005,7 @@ var require_recursive_readdir = __commonJS((exports, module) => {
|
|
|
200005
200005
|
return callback(null, list);
|
|
200006
200006
|
}
|
|
200007
200007
|
files.forEach(function(file) {
|
|
200008
|
-
var filePath = p.join(
|
|
200008
|
+
var filePath = p.join(path7, file);
|
|
200009
200009
|
fs4.stat(filePath, function(_err, stats) {
|
|
200010
200010
|
if (_err) {
|
|
200011
200011
|
return callback(_err);
|
|
@@ -200851,8 +200851,8 @@ var require_uri_all = __commonJS((exports, module) => {
|
|
|
200851
200851
|
wsComponents.secure = undefined;
|
|
200852
200852
|
}
|
|
200853
200853
|
if (wsComponents.resourceName) {
|
|
200854
|
-
var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2),
|
|
200855
|
-
wsComponents.path =
|
|
200854
|
+
var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2), path12 = _wsComponents$resourc2[0], query = _wsComponents$resourc2[1];
|
|
200855
|
+
wsComponents.path = path12 && path12 !== "/" ? path12 : undefined;
|
|
200856
200856
|
wsComponents.query = query;
|
|
200857
200857
|
wsComponents.resourceName = undefined;
|
|
200858
200858
|
}
|
|
@@ -201245,12 +201245,12 @@ var require_util13 = __commonJS((exports, module) => {
|
|
|
201245
201245
|
return "'" + escapeQuotes(str) + "'";
|
|
201246
201246
|
}
|
|
201247
201247
|
function getPathExpr(currentPath, expr, jsonPointers, isNumber2) {
|
|
201248
|
-
var
|
|
201249
|
-
return joinPaths(currentPath,
|
|
201248
|
+
var path12 = jsonPointers ? "'/' + " + expr + (isNumber2 ? "" : ".replace(/~/g, '~0').replace(/\\//g, '~1')") : isNumber2 ? "'[' + " + expr + " + ']'" : "'[\\'' + " + expr + " + '\\']'";
|
|
201249
|
+
return joinPaths(currentPath, path12);
|
|
201250
201250
|
}
|
|
201251
201251
|
function getPath(currentPath, prop, jsonPointers) {
|
|
201252
|
-
var
|
|
201253
|
-
return joinPaths(currentPath,
|
|
201252
|
+
var path12 = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
|
|
201253
|
+
return joinPaths(currentPath, path12);
|
|
201254
201254
|
}
|
|
201255
201255
|
var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/;
|
|
201256
201256
|
var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
|
|
@@ -207304,11 +207304,11 @@ var require_ast = __commonJS((exports, module) => {
|
|
|
207304
207304
|
helperExpression: function helperExpression(node) {
|
|
207305
207305
|
return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
|
|
207306
207306
|
},
|
|
207307
|
-
scopedId: function scopedId(
|
|
207308
|
-
return /^\.|this\b/.test(
|
|
207307
|
+
scopedId: function scopedId(path12) {
|
|
207308
|
+
return /^\.|this\b/.test(path12.original);
|
|
207309
207309
|
},
|
|
207310
|
-
simpleId: function simpleId(
|
|
207311
|
-
return
|
|
207310
|
+
simpleId: function simpleId(path12) {
|
|
207311
|
+
return path12.parts.length === 1 && !AST.helpers.scopedId(path12) && !path12.depth;
|
|
207312
207312
|
}
|
|
207313
207313
|
}
|
|
207314
207314
|
};
|
|
@@ -208368,12 +208368,12 @@ var require_helpers3 = __commonJS((exports) => {
|
|
|
208368
208368
|
loc
|
|
208369
208369
|
};
|
|
208370
208370
|
}
|
|
208371
|
-
function prepareMustache(
|
|
208371
|
+
function prepareMustache(path12, params, hash, open2, strip, locInfo) {
|
|
208372
208372
|
var escapeFlag = open2.charAt(3) || open2.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
|
|
208373
208373
|
var decorator = /\*/.test(open2);
|
|
208374
208374
|
return {
|
|
208375
208375
|
type: decorator ? "Decorator" : "MustacheStatement",
|
|
208376
|
-
path:
|
|
208376
|
+
path: path12,
|
|
208377
208377
|
params,
|
|
208378
208378
|
hash,
|
|
208379
208379
|
escaped,
|
|
@@ -208637,9 +208637,9 @@ var require_compiler = __commonJS((exports) => {
|
|
|
208637
208637
|
},
|
|
208638
208638
|
DecoratorBlock: function DecoratorBlock(decorator) {
|
|
208639
208639
|
var program = decorator.program && this.compileProgram(decorator.program);
|
|
208640
|
-
var params = this.setupFullMustacheParams(decorator, program, undefined),
|
|
208640
|
+
var params = this.setupFullMustacheParams(decorator, program, undefined), path12 = decorator.path;
|
|
208641
208641
|
this.useDecorators = true;
|
|
208642
|
-
this.opcode("registerDecorator", params.length,
|
|
208642
|
+
this.opcode("registerDecorator", params.length, path12.original);
|
|
208643
208643
|
},
|
|
208644
208644
|
PartialStatement: function PartialStatement(partial) {
|
|
208645
208645
|
this.usePartial = true;
|
|
@@ -208702,46 +208702,46 @@ var require_compiler = __commonJS((exports) => {
|
|
|
208702
208702
|
}
|
|
208703
208703
|
},
|
|
208704
208704
|
ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) {
|
|
208705
|
-
var
|
|
208706
|
-
this.opcode("getContext",
|
|
208705
|
+
var path12 = sexpr.path, name = path12.parts[0], isBlock = program != null || inverse != null;
|
|
208706
|
+
this.opcode("getContext", path12.depth);
|
|
208707
208707
|
this.opcode("pushProgram", program);
|
|
208708
208708
|
this.opcode("pushProgram", inverse);
|
|
208709
|
-
|
|
208710
|
-
this.accept(
|
|
208709
|
+
path12.strict = true;
|
|
208710
|
+
this.accept(path12);
|
|
208711
208711
|
this.opcode("invokeAmbiguous", name, isBlock);
|
|
208712
208712
|
},
|
|
208713
208713
|
simpleSexpr: function simpleSexpr(sexpr) {
|
|
208714
|
-
var
|
|
208715
|
-
|
|
208716
|
-
this.accept(
|
|
208714
|
+
var path12 = sexpr.path;
|
|
208715
|
+
path12.strict = true;
|
|
208716
|
+
this.accept(path12);
|
|
208717
208717
|
this.opcode("resolvePossibleLambda");
|
|
208718
208718
|
},
|
|
208719
208719
|
helperSexpr: function helperSexpr(sexpr, program, inverse) {
|
|
208720
|
-
var params = this.setupFullMustacheParams(sexpr, program, inverse),
|
|
208720
|
+
var params = this.setupFullMustacheParams(sexpr, program, inverse), path12 = sexpr.path, name = path12.parts[0];
|
|
208721
208721
|
if (this.options.knownHelpers[name]) {
|
|
208722
208722
|
this.opcode("invokeKnownHelper", params.length, name);
|
|
208723
208723
|
} else if (this.options.knownHelpersOnly) {
|
|
208724
208724
|
throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
|
|
208725
208725
|
} else {
|
|
208726
|
-
|
|
208727
|
-
|
|
208728
|
-
this.accept(
|
|
208729
|
-
this.opcode("invokeHelper", params.length,
|
|
208726
|
+
path12.strict = true;
|
|
208727
|
+
path12.falsy = true;
|
|
208728
|
+
this.accept(path12);
|
|
208729
|
+
this.opcode("invokeHelper", params.length, path12.original, _ast2["default"].helpers.simpleId(path12));
|
|
208730
208730
|
}
|
|
208731
208731
|
},
|
|
208732
|
-
PathExpression: function PathExpression(
|
|
208733
|
-
this.addDepth(
|
|
208734
|
-
this.opcode("getContext",
|
|
208735
|
-
var name =
|
|
208732
|
+
PathExpression: function PathExpression(path12) {
|
|
208733
|
+
this.addDepth(path12.depth);
|
|
208734
|
+
this.opcode("getContext", path12.depth);
|
|
208735
|
+
var name = path12.parts[0], scoped = _ast2["default"].helpers.scopedId(path12), blockParamId = !path12.depth && !scoped && this.blockParamIndex(name);
|
|
208736
208736
|
if (blockParamId) {
|
|
208737
|
-
this.opcode("lookupBlockParam", blockParamId,
|
|
208737
|
+
this.opcode("lookupBlockParam", blockParamId, path12.parts);
|
|
208738
208738
|
} else if (!name) {
|
|
208739
208739
|
this.opcode("pushContext");
|
|
208740
|
-
} else if (
|
|
208740
|
+
} else if (path12.data) {
|
|
208741
208741
|
this.options.data = true;
|
|
208742
|
-
this.opcode("lookupData",
|
|
208742
|
+
this.opcode("lookupData", path12.depth, path12.parts, path12.strict);
|
|
208743
208743
|
} else {
|
|
208744
|
-
this.opcode("lookupOnContext",
|
|
208744
|
+
this.opcode("lookupOnContext", path12.parts, path12.falsy, path12.strict, scoped);
|
|
208745
208745
|
}
|
|
208746
208746
|
},
|
|
208747
208747
|
StringLiteral: function StringLiteral(string2) {
|
|
@@ -209085,16 +209085,16 @@ var require_util14 = __commonJS((exports) => {
|
|
|
209085
209085
|
}
|
|
209086
209086
|
exports.urlGenerate = urlGenerate;
|
|
209087
209087
|
function normalize2(aPath) {
|
|
209088
|
-
var
|
|
209088
|
+
var path12 = aPath;
|
|
209089
209089
|
var url2 = urlParse(aPath);
|
|
209090
209090
|
if (url2) {
|
|
209091
209091
|
if (!url2.path) {
|
|
209092
209092
|
return aPath;
|
|
209093
209093
|
}
|
|
209094
|
-
|
|
209094
|
+
path12 = url2.path;
|
|
209095
209095
|
}
|
|
209096
|
-
var isAbsolute3 = exports.isAbsolute(
|
|
209097
|
-
var parts =
|
|
209096
|
+
var isAbsolute3 = exports.isAbsolute(path12);
|
|
209097
|
+
var parts = path12.split(/\/+/);
|
|
209098
209098
|
for (var part, up = 0, i = parts.length - 1;i >= 0; i--) {
|
|
209099
209099
|
part = parts[i];
|
|
209100
209100
|
if (part === ".") {
|
|
@@ -209111,15 +209111,15 @@ var require_util14 = __commonJS((exports) => {
|
|
|
209111
209111
|
}
|
|
209112
209112
|
}
|
|
209113
209113
|
}
|
|
209114
|
-
|
|
209115
|
-
if (
|
|
209116
|
-
|
|
209114
|
+
path12 = parts.join("/");
|
|
209115
|
+
if (path12 === "") {
|
|
209116
|
+
path12 = isAbsolute3 ? "/" : ".";
|
|
209117
209117
|
}
|
|
209118
209118
|
if (url2) {
|
|
209119
|
-
url2.path =
|
|
209119
|
+
url2.path = path12;
|
|
209120
209120
|
return urlGenerate(url2);
|
|
209121
209121
|
}
|
|
209122
|
-
return
|
|
209122
|
+
return path12;
|
|
209123
209123
|
}
|
|
209124
209124
|
exports.normalize = normalize2;
|
|
209125
209125
|
function join9(aRoot, aPath) {
|
|
@@ -211676,8 +211676,8 @@ var require_printer = __commonJS((exports) => {
|
|
|
211676
211676
|
return this.accept(sexpr.path) + " " + params + hash;
|
|
211677
211677
|
};
|
|
211678
211678
|
PrintVisitor.prototype.PathExpression = function(id) {
|
|
211679
|
-
var
|
|
211680
|
-
return (id.data ? "@" : "") + "PATH:" +
|
|
211679
|
+
var path12 = id.parts.join("/");
|
|
211680
|
+
return (id.data ? "@" : "") + "PATH:" + path12;
|
|
211681
211681
|
};
|
|
211682
211682
|
PrintVisitor.prototype.StringLiteral = function(string2) {
|
|
211683
211683
|
return '"' + string2.value + '"';
|
|
@@ -211754,9 +211754,7 @@ var getLogLevel = () => {
|
|
|
211754
211754
|
};
|
|
211755
211755
|
var logger = import_winston.default.createLogger({
|
|
211756
211756
|
level: getLogLevel(),
|
|
211757
|
-
format: isTelemetryEnabled ? import_winston.default.format.combine(import_winston.default.format.uncolorize(), import_winston.default.format.timestamp(), import_winston.default.format.
|
|
211758
|
-
fillExcept: ["message", "level", "timestamp"]
|
|
211759
|
-
}), import_winston.default.format.json()) : import_winston.default.format.combine(import_winston.default.format.colorize(), import_winston.default.format.simple()),
|
|
211757
|
+
format: isTelemetryEnabled ? import_winston.default.format.combine(import_winston.default.format.uncolorize(), import_winston.default.format.timestamp(), import_winston.default.format.errors({ stack: true }), import_winston.default.format.json()) : import_winston.default.format.combine(import_winston.default.format.colorize(), import_winston.default.format.simple()),
|
|
211760
211758
|
transports: [new import_winston.default.transports.Console]
|
|
211761
211759
|
});
|
|
211762
211760
|
function extractTraceIdFromTraceparent(traceparent) {
|
|
@@ -216946,8 +216944,8 @@ var import_cors = __toESM(require_lib7(), 1);
|
|
|
216946
216944
|
var import_express = __toESM(require_express(), 1);
|
|
216947
216945
|
var import_http_proxy_middleware = __toESM(require_dist4(), 1);
|
|
216948
216946
|
import * as http2 from "http";
|
|
216949
|
-
import * as
|
|
216950
|
-
import { fileURLToPath as
|
|
216947
|
+
import * as path12 from "path";
|
|
216948
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
216951
216949
|
|
|
216952
216950
|
// src/controller/compile.controller.ts
|
|
216953
216951
|
class CompileController {
|
|
@@ -216998,6 +216996,8 @@ function internalErrorToHttpError(error) {
|
|
|
216998
216996
|
return httpError(400, error.message);
|
|
216999
216997
|
} else if (error instanceof ConnectionNotFoundError) {
|
|
217000
216998
|
return httpError(404, error.message);
|
|
216999
|
+
} else if (error instanceof ConnectionAuthError) {
|
|
217000
|
+
return httpError(422, error.message);
|
|
217001
217001
|
} else if (error instanceof ModelCompilationError) {
|
|
217002
217002
|
return httpError(424, error.message);
|
|
217003
217003
|
} else if (error instanceof ConnectionError) {
|
|
@@ -217064,6 +217064,12 @@ class ConnectionError extends Error {
|
|
|
217064
217064
|
}
|
|
217065
217065
|
}
|
|
217066
217066
|
|
|
217067
|
+
class ConnectionAuthError extends Error {
|
|
217068
|
+
constructor(message) {
|
|
217069
|
+
super(message);
|
|
217070
|
+
}
|
|
217071
|
+
}
|
|
217072
|
+
|
|
217067
217073
|
class ModelCompilationError extends Error {
|
|
217068
217074
|
constructor(error) {
|
|
217069
217075
|
super(error.message);
|
|
@@ -220380,6 +220386,63 @@ var {
|
|
|
220380
220386
|
import fs from "fs/promises";
|
|
220381
220387
|
import path2 from "path";
|
|
220382
220388
|
|
|
220389
|
+
// src/pg_helpers.ts
|
|
220390
|
+
function pgConnectTimeoutSeconds() {
|
|
220391
|
+
const raw = process.env.PG_CONNECT_TIMEOUT_SECONDS;
|
|
220392
|
+
if (!raw)
|
|
220393
|
+
return 5;
|
|
220394
|
+
const parsed = Number.parseInt(raw, 10);
|
|
220395
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 5;
|
|
220396
|
+
}
|
|
220397
|
+
var URI_FORM_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
220398
|
+
var HAS_CONNECT_TIMEOUT_RE = /[?&\s]connect_timeout=|^connect_timeout=/;
|
|
220399
|
+
function withPgConnectTimeout(connectionString, timeout) {
|
|
220400
|
+
if (HAS_CONNECT_TIMEOUT_RE.test(connectionString)) {
|
|
220401
|
+
return connectionString;
|
|
220402
|
+
}
|
|
220403
|
+
if (URI_FORM_RE.test(connectionString)) {
|
|
220404
|
+
if (!connectionString.includes("?")) {
|
|
220405
|
+
return `${connectionString}?connect_timeout=${timeout}`;
|
|
220406
|
+
}
|
|
220407
|
+
if (connectionString.endsWith("?")) {
|
|
220408
|
+
return `${connectionString}connect_timeout=${timeout}`;
|
|
220409
|
+
}
|
|
220410
|
+
return `${connectionString}&connect_timeout=${timeout}`;
|
|
220411
|
+
}
|
|
220412
|
+
return `${connectionString} connect_timeout=${timeout}`;
|
|
220413
|
+
}
|
|
220414
|
+
function redactPgSecrets(s) {
|
|
220415
|
+
return s.replace(/password=('[^']*'|"[^"]*"|\S+)/gi, "password=***");
|
|
220416
|
+
}
|
|
220417
|
+
function classifyPgError(error, context) {
|
|
220418
|
+
if (!(error instanceof Error))
|
|
220419
|
+
return;
|
|
220420
|
+
const msg = error.message;
|
|
220421
|
+
const patterns = [
|
|
220422
|
+
/password authentication failed/i,
|
|
220423
|
+
/pg_hba\.conf/i,
|
|
220424
|
+
/role ".*" does not exist/i,
|
|
220425
|
+
/database ".*" does not exist/i,
|
|
220426
|
+
/permission denied/i
|
|
220427
|
+
];
|
|
220428
|
+
if (!patterns.some((p) => p.test(msg)))
|
|
220429
|
+
return;
|
|
220430
|
+
return new ConnectionAuthError(`${context}: ${redactPgSecrets(msg)}`);
|
|
220431
|
+
}
|
|
220432
|
+
function handlePgAttachError(error, context) {
|
|
220433
|
+
if (error instanceof Error && (error.message.includes("already exists") || error.message.includes("already attached"))) {
|
|
220434
|
+
return { action: "swallow" };
|
|
220435
|
+
}
|
|
220436
|
+
const authErr = classifyPgError(error, context);
|
|
220437
|
+
if (authErr) {
|
|
220438
|
+
return { action: "throw", error: authErr };
|
|
220439
|
+
}
|
|
220440
|
+
if (error instanceof Error) {
|
|
220441
|
+
return { action: "throw", error };
|
|
220442
|
+
}
|
|
220443
|
+
return { action: "throw", error: new Error(String(error)) };
|
|
220444
|
+
}
|
|
220445
|
+
|
|
220383
220446
|
// src/service/connection_config.ts
|
|
220384
220447
|
import { createPrivateKey } from "crypto";
|
|
220385
220448
|
import path from "path";
|
|
@@ -220563,7 +220626,7 @@ function validateConnectionShape(connection) {
|
|
|
220563
220626
|
{
|
|
220564
220627
|
const attached = connection.duckdbConnection.attachedDatabases ?? [];
|
|
220565
220628
|
if (attached.length === 0) {
|
|
220566
|
-
throw new Error(
|
|
220629
|
+
throw new Error(`DuckDB connection "${connection.name}" has no attached databases. Add at least one foreign database (BigQuery, Snowflake, Postgres, GCS, S3, Azure) to attachedDatabases, or remove this connection entirely — each package already gets a per-package DuckDB sandbox named "duckdb" automatically.`);
|
|
220567
220630
|
}
|
|
220568
220631
|
}
|
|
220569
220632
|
break;
|
|
@@ -220636,7 +220699,7 @@ function assembleEnvironmentConnections(connections = [], environmentPath = "")
|
|
|
220636
220699
|
continue;
|
|
220637
220700
|
}
|
|
220638
220701
|
if (connection.name === "duckdb") {
|
|
220639
|
-
throw new Error("
|
|
220702
|
+
throw new Error("Connection name 'duckdb' is reserved for per-package sandboxes. Choose a different name for environment-level DuckDB connections (e.g. 'shared_duckdb').");
|
|
220640
220703
|
}
|
|
220641
220704
|
processedConnections.add(connection.name);
|
|
220642
220705
|
validateDuckdbApiSurface(connection);
|
|
@@ -221005,13 +221068,13 @@ async function attachDuckLake(connection, dbName, ducklakeConfig) {
|
|
|
221005
221068
|
}
|
|
221006
221069
|
const pg = ducklakeConfig.catalog.postgresConnection;
|
|
221007
221070
|
const pgConnString = buildPgConnectionString(pg);
|
|
221008
|
-
logger.info(`pgConnString: ${pgConnString}`);
|
|
221071
|
+
logger.info(`pgConnString: ${redactPgSecrets(pgConnString)}`);
|
|
221009
221072
|
const escapedPgConnString = escapeSQL(pgConnString);
|
|
221010
|
-
logger.info(`Final escaped connection string: ${escapedPgConnString}`);
|
|
221073
|
+
logger.info(`Final escaped connection string: ${redactPgSecrets(escapedPgConnString)}`);
|
|
221011
221074
|
const escapedBucketUrl = escapeSQL(ducklakeConfig.storage.bucketUrl);
|
|
221012
221075
|
logger.info(`escapedBucketUrl: ${escapedBucketUrl}`);
|
|
221013
221076
|
const attachCommand = `ATTACH OR REPLACE 'ducklake:postgres:${escapedPgConnString}' AS ${dbName} (DATA_PATH '${escapedBucketUrl}', OVERRIDE_DATA_PATH true, READ_ONLY true);`;
|
|
221014
|
-
logger.info(`Attaching DuckLake database using command: ${attachCommand}`);
|
|
221077
|
+
logger.info(`Attaching DuckLake database using command: ${redactPgSecrets(attachCommand)}`);
|
|
221015
221078
|
try {
|
|
221016
221079
|
await connection.runSQL(attachCommand);
|
|
221017
221080
|
logger.info(`Successfully attached DuckLake database in READ_ONLY mode: ${dbName}`);
|
|
@@ -222783,7 +222846,7 @@ function validateAzureAttachedDatabases(connectionConfig) {
|
|
|
222783
222846
|
}
|
|
222784
222847
|
function validateAdminAuthoredConnection(connectionName, connectionConfig) {
|
|
222785
222848
|
if (connectionName === "duckdb" || connectionConfig.name === "duckdb") {
|
|
222786
|
-
throw new BadRequestError("
|
|
222849
|
+
throw new BadRequestError("Connection name 'duckdb' is reserved for per-package sandboxes. Choose a different name for environment-level DuckDB connections (e.g. 'shared_duckdb').");
|
|
222787
222850
|
}
|
|
222788
222851
|
try {
|
|
222789
222852
|
validateDuckdbApiSurface(connectionConfig);
|
|
@@ -224801,7 +224864,7 @@ function watch(paths, options = {}) {
|
|
|
224801
224864
|
var esm_default = { watch, FSWatcher };
|
|
224802
224865
|
|
|
224803
224866
|
// src/controller/watch-mode.controller.ts
|
|
224804
|
-
import
|
|
224867
|
+
import path11 from "path";
|
|
224805
224868
|
|
|
224806
224869
|
// src/service/environment_store.ts
|
|
224807
224870
|
var import_client_s32 = __toESM(require_dist_cjs75(), 1);
|
|
@@ -225019,7 +225082,7 @@ class Mutex {
|
|
|
225019
225082
|
// src/service/environment_store.ts
|
|
225020
225083
|
import crypto4 from "crypto";
|
|
225021
225084
|
import * as fs7 from "fs";
|
|
225022
|
-
import * as
|
|
225085
|
+
import * as path10 from "path";
|
|
225023
225086
|
|
|
225024
225087
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
225025
225088
|
var import_file_exists = __toESM(require_dist11(), 1);
|
|
@@ -229007,6 +229070,25 @@ import { Writable } from "stream";
|
|
|
229007
229070
|
// src/config.ts
|
|
229008
229071
|
import fs2 from "fs";
|
|
229009
229072
|
import path4 from "path";
|
|
229073
|
+
import { fileURLToPath } from "url";
|
|
229074
|
+
var BUNDLED_DEFAULT_CONFIG_PATH = path4.join(path4.dirname(fileURLToPath(import.meta.url)), "default-publisher.config.json");
|
|
229075
|
+
function resolvePublisherConfigPath(serverRoot) {
|
|
229076
|
+
const explicitPath = process.env.PUBLISHER_CONFIG_PATH;
|
|
229077
|
+
if (explicitPath && explicitPath.length > 0) {
|
|
229078
|
+
if (!fs2.existsSync(explicitPath)) {
|
|
229079
|
+
return null;
|
|
229080
|
+
}
|
|
229081
|
+
return { path: explicitPath, isBundledDefault: false };
|
|
229082
|
+
}
|
|
229083
|
+
const serverRootPath = path4.join(serverRoot, PUBLISHER_CONFIG_NAME);
|
|
229084
|
+
if (fs2.existsSync(serverRootPath)) {
|
|
229085
|
+
return { path: serverRootPath, isBundledDefault: false };
|
|
229086
|
+
}
|
|
229087
|
+
if (process.env.PUBLISHER_USE_BUNDLED_DEFAULT === "true" && fs2.existsSync(BUNDLED_DEFAULT_CONFIG_PATH)) {
|
|
229088
|
+
return { path: BUNDLED_DEFAULT_CONFIG_PATH, isBundledDefault: true };
|
|
229089
|
+
}
|
|
229090
|
+
return null;
|
|
229091
|
+
}
|
|
229010
229092
|
function substituteEnvVars(value) {
|
|
229011
229093
|
const envVarPattern = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
|
|
229012
229094
|
return value.replace(envVarPattern, (_match, varName) => {
|
|
@@ -229034,13 +229116,20 @@ function processConfigValue(value) {
|
|
|
229034
229116
|
return value;
|
|
229035
229117
|
}
|
|
229036
229118
|
var getPublisherConfig = (serverRoot) => {
|
|
229037
|
-
const
|
|
229038
|
-
if (!
|
|
229119
|
+
const resolved = resolvePublisherConfigPath(serverRoot);
|
|
229120
|
+
if (!resolved) {
|
|
229121
|
+
if (process.env.PUBLISHER_CONFIG_PATH && process.env.PUBLISHER_CONFIG_PATH.length > 0) {
|
|
229122
|
+
logger.error(`--config path not found: ${process.env.PUBLISHER_CONFIG_PATH}. Using default empty config.`);
|
|
229123
|
+
}
|
|
229039
229124
|
return {
|
|
229040
229125
|
frozenConfig: false,
|
|
229041
229126
|
environments: []
|
|
229042
229127
|
};
|
|
229043
229128
|
}
|
|
229129
|
+
const publisherConfigPath = resolved.path;
|
|
229130
|
+
if (resolved.isBundledDefault) {
|
|
229131
|
+
logger.info(`No publisher.config.json found at ${path4.join(serverRoot, PUBLISHER_CONFIG_NAME)}; falling back to bundled DuckDB-only default. Pass --config <path> or place a config in the server root to override.`);
|
|
229132
|
+
}
|
|
229044
229133
|
let rawConfig;
|
|
229045
229134
|
try {
|
|
229046
229135
|
const fileContent = fs2.readFileSync(publisherConfigPath, "utf8");
|
|
@@ -229185,6 +229274,15 @@ function markReady() {
|
|
|
229185
229274
|
logger.error("Service is already draining - cannot mark as ready");
|
|
229186
229275
|
}
|
|
229187
229276
|
}
|
|
229277
|
+
function markDegraded() {
|
|
229278
|
+
if (operationalState !== "draining") {
|
|
229279
|
+
operationalState = "degraded";
|
|
229280
|
+
ready = false;
|
|
229281
|
+
logger.warn("Service marked as degraded; one or more environments failed to initialize. Readiness probe will fail until the config is fixed and the process restarts.");
|
|
229282
|
+
} else {
|
|
229283
|
+
logger.error("Service is already draining - cannot mark as degraded");
|
|
229284
|
+
}
|
|
229285
|
+
}
|
|
229188
229286
|
function markNotReady() {
|
|
229189
229287
|
ready = false;
|
|
229190
229288
|
logger.info("Service marked as not ready - readiness probe will fail");
|
|
@@ -230293,6 +230391,7 @@ class StorageManager {
|
|
|
230293
230391
|
defaultManifestStore = null;
|
|
230294
230392
|
environmentManifestStores = new Map;
|
|
230295
230393
|
attachedCatalogs = new Map;
|
|
230394
|
+
duckLakeAttachMutex = new Mutex;
|
|
230296
230395
|
config;
|
|
230297
230396
|
constructor(config) {
|
|
230298
230397
|
this.config = config;
|
|
@@ -230330,12 +230429,15 @@ class StorageManager {
|
|
|
230330
230429
|
throw new Error("Storage not initialized. Call initialize() first.");
|
|
230331
230430
|
}
|
|
230332
230431
|
const key = configKey(config);
|
|
230333
|
-
|
|
230334
|
-
|
|
230335
|
-
|
|
230336
|
-
|
|
230337
|
-
|
|
230338
|
-
|
|
230432
|
+
const catalogName = await this.duckLakeAttachMutex.runExclusive(async () => {
|
|
230433
|
+
const existing = this.attachedCatalogs.get(key);
|
|
230434
|
+
if (existing)
|
|
230435
|
+
return existing;
|
|
230436
|
+
const name = catalogNameForConfig(config);
|
|
230437
|
+
await this.attachDuckLakeCatalog(config, name);
|
|
230438
|
+
this.attachedCatalogs.set(key, name);
|
|
230439
|
+
return name;
|
|
230440
|
+
});
|
|
230339
230441
|
const store = new DuckLakeManifestStore(this.duckDbConnection, catalogName, environmentName);
|
|
230340
230442
|
await store.bootstrapSchema();
|
|
230341
230443
|
this.environmentManifestStores.set(environmentId, store);
|
|
@@ -230352,9 +230454,13 @@ class StorageManager {
|
|
|
230352
230454
|
if (isPostgres) {
|
|
230353
230455
|
await connection.run("INSTALL postgres; LOAD postgres;");
|
|
230354
230456
|
}
|
|
230355
|
-
const
|
|
230457
|
+
const catalogUrl = isPostgres ? withPgConnectTimeout(config.catalogUrl, pgConnectTimeoutSeconds()) : config.catalogUrl;
|
|
230458
|
+
const escapedCatalogUrl = escapeSQL2(catalogUrl);
|
|
230356
230459
|
const escapedDataPath = escapeSQL2(config.dataPath);
|
|
230357
230460
|
const isCloudStorage = config.dataPath.startsWith("gs://") || config.dataPath.startsWith("s3://");
|
|
230461
|
+
if (isCloudStorage) {
|
|
230462
|
+
await connection.run("INSTALL httpfs; LOAD httpfs;");
|
|
230463
|
+
}
|
|
230358
230464
|
let attachCmd = `ATTACH 'ducklake:${escapedCatalogUrl}' AS ${catalogName}`;
|
|
230359
230465
|
const attachOpts = [
|
|
230360
230466
|
`DATA_PATH '${escapedDataPath}'`,
|
|
@@ -230364,8 +230470,22 @@ class StorageManager {
|
|
|
230364
230470
|
attachOpts.push("OVERRIDE_DATA_PATH true");
|
|
230365
230471
|
}
|
|
230366
230472
|
attachCmd += ` (${attachOpts.join(", ")});`;
|
|
230367
|
-
logger.info(`Attaching DuckLake manifest catalog: ${attachCmd}`);
|
|
230368
|
-
|
|
230473
|
+
logger.info(`Attaching DuckLake manifest catalog: ${redactPgSecrets(attachCmd)}`);
|
|
230474
|
+
try {
|
|
230475
|
+
await connection.run(attachCmd);
|
|
230476
|
+
} catch (error) {
|
|
230477
|
+
const outcome = handlePgAttachError(error, `DuckLake catalog credentials rejected for ${catalogName}`);
|
|
230478
|
+
if (outcome.action === "swallow") {
|
|
230479
|
+
logger.info(`DuckLake catalog ${catalogName} is already attached, skipping`);
|
|
230480
|
+
return;
|
|
230481
|
+
}
|
|
230482
|
+
if (outcome.error instanceof ConnectionAuthError) {
|
|
230483
|
+
logger.warn("DuckLake catalog credentials rejected", {
|
|
230484
|
+
catalogName
|
|
230485
|
+
});
|
|
230486
|
+
}
|
|
230487
|
+
throw outcome.error;
|
|
230488
|
+
}
|
|
230369
230489
|
}
|
|
230370
230490
|
getRepository() {
|
|
230371
230491
|
if (!this.repository) {
|
|
@@ -230404,26 +230524,30 @@ class StorageManager {
|
|
|
230404
230524
|
// src/service/environment.ts
|
|
230405
230525
|
import { MalloyError as MalloyError3, Runtime as Runtime2 } from "@malloydata/malloy";
|
|
230406
230526
|
import * as fs6 from "fs";
|
|
230407
|
-
import * as
|
|
230527
|
+
import * as path9 from "path";
|
|
230408
230528
|
|
|
230409
230529
|
// src/utils.ts
|
|
230410
230530
|
import * as fs3 from "fs";
|
|
230411
|
-
import
|
|
230531
|
+
import * as path6 from "path";
|
|
230532
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
230412
230533
|
var URL_READER = {
|
|
230413
230534
|
readURL: (url2) => {
|
|
230414
|
-
let
|
|
230535
|
+
let path7 = url2.toString();
|
|
230415
230536
|
if (url2.protocol == "file:") {
|
|
230416
|
-
|
|
230537
|
+
path7 = fileURLToPath2(url2);
|
|
230417
230538
|
}
|
|
230418
|
-
return fs3.promises.readFile(
|
|
230539
|
+
return fs3.promises.readFile(path7, "utf8");
|
|
230419
230540
|
}
|
|
230420
230541
|
};
|
|
230542
|
+
function ignoreDotfiles(file) {
|
|
230543
|
+
return path6.basename(file).startsWith(".");
|
|
230544
|
+
}
|
|
230421
230545
|
|
|
230422
230546
|
// src/service/package.ts
|
|
230423
230547
|
var import_api3 = __toESM(require_src(), 1);
|
|
230424
230548
|
var import_recursive_readdir = __toESM(require_recursive_readdir(), 1);
|
|
230425
230549
|
import * as fs5 from "fs/promises";
|
|
230426
|
-
import * as
|
|
230550
|
+
import * as path8 from "path";
|
|
230427
230551
|
import { DuckDBConnection as DuckDBConnection3 } from "@malloydata/db-duckdb";
|
|
230428
230552
|
import"@malloydata/db-duckdb/native";
|
|
230429
230553
|
import {
|
|
@@ -230451,7 +230575,7 @@ import {
|
|
|
230451
230575
|
} from "@malloydata/malloy-sql";
|
|
230452
230576
|
import * as fs4 from "fs/promises";
|
|
230453
230577
|
import { createRequire as createRequire2 } from "module";
|
|
230454
|
-
import * as
|
|
230578
|
+
import * as path7 from "path";
|
|
230455
230579
|
|
|
230456
230580
|
// src/data_styles.ts
|
|
230457
230581
|
function compileDataStyles(styles) {
|
|
@@ -231062,7 +231186,7 @@ run: ${sourceName ? sourceName + "->" : ""}${queryName}`;
|
|
|
231062
231186
|
};
|
|
231063
231187
|
}
|
|
231064
231188
|
static async getModelRuntime(packagePath, modelPath, malloyConfig, options) {
|
|
231065
|
-
const fullModelPath =
|
|
231189
|
+
const fullModelPath = path7.join(packagePath, modelPath);
|
|
231066
231190
|
try {
|
|
231067
231191
|
if (!(await fs4.stat(fullModelPath)).isFile()) {
|
|
231068
231192
|
throw new ModelNotFoundError(`${modelPath} is not a file.`);
|
|
@@ -231275,7 +231399,7 @@ run: ${sourceName ? sourceName + "->" : ""}${queryName}`;
|
|
|
231275
231399
|
return this.modelType;
|
|
231276
231400
|
}
|
|
231277
231401
|
async getFileText(packagePath) {
|
|
231278
|
-
const fullPath =
|
|
231402
|
+
const fullPath = path7.join(packagePath, this.modelPath);
|
|
231279
231403
|
try {
|
|
231280
231404
|
return await fs4.readFile(fullPath, "utf8");
|
|
231281
231405
|
} catch {
|
|
@@ -231510,17 +231634,17 @@ class Package {
|
|
|
231510
231634
|
static async getModelPaths(packagePath) {
|
|
231511
231635
|
let files = undefined;
|
|
231512
231636
|
try {
|
|
231513
|
-
files = await import_recursive_readdir.default(packagePath);
|
|
231637
|
+
files = await import_recursive_readdir.default(packagePath, [ignoreDotfiles]);
|
|
231514
231638
|
} catch (error) {
|
|
231515
231639
|
logger.error(error);
|
|
231516
231640
|
throw new PackageNotFoundError(`Package config for ${packagePath} does not exist.`);
|
|
231517
231641
|
}
|
|
231518
231642
|
return files.map((fullPath) => {
|
|
231519
|
-
return
|
|
231643
|
+
return path8.relative(packagePath, fullPath).replace(/\\/g, "/");
|
|
231520
231644
|
}).filter((modelPath) => modelPath.endsWith(MODEL_FILE_SUFFIX) || modelPath.endsWith(NOTEBOOK_FILE_SUFFIX));
|
|
231521
231645
|
}
|
|
231522
231646
|
static async validatePackageManifestExistsOrThrowError(packagePath) {
|
|
231523
|
-
const packageConfigPath =
|
|
231647
|
+
const packageConfigPath = path8.join(packagePath, PACKAGE_MANIFEST_NAME);
|
|
231524
231648
|
try {
|
|
231525
231649
|
await fs5.stat(packageConfigPath);
|
|
231526
231650
|
} catch {
|
|
@@ -231529,7 +231653,7 @@ class Package {
|
|
|
231529
231653
|
}
|
|
231530
231654
|
}
|
|
231531
231655
|
static async readPackageConfig(packagePath) {
|
|
231532
|
-
const packageConfigPath =
|
|
231656
|
+
const packageConfigPath = path8.join(packagePath, PACKAGE_MANIFEST_NAME);
|
|
231533
231657
|
const packageConfigContents = await fs5.readFile(packageConfigPath);
|
|
231534
231658
|
const packageManifest = JSON.parse(packageConfigContents.toString());
|
|
231535
231659
|
return {
|
|
@@ -231548,14 +231672,13 @@ class Package {
|
|
|
231548
231672
|
}));
|
|
231549
231673
|
}
|
|
231550
231674
|
static async getDatabasePaths(packagePath) {
|
|
231551
|
-
|
|
231552
|
-
files = await import_recursive_readdir.default(packagePath);
|
|
231675
|
+
const files = await import_recursive_readdir.default(packagePath, [ignoreDotfiles]);
|
|
231553
231676
|
return files.map((fullPath) => {
|
|
231554
|
-
return
|
|
231677
|
+
return path8.relative(packagePath, fullPath).replace(/\\/g, "/");
|
|
231555
231678
|
}).filter((modelPath) => modelPath.endsWith(".parquet") || modelPath.endsWith(".csv"));
|
|
231556
231679
|
}
|
|
231557
231680
|
static async getDatabaseInfo(packagePath, databasePath) {
|
|
231558
|
-
const fullPath =
|
|
231681
|
+
const fullPath = path8.join(packagePath, databasePath);
|
|
231559
231682
|
const runtime = new ConnectionRuntime({
|
|
231560
231683
|
urlReader: new EmptyURLReader,
|
|
231561
231684
|
connections: [new DuckDBConnection3("duckdb")]
|
|
@@ -231612,7 +231735,7 @@ class Environment {
|
|
|
231612
231735
|
async writeEnvironmentReadme(readme) {
|
|
231613
231736
|
if (readme === undefined)
|
|
231614
231737
|
return;
|
|
231615
|
-
const readmePath =
|
|
231738
|
+
const readmePath = path9.join(this.environmentPath, "README.md");
|
|
231616
231739
|
try {
|
|
231617
231740
|
await fs6.promises.writeFile(readmePath, readme, "utf-8");
|
|
231618
231741
|
logger.info(`Updated README.md for environment ${this.environmentName}`);
|
|
@@ -231659,7 +231782,7 @@ class Environment {
|
|
|
231659
231782
|
async reloadEnvironmentMetadata() {
|
|
231660
231783
|
let readme = "";
|
|
231661
231784
|
try {
|
|
231662
|
-
readme = (await fs6.promises.readFile(
|
|
231785
|
+
readme = (await fs6.promises.readFile(path9.join(this.environmentPath, README_NAME))).toString();
|
|
231663
231786
|
} catch {}
|
|
231664
231787
|
this.metadata = {
|
|
231665
231788
|
...this.metadata,
|
|
@@ -231670,10 +231793,10 @@ class Environment {
|
|
|
231670
231793
|
return this.metadata;
|
|
231671
231794
|
}
|
|
231672
231795
|
async compileSource(packageName, modelName, source, includeSql = false) {
|
|
231673
|
-
const modelDir =
|
|
231674
|
-
const virtualUri = `file://${
|
|
231796
|
+
const modelDir = path9.dirname(path9.join(this.environmentPath, packageName, modelName));
|
|
231797
|
+
const virtualUri = `file://${path9.join(modelDir, "__compile_check.malloy")}`;
|
|
231675
231798
|
const virtualUrl = new URL(virtualUri);
|
|
231676
|
-
const modelPath =
|
|
231799
|
+
const modelPath = path9.join(this.environmentPath, packageName, modelName);
|
|
231677
231800
|
let modelContent = "";
|
|
231678
231801
|
try {
|
|
231679
231802
|
modelContent = await fs6.promises.readFile(modelPath, "utf8");
|
|
@@ -231785,24 +231908,30 @@ ${source}` : source;
|
|
|
231785
231908
|
throw error;
|
|
231786
231909
|
}
|
|
231787
231910
|
}
|
|
231911
|
+
getOrCreatePackageMutex(packageName) {
|
|
231912
|
+
let packageMutex = this.packageMutexes.get(packageName);
|
|
231913
|
+
if (packageMutex === undefined) {
|
|
231914
|
+
packageMutex = new Mutex;
|
|
231915
|
+
this.packageMutexes.set(packageName, packageMutex);
|
|
231916
|
+
}
|
|
231917
|
+
return packageMutex;
|
|
231918
|
+
}
|
|
231788
231919
|
async getPackage(packageName, reload = false) {
|
|
231789
231920
|
const _package = this.packages.get(packageName);
|
|
231790
231921
|
if (_package !== undefined && !reload) {
|
|
231791
231922
|
return _package;
|
|
231792
231923
|
}
|
|
231793
|
-
|
|
231794
|
-
if (packageMutex
|
|
231924
|
+
const packageMutex = this.getOrCreatePackageMutex(packageName);
|
|
231925
|
+
if (packageMutex.isLocked()) {
|
|
231795
231926
|
logger.debug(`Package ${packageName} is being loaded, waiting for unlock...`);
|
|
231796
231927
|
await packageMutex.waitForUnlock();
|
|
231797
231928
|
logger.debug(`Package ${packageName} unlocked`);
|
|
231798
231929
|
const existingPackage = this.packages.get(packageName);
|
|
231799
|
-
if (existingPackage) {
|
|
231930
|
+
if (existingPackage !== undefined && !reload) {
|
|
231800
231931
|
logger.debug(`Package ${packageName} loaded by another request`);
|
|
231801
231932
|
return existingPackage;
|
|
231802
231933
|
}
|
|
231803
231934
|
}
|
|
231804
|
-
packageMutex = new Mutex;
|
|
231805
|
-
this.packageMutexes.set(packageName, packageMutex);
|
|
231806
231935
|
return packageMutex.runExclusive(async () => {
|
|
231807
231936
|
const existingPackage = this.packages.get(packageName);
|
|
231808
231937
|
if (existingPackage !== undefined && !reload) {
|
|
@@ -231811,7 +231940,7 @@ ${source}` : source;
|
|
|
231811
231940
|
this.setPackageStatus(packageName, "loading" /* LOADING */);
|
|
231812
231941
|
try {
|
|
231813
231942
|
logger.debug(`Loading package ${packageName}...`);
|
|
231814
|
-
const packagePath =
|
|
231943
|
+
const packagePath = path9.join(this.environmentPath, packageName);
|
|
231815
231944
|
const _package2 = await Package.create(this.environmentName, packageName, packagePath, () => this.malloyConfig.malloyConfig);
|
|
231816
231945
|
if (existingPackage !== undefined && reload) {
|
|
231817
231946
|
this.retireConnectionGeneration(`package ${packageName}`, () => existingPackage.getMalloyConfig().releaseConnections());
|
|
@@ -231829,7 +231958,7 @@ ${source}` : source;
|
|
|
231829
231958
|
});
|
|
231830
231959
|
}
|
|
231831
231960
|
async addPackage(packageName) {
|
|
231832
|
-
const packagePath =
|
|
231961
|
+
const packagePath = path9.join(this.environmentPath, packageName);
|
|
231833
231962
|
if (!await fs6.promises.access(packagePath).then(() => true).catch(() => false) || !(await fs6.promises.stat(packagePath))?.isDirectory()) {
|
|
231834
231963
|
throw new PackageNotFoundError(`Package ${packageName} not found`);
|
|
231835
231964
|
}
|
|
@@ -231837,20 +231966,35 @@ ${source}` : source;
|
|
|
231837
231966
|
packagePath,
|
|
231838
231967
|
malloyConfig: this.malloyConfig.malloyConfig
|
|
231839
231968
|
});
|
|
231840
|
-
this.
|
|
231841
|
-
|
|
231842
|
-
|
|
231843
|
-
|
|
231844
|
-
|
|
231845
|
-
|
|
231846
|
-
|
|
231969
|
+
const packageMutex = this.getOrCreatePackageMutex(packageName);
|
|
231970
|
+
if (packageMutex.isLocked()) {
|
|
231971
|
+
logger.debug(`Package ${packageName} is being loaded, waiting before addPackage...`);
|
|
231972
|
+
await packageMutex.waitForUnlock();
|
|
231973
|
+
const alreadyLoaded = this.packages.get(packageName);
|
|
231974
|
+
if (alreadyLoaded !== undefined) {
|
|
231975
|
+
return alreadyLoaded;
|
|
231976
|
+
}
|
|
231847
231977
|
}
|
|
231848
|
-
|
|
231849
|
-
|
|
231978
|
+
return packageMutex.runExclusive(async () => {
|
|
231979
|
+
const existingPackage = this.packages.get(packageName);
|
|
231980
|
+
if (existingPackage !== undefined) {
|
|
231981
|
+
return existingPackage;
|
|
231982
|
+
}
|
|
231983
|
+
this.setPackageStatus(packageName, "loading" /* LOADING */);
|
|
231984
|
+
try {
|
|
231985
|
+
this.packages.set(packageName, await Package.create(this.environmentName, packageName, packagePath, () => this.malloyConfig.malloyConfig));
|
|
231986
|
+
} catch (error) {
|
|
231987
|
+
logger.error("Error adding package", { error });
|
|
231988
|
+
this.deletePackageStatus(packageName);
|
|
231989
|
+
throw error;
|
|
231990
|
+
}
|
|
231991
|
+
this.setPackageStatus(packageName, "serving" /* SERVING */);
|
|
231992
|
+
return this.packages.get(packageName);
|
|
231993
|
+
});
|
|
231850
231994
|
}
|
|
231851
231995
|
async writePackageManifest(packageName, metadata) {
|
|
231852
|
-
const packagePath =
|
|
231853
|
-
const manifestPath =
|
|
231996
|
+
const packagePath = path9.join(this.environmentPath, packageName);
|
|
231997
|
+
const manifestPath = path9.join(packagePath, "publisher.json");
|
|
231854
231998
|
try {
|
|
231855
231999
|
let existingManifest = {};
|
|
231856
232000
|
try {
|
|
@@ -231922,7 +232066,7 @@ ${source}` : source;
|
|
|
231922
232066
|
}
|
|
231923
232067
|
await _package.getMalloyConfig().releaseConnections();
|
|
231924
232068
|
try {
|
|
231925
|
-
await fs6.promises.rm(
|
|
232069
|
+
await fs6.promises.rm(path9.join(this.environmentPath, packageName), {
|
|
231926
232070
|
recursive: true,
|
|
231927
232071
|
force: true
|
|
231928
232072
|
});
|
|
@@ -231986,7 +232130,7 @@ ${source}` : source;
|
|
|
231986
232130
|
};
|
|
231987
232131
|
}
|
|
231988
232132
|
async deleteDuckDBConnection(connectionName) {
|
|
231989
|
-
const duckdbPath =
|
|
232133
|
+
const duckdbPath = path9.join(this.environmentPath, `${connectionName}.duckdb`);
|
|
231990
232134
|
try {
|
|
231991
232135
|
await fs6.promises.rm(duckdbPath, { force: true });
|
|
231992
232136
|
logger.info(`Removed DuckDB connection file ${connectionName} from environment ${this.environmentName}`);
|
|
@@ -232055,6 +232199,7 @@ class EnvironmentStore {
|
|
|
232055
232199
|
publisherConfigIsFrozen;
|
|
232056
232200
|
finishedInitialization;
|
|
232057
232201
|
isInitialized = false;
|
|
232202
|
+
failedEnvironments = [];
|
|
232058
232203
|
storageManager;
|
|
232059
232204
|
s3Client = new import_client_s32.S3({
|
|
232060
232205
|
followRegionRedirects: true
|
|
@@ -232066,7 +232211,7 @@ class EnvironmentStore {
|
|
|
232066
232211
|
const storageConfig = {
|
|
232067
232212
|
type: "duckdb",
|
|
232068
232213
|
duckdb: {
|
|
232069
|
-
path:
|
|
232214
|
+
path: path10.join(serverRootPath, "publisher.db")
|
|
232070
232215
|
}
|
|
232071
232216
|
};
|
|
232072
232217
|
this.storageManager = new StorageManager(storageConfig);
|
|
@@ -232087,6 +232232,10 @@ class EnvironmentStore {
|
|
|
232087
232232
|
logEnvironmentInitializationError(environmentName, error) {
|
|
232088
232233
|
const label = environmentName ? ` "${environmentName}"` : "";
|
|
232089
232234
|
logger.error(`Error initializing environment${label}; skipping environment`, this.extractErrorDataFromError(error));
|
|
232235
|
+
this.failedEnvironments.push({
|
|
232236
|
+
name: environmentName ?? "<unknown>",
|
|
232237
|
+
error: error instanceof Error ? error.message : String(error)
|
|
232238
|
+
});
|
|
232090
232239
|
}
|
|
232091
232240
|
async initialize() {
|
|
232092
232241
|
const reInit = process.env.INITIALIZE_STORAGE === "true";
|
|
@@ -232149,7 +232298,11 @@ class EnvironmentStore {
|
|
|
232149
232298
|
}
|
|
232150
232299
|
}
|
|
232151
232300
|
this.isInitialized = true;
|
|
232152
|
-
|
|
232301
|
+
if (this.failedEnvironments.length > 0) {
|
|
232302
|
+
markDegraded();
|
|
232303
|
+
} else {
|
|
232304
|
+
markReady();
|
|
232305
|
+
}
|
|
232153
232306
|
const initializationDuration = performance.now() - initialTime;
|
|
232154
232307
|
logger.info(`Environment store successfully initialized in ${formatDuration(initializationDuration)}`);
|
|
232155
232308
|
} catch (error) {
|
|
@@ -232353,7 +232506,7 @@ class EnvironmentStore {
|
|
|
232353
232506
|
const reInit = process.env.INITIALIZE_STORAGE === "true";
|
|
232354
232507
|
await fs7.promises.mkdir(this.serverRootPath, { recursive: true });
|
|
232355
232508
|
if (reInit) {
|
|
232356
|
-
const uploadDocsPath2 =
|
|
232509
|
+
const uploadDocsPath2 = path10.join(this.serverRootPath, PUBLISHER_DATA_DIR);
|
|
232357
232510
|
logger.info(`Reinitialization mode: Cleaning up upload documents path ${uploadDocsPath2}`);
|
|
232358
232511
|
try {
|
|
232359
232512
|
await fs7.promises.rm(uploadDocsPath2, {
|
|
@@ -232370,7 +232523,7 @@ class EnvironmentStore {
|
|
|
232370
232523
|
} else {
|
|
232371
232524
|
logger.info(`Using existing publisher path`);
|
|
232372
232525
|
}
|
|
232373
|
-
const uploadDocsPath =
|
|
232526
|
+
const uploadDocsPath = path10.join(this.serverRootPath, PUBLISHER_DATA_DIR);
|
|
232374
232527
|
await fs7.promises.mkdir(uploadDocsPath, { recursive: true });
|
|
232375
232528
|
}
|
|
232376
232529
|
async listEnvironments(skipInitializationCheck = false) {
|
|
@@ -232385,7 +232538,12 @@ class EnvironmentStore {
|
|
|
232385
232538
|
environments: [],
|
|
232386
232539
|
initialized: this.isInitialized,
|
|
232387
232540
|
frozenConfig: isPublisherConfigFrozen(this.serverRootPath),
|
|
232388
|
-
operationalState: getOperationalState()
|
|
232541
|
+
operationalState: getOperationalState(),
|
|
232542
|
+
...this.failedEnvironments.length > 0 && {
|
|
232543
|
+
failedEnvironments: [
|
|
232544
|
+
...this.failedEnvironments
|
|
232545
|
+
]
|
|
232546
|
+
}
|
|
232389
232547
|
};
|
|
232390
232548
|
const environments = await this.listEnvironments(true);
|
|
232391
232549
|
await Promise.all(environments.map(async (environment) => {
|
|
@@ -232602,12 +232760,12 @@ class EnvironmentStore {
|
|
|
232602
232760
|
const absoluteEnvironmentPath = `${this.serverRootPath}/${PUBLISHER_DATA_DIR}/${environmentName}`;
|
|
232603
232761
|
await fs7.promises.mkdir(absoluteEnvironmentPath, { recursive: true });
|
|
232604
232762
|
if (environment.readme) {
|
|
232605
|
-
await fs7.promises.writeFile(
|
|
232763
|
+
await fs7.promises.writeFile(path10.join(absoluteEnvironmentPath, "README.md"), environment.readme);
|
|
232606
232764
|
}
|
|
232607
232765
|
return absoluteEnvironmentPath;
|
|
232608
232766
|
}
|
|
232609
232767
|
isLocalPath(location) {
|
|
232610
|
-
return location.startsWith("./") || location.startsWith("../") || location.startsWith("~/") || location.startsWith("/") ||
|
|
232768
|
+
return location.startsWith("./") || location.startsWith("../") || location.startsWith("~/") || location.startsWith("/") || path10.isAbsolute(location);
|
|
232611
232769
|
}
|
|
232612
232770
|
isGitHubURL(location) {
|
|
232613
232771
|
return location.startsWith("https://github.com/") || location.startsWith("git@github.com:");
|
|
@@ -232663,7 +232821,7 @@ class EnvironmentStore {
|
|
|
232663
232821
|
if (githubInfo && githubInfo.packagePath) {
|
|
232664
232822
|
const subPathMatch = _package.location.match(/\/tree\/[^/]+\/(.+)$/);
|
|
232665
232823
|
if (subPathMatch) {
|
|
232666
|
-
sourcePath =
|
|
232824
|
+
sourcePath = path10.join(tempDownloadPath, subPathMatch[1]);
|
|
232667
232825
|
} else {
|
|
232668
232826
|
sourcePath = tempDownloadPath;
|
|
232669
232827
|
}
|
|
@@ -232674,7 +232832,7 @@ class EnvironmentStore {
|
|
|
232674
232832
|
if (this.isLocalPath(_package.location)) {
|
|
232675
232833
|
sourcePath = _package.location;
|
|
232676
232834
|
} else {
|
|
232677
|
-
sourcePath =
|
|
232835
|
+
sourcePath = path10.join(tempDownloadPath, groupedLocation);
|
|
232678
232836
|
}
|
|
232679
232837
|
}
|
|
232680
232838
|
const sourceExists = await fs7.promises.access(sourcePath).then(() => true).catch(() => false);
|
|
@@ -232750,7 +232908,7 @@ class EnvironmentStore {
|
|
|
232750
232908
|
}
|
|
232751
232909
|
}
|
|
232752
232910
|
if (this.isLocalPath(location)) {
|
|
232753
|
-
const packagePath =
|
|
232911
|
+
const packagePath = path10.isAbsolute(location) ? location : path10.join(this.serverRootPath, location);
|
|
232754
232912
|
try {
|
|
232755
232913
|
logger.info(`Mounting local directory at "${packagePath}" to "${targetPath}"`);
|
|
232756
232914
|
await this.mountLocalDirectory(packagePath, targetPath, environmentName, packageName);
|
|
@@ -232804,11 +232962,11 @@ class EnvironmentStore {
|
|
|
232804
232962
|
}
|
|
232805
232963
|
await Promise.all(files.map(async (file) => {
|
|
232806
232964
|
const relativeFilePath = file.name.replace(prefix, "");
|
|
232807
|
-
const absoluteFilePath = isCompressedFile ? absoluteDirPath :
|
|
232965
|
+
const absoluteFilePath = isCompressedFile ? absoluteDirPath : path10.join(absoluteDirPath, relativeFilePath);
|
|
232808
232966
|
if (file.name.endsWith("/")) {
|
|
232809
232967
|
return;
|
|
232810
232968
|
}
|
|
232811
|
-
await fs7.promises.mkdir(
|
|
232969
|
+
await fs7.promises.mkdir(path10.dirname(absoluteFilePath), {
|
|
232812
232970
|
recursive: true
|
|
232813
232971
|
});
|
|
232814
232972
|
return fs7.promises.writeFile(absoluteFilePath, await file.download());
|
|
@@ -232824,7 +232982,7 @@ class EnvironmentStore {
|
|
|
232824
232982
|
const prefix = prefixParts.join("/");
|
|
232825
232983
|
if (isCompressedFile) {
|
|
232826
232984
|
const zipFilePath = `${absoluteDirPath}.zip`;
|
|
232827
|
-
await fs7.promises.mkdir(
|
|
232985
|
+
await fs7.promises.mkdir(path10.dirname(zipFilePath), {
|
|
232828
232986
|
recursive: true
|
|
232829
232987
|
});
|
|
232830
232988
|
const command = new import_client_s32.GetObjectCommand({
|
|
@@ -232863,8 +233021,8 @@ class EnvironmentStore {
|
|
|
232863
233021
|
if (!relativeFilePath || relativeFilePath.endsWith("/")) {
|
|
232864
233022
|
return;
|
|
232865
233023
|
}
|
|
232866
|
-
const absoluteFilePath =
|
|
232867
|
-
await fs7.promises.mkdir(
|
|
233024
|
+
const absoluteFilePath = path10.join(absoluteDirPath, relativeFilePath);
|
|
233025
|
+
await fs7.promises.mkdir(path10.dirname(absoluteFilePath), {
|
|
232868
233026
|
recursive: true
|
|
232869
233027
|
});
|
|
232870
233028
|
const command = new import_client_s32.GetObjectCommand({
|
|
@@ -232926,7 +233084,7 @@ class EnvironmentStore {
|
|
|
232926
233084
|
logger.info(`Successfully cloned entire repository to: ${absoluteDirPath}`);
|
|
232927
233085
|
return;
|
|
232928
233086
|
}
|
|
232929
|
-
const packageFullPath =
|
|
233087
|
+
const packageFullPath = path10.join(absoluteDirPath, cleanPackagePath);
|
|
232930
233088
|
const packageExists = await fs7.promises.access(packageFullPath).then(() => true).catch(() => false);
|
|
232931
233089
|
if (!packageExists) {
|
|
232932
233090
|
throw new Error(`Package path "${cleanPackagePath}" does not exist in the cloned repository.`);
|
|
@@ -232934,7 +233092,7 @@ class EnvironmentStore {
|
|
|
232934
233092
|
const dirContents = await fs7.promises.readdir(absoluteDirPath);
|
|
232935
233093
|
for (const entry of dirContents) {
|
|
232936
233094
|
if (entry !== cleanPackagePath.replace(/^\/+/, "").split("/")[0]) {
|
|
232937
|
-
await fs7.promises.rm(
|
|
233095
|
+
await fs7.promises.rm(path10.join(absoluteDirPath, entry), {
|
|
232938
233096
|
recursive: true,
|
|
232939
233097
|
force: true
|
|
232940
233098
|
});
|
|
@@ -232942,7 +233100,7 @@ class EnvironmentStore {
|
|
|
232942
233100
|
}
|
|
232943
233101
|
const packageContents = await fs7.promises.readdir(packageFullPath);
|
|
232944
233102
|
for (const entry of packageContents) {
|
|
232945
|
-
await fs7.promises.rename(
|
|
233103
|
+
await fs7.promises.rename(path10.join(packageFullPath, entry), path10.join(absoluteDirPath, entry));
|
|
232946
233104
|
}
|
|
232947
233105
|
await fs7.promises.rm(packageFullPath, { recursive: true, force: true });
|
|
232948
233106
|
}
|
|
@@ -232990,9 +233148,9 @@ class WatchModeController {
|
|
|
232990
233148
|
});
|
|
232991
233149
|
return;
|
|
232992
233150
|
}
|
|
232993
|
-
this.watchingPath =
|
|
233151
|
+
this.watchingPath = path11.join(this.environmentStore.serverRootPath, watchName);
|
|
232994
233152
|
this.watcher = esm_default.watch(this.watchingPath, {
|
|
232995
|
-
ignored: (
|
|
233153
|
+
ignored: (path12, stats) => !!stats?.isFile() && !path12.endsWith(".malloy") && !path12.endsWith(".md"),
|
|
232996
233154
|
ignoreInitial: true
|
|
232997
233155
|
});
|
|
232998
233156
|
const reloadEnvironment = async () => {
|
|
@@ -233000,16 +233158,16 @@ class WatchModeController {
|
|
|
233000
233158
|
await this.environmentStore.addEnvironment(environment2.metadata);
|
|
233001
233159
|
logger.info(`Reloaded environment ${watchName}`);
|
|
233002
233160
|
};
|
|
233003
|
-
this.watcher.on("add", async (
|
|
233004
|
-
logger.info(`Detected new file ${
|
|
233161
|
+
this.watcher.on("add", async (path12) => {
|
|
233162
|
+
logger.info(`Detected new file ${path12}, reloading environment ${watchName}`);
|
|
233005
233163
|
await reloadEnvironment();
|
|
233006
233164
|
});
|
|
233007
|
-
this.watcher.on("unlink", async (
|
|
233008
|
-
logger.info(`Detected deletion of ${
|
|
233165
|
+
this.watcher.on("unlink", async (path12) => {
|
|
233166
|
+
logger.info(`Detected deletion of ${path12}, reloading environment ${watchName}`);
|
|
233009
233167
|
await reloadEnvironment();
|
|
233010
233168
|
});
|
|
233011
|
-
this.watcher.on("change", async (
|
|
233012
|
-
logger.info(`Detected change on ${
|
|
233169
|
+
this.watcher.on("change", async (path12) => {
|
|
233170
|
+
logger.info(`Detected change on ${path12}, reloading environment ${watchName}`);
|
|
233013
233171
|
await reloadEnvironment();
|
|
233014
233172
|
});
|
|
233015
233173
|
res.json();
|
|
@@ -236100,25 +236258,25 @@ async function getModelForQuery(environmentStore, environmentName, packageName,
|
|
|
236100
236258
|
}
|
|
236101
236259
|
}
|
|
236102
236260
|
function buildMalloyUri(components, fragment) {
|
|
236103
|
-
let
|
|
236261
|
+
let path12 = "/environment/";
|
|
236104
236262
|
if (components.environment) {
|
|
236105
|
-
|
|
236263
|
+
path12 += encodeURIComponent(components.environment);
|
|
236106
236264
|
} else {
|
|
236107
|
-
|
|
236265
|
+
path12 += "home";
|
|
236108
236266
|
}
|
|
236109
236267
|
if (components.package) {
|
|
236110
|
-
|
|
236268
|
+
path12 += "/package/" + encodeURIComponent(components.package);
|
|
236111
236269
|
}
|
|
236112
236270
|
if (components.resourceType) {
|
|
236113
|
-
|
|
236271
|
+
path12 += "/" + components.resourceType;
|
|
236114
236272
|
if (components.resourceName) {
|
|
236115
|
-
|
|
236273
|
+
path12 += "/" + encodeURIComponent(components.resourceName);
|
|
236116
236274
|
if (components.subResourceType && components.subResourceName) {
|
|
236117
|
-
|
|
236275
|
+
path12 += "/" + components.subResourceType + "/" + encodeURIComponent(components.subResourceName);
|
|
236118
236276
|
}
|
|
236119
236277
|
}
|
|
236120
236278
|
}
|
|
236121
|
-
let uriString = "malloy:/" +
|
|
236279
|
+
let uriString = "malloy:/" + path12;
|
|
236122
236280
|
if (fragment) {
|
|
236123
236281
|
uriString += "#" + fragment;
|
|
236124
236282
|
}
|
|
@@ -237637,8 +237795,8 @@ import {
|
|
|
237637
237795
|
} from "@malloydata/malloy";
|
|
237638
237796
|
|
|
237639
237797
|
// src/service/quoting.ts
|
|
237640
|
-
function quoteTablePath(
|
|
237641
|
-
return
|
|
237798
|
+
function quoteTablePath(path12, dialect) {
|
|
237799
|
+
return path12.split(".").map((seg) => dialect.quoteTablePath(seg)).join(".");
|
|
237642
237800
|
}
|
|
237643
237801
|
function splitTablePath(tableName) {
|
|
237644
237802
|
const lastDot = tableName.lastIndexOf(".");
|
|
@@ -238236,6 +238394,8 @@ function normalizeQueryArray(value) {
|
|
|
238236
238394
|
}
|
|
238237
238395
|
function parseArgs() {
|
|
238238
238396
|
const args = process.argv.slice(2);
|
|
238397
|
+
let sawServerRoot = false;
|
|
238398
|
+
let sawConfig = false;
|
|
238239
238399
|
for (let i = 0;i < args.length; i++) {
|
|
238240
238400
|
const arg = args[i];
|
|
238241
238401
|
if (arg === "--port" && args[i + 1]) {
|
|
@@ -238245,8 +238405,13 @@ function parseArgs() {
|
|
|
238245
238405
|
process.env.PUBLISHER_HOST = args[i + 1];
|
|
238246
238406
|
i++;
|
|
238247
238407
|
} else if (arg === "--server_root" && args[i + 1]) {
|
|
238408
|
+
sawServerRoot = true;
|
|
238248
238409
|
process.env.SERVER_ROOT = args[i + 1];
|
|
238249
238410
|
i++;
|
|
238411
|
+
} else if (arg === "--config" && args[i + 1]) {
|
|
238412
|
+
sawConfig = true;
|
|
238413
|
+
process.env.PUBLISHER_CONFIG_PATH = args[i + 1];
|
|
238414
|
+
i++;
|
|
238250
238415
|
} else if (arg === "--mcp_port" && args[i + 1]) {
|
|
238251
238416
|
process.env.MCP_PORT = args[i + 1];
|
|
238252
238417
|
i++;
|
|
@@ -238267,6 +238432,7 @@ function parseArgs() {
|
|
|
238267
238432
|
console.log(" --port <number> Port to run the server on (default: 4000)");
|
|
238268
238433
|
console.log(" --host <string> Host to bind the server to (default: localhost)");
|
|
238269
238434
|
console.log(" --server_root <path> Root directory to serve files from (default: .)");
|
|
238435
|
+
console.log(" --config <path> Path to publisher.config.json (default: <server_root>/publisher.config.json; falls back to bundled DuckDB-only sample config if missing)");
|
|
238270
238436
|
console.log(" --mcp_port <number> Port for MCP server (default: 4040)");
|
|
238271
238437
|
console.log(" --shutdown_drain_duration_seconds <number> Time in seconds to keep service in draining state before closing servers (default: 0)");
|
|
238272
238438
|
console.log(" --shutdown_graceful_close_timeout_seconds <number> Time in seconds to wait after closing servers before exit (default: 0)");
|
|
@@ -238275,6 +238441,9 @@ function parseArgs() {
|
|
|
238275
238441
|
process.exit(0);
|
|
238276
238442
|
}
|
|
238277
238443
|
}
|
|
238444
|
+
if (!sawServerRoot && !sawConfig && true) {
|
|
238445
|
+
process.env.PUBLISHER_USE_BUNDLED_DEFAULT = "true";
|
|
238446
|
+
}
|
|
238278
238447
|
}
|
|
238279
238448
|
parseArgs();
|
|
238280
238449
|
var PUBLISHER_PORT = Number(process.env.PUBLISHER_PORT || 4000);
|
|
@@ -238283,9 +238452,9 @@ var MCP_PORT = Number(process.env.MCP_PORT || 4040);
|
|
|
238283
238452
|
var MCP_ENDPOINT = "/mcp";
|
|
238284
238453
|
var SHUTDOWN_DRAIN_DURATION_SECONDS = Number(process.env.SHUTDOWN_DRAIN_DURATION_SECONDS || 0);
|
|
238285
238454
|
var SHUTDOWN_GRACEFUL_CLOSE_TIMEOUT_SECONDS = Number(process.env.SHUTDOWN_GRACEFUL_CLOSE_TIMEOUT_SECONDS || 0);
|
|
238286
|
-
var __filename_esm =
|
|
238287
|
-
var ROOT =
|
|
238288
|
-
var SERVER_ROOT =
|
|
238455
|
+
var __filename_esm = fileURLToPath3(import.meta.url);
|
|
238456
|
+
var ROOT = path12.join(path12.dirname(__filename_esm), "app");
|
|
238457
|
+
var SERVER_ROOT = path12.resolve(process.cwd(), process.env.SERVER_ROOT || ".");
|
|
238289
238458
|
var API_PREFIX2 = "/api/v0";
|
|
238290
238459
|
var isDevelopment = process.env["NODE_ENV"] === "development";
|
|
238291
238460
|
var app = import_express.default();
|
|
@@ -238366,14 +238535,14 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
|
|
|
238366
238535
|
});
|
|
238367
238536
|
if (!isDevelopment) {
|
|
238368
238537
|
app.use("/", import_express.default.static(ROOT));
|
|
238369
|
-
app.use("/api-doc.html", import_express.default.static(
|
|
238538
|
+
app.use("/api-doc.html", import_express.default.static(path12.join(ROOT, "api-doc.html")));
|
|
238370
238539
|
} else {
|
|
238371
238540
|
app.use(`${API_PREFIX2}`, loggerMiddleware);
|
|
238372
238541
|
app.use(import_http_proxy_middleware.createProxyMiddleware({
|
|
238373
238542
|
target: "http://localhost:5173",
|
|
238374
238543
|
changeOrigin: true,
|
|
238375
238544
|
ws: true,
|
|
238376
|
-
pathFilter: (
|
|
238545
|
+
pathFilter: (path13) => !path13.startsWith("/api/") && !path13.startsWith("/metrics") && !path13.startsWith("/health")
|
|
238377
238546
|
}));
|
|
238378
238547
|
}
|
|
238379
238548
|
var setVersionIdError2 = (res) => {
|
|
@@ -238973,7 +239142,7 @@ registerLegacyRoutes(app, {
|
|
|
238973
239142
|
manifestController
|
|
238974
239143
|
});
|
|
238975
239144
|
if (!isDevelopment) {
|
|
238976
|
-
app.get("*", (_req, res) => res.sendFile(
|
|
239145
|
+
app.get("*", (_req, res) => res.sendFile(path12.resolve(ROOT, "index.html")));
|
|
238977
239146
|
}
|
|
238978
239147
|
app.use((err, _req, res, _next) => {
|
|
238979
239148
|
logger.error("Unhandled error:", err);
|