@malloy-publisher/server 0.0.197-dev → 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.
Files changed (60) hide show
  1. package/README.docker.md +88 -20
  2. package/README.md +15 -0
  3. package/build.ts +16 -0
  4. package/dist/app/api-doc.yaml +20 -3
  5. package/dist/app/assets/EnvironmentPage-BVkQH_xQ.js +1 -0
  6. package/dist/app/assets/HomePage-BgH9UkjK.js +1 -0
  7. package/dist/app/assets/MainPage-DiBxABem.js +2 -0
  8. package/dist/app/assets/ModelPage-oS70fj83.js +1 -0
  9. package/dist/app/assets/PackagePage-F_qLDAdv.js +1 -0
  10. package/dist/app/assets/RouteError-WqpffppN.js +1 -0
  11. package/dist/app/assets/WorkbookPage-_YmC-ebR.js +1 -0
  12. package/dist/app/assets/{core-w79IMXAG.es-Bd0UlzOL.js → core-B8L9xCYT.es-BcRLJTnC.js} +14 -14
  13. package/dist/app/assets/index-BMViiwtJ.js +451 -0
  14. package/dist/app/assets/{index-C513UodQ.js → index-C3XPaTaS.js} +15 -15
  15. package/dist/app/assets/index-rg8Ok8nl.js +1803 -0
  16. package/dist/app/assets/{index.umd-BMeMPq_9.js → index.umd-CCAfKkxY.js} +1 -1
  17. package/dist/app/index.html +2 -3
  18. package/dist/default-publisher.config.json +23 -0
  19. package/dist/instrumentation.mjs +1 -3
  20. package/dist/server.mjs +958 -177
  21. package/package.json +11 -12
  22. package/publisher.config.example.bigquery.json +33 -0
  23. package/publisher.config.example.duckdb.json +23 -0
  24. package/publisher.config.json +1 -11
  25. package/src/config.spec.ts +225 -0
  26. package/src/config.ts +96 -2
  27. package/src/controller/connection.controller.ts +1 -1
  28. package/src/default-publisher.config.json +23 -0
  29. package/src/errors.spec.ts +42 -0
  30. package/src/errors.ts +8 -0
  31. package/src/health.ts +26 -0
  32. package/src/logger.ts +1 -3
  33. package/src/pg_helpers.spec.ts +226 -0
  34. package/src/pg_helpers.ts +129 -0
  35. package/src/server-old.ts +1119 -0
  36. package/src/server.ts +36 -0
  37. package/src/service/connection.spec.ts +6 -4
  38. package/src/service/connection.ts +8 -3
  39. package/src/service/connection_config.ts +2 -2
  40. package/src/service/environment.ts +53 -25
  41. package/src/service/environment_store.spec.ts +19 -0
  42. package/src/service/environment_store.ts +21 -2
  43. package/src/service/package.ts +4 -3
  44. package/src/storage/StorageManager.ts +71 -11
  45. package/src/storage/duckdb/schema.ts +41 -0
  46. package/src/utils.ts +11 -0
  47. package/tests/harness/rest_e2e.ts +2 -2
  48. package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +259 -0
  49. package/tests/unit/duckdb/attached_databases.test.ts +5 -5
  50. package/tests/unit/duckdb/legacy_schema_migration.test.ts +194 -0
  51. package/tests/unit/storage/StorageManager.test.ts +166 -0
  52. package/dist/app/assets/EnvironmentPage-1j6QDWAy.js +0 -1
  53. package/dist/app/assets/HomePage-DMop21VG.js +0 -1
  54. package/dist/app/assets/MainPage-BbE8ETz1.js +0 -2
  55. package/dist/app/assets/ModelPage-D2jvfe3t.js +0 -1
  56. package/dist/app/assets/PackagePage-BbnhGoD3.js +0 -1
  57. package/dist/app/assets/RouteError-D3LGEZ3i.js +0 -1
  58. package/dist/app/assets/WorkbookPage-DttVIj4u.js +0 -1
  59. package/dist/app/assets/index-5K9YjIxF.js +0 -456
  60. 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 path6 = function() {
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 = path6.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 && path6.sep !== "/") {
199480
- pattern = pattern.split(path6.sep).join("/");
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 (path6.sep !== "/") {
199858
- f = f.split(path6.sep).join("/");
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(path6, stats) {
199969
+ return function(path7, stats) {
199970
199970
  var minimatcher = new minimatch.Minimatch(pattern, { matchBase: true });
199971
- return (!minimatcher.negate || stats.isFile()) && minimatcher.match(path6);
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(path6, ignores, callback) {
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(path6, ignores || [], function(err, data) {
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(path6, function(err, files) {
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(path6, file);
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), path11 = _wsComponents$resourc2[0], query = _wsComponents$resourc2[1];
200855
- wsComponents.path = path11 && path11 !== "/" ? path11 : undefined;
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 path11 = jsonPointers ? "'/' + " + expr + (isNumber2 ? "" : ".replace(/~/g, '~0').replace(/\\//g, '~1')") : isNumber2 ? "'[' + " + expr + " + ']'" : "'[\\'' + " + expr + " + '\\']'";
201249
- return joinPaths(currentPath, path11);
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 path11 = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
201253
- return joinPaths(currentPath, path11);
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(path11) {
207308
- return /^\.|this\b/.test(path11.original);
207307
+ scopedId: function scopedId(path12) {
207308
+ return /^\.|this\b/.test(path12.original);
207309
207309
  },
207310
- simpleId: function simpleId(path11) {
207311
- return path11.parts.length === 1 && !AST.helpers.scopedId(path11) && !path11.depth;
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(path11, params, hash, open2, strip, locInfo) {
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: path11,
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), path11 = decorator.path;
208640
+ var params = this.setupFullMustacheParams(decorator, program, undefined), path12 = decorator.path;
208641
208641
  this.useDecorators = true;
208642
- this.opcode("registerDecorator", params.length, path11.original);
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 path11 = sexpr.path, name = path11.parts[0], isBlock = program != null || inverse != null;
208706
- this.opcode("getContext", path11.depth);
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
- path11.strict = true;
208710
- this.accept(path11);
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 path11 = sexpr.path;
208715
- path11.strict = true;
208716
- this.accept(path11);
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), path11 = sexpr.path, name = path11.parts[0];
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
- path11.strict = true;
208727
- path11.falsy = true;
208728
- this.accept(path11);
208729
- this.opcode("invokeHelper", params.length, path11.original, _ast2["default"].helpers.simpleId(path11));
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(path11) {
208733
- this.addDepth(path11.depth);
208734
- this.opcode("getContext", path11.depth);
208735
- var name = path11.parts[0], scoped = _ast2["default"].helpers.scopedId(path11), blockParamId = !path11.depth && !scoped && this.blockParamIndex(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, path11.parts);
208737
+ this.opcode("lookupBlockParam", blockParamId, path12.parts);
208738
208738
  } else if (!name) {
208739
208739
  this.opcode("pushContext");
208740
- } else if (path11.data) {
208740
+ } else if (path12.data) {
208741
208741
  this.options.data = true;
208742
- this.opcode("lookupData", path11.depth, path11.parts, path11.strict);
208742
+ this.opcode("lookupData", path12.depth, path12.parts, path12.strict);
208743
208743
  } else {
208744
- this.opcode("lookupOnContext", path11.parts, path11.falsy, path11.strict, scoped);
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 path11 = aPath;
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
- path11 = url2.path;
209094
+ path12 = url2.path;
209095
209095
  }
209096
- var isAbsolute3 = exports.isAbsolute(path11);
209097
- var parts = path11.split(/\/+/);
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
- path11 = parts.join("/");
209115
- if (path11 === "") {
209116
- path11 = isAbsolute3 ? "/" : ".";
209114
+ path12 = parts.join("/");
209115
+ if (path12 === "") {
209116
+ path12 = isAbsolute3 ? "/" : ".";
209117
209117
  }
209118
209118
  if (url2) {
209119
- url2.path = path11;
209119
+ url2.path = path12;
209120
209120
  return urlGenerate(url2);
209121
209121
  }
209122
- return path11;
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 path11 = id.parts.join("/");
211680
- return (id.data ? "@" : "") + "PATH:" + path11;
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.metadata({
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) {
@@ -216941,13 +216939,13 @@ class StreamableHTTPServerTransport {
216941
216939
  }
216942
216940
 
216943
216941
  // src/server.ts
216944
- var import_body_parser = __toESM(require_body_parser(), 1);
216942
+ var import_body_parser2 = __toESM(require_body_parser(), 1);
216945
216943
  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 path11 from "path";
216950
- import { fileURLToPath as fileURLToPath2 } from "url";
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("DuckDB connection must have at least one attached database");
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("DuckDB connection name cannot be 'duckdb'; it is reserved for Publisher package sandboxes.");
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("DuckDB connection name cannot be 'duckdb'; it is reserved for Publisher package sandboxes.");
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 path10 from "path";
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 path9 from "path";
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 publisherConfigPath = path4.join(serverRoot, PUBLISHER_CONFIG_NAME);
229038
- if (!fs2.existsSync(publisherConfigPath)) {
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");
@@ -229058,6 +229147,10 @@ var getPublisherConfig = (serverRoot) => {
229058
229147
  };
229059
229148
  }
229060
229149
  const processedConfig = processConfigValue(rawConfig);
229150
+ if (processedConfig && typeof processedConfig === "object" && !("environments" in processedConfig) && "projects" in processedConfig) {
229151
+ logger.warn(`${PUBLISHER_CONFIG_NAME} uses deprecated "projects" key; rename to "environments".`);
229152
+ processedConfig.environments = processedConfig.projects;
229153
+ }
229061
229154
  if (processedConfig && typeof processedConfig === "object" && "environments" in processedConfig && processedConfig.environments && typeof processedConfig.environments === "object" && !Array.isArray(processedConfig.environments)) {
229062
229155
  logger.error(`Invalid ${PUBLISHER_CONFIG_NAME}: the "environments" field must be a JSON array. Using default empty config.`);
229063
229156
  return {
@@ -229181,6 +229274,15 @@ function markReady() {
229181
229274
  logger.error("Service is already draining - cannot mark as ready");
229182
229275
  }
229183
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
+ }
229184
229286
  function markNotReady() {
229185
229287
  ready = false;
229186
229288
  logger.info("Service marked as not ready - readiness probe will fail");
@@ -230075,6 +230177,7 @@ async function initializeSchema(db, force = false) {
230075
230177
  logger.info("Reinitializing database schema dropping and recreating all tables");
230076
230178
  await dropAllTables(db);
230077
230179
  } else {
230180
+ await dropLegacyProjectSchema(db);
230078
230181
  logger.info("Creating database schema for the first time...");
230079
230182
  }
230080
230183
  await db.run(`
@@ -230152,6 +230255,27 @@ async function initializeSchema(db, force = false) {
230152
230255
  await db.run("CREATE UNIQUE INDEX IF NOT EXISTS idx_materializations_active_key ON materializations(active_key)");
230153
230256
  await db.run("CREATE INDEX IF NOT EXISTS idx_build_manifests_environment_package ON build_manifests(environment_id, package_name)");
230154
230257
  }
230258
+ var LEGACY_TABLES_DROP_ORDER = [
230259
+ "build_manifests",
230260
+ "materializations",
230261
+ "packages",
230262
+ "connections",
230263
+ "projects"
230264
+ ];
230265
+ async function dropLegacyProjectSchema(db) {
230266
+ const legacy = await db.all("SELECT name FROM sqlite_master WHERE type='table' AND name='projects'");
230267
+ if (!legacy || legacy.length === 0) {
230268
+ return;
230269
+ }
230270
+ logger.warn("Detected legacy 'projects' schema. Dropping legacy tables; existing environments/packages/connections/materializations data will be lost. Re-create them via the API after upgrade.");
230271
+ for (const table of LEGACY_TABLES_DROP_ORDER) {
230272
+ try {
230273
+ await db.run(`DROP TABLE IF EXISTS ${table}`);
230274
+ } catch (err) {
230275
+ logger.warn(`Failed to drop legacy table ${table}:`, err);
230276
+ }
230277
+ }
230278
+ }
230155
230279
  async function dropAllTables(db) {
230156
230280
  const tables = [
230157
230281
  "build_manifests",
@@ -230267,6 +230391,7 @@ class StorageManager {
230267
230391
  defaultManifestStore = null;
230268
230392
  environmentManifestStores = new Map;
230269
230393
  attachedCatalogs = new Map;
230394
+ duckLakeAttachMutex = new Mutex;
230270
230395
  config;
230271
230396
  constructor(config) {
230272
230397
  this.config = config;
@@ -230304,12 +230429,15 @@ class StorageManager {
230304
230429
  throw new Error("Storage not initialized. Call initialize() first.");
230305
230430
  }
230306
230431
  const key = configKey(config);
230307
- let catalogName = this.attachedCatalogs.get(key);
230308
- if (!catalogName) {
230309
- catalogName = catalogNameForConfig(config);
230310
- await this.attachDuckLakeCatalog(config, catalogName);
230311
- this.attachedCatalogs.set(key, catalogName);
230312
- }
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
+ });
230313
230441
  const store = new DuckLakeManifestStore(this.duckDbConnection, catalogName, environmentName);
230314
230442
  await store.bootstrapSchema();
230315
230443
  this.environmentManifestStores.set(environmentId, store);
@@ -230326,9 +230454,13 @@ class StorageManager {
230326
230454
  if (isPostgres) {
230327
230455
  await connection.run("INSTALL postgres; LOAD postgres;");
230328
230456
  }
230329
- const escapedCatalogUrl = escapeSQL2(config.catalogUrl);
230457
+ const catalogUrl = isPostgres ? withPgConnectTimeout(config.catalogUrl, pgConnectTimeoutSeconds()) : config.catalogUrl;
230458
+ const escapedCatalogUrl = escapeSQL2(catalogUrl);
230330
230459
  const escapedDataPath = escapeSQL2(config.dataPath);
230331
230460
  const isCloudStorage = config.dataPath.startsWith("gs://") || config.dataPath.startsWith("s3://");
230461
+ if (isCloudStorage) {
230462
+ await connection.run("INSTALL httpfs; LOAD httpfs;");
230463
+ }
230332
230464
  let attachCmd = `ATTACH 'ducklake:${escapedCatalogUrl}' AS ${catalogName}`;
230333
230465
  const attachOpts = [
230334
230466
  `DATA_PATH '${escapedDataPath}'`,
@@ -230338,8 +230470,22 @@ class StorageManager {
230338
230470
  attachOpts.push("OVERRIDE_DATA_PATH true");
230339
230471
  }
230340
230472
  attachCmd += ` (${attachOpts.join(", ")});`;
230341
- logger.info(`Attaching DuckLake manifest catalog: ${attachCmd}`);
230342
- await connection.run(attachCmd);
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
+ }
230343
230489
  }
230344
230490
  getRepository() {
230345
230491
  if (!this.repository) {
@@ -230378,26 +230524,30 @@ class StorageManager {
230378
230524
  // src/service/environment.ts
230379
230525
  import { MalloyError as MalloyError3, Runtime as Runtime2 } from "@malloydata/malloy";
230380
230526
  import * as fs6 from "fs";
230381
- import * as path8 from "path";
230527
+ import * as path9 from "path";
230382
230528
 
230383
230529
  // src/utils.ts
230384
230530
  import * as fs3 from "fs";
230385
- import { fileURLToPath } from "url";
230531
+ import * as path6 from "path";
230532
+ import { fileURLToPath as fileURLToPath2 } from "url";
230386
230533
  var URL_READER = {
230387
230534
  readURL: (url2) => {
230388
- let path6 = url2.toString();
230535
+ let path7 = url2.toString();
230389
230536
  if (url2.protocol == "file:") {
230390
- path6 = fileURLToPath(url2);
230537
+ path7 = fileURLToPath2(url2);
230391
230538
  }
230392
- return fs3.promises.readFile(path6, "utf8");
230539
+ return fs3.promises.readFile(path7, "utf8");
230393
230540
  }
230394
230541
  };
230542
+ function ignoreDotfiles(file) {
230543
+ return path6.basename(file).startsWith(".");
230544
+ }
230395
230545
 
230396
230546
  // src/service/package.ts
230397
230547
  var import_api3 = __toESM(require_src(), 1);
230398
230548
  var import_recursive_readdir = __toESM(require_recursive_readdir(), 1);
230399
230549
  import * as fs5 from "fs/promises";
230400
- import * as path7 from "path";
230550
+ import * as path8 from "path";
230401
230551
  import { DuckDBConnection as DuckDBConnection3 } from "@malloydata/db-duckdb";
230402
230552
  import"@malloydata/db-duckdb/native";
230403
230553
  import {
@@ -230425,7 +230575,7 @@ import {
230425
230575
  } from "@malloydata/malloy-sql";
230426
230576
  import * as fs4 from "fs/promises";
230427
230577
  import { createRequire as createRequire2 } from "module";
230428
- import * as path6 from "path";
230578
+ import * as path7 from "path";
230429
230579
 
230430
230580
  // src/data_styles.ts
230431
230581
  function compileDataStyles(styles) {
@@ -231036,7 +231186,7 @@ run: ${sourceName ? sourceName + "->" : ""}${queryName}`;
231036
231186
  };
231037
231187
  }
231038
231188
  static async getModelRuntime(packagePath, modelPath, malloyConfig, options) {
231039
- const fullModelPath = path6.join(packagePath, modelPath);
231189
+ const fullModelPath = path7.join(packagePath, modelPath);
231040
231190
  try {
231041
231191
  if (!(await fs4.stat(fullModelPath)).isFile()) {
231042
231192
  throw new ModelNotFoundError(`${modelPath} is not a file.`);
@@ -231249,7 +231399,7 @@ run: ${sourceName ? sourceName + "->" : ""}${queryName}`;
231249
231399
  return this.modelType;
231250
231400
  }
231251
231401
  async getFileText(packagePath) {
231252
- const fullPath = path6.join(packagePath, this.modelPath);
231402
+ const fullPath = path7.join(packagePath, this.modelPath);
231253
231403
  try {
231254
231404
  return await fs4.readFile(fullPath, "utf8");
231255
231405
  } catch {
@@ -231484,17 +231634,17 @@ class Package {
231484
231634
  static async getModelPaths(packagePath) {
231485
231635
  let files = undefined;
231486
231636
  try {
231487
- files = await import_recursive_readdir.default(packagePath);
231637
+ files = await import_recursive_readdir.default(packagePath, [ignoreDotfiles]);
231488
231638
  } catch (error) {
231489
231639
  logger.error(error);
231490
231640
  throw new PackageNotFoundError(`Package config for ${packagePath} does not exist.`);
231491
231641
  }
231492
231642
  return files.map((fullPath) => {
231493
- return path7.relative(packagePath, fullPath).replace(/\\/g, "/");
231643
+ return path8.relative(packagePath, fullPath).replace(/\\/g, "/");
231494
231644
  }).filter((modelPath) => modelPath.endsWith(MODEL_FILE_SUFFIX) || modelPath.endsWith(NOTEBOOK_FILE_SUFFIX));
231495
231645
  }
231496
231646
  static async validatePackageManifestExistsOrThrowError(packagePath) {
231497
- const packageConfigPath = path7.join(packagePath, PACKAGE_MANIFEST_NAME);
231647
+ const packageConfigPath = path8.join(packagePath, PACKAGE_MANIFEST_NAME);
231498
231648
  try {
231499
231649
  await fs5.stat(packageConfigPath);
231500
231650
  } catch {
@@ -231503,7 +231653,7 @@ class Package {
231503
231653
  }
231504
231654
  }
231505
231655
  static async readPackageConfig(packagePath) {
231506
- const packageConfigPath = path7.join(packagePath, PACKAGE_MANIFEST_NAME);
231656
+ const packageConfigPath = path8.join(packagePath, PACKAGE_MANIFEST_NAME);
231507
231657
  const packageConfigContents = await fs5.readFile(packageConfigPath);
231508
231658
  const packageManifest = JSON.parse(packageConfigContents.toString());
231509
231659
  return {
@@ -231522,14 +231672,13 @@ class Package {
231522
231672
  }));
231523
231673
  }
231524
231674
  static async getDatabasePaths(packagePath) {
231525
- let files = undefined;
231526
- files = await import_recursive_readdir.default(packagePath);
231675
+ const files = await import_recursive_readdir.default(packagePath, [ignoreDotfiles]);
231527
231676
  return files.map((fullPath) => {
231528
- return path7.relative(packagePath, fullPath).replace(/\\/g, "/");
231677
+ return path8.relative(packagePath, fullPath).replace(/\\/g, "/");
231529
231678
  }).filter((modelPath) => modelPath.endsWith(".parquet") || modelPath.endsWith(".csv"));
231530
231679
  }
231531
231680
  static async getDatabaseInfo(packagePath, databasePath) {
231532
- const fullPath = path7.join(packagePath, databasePath);
231681
+ const fullPath = path8.join(packagePath, databasePath);
231533
231682
  const runtime = new ConnectionRuntime({
231534
231683
  urlReader: new EmptyURLReader,
231535
231684
  connections: [new DuckDBConnection3("duckdb")]
@@ -231586,7 +231735,7 @@ class Environment {
231586
231735
  async writeEnvironmentReadme(readme) {
231587
231736
  if (readme === undefined)
231588
231737
  return;
231589
- const readmePath = path8.join(this.environmentPath, "README.md");
231738
+ const readmePath = path9.join(this.environmentPath, "README.md");
231590
231739
  try {
231591
231740
  await fs6.promises.writeFile(readmePath, readme, "utf-8");
231592
231741
  logger.info(`Updated README.md for environment ${this.environmentName}`);
@@ -231633,7 +231782,7 @@ class Environment {
231633
231782
  async reloadEnvironmentMetadata() {
231634
231783
  let readme = "";
231635
231784
  try {
231636
- readme = (await fs6.promises.readFile(path8.join(this.environmentPath, README_NAME))).toString();
231785
+ readme = (await fs6.promises.readFile(path9.join(this.environmentPath, README_NAME))).toString();
231637
231786
  } catch {}
231638
231787
  this.metadata = {
231639
231788
  ...this.metadata,
@@ -231644,10 +231793,10 @@ class Environment {
231644
231793
  return this.metadata;
231645
231794
  }
231646
231795
  async compileSource(packageName, modelName, source, includeSql = false) {
231647
- const modelDir = path8.dirname(path8.join(this.environmentPath, packageName, modelName));
231648
- const virtualUri = `file://${path8.join(modelDir, "__compile_check.malloy")}`;
231796
+ const modelDir = path9.dirname(path9.join(this.environmentPath, packageName, modelName));
231797
+ const virtualUri = `file://${path9.join(modelDir, "__compile_check.malloy")}`;
231649
231798
  const virtualUrl = new URL(virtualUri);
231650
- const modelPath = path8.join(this.environmentPath, packageName, modelName);
231799
+ const modelPath = path9.join(this.environmentPath, packageName, modelName);
231651
231800
  let modelContent = "";
231652
231801
  try {
231653
231802
  modelContent = await fs6.promises.readFile(modelPath, "utf8");
@@ -231759,24 +231908,30 @@ ${source}` : source;
231759
231908
  throw error;
231760
231909
  }
231761
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
+ }
231762
231919
  async getPackage(packageName, reload = false) {
231763
231920
  const _package = this.packages.get(packageName);
231764
231921
  if (_package !== undefined && !reload) {
231765
231922
  return _package;
231766
231923
  }
231767
- let packageMutex = this.packageMutexes.get(packageName);
231768
- if (packageMutex?.isLocked()) {
231924
+ const packageMutex = this.getOrCreatePackageMutex(packageName);
231925
+ if (packageMutex.isLocked()) {
231769
231926
  logger.debug(`Package ${packageName} is being loaded, waiting for unlock...`);
231770
231927
  await packageMutex.waitForUnlock();
231771
231928
  logger.debug(`Package ${packageName} unlocked`);
231772
231929
  const existingPackage = this.packages.get(packageName);
231773
- if (existingPackage) {
231930
+ if (existingPackage !== undefined && !reload) {
231774
231931
  logger.debug(`Package ${packageName} loaded by another request`);
231775
231932
  return existingPackage;
231776
231933
  }
231777
231934
  }
231778
- packageMutex = new Mutex;
231779
- this.packageMutexes.set(packageName, packageMutex);
231780
231935
  return packageMutex.runExclusive(async () => {
231781
231936
  const existingPackage = this.packages.get(packageName);
231782
231937
  if (existingPackage !== undefined && !reload) {
@@ -231785,7 +231940,7 @@ ${source}` : source;
231785
231940
  this.setPackageStatus(packageName, "loading" /* LOADING */);
231786
231941
  try {
231787
231942
  logger.debug(`Loading package ${packageName}...`);
231788
- const packagePath = path8.join(this.environmentPath, packageName);
231943
+ const packagePath = path9.join(this.environmentPath, packageName);
231789
231944
  const _package2 = await Package.create(this.environmentName, packageName, packagePath, () => this.malloyConfig.malloyConfig);
231790
231945
  if (existingPackage !== undefined && reload) {
231791
231946
  this.retireConnectionGeneration(`package ${packageName}`, () => existingPackage.getMalloyConfig().releaseConnections());
@@ -231803,7 +231958,7 @@ ${source}` : source;
231803
231958
  });
231804
231959
  }
231805
231960
  async addPackage(packageName) {
231806
- const packagePath = path8.join(this.environmentPath, packageName);
231961
+ const packagePath = path9.join(this.environmentPath, packageName);
231807
231962
  if (!await fs6.promises.access(packagePath).then(() => true).catch(() => false) || !(await fs6.promises.stat(packagePath))?.isDirectory()) {
231808
231963
  throw new PackageNotFoundError(`Package ${packageName} not found`);
231809
231964
  }
@@ -231811,20 +231966,35 @@ ${source}` : source;
231811
231966
  packagePath,
231812
231967
  malloyConfig: this.malloyConfig.malloyConfig
231813
231968
  });
231814
- this.setPackageStatus(packageName, "loading" /* LOADING */);
231815
- try {
231816
- this.packages.set(packageName, await Package.create(this.environmentName, packageName, packagePath, () => this.malloyConfig.malloyConfig));
231817
- } catch (error) {
231818
- logger.error("Error adding package", { error });
231819
- this.deletePackageStatus(packageName);
231820
- throw error;
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
+ }
231821
231977
  }
231822
- this.setPackageStatus(packageName, "serving" /* SERVING */);
231823
- return this.packages.get(packageName);
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
+ });
231824
231994
  }
231825
231995
  async writePackageManifest(packageName, metadata) {
231826
- const packagePath = path8.join(this.environmentPath, packageName);
231827
- const manifestPath = path8.join(packagePath, "publisher.json");
231996
+ const packagePath = path9.join(this.environmentPath, packageName);
231997
+ const manifestPath = path9.join(packagePath, "publisher.json");
231828
231998
  try {
231829
231999
  let existingManifest = {};
231830
232000
  try {
@@ -231896,7 +232066,7 @@ ${source}` : source;
231896
232066
  }
231897
232067
  await _package.getMalloyConfig().releaseConnections();
231898
232068
  try {
231899
- await fs6.promises.rm(path8.join(this.environmentPath, packageName), {
232069
+ await fs6.promises.rm(path9.join(this.environmentPath, packageName), {
231900
232070
  recursive: true,
231901
232071
  force: true
231902
232072
  });
@@ -231960,7 +232130,7 @@ ${source}` : source;
231960
232130
  };
231961
232131
  }
231962
232132
  async deleteDuckDBConnection(connectionName) {
231963
- const duckdbPath = path8.join(this.environmentPath, `${connectionName}.duckdb`);
232133
+ const duckdbPath = path9.join(this.environmentPath, `${connectionName}.duckdb`);
231964
232134
  try {
231965
232135
  await fs6.promises.rm(duckdbPath, { force: true });
231966
232136
  logger.info(`Removed DuckDB connection file ${connectionName} from environment ${this.environmentName}`);
@@ -232029,6 +232199,7 @@ class EnvironmentStore {
232029
232199
  publisherConfigIsFrozen;
232030
232200
  finishedInitialization;
232031
232201
  isInitialized = false;
232202
+ failedEnvironments = [];
232032
232203
  storageManager;
232033
232204
  s3Client = new import_client_s32.S3({
232034
232205
  followRegionRedirects: true
@@ -232040,7 +232211,7 @@ class EnvironmentStore {
232040
232211
  const storageConfig = {
232041
232212
  type: "duckdb",
232042
232213
  duckdb: {
232043
- path: path9.join(serverRootPath, "publisher.db")
232214
+ path: path10.join(serverRootPath, "publisher.db")
232044
232215
  }
232045
232216
  };
232046
232217
  this.storageManager = new StorageManager(storageConfig);
@@ -232061,6 +232232,10 @@ class EnvironmentStore {
232061
232232
  logEnvironmentInitializationError(environmentName, error) {
232062
232233
  const label = environmentName ? ` "${environmentName}"` : "";
232063
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
+ });
232064
232239
  }
232065
232240
  async initialize() {
232066
232241
  const reInit = process.env.INITIALIZE_STORAGE === "true";
@@ -232123,7 +232298,11 @@ class EnvironmentStore {
232123
232298
  }
232124
232299
  }
232125
232300
  this.isInitialized = true;
232126
- markReady();
232301
+ if (this.failedEnvironments.length > 0) {
232302
+ markDegraded();
232303
+ } else {
232304
+ markReady();
232305
+ }
232127
232306
  const initializationDuration = performance.now() - initialTime;
232128
232307
  logger.info(`Environment store successfully initialized in ${formatDuration(initializationDuration)}`);
232129
232308
  } catch (error) {
@@ -232327,7 +232506,7 @@ class EnvironmentStore {
232327
232506
  const reInit = process.env.INITIALIZE_STORAGE === "true";
232328
232507
  await fs7.promises.mkdir(this.serverRootPath, { recursive: true });
232329
232508
  if (reInit) {
232330
- const uploadDocsPath2 = path9.join(this.serverRootPath, PUBLISHER_DATA_DIR);
232509
+ const uploadDocsPath2 = path10.join(this.serverRootPath, PUBLISHER_DATA_DIR);
232331
232510
  logger.info(`Reinitialization mode: Cleaning up upload documents path ${uploadDocsPath2}`);
232332
232511
  try {
232333
232512
  await fs7.promises.rm(uploadDocsPath2, {
@@ -232344,7 +232523,7 @@ class EnvironmentStore {
232344
232523
  } else {
232345
232524
  logger.info(`Using existing publisher path`);
232346
232525
  }
232347
- const uploadDocsPath = path9.join(this.serverRootPath, PUBLISHER_DATA_DIR);
232526
+ const uploadDocsPath = path10.join(this.serverRootPath, PUBLISHER_DATA_DIR);
232348
232527
  await fs7.promises.mkdir(uploadDocsPath, { recursive: true });
232349
232528
  }
232350
232529
  async listEnvironments(skipInitializationCheck = false) {
@@ -232359,7 +232538,12 @@ class EnvironmentStore {
232359
232538
  environments: [],
232360
232539
  initialized: this.isInitialized,
232361
232540
  frozenConfig: isPublisherConfigFrozen(this.serverRootPath),
232362
- operationalState: getOperationalState()
232541
+ operationalState: getOperationalState(),
232542
+ ...this.failedEnvironments.length > 0 && {
232543
+ failedEnvironments: [
232544
+ ...this.failedEnvironments
232545
+ ]
232546
+ }
232363
232547
  };
232364
232548
  const environments = await this.listEnvironments(true);
232365
232549
  await Promise.all(environments.map(async (environment) => {
@@ -232576,12 +232760,12 @@ class EnvironmentStore {
232576
232760
  const absoluteEnvironmentPath = `${this.serverRootPath}/${PUBLISHER_DATA_DIR}/${environmentName}`;
232577
232761
  await fs7.promises.mkdir(absoluteEnvironmentPath, { recursive: true });
232578
232762
  if (environment.readme) {
232579
- await fs7.promises.writeFile(path9.join(absoluteEnvironmentPath, "README.md"), environment.readme);
232763
+ await fs7.promises.writeFile(path10.join(absoluteEnvironmentPath, "README.md"), environment.readme);
232580
232764
  }
232581
232765
  return absoluteEnvironmentPath;
232582
232766
  }
232583
232767
  isLocalPath(location) {
232584
- return location.startsWith("./") || location.startsWith("../") || location.startsWith("~/") || location.startsWith("/") || path9.isAbsolute(location);
232768
+ return location.startsWith("./") || location.startsWith("../") || location.startsWith("~/") || location.startsWith("/") || path10.isAbsolute(location);
232585
232769
  }
232586
232770
  isGitHubURL(location) {
232587
232771
  return location.startsWith("https://github.com/") || location.startsWith("git@github.com:");
@@ -232637,7 +232821,7 @@ class EnvironmentStore {
232637
232821
  if (githubInfo && githubInfo.packagePath) {
232638
232822
  const subPathMatch = _package.location.match(/\/tree\/[^/]+\/(.+)$/);
232639
232823
  if (subPathMatch) {
232640
- sourcePath = path9.join(tempDownloadPath, subPathMatch[1]);
232824
+ sourcePath = path10.join(tempDownloadPath, subPathMatch[1]);
232641
232825
  } else {
232642
232826
  sourcePath = tempDownloadPath;
232643
232827
  }
@@ -232648,7 +232832,7 @@ class EnvironmentStore {
232648
232832
  if (this.isLocalPath(_package.location)) {
232649
232833
  sourcePath = _package.location;
232650
232834
  } else {
232651
- sourcePath = path9.join(tempDownloadPath, groupedLocation);
232835
+ sourcePath = path10.join(tempDownloadPath, groupedLocation);
232652
232836
  }
232653
232837
  }
232654
232838
  const sourceExists = await fs7.promises.access(sourcePath).then(() => true).catch(() => false);
@@ -232724,7 +232908,7 @@ class EnvironmentStore {
232724
232908
  }
232725
232909
  }
232726
232910
  if (this.isLocalPath(location)) {
232727
- const packagePath = path9.isAbsolute(location) ? location : path9.join(this.serverRootPath, location);
232911
+ const packagePath = path10.isAbsolute(location) ? location : path10.join(this.serverRootPath, location);
232728
232912
  try {
232729
232913
  logger.info(`Mounting local directory at "${packagePath}" to "${targetPath}"`);
232730
232914
  await this.mountLocalDirectory(packagePath, targetPath, environmentName, packageName);
@@ -232778,11 +232962,11 @@ class EnvironmentStore {
232778
232962
  }
232779
232963
  await Promise.all(files.map(async (file) => {
232780
232964
  const relativeFilePath = file.name.replace(prefix, "");
232781
- const absoluteFilePath = isCompressedFile ? absoluteDirPath : path9.join(absoluteDirPath, relativeFilePath);
232965
+ const absoluteFilePath = isCompressedFile ? absoluteDirPath : path10.join(absoluteDirPath, relativeFilePath);
232782
232966
  if (file.name.endsWith("/")) {
232783
232967
  return;
232784
232968
  }
232785
- await fs7.promises.mkdir(path9.dirname(absoluteFilePath), {
232969
+ await fs7.promises.mkdir(path10.dirname(absoluteFilePath), {
232786
232970
  recursive: true
232787
232971
  });
232788
232972
  return fs7.promises.writeFile(absoluteFilePath, await file.download());
@@ -232798,7 +232982,7 @@ class EnvironmentStore {
232798
232982
  const prefix = prefixParts.join("/");
232799
232983
  if (isCompressedFile) {
232800
232984
  const zipFilePath = `${absoluteDirPath}.zip`;
232801
- await fs7.promises.mkdir(path9.dirname(zipFilePath), {
232985
+ await fs7.promises.mkdir(path10.dirname(zipFilePath), {
232802
232986
  recursive: true
232803
232987
  });
232804
232988
  const command = new import_client_s32.GetObjectCommand({
@@ -232837,8 +233021,8 @@ class EnvironmentStore {
232837
233021
  if (!relativeFilePath || relativeFilePath.endsWith("/")) {
232838
233022
  return;
232839
233023
  }
232840
- const absoluteFilePath = path9.join(absoluteDirPath, relativeFilePath);
232841
- await fs7.promises.mkdir(path9.dirname(absoluteFilePath), {
233024
+ const absoluteFilePath = path10.join(absoluteDirPath, relativeFilePath);
233025
+ await fs7.promises.mkdir(path10.dirname(absoluteFilePath), {
232842
233026
  recursive: true
232843
233027
  });
232844
233028
  const command = new import_client_s32.GetObjectCommand({
@@ -232900,7 +233084,7 @@ class EnvironmentStore {
232900
233084
  logger.info(`Successfully cloned entire repository to: ${absoluteDirPath}`);
232901
233085
  return;
232902
233086
  }
232903
- const packageFullPath = path9.join(absoluteDirPath, cleanPackagePath);
233087
+ const packageFullPath = path10.join(absoluteDirPath, cleanPackagePath);
232904
233088
  const packageExists = await fs7.promises.access(packageFullPath).then(() => true).catch(() => false);
232905
233089
  if (!packageExists) {
232906
233090
  throw new Error(`Package path "${cleanPackagePath}" does not exist in the cloned repository.`);
@@ -232908,7 +233092,7 @@ class EnvironmentStore {
232908
233092
  const dirContents = await fs7.promises.readdir(absoluteDirPath);
232909
233093
  for (const entry of dirContents) {
232910
233094
  if (entry !== cleanPackagePath.replace(/^\/+/, "").split("/")[0]) {
232911
- await fs7.promises.rm(path9.join(absoluteDirPath, entry), {
233095
+ await fs7.promises.rm(path10.join(absoluteDirPath, entry), {
232912
233096
  recursive: true,
232913
233097
  force: true
232914
233098
  });
@@ -232916,7 +233100,7 @@ class EnvironmentStore {
232916
233100
  }
232917
233101
  const packageContents = await fs7.promises.readdir(packageFullPath);
232918
233102
  for (const entry of packageContents) {
232919
- await fs7.promises.rename(path9.join(packageFullPath, entry), path9.join(absoluteDirPath, entry));
233103
+ await fs7.promises.rename(path10.join(packageFullPath, entry), path10.join(absoluteDirPath, entry));
232920
233104
  }
232921
233105
  await fs7.promises.rm(packageFullPath, { recursive: true, force: true });
232922
233106
  }
@@ -232964,9 +233148,9 @@ class WatchModeController {
232964
233148
  });
232965
233149
  return;
232966
233150
  }
232967
- this.watchingPath = path10.join(this.environmentStore.serverRootPath, watchName);
233151
+ this.watchingPath = path11.join(this.environmentStore.serverRootPath, watchName);
232968
233152
  this.watcher = esm_default.watch(this.watchingPath, {
232969
- ignored: (path11, stats) => !!stats?.isFile() && !path11.endsWith(".malloy") && !path11.endsWith(".md"),
233153
+ ignored: (path12, stats) => !!stats?.isFile() && !path12.endsWith(".malloy") && !path12.endsWith(".md"),
232970
233154
  ignoreInitial: true
232971
233155
  });
232972
233156
  const reloadEnvironment = async () => {
@@ -232974,16 +233158,16 @@ class WatchModeController {
232974
233158
  await this.environmentStore.addEnvironment(environment2.metadata);
232975
233159
  logger.info(`Reloaded environment ${watchName}`);
232976
233160
  };
232977
- this.watcher.on("add", async (path11) => {
232978
- logger.info(`Detected new file ${path11}, reloading environment ${watchName}`);
233161
+ this.watcher.on("add", async (path12) => {
233162
+ logger.info(`Detected new file ${path12}, reloading environment ${watchName}`);
232979
233163
  await reloadEnvironment();
232980
233164
  });
232981
- this.watcher.on("unlink", async (path11) => {
232982
- logger.info(`Detected deletion of ${path11}, reloading environment ${watchName}`);
233165
+ this.watcher.on("unlink", async (path12) => {
233166
+ logger.info(`Detected deletion of ${path12}, reloading environment ${watchName}`);
232983
233167
  await reloadEnvironment();
232984
233168
  });
232985
- this.watcher.on("change", async (path11) => {
232986
- logger.info(`Detected change on ${path11}, reloading environment ${watchName}`);
233169
+ this.watcher.on("change", async (path12) => {
233170
+ logger.info(`Detected change on ${path12}, reloading environment ${watchName}`);
232987
233171
  await reloadEnvironment();
232988
233172
  });
232989
233173
  res.json();
@@ -236074,25 +236258,25 @@ async function getModelForQuery(environmentStore, environmentName, packageName,
236074
236258
  }
236075
236259
  }
236076
236260
  function buildMalloyUri(components, fragment) {
236077
- let path11 = "/environment/";
236261
+ let path12 = "/environment/";
236078
236262
  if (components.environment) {
236079
- path11 += encodeURIComponent(components.environment);
236263
+ path12 += encodeURIComponent(components.environment);
236080
236264
  } else {
236081
- path11 += "home";
236265
+ path12 += "home";
236082
236266
  }
236083
236267
  if (components.package) {
236084
- path11 += "/package/" + encodeURIComponent(components.package);
236268
+ path12 += "/package/" + encodeURIComponent(components.package);
236085
236269
  }
236086
236270
  if (components.resourceType) {
236087
- path11 += "/" + components.resourceType;
236271
+ path12 += "/" + components.resourceType;
236088
236272
  if (components.resourceName) {
236089
- path11 += "/" + encodeURIComponent(components.resourceName);
236273
+ path12 += "/" + encodeURIComponent(components.resourceName);
236090
236274
  if (components.subResourceType && components.subResourceName) {
236091
- path11 += "/" + components.subResourceType + "/" + encodeURIComponent(components.subResourceName);
236275
+ path12 += "/" + components.subResourceType + "/" + encodeURIComponent(components.subResourceName);
236092
236276
  }
236093
236277
  }
236094
236278
  }
236095
- let uriString = "malloy:/" + path11;
236279
+ let uriString = "malloy:/" + path12;
236096
236280
  if (fragment) {
236097
236281
  uriString += "#" + fragment;
236098
236282
  }
@@ -236986,6 +237170,581 @@ function initializeMcpServer(environmentStore) {
236986
237170
  return mcpServer;
236987
237171
  }
236988
237172
 
237173
+ // src/server-old.ts
237174
+ var import_body_parser = __toESM(require_body_parser(), 1);
237175
+ var LEGACY_API_PREFIX = "/api/v0";
237176
+ function remapMaterializationResponse(mat) {
237177
+ if (!mat || typeof mat !== "object")
237178
+ return mat;
237179
+ if (Array.isArray(mat)) {
237180
+ return mat.map(remapMaterializationResponse);
237181
+ }
237182
+ const out = { ...mat };
237183
+ if ("environmentId" in out) {
237184
+ out.projectId = out.environmentId;
237185
+ delete out.environmentId;
237186
+ }
237187
+ return out;
237188
+ }
237189
+ var setVersionIdError = (res) => {
237190
+ const { json, status } = internalErrorToHttpError(new NotImplementedError("Version IDs not implemented."));
237191
+ res.status(status).json(json);
237192
+ };
237193
+ function registerLegacyRoutes(app, controllers) {
237194
+ const {
237195
+ environmentStore,
237196
+ connectionController,
237197
+ modelController,
237198
+ packageController,
237199
+ databaseController,
237200
+ queryController,
237201
+ compileController,
237202
+ materializationController,
237203
+ manifestController
237204
+ } = controllers;
237205
+ app.get(`${LEGACY_API_PREFIX}/projects`, async (_req, res) => {
237206
+ try {
237207
+ res.status(200).json(await environmentStore.listEnvironments());
237208
+ } catch (error) {
237209
+ logger.error(error);
237210
+ const { json, status } = internalErrorToHttpError(error);
237211
+ res.status(status).json(json);
237212
+ }
237213
+ });
237214
+ app.post(`${LEGACY_API_PREFIX}/projects`, async (req, res) => {
237215
+ try {
237216
+ logger.info("Adding project", { body: req.body });
237217
+ const environment = await environmentStore.addEnvironment(req.body);
237218
+ res.status(200).json(await environment.serialize());
237219
+ } catch (error) {
237220
+ logger.error(error);
237221
+ const { json, status } = internalErrorToHttpError(error);
237222
+ res.status(status).json(json);
237223
+ }
237224
+ });
237225
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName`, async (req, res) => {
237226
+ try {
237227
+ const environment = await environmentStore.getEnvironment(req.params.projectName, req.query.reload === "true");
237228
+ res.status(200).json(await environment.serialize());
237229
+ } catch (error) {
237230
+ logger.error(error);
237231
+ const { json, status } = internalErrorToHttpError(error);
237232
+ res.status(status).json(json);
237233
+ }
237234
+ });
237235
+ app.patch(`${LEGACY_API_PREFIX}/projects/:projectName`, async (req, res) => {
237236
+ try {
237237
+ const environment = await environmentStore.updateEnvironment(req.body);
237238
+ res.status(200).json(await environment.serialize());
237239
+ } catch (error) {
237240
+ logger.error(error);
237241
+ const { json, status } = internalErrorToHttpError(error);
237242
+ res.status(status).json(json);
237243
+ }
237244
+ });
237245
+ app.delete(`${LEGACY_API_PREFIX}/projects/:projectName`, async (req, res) => {
237246
+ try {
237247
+ const environment = await environmentStore.deleteEnvironment(req.params.projectName);
237248
+ res.status(200).json(await environment?.serialize());
237249
+ } catch (error) {
237250
+ logger.error(error);
237251
+ const { json, status } = internalErrorToHttpError(error);
237252
+ res.status(status).json(json);
237253
+ }
237254
+ });
237255
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections`, async (req, res) => {
237256
+ try {
237257
+ res.status(200).json(await connectionController.listConnections(req.params.projectName));
237258
+ } catch (error) {
237259
+ logger.error(error);
237260
+ const { json, status } = internalErrorToHttpError(error);
237261
+ res.status(status).json(json);
237262
+ }
237263
+ });
237264
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName`, async (req, res) => {
237265
+ try {
237266
+ res.status(200).json(await connectionController.getConnection(req.params.projectName, req.params.connectionName));
237267
+ } catch (error) {
237268
+ logger.error(error);
237269
+ const { json, status } = internalErrorToHttpError(error);
237270
+ res.status(status).json(json);
237271
+ }
237272
+ });
237273
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName`, async (req, res) => {
237274
+ try {
237275
+ const result = await connectionController.addConnection(req.params.projectName, req.params.connectionName, req.body);
237276
+ res.status(201).json(result);
237277
+ } catch (error) {
237278
+ logger.error("Error creating connection", { error });
237279
+ const { json, status } = internalErrorToHttpError(error);
237280
+ res.status(status).json(json);
237281
+ }
237282
+ });
237283
+ app.patch(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName`, async (req, res) => {
237284
+ try {
237285
+ const result = await connectionController.updateConnection(req.params.projectName, req.params.connectionName, req.body);
237286
+ res.status(200).json(result);
237287
+ } catch (error) {
237288
+ logger.error("Error updating connection", { error });
237289
+ const { json, status } = internalErrorToHttpError(error);
237290
+ res.status(status).json(json);
237291
+ }
237292
+ });
237293
+ app.delete(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName`, async (req, res) => {
237294
+ try {
237295
+ const result = await connectionController.deleteConnection(req.params.projectName, req.params.connectionName);
237296
+ res.status(200).json(result);
237297
+ } catch (error) {
237298
+ logger.error("Error deleting connection", { error });
237299
+ const { json, status } = internalErrorToHttpError(error);
237300
+ res.status(status).json(json);
237301
+ }
237302
+ });
237303
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/schemas`, async (req, res) => {
237304
+ try {
237305
+ res.status(200).json(await connectionController.listSchemas(req.params.projectName, req.params.connectionName));
237306
+ } catch (error) {
237307
+ logger.error(error);
237308
+ const { json, status } = internalErrorToHttpError(error);
237309
+ res.status(status).json(json);
237310
+ }
237311
+ });
237312
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/schemas/:schemaName/tables`, async (req, res) => {
237313
+ try {
237314
+ const results = await connectionController.listTables(req.params.projectName, req.params.connectionName, req.params.schemaName, normalizeQueryArray(req.query.tableNames));
237315
+ res.status(200).json(results);
237316
+ } catch (error) {
237317
+ logger.error(error);
237318
+ const { json, status } = internalErrorToHttpError(error);
237319
+ res.status(status).json(json);
237320
+ }
237321
+ });
237322
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/schemas/:schemaName/tables/:tablePath`, async (req, res) => {
237323
+ try {
237324
+ const results = await connectionController.getTable(req.params.projectName, req.params.connectionName, req.params.schemaName, req.params.tablePath);
237325
+ res.status(200).json(results);
237326
+ } catch (error) {
237327
+ logger.error(error);
237328
+ const { json, status } = internalErrorToHttpError(error);
237329
+ res.status(status).json(json);
237330
+ }
237331
+ });
237332
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/schemas`, async (req, res) => {
237333
+ try {
237334
+ res.status(200).json(await connectionController.listSchemas(req.params.projectName, req.params.connectionName, req.params.packageName));
237335
+ } catch (error) {
237336
+ logger.error(error);
237337
+ const { json, status } = internalErrorToHttpError(error);
237338
+ res.status(status).json(json);
237339
+ }
237340
+ });
237341
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/schemas/:schemaName/tables`, async (req, res) => {
237342
+ try {
237343
+ res.status(200).json(await connectionController.listTables(req.params.projectName, req.params.connectionName, req.params.schemaName, normalizeQueryArray(req.query.tableNames), req.params.packageName));
237344
+ } catch (error) {
237345
+ logger.error(error);
237346
+ const { json, status } = internalErrorToHttpError(error);
237347
+ res.status(status).json(json);
237348
+ }
237349
+ });
237350
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/schemas/:schemaName/tables/:tablePath`, async (req, res) => {
237351
+ try {
237352
+ res.status(200).json(await connectionController.getTable(req.params.projectName, req.params.connectionName, req.params.schemaName, req.params.tablePath, req.params.packageName));
237353
+ } catch (error) {
237354
+ logger.error(error);
237355
+ const { json, status } = internalErrorToHttpError(error);
237356
+ res.status(status).json(json);
237357
+ }
237358
+ });
237359
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/sqlSource`, async (req, res) => {
237360
+ try {
237361
+ res.status(200).json(await connectionController.getConnectionSqlSource(req.params.projectName, req.params.connectionName, req.query.sqlStatement));
237362
+ } catch (error) {
237363
+ logger.error(error);
237364
+ const { json, status } = internalErrorToHttpError(error);
237365
+ res.status(status).json(json);
237366
+ }
237367
+ });
237368
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/sqlSource`, async (req, res) => {
237369
+ try {
237370
+ res.status(200).json(await connectionController.getConnectionSqlSource(req.params.projectName, req.params.connectionName, req.body.sqlStatement));
237371
+ } catch (error) {
237372
+ logger.error(error);
237373
+ const { json, status } = internalErrorToHttpError(error);
237374
+ res.status(status).json(json);
237375
+ }
237376
+ });
237377
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/sqlSource`, async (req, res) => {
237378
+ try {
237379
+ res.status(200).json(await connectionController.getConnectionSqlSource(req.params.projectName, req.params.connectionName, req.query.sqlStatement, req.params.packageName));
237380
+ } catch (error) {
237381
+ logger.error(error);
237382
+ const { json, status } = internalErrorToHttpError(error);
237383
+ res.status(status).json(json);
237384
+ }
237385
+ });
237386
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/sqlSource`, async (req, res) => {
237387
+ try {
237388
+ res.status(200).json(await connectionController.getConnectionSqlSource(req.params.projectName, req.params.connectionName, req.body.sqlStatement, req.params.packageName));
237389
+ } catch (error) {
237390
+ logger.error(error);
237391
+ const { json, status } = internalErrorToHttpError(error);
237392
+ res.status(status).json(json);
237393
+ }
237394
+ });
237395
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/queryData`, async (req, res) => {
237396
+ try {
237397
+ res.status(200).json(await connectionController.getConnectionQueryData(req.params.projectName, req.params.connectionName, req.query.sqlStatement, req.query.options));
237398
+ } catch (error) {
237399
+ logger.error(error);
237400
+ const { json, status } = internalErrorToHttpError(error);
237401
+ res.status(status).json(json);
237402
+ }
237403
+ });
237404
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/queryData`, async (req, res) => {
237405
+ try {
237406
+ res.status(200).json(await connectionController.getConnectionQueryData(req.params.projectName, req.params.connectionName, req.query.sqlStatement, req.query.options, req.params.packageName));
237407
+ } catch (error) {
237408
+ logger.error(error);
237409
+ const { json, status } = internalErrorToHttpError(error);
237410
+ res.status(status).json(json);
237411
+ }
237412
+ });
237413
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/sqlQuery`, async (req, res) => {
237414
+ try {
237415
+ let options;
237416
+ if (req.body?.options) {
237417
+ options = req.body.options;
237418
+ } else {
237419
+ options = req.query.options;
237420
+ }
237421
+ res.status(200).json(await connectionController.getConnectionQueryData(req.params.projectName, req.params.connectionName, req.body.sqlStatement, options));
237422
+ } catch (error) {
237423
+ logger.error(error);
237424
+ const { json, status } = internalErrorToHttpError(error);
237425
+ res.status(status).json(json);
237426
+ }
237427
+ });
237428
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/sqlQuery`, async (req, res) => {
237429
+ try {
237430
+ let options;
237431
+ if (req.body?.options) {
237432
+ options = req.body.options;
237433
+ } else {
237434
+ options = req.query.options;
237435
+ }
237436
+ res.status(200).json(await connectionController.getConnectionQueryData(req.params.projectName, req.params.connectionName, req.body.sqlStatement, options, req.params.packageName));
237437
+ } catch (error) {
237438
+ logger.error(error);
237439
+ const { json, status } = internalErrorToHttpError(error);
237440
+ res.status(status).json(json);
237441
+ }
237442
+ });
237443
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/temporaryTable`, async (req, res) => {
237444
+ try {
237445
+ res.status(200).json(await connectionController.getConnectionTemporaryTable(req.params.projectName, req.params.connectionName, req.query.sqlStatement));
237446
+ } catch (error) {
237447
+ logger.error(error);
237448
+ const { json, status } = internalErrorToHttpError(error);
237449
+ res.status(status).json(json);
237450
+ }
237451
+ });
237452
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/temporaryTable`, async (req, res) => {
237453
+ try {
237454
+ res.status(200).json(await connectionController.getConnectionTemporaryTable(req.params.projectName, req.params.connectionName, req.query.sqlStatement, req.params.packageName));
237455
+ } catch (error) {
237456
+ logger.error(error);
237457
+ const { json, status } = internalErrorToHttpError(error);
237458
+ res.status(status).json(json);
237459
+ }
237460
+ });
237461
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/connections/:connectionName/sqlTemporaryTable`, async (req, res) => {
237462
+ try {
237463
+ res.status(200).json(await connectionController.getConnectionTemporaryTable(req.params.projectName, req.params.connectionName, req.body.sqlStatement));
237464
+ } catch (error) {
237465
+ logger.error(error);
237466
+ const { json, status } = internalErrorToHttpError(error);
237467
+ res.status(status).json(json);
237468
+ }
237469
+ });
237470
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/connections/:connectionName/sqlTemporaryTable`, async (req, res) => {
237471
+ try {
237472
+ res.status(200).json(await connectionController.getConnectionTemporaryTable(req.params.projectName, req.params.connectionName, req.body.sqlStatement, req.params.packageName));
237473
+ } catch (error) {
237474
+ logger.error(error);
237475
+ const { json, status } = internalErrorToHttpError(error);
237476
+ res.status(status).json(json);
237477
+ }
237478
+ });
237479
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages`, async (req, res) => {
237480
+ if (req.query.versionId) {
237481
+ setVersionIdError(res);
237482
+ return;
237483
+ }
237484
+ try {
237485
+ res.status(200).json(await packageController.listPackages(req.params.projectName));
237486
+ } catch (error) {
237487
+ logger.error(error);
237488
+ const { json, status } = internalErrorToHttpError(error);
237489
+ res.status(status).json(json);
237490
+ }
237491
+ });
237492
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages`, async (req, res) => {
237493
+ try {
237494
+ const autoLoadManifest = req.query.autoLoadManifest === "true";
237495
+ const _package = await packageController.addPackage(req.params.projectName, req.body, { autoLoadManifest });
237496
+ res.status(200).json(_package?.getPackageMetadata());
237497
+ } catch (error) {
237498
+ logger.error(error);
237499
+ const { json, status } = internalErrorToHttpError(error);
237500
+ res.status(status).json(json);
237501
+ }
237502
+ });
237503
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName`, async (req, res) => {
237504
+ if (req.query.versionId) {
237505
+ setVersionIdError(res);
237506
+ return;
237507
+ }
237508
+ try {
237509
+ res.status(200).json(await packageController.getPackage(req.params.projectName, req.params.packageName, req.query.reload === "true"));
237510
+ } catch (error) {
237511
+ logger.error(error);
237512
+ const { json, status } = internalErrorToHttpError(error);
237513
+ res.status(status).json(json);
237514
+ }
237515
+ });
237516
+ app.patch(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName`, async (req, res) => {
237517
+ try {
237518
+ res.status(200).json(await packageController.updatePackage(req.params.projectName, req.params.packageName, req.body));
237519
+ } catch (error) {
237520
+ logger.error(error);
237521
+ const { json, status } = internalErrorToHttpError(error);
237522
+ res.status(status).json(json);
237523
+ }
237524
+ });
237525
+ app.delete(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName`, async (req, res) => {
237526
+ try {
237527
+ res.status(200).json(await packageController.deletePackage(req.params.projectName, req.params.packageName));
237528
+ } catch (error) {
237529
+ logger.error(error);
237530
+ const { json, status } = internalErrorToHttpError(error);
237531
+ res.status(status).json(json);
237532
+ }
237533
+ });
237534
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/models`, async (req, res) => {
237535
+ if (req.query.versionId) {
237536
+ setVersionIdError(res);
237537
+ return;
237538
+ }
237539
+ try {
237540
+ res.status(200).json(await modelController.listModels(req.params.projectName, req.params.packageName));
237541
+ } catch (error) {
237542
+ logger.error(error);
237543
+ const { json, status } = internalErrorToHttpError(error);
237544
+ res.status(status).json(json);
237545
+ }
237546
+ });
237547
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/models/*?`, async (req, res) => {
237548
+ if (req.query.versionId) {
237549
+ setVersionIdError(res);
237550
+ return;
237551
+ }
237552
+ try {
237553
+ const modelPath = req.params["0"];
237554
+ res.status(200).json(await modelController.getModel(req.params.projectName, req.params.packageName, modelPath));
237555
+ } catch (error) {
237556
+ logger.error(error);
237557
+ const { json, status } = internalErrorToHttpError(error);
237558
+ res.status(status).json(json);
237559
+ }
237560
+ });
237561
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/models/*?/query`, async (req, res) => {
237562
+ if (req.body.versionId) {
237563
+ setVersionIdError(res);
237564
+ return;
237565
+ }
237566
+ try {
237567
+ const modelPath = req.params["0"];
237568
+ res.status(200).json(await queryController.getQuery(req.params.projectName, req.params.packageName, modelPath, req.body.sourceName, req.body.queryName, req.body.query, req.body.compactJson === true, req.body.filterParams ?? req.body.sourceFilters, req.body.bypassFilters === true ? true : undefined));
237569
+ } catch (error) {
237570
+ logger.error(error);
237571
+ const { json, status } = internalErrorToHttpError(error);
237572
+ res.status(status).json(json);
237573
+ }
237574
+ });
237575
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/models/:modelName/compile`, async (req, res) => {
237576
+ try {
237577
+ const result = await compileController.compile(req.params.projectName, req.params.packageName, req.params.modelName, req.body.source, req.body.includeSql === true);
237578
+ res.status(200).json(result);
237579
+ } catch (error) {
237580
+ logger.error("Compilation error", { error });
237581
+ const { json, status } = internalErrorToHttpError(error);
237582
+ res.status(status).json(json);
237583
+ }
237584
+ });
237585
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/notebooks`, async (req, res) => {
237586
+ if (req.query.versionId) {
237587
+ setVersionIdError(res);
237588
+ return;
237589
+ }
237590
+ try {
237591
+ res.status(200).json(await modelController.listNotebooks(req.params.projectName, req.params.packageName));
237592
+ } catch (error) {
237593
+ logger.error(error);
237594
+ const { json, status } = internalErrorToHttpError(error);
237595
+ res.status(status).json(json);
237596
+ }
237597
+ });
237598
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/notebooks/*/cells/:cellIndex`, async (req, res) => {
237599
+ if (req.query.versionId) {
237600
+ setVersionIdError(res);
237601
+ return;
237602
+ }
237603
+ try {
237604
+ const cellIndex = parseInt(req.params.cellIndex, 10);
237605
+ if (isNaN(cellIndex)) {
237606
+ res.status(400).json({ error: "Invalid cell index" });
237607
+ return;
237608
+ }
237609
+ const notebookPath = req.params["0"];
237610
+ let filterParams;
237611
+ if (typeof req.query.filter_params === "string") {
237612
+ try {
237613
+ filterParams = JSON.parse(req.query.filter_params);
237614
+ } catch {
237615
+ res.status(400).json({
237616
+ error: "Invalid filter_params: must be valid JSON"
237617
+ });
237618
+ return;
237619
+ }
237620
+ }
237621
+ const bypassFilters = req.query.bypass_filters === "true" ? true : undefined;
237622
+ res.status(200).json(await modelController.executeNotebookCell(req.params.projectName, req.params.packageName, notebookPath, cellIndex, filterParams, bypassFilters));
237623
+ } catch (error) {
237624
+ logger.error(error);
237625
+ const { json, status } = internalErrorToHttpError(error);
237626
+ res.status(status).json(json);
237627
+ }
237628
+ });
237629
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/notebooks/*?`, async (req, res) => {
237630
+ if (req.query.versionId) {
237631
+ setVersionIdError(res);
237632
+ return;
237633
+ }
237634
+ try {
237635
+ const notebookPath = req.params["0"];
237636
+ res.status(200).json(await modelController.getNotebook(req.params.projectName, req.params.packageName, notebookPath));
237637
+ } catch (error) {
237638
+ logger.error(error);
237639
+ const { json, status } = internalErrorToHttpError(error);
237640
+ res.status(status).json(json);
237641
+ }
237642
+ });
237643
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/databases`, async (req, res) => {
237644
+ if (req.query.versionId) {
237645
+ setVersionIdError(res);
237646
+ return;
237647
+ }
237648
+ try {
237649
+ res.status(200).json(await databaseController.listDatabases(req.params.projectName, req.params.packageName));
237650
+ } catch (error) {
237651
+ logger.error(error);
237652
+ const { json, status } = internalErrorToHttpError(error);
237653
+ res.status(status).json(json);
237654
+ }
237655
+ });
237656
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/materializations`, async (req, res) => {
237657
+ try {
237658
+ const build = await materializationController.createMaterialization(req.params.projectName, req.params.packageName, req.body || {});
237659
+ res.status(201).json(remapMaterializationResponse(build));
237660
+ } catch (error) {
237661
+ const { json, status } = internalErrorToHttpError(error);
237662
+ res.status(status).json(json);
237663
+ }
237664
+ });
237665
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/materializations`, async (req, res) => {
237666
+ try {
237667
+ const limit = req.query.limit ? parseInt(req.query.limit, 10) : undefined;
237668
+ const offset = req.query.offset ? parseInt(req.query.offset, 10) : undefined;
237669
+ const builds = await materializationController.listMaterializations(req.params.projectName, req.params.packageName, { limit, offset });
237670
+ res.status(200).json(remapMaterializationResponse(builds));
237671
+ } catch (error) {
237672
+ const { json, status } = internalErrorToHttpError(error);
237673
+ res.status(status).json(json);
237674
+ }
237675
+ });
237676
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/materializations/:materializationId`, async (req, res) => {
237677
+ try {
237678
+ const build = await materializationController.getMaterialization(req.params.projectName, req.params.packageName, req.params.materializationId);
237679
+ res.status(200).json(remapMaterializationResponse(build));
237680
+ } catch (error) {
237681
+ const { json, status } = internalErrorToHttpError(error);
237682
+ res.status(status).json(json);
237683
+ }
237684
+ });
237685
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/materializations/teardown`, async (req, res) => {
237686
+ try {
237687
+ const result = await materializationController.teardownPackage(req.params.projectName, req.params.packageName, req.body || {});
237688
+ res.status(200).json(result);
237689
+ } catch (error) {
237690
+ const { json, status } = internalErrorToHttpError(error);
237691
+ res.status(status).json(json);
237692
+ }
237693
+ });
237694
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/materializations/:materializationId`, async (req, res) => {
237695
+ try {
237696
+ const action = req.query.action;
237697
+ if (action === "start") {
237698
+ const build = await materializationController.startMaterialization(req.params.projectName, req.params.packageName, req.params.materializationId);
237699
+ res.status(202).json(remapMaterializationResponse(build));
237700
+ } else if (action === "stop") {
237701
+ const build = await materializationController.stopMaterialization(req.params.projectName, req.params.packageName, req.params.materializationId);
237702
+ res.status(200).json(remapMaterializationResponse(build));
237703
+ } else {
237704
+ throw new BadRequestError(`Unsupported action '${String(action ?? "")}'. Expected 'start' or 'stop'.`);
237705
+ }
237706
+ } catch (error) {
237707
+ const { json, status } = internalErrorToHttpError(error);
237708
+ res.status(status).json(json);
237709
+ }
237710
+ });
237711
+ app.delete(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/materializations/:materializationId`, async (req, res) => {
237712
+ try {
237713
+ await materializationController.deleteMaterialization(req.params.projectName, req.params.packageName, req.params.materializationId);
237714
+ res.status(204).send();
237715
+ } catch (error) {
237716
+ const { json, status } = internalErrorToHttpError(error);
237717
+ res.status(status).json(json);
237718
+ }
237719
+ });
237720
+ app.get(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/manifest`, async (req, res) => {
237721
+ try {
237722
+ const manifest = await manifestController.getManifest(req.params.projectName, req.params.packageName);
237723
+ res.status(200).json(manifest);
237724
+ } catch (error) {
237725
+ logger.error("Get manifest error", { error });
237726
+ const { json, status } = internalErrorToHttpError(error);
237727
+ res.status(status).json(json);
237728
+ }
237729
+ });
237730
+ app.post(`${LEGACY_API_PREFIX}/projects/:projectName/packages/:packageName/manifest`, async (req, res) => {
237731
+ try {
237732
+ const action = req.query.action;
237733
+ if (action === "reload") {
237734
+ const manifest = await manifestController.reloadManifest(req.params.projectName, req.params.packageName);
237735
+ res.status(200).json(manifest);
237736
+ } else {
237737
+ throw new BadRequestError(`Unsupported action '${String(action ?? "")}'. Expected 'reload'.`);
237738
+ }
237739
+ } catch (error) {
237740
+ logger.error("Manifest action error", { error });
237741
+ const { json, status } = internalErrorToHttpError(error);
237742
+ res.status(status).json(json);
237743
+ }
237744
+ });
237745
+ logger.info("Legacy /projects/* routes registered for backwards compatibility");
237746
+ }
237747
+
236989
237748
  // src/service/manifest_service.ts
236990
237749
  class ManifestService {
236991
237750
  environmentStore;
@@ -237036,8 +237795,8 @@ import {
237036
237795
  } from "@malloydata/malloy";
237037
237796
 
237038
237797
  // src/service/quoting.ts
237039
- function quoteTablePath(path11, dialect) {
237040
- return path11.split(".").map((seg) => dialect.quoteTablePath(seg)).join(".");
237798
+ function quoteTablePath(path12, dialect) {
237799
+ return path12.split(".").map((seg) => dialect.quoteTablePath(seg)).join(".");
237041
237800
  }
237042
237801
  function splitTablePath(tableName) {
237043
237802
  const lastDot = tableName.lastIndexOf(".");
@@ -237635,6 +238394,8 @@ function normalizeQueryArray(value) {
237635
238394
  }
237636
238395
  function parseArgs() {
237637
238396
  const args = process.argv.slice(2);
238397
+ let sawServerRoot = false;
238398
+ let sawConfig = false;
237638
238399
  for (let i = 0;i < args.length; i++) {
237639
238400
  const arg = args[i];
237640
238401
  if (arg === "--port" && args[i + 1]) {
@@ -237644,8 +238405,13 @@ function parseArgs() {
237644
238405
  process.env.PUBLISHER_HOST = args[i + 1];
237645
238406
  i++;
237646
238407
  } else if (arg === "--server_root" && args[i + 1]) {
238408
+ sawServerRoot = true;
237647
238409
  process.env.SERVER_ROOT = args[i + 1];
237648
238410
  i++;
238411
+ } else if (arg === "--config" && args[i + 1]) {
238412
+ sawConfig = true;
238413
+ process.env.PUBLISHER_CONFIG_PATH = args[i + 1];
238414
+ i++;
237649
238415
  } else if (arg === "--mcp_port" && args[i + 1]) {
237650
238416
  process.env.MCP_PORT = args[i + 1];
237651
238417
  i++;
@@ -237666,6 +238432,7 @@ function parseArgs() {
237666
238432
  console.log(" --port <number> Port to run the server on (default: 4000)");
237667
238433
  console.log(" --host <string> Host to bind the server to (default: localhost)");
237668
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)");
237669
238436
  console.log(" --mcp_port <number> Port for MCP server (default: 4040)");
237670
238437
  console.log(" --shutdown_drain_duration_seconds <number> Time in seconds to keep service in draining state before closing servers (default: 0)");
237671
238438
  console.log(" --shutdown_graceful_close_timeout_seconds <number> Time in seconds to wait after closing servers before exit (default: 0)");
@@ -237674,6 +238441,9 @@ function parseArgs() {
237674
238441
  process.exit(0);
237675
238442
  }
237676
238443
  }
238444
+ if (!sawServerRoot && !sawConfig && true) {
238445
+ process.env.PUBLISHER_USE_BUNDLED_DEFAULT = "true";
238446
+ }
237677
238447
  }
237678
238448
  parseArgs();
237679
238449
  var PUBLISHER_PORT = Number(process.env.PUBLISHER_PORT || 4000);
@@ -237682,9 +238452,9 @@ var MCP_PORT = Number(process.env.MCP_PORT || 4040);
237682
238452
  var MCP_ENDPOINT = "/mcp";
237683
238453
  var SHUTDOWN_DRAIN_DURATION_SECONDS = Number(process.env.SHUTDOWN_DRAIN_DURATION_SECONDS || 0);
237684
238454
  var SHUTDOWN_GRACEFUL_CLOSE_TIMEOUT_SECONDS = Number(process.env.SHUTDOWN_GRACEFUL_CLOSE_TIMEOUT_SECONDS || 0);
237685
- var __filename_esm = fileURLToPath2(import.meta.url);
237686
- var ROOT = path11.join(path11.dirname(__filename_esm), "app");
237687
- var SERVER_ROOT = path11.resolve(process.cwd(), process.env.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 || ".");
237688
238458
  var API_PREFIX2 = "/api/v0";
237689
238459
  var isDevelopment = process.env["NODE_ENV"] === "development";
237690
238460
  var app = import_express.default();
@@ -237765,17 +238535,17 @@ mcpApp.all(MCP_ENDPOINT, async (req, res) => {
237765
238535
  });
237766
238536
  if (!isDevelopment) {
237767
238537
  app.use("/", import_express.default.static(ROOT));
237768
- app.use("/api-doc.html", import_express.default.static(path11.join(ROOT, "api-doc.html")));
238538
+ app.use("/api-doc.html", import_express.default.static(path12.join(ROOT, "api-doc.html")));
237769
238539
  } else {
237770
238540
  app.use(`${API_PREFIX2}`, loggerMiddleware);
237771
238541
  app.use(import_http_proxy_middleware.createProxyMiddleware({
237772
238542
  target: "http://localhost:5173",
237773
238543
  changeOrigin: true,
237774
238544
  ws: true,
237775
- pathFilter: (path12) => !path12.startsWith("/api/") && !path12.startsWith("/metrics") && !path12.startsWith("/health")
238545
+ pathFilter: (path13) => !path13.startsWith("/api/") && !path13.startsWith("/metrics") && !path13.startsWith("/health")
237776
238546
  }));
237777
238547
  }
237778
- var setVersionIdError = (res) => {
238548
+ var setVersionIdError2 = (res) => {
237779
238549
  const { json, status } = internalErrorToHttpError(new NotImplementedError("Version IDs not implemented."));
237780
238550
  res.status(status).json(json);
237781
238551
  };
@@ -237783,7 +238553,7 @@ app.use(import_cors.default({
237783
238553
  origin: "http://localhost:5173",
237784
238554
  credentials: true
237785
238555
  }));
237786
- app.use(import_body_parser.default.json({ limit: "1mb" }));
238556
+ app.use(import_body_parser2.default.json({ limit: "1mb" }));
237787
238557
  registerHealthEndpoints(app);
237788
238558
  try {
237789
238559
  const metricsHandler = getPrometheusMetricsHandler();
@@ -238094,7 +238864,7 @@ app.post(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/con
238094
238864
  });
238095
238865
  app.get(`${API_PREFIX2}/environments/:environmentName/packages`, async (req, res) => {
238096
238866
  if (req.query.versionId) {
238097
- setVersionIdError(res);
238867
+ setVersionIdError2(res);
238098
238868
  return;
238099
238869
  }
238100
238870
  try {
@@ -238118,7 +238888,7 @@ app.post(`${API_PREFIX2}/environments/:environmentName/packages`, async (req, re
238118
238888
  });
238119
238889
  app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName`, async (req, res) => {
238120
238890
  if (req.query.versionId) {
238121
- setVersionIdError(res);
238891
+ setVersionIdError2(res);
238122
238892
  return;
238123
238893
  }
238124
238894
  try {
@@ -238149,7 +238919,7 @@ app.delete(`${API_PREFIX2}/environments/:environmentName/packages/:packageName`,
238149
238919
  });
238150
238920
  app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/models`, async (req, res) => {
238151
238921
  if (req.query.versionId) {
238152
- setVersionIdError(res);
238922
+ setVersionIdError2(res);
238153
238923
  return;
238154
238924
  }
238155
238925
  try {
@@ -238162,7 +238932,7 @@ app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/mode
238162
238932
  });
238163
238933
  app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/models/*?`, async (req, res) => {
238164
238934
  if (req.query.versionId) {
238165
- setVersionIdError(res);
238935
+ setVersionIdError2(res);
238166
238936
  return;
238167
238937
  }
238168
238938
  try {
@@ -238176,7 +238946,7 @@ app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/mode
238176
238946
  });
238177
238947
  app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/notebooks`, async (req, res) => {
238178
238948
  if (req.query.versionId) {
238179
- setVersionIdError(res);
238949
+ setVersionIdError2(res);
238180
238950
  return;
238181
238951
  }
238182
238952
  try {
@@ -238189,7 +238959,7 @@ app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/note
238189
238959
  });
238190
238960
  app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/notebooks/*/cells/:cellIndex`, async (req, res) => {
238191
238961
  if (req.query.versionId) {
238192
- setVersionIdError(res);
238962
+ setVersionIdError2(res);
238193
238963
  return;
238194
238964
  }
238195
238965
  try {
@@ -238222,7 +238992,7 @@ app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/note
238222
238992
  });
238223
238993
  app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/notebooks/*?`, async (req, res) => {
238224
238994
  if (req.query.versionId) {
238225
- setVersionIdError(res);
238995
+ setVersionIdError2(res);
238226
238996
  return;
238227
238997
  }
238228
238998
  try {
@@ -238236,7 +239006,7 @@ app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/note
238236
239006
  });
238237
239007
  app.post(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/models/*?/query`, async (req, res) => {
238238
239008
  if (req.body.versionId) {
238239
- setVersionIdError(res);
239009
+ setVersionIdError2(res);
238240
239010
  return;
238241
239011
  }
238242
239012
  try {
@@ -238250,7 +239020,7 @@ app.post(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/mod
238250
239020
  });
238251
239021
  app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/databases`, async (req, res) => {
238252
239022
  if (req.query.versionId) {
238253
- setVersionIdError(res);
239023
+ setVersionIdError2(res);
238254
239024
  return;
238255
239025
  }
238256
239026
  try {
@@ -238360,8 +239130,19 @@ app.post(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/man
238360
239130
  res.status(status).json(json);
238361
239131
  }
238362
239132
  });
239133
+ registerLegacyRoutes(app, {
239134
+ environmentStore,
239135
+ connectionController,
239136
+ modelController,
239137
+ packageController,
239138
+ databaseController,
239139
+ queryController,
239140
+ compileController,
239141
+ materializationController,
239142
+ manifestController
239143
+ });
238363
239144
  if (!isDevelopment) {
238364
- app.get("*", (_req, res) => res.sendFile(path11.resolve(ROOT, "index.html")));
239145
+ app.get("*", (_req, res) => res.sendFile(path12.resolve(ROOT, "index.html")));
238365
239146
  }
238366
239147
  app.use((err, _req, res, _next) => {
238367
239148
  logger.error("Unhandled error:", err);