@malloy-publisher/server 0.2.6 → 0.2.7

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.
@@ -251,27 +251,73 @@
251
251
  var pad = parseFloat(bodyStyle.paddingBottom) || 0;
252
252
  return Math.ceil(maxBottom + scrollTop + pad);
253
253
  }
254
- function postSize() {
254
+ // Smallest SHRINK worth reporting. measureContentHeight already ceils,
255
+ // so nothing sub-pixel reaches here; what this absorbs is a 1-2px
256
+ // rounding flip-flop between two layout passes, which the host cannot
257
+ // act on without the resize itself becoming a layout change inside this
258
+ // frame — the feedback loop.
259
+ //
260
+ // Applied to shrinks ONLY, deliberately. A frame a few pixels too tall
261
+ // shows dead space; a few pixels too short CLIPS, and the frame has no
262
+ // internal scrollbar. The asymmetry also stops the two sides' bands
263
+ // compounding: the host runs its own epsilon against ITS height, which
264
+ // its MIN clamp guarantees can differ from lastHeight here, so two
265
+ // symmetric 8px bands leave a dead zone wider than either. Growing at
266
+ // 1px precision keeps the host's band the only one that applies.
267
+ var RESIZE_EPSILON = 8;
268
+ var resizePending = false;
269
+
270
+ function emitSize() {
255
271
  var h = measureContentHeight();
256
- if (h !== lastHeight) {
257
- lastHeight = h;
258
- try {
259
- window.parent.postMessage(
260
- { type: "publisher:resize", height: h },
261
- "*",
262
- );
263
- } catch (_e) {
264
- /* ignore */
265
- }
272
+ if (h < lastHeight && lastHeight - h < RESIZE_EPSILON) return;
273
+ if (h === lastHeight) return;
274
+ lastHeight = h;
275
+ try {
276
+ window.parent.postMessage(
277
+ { type: "publisher:resize", height: h },
278
+ "*",
279
+ );
280
+ } catch (_e) {
281
+ /* ignore */
266
282
  }
267
283
  }
284
+
285
+ // Coalesce a burst into one message per frame.
286
+ //
287
+ // ResizeObserver on documentElement fires on EVERY layout change, so a
288
+ // dashboard whose tiles resolve one by one used to post a height per
289
+ // tile. The host resized on each, which is what made an embedded app
290
+ // visibly jitter while it loaded. Same `pending`-flag idiom as the SSE
291
+ // reload debounce below; rAF rather than a timeout because the next
292
+ // paint is exactly when a coalesced layout is worth measuring.
293
+ //
294
+ // Trailing edge, not leading: the last measurement in a burst is the
295
+ // settled one, and reporting the first would send a stale height.
296
+ function postSize() {
297
+ if (resizePending) return;
298
+ resizePending = true;
299
+ var schedule =
300
+ typeof requestAnimationFrame === "function"
301
+ ? requestAnimationFrame
302
+ : function (fn) {
303
+ setTimeout(fn, 16);
304
+ };
305
+ schedule(function () {
306
+ resizePending = false;
307
+ emitSize();
308
+ });
309
+ }
268
310
  // Initial + observe content changes
311
+ // The first report goes out synchronously: the host shows a
312
+ // MIN_EMBED_HEIGHT placeholder until one arrives, so deferring it by a
313
+ // frame would leave a collapsed frame for no benefit. Only the bursts
314
+ // that follow need coalescing.
269
315
  if (document.readyState === "loading") {
270
- document.addEventListener("DOMContentLoaded", postSize);
316
+ document.addEventListener("DOMContentLoaded", emitSize);
271
317
  } else {
272
- postSize();
318
+ emitSize();
273
319
  }
274
- window.addEventListener("load", postSize);
320
+ window.addEventListener("load", emitSize);
275
321
  if (typeof ResizeObserver !== "undefined") {
276
322
  var ro = new ResizeObserver(postSize);
277
323
  // Observe documentElement so we catch any layout change
package/dist/server.mjs CHANGED
@@ -156437,7 +156437,7 @@ var BUNDLED_DEFAULT_CONFIG_PATH, PER_MODE_COLOR_KEYS, DEFAULT_HIGH_WATER_FRACTIO
156437
156437
  return false;
156438
156438
  }
156439
156439
  if (!conn.name || typeof conn.name !== "string") {
156440
- logger.warn(`Invalid connection: missing or invalid "name" field. Skipping.`, { connection: conn });
156440
+ logger.warn(`Invalid connection: missing or invalid "name" field. Skipping.`, { type: typeof conn.type === "string" ? conn.type : undefined });
156441
156441
  return false;
156442
156442
  }
156443
156443
  if (!conn.type || typeof conn.type !== "string") {
@@ -156461,13 +156461,13 @@ var BUNDLED_DEFAULT_CONFIG_PATH, PER_MODE_COLOR_KEYS, DEFAULT_HIGH_WATER_FRACTIO
156461
156461
  };
156462
156462
  }
156463
156463
  const validEnvironments = [];
156464
- for (const environment of rawConfig.environments) {
156464
+ for (const [index, environment] of rawConfig.environments.entries()) {
156465
156465
  if (!environment || typeof environment !== "object") {
156466
156466
  logger.warn(`Invalid environment in ${PUBLISHER_CONFIG_NAME}: entry must be an object. Skipping.`);
156467
156467
  continue;
156468
156468
  }
156469
156469
  if (!environment.name || typeof environment.name !== "string") {
156470
- logger.warn(`Invalid environment in ${PUBLISHER_CONFIG_NAME}: missing or invalid "name" field. Skipping entry.`, { environment });
156470
+ logger.warn(`Invalid environment in ${PUBLISHER_CONFIG_NAME}: missing or invalid "name" field. Skipping entry.`, { index });
156471
156471
  continue;
156472
156472
  }
156473
156473
  if (!Array.isArray(environment.packages)) {
@@ -156539,6 +156539,19 @@ var init_config = __esm(() => {
156539
156539
 
156540
156540
  // src/errors.ts
156541
156541
  import { MalloyError } from "@malloydata/malloy";
156542
+ function logInternalFailure(summary, error, level = "error") {
156543
+ const strip = (value) => value.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, " ");
156544
+ const message = error.message ?? "";
156545
+ const stack = error.stack ?? "";
156546
+ const framesOnly = stack.startsWith(`${error.name}: ${message}`) ? stack.slice(`${error.name}: ${message}`.length).replace(/^\r?\n/, "") : stack;
156547
+ logger[level](summary, {
156548
+ error: {
156549
+ name: error.name,
156550
+ message: strip(message).slice(0, MAX_LOGGED_DETAIL_CHARS),
156551
+ stack: strip(framesOnly).slice(0, MAX_LOGGED_DETAIL_CHARS)
156552
+ }
156553
+ });
156554
+ }
156542
156555
  function internalErrorToHttpError(error) {
156543
156556
  if (error instanceof BadRequestError) {
156544
156557
  return httpError(400, error.message);
@@ -156573,7 +156586,11 @@ function internalErrorToHttpError(error) {
156573
156586
  } else if (error instanceof ModelCompilationError) {
156574
156587
  return httpError(424, error.message);
156575
156588
  } else if (error instanceof ConnectionError) {
156576
- return httpError(502, error.message);
156589
+ if (error.callerSafe) {
156590
+ return httpError(502, error.message);
156591
+ }
156592
+ logInternalFailure("Upstream connection error", error, "warn");
156593
+ return httpError(502, GENERIC_UPSTREAM_MESSAGE);
156577
156594
  } else if (error instanceof MaterializationNotFoundError) {
156578
156595
  return httpError(404, error.message);
156579
156596
  } else if (error instanceof MaterializationConflictError) {
@@ -156589,7 +156606,8 @@ function internalErrorToHttpError(error) {
156589
156606
  } else if (error instanceof NotImplementedError) {
156590
156607
  return httpError(501, error.message);
156591
156608
  } else {
156592
- return httpError(500, error.message);
156609
+ logInternalFailure("Unhandled internal error", error);
156610
+ return httpError(500, GENERIC_INTERNAL_MESSAGE);
156593
156611
  }
156594
156612
  }
156595
156613
  function httpError(code, message, reason) {
@@ -156602,9 +156620,10 @@ function httpError(code, message, reason) {
156602
156620
  }
156603
156621
  };
156604
156622
  }
156605
- var NotImplementedError, BadRequestError, InvalidArgumentError, EnvironmentNotFoundError, PackageNotFoundError, ModelNotFoundError, DashboardNotFoundError, ConnectionNotFoundError, TableNotFoundError, ConnectionError, DestinationNotFoundError, ConnectionAuthError, UnsupportedCatalogFormatError, ModelCompilationError, MaterializationEligibilityError, PublisherConfigError, FrozenConfigError, AccessDeniedError, NotQueryableError, MaterializationNotFoundError, MaterializationConflictError, InvalidStateTransitionError, ServiceUnavailableError, PayloadTooLargeError, ResponseUnserializableError, QueryTimeoutError;
156623
+ var GENERIC_INTERNAL_MESSAGE = "Internal server error.", GENERIC_UPSTREAM_MESSAGE = "Upstream connection error.", MAX_LOGGED_DETAIL_CHARS = 2000, NotImplementedError, BadRequestError, InvalidArgumentError, EnvironmentNotFoundError, PackageNotFoundError, ModelNotFoundError, DashboardNotFoundError, ConnectionNotFoundError, TableNotFoundError, ConnectionError, DestinationNotFoundError, ConnectionAuthError, UnsupportedCatalogFormatError, ModelCompilationError, MaterializationEligibilityError, PublisherConfigError, FrozenConfigError, AccessDeniedError, NotQueryableError, MaterializationNotFoundError, MaterializationConflictError, InvalidStateTransitionError, ServiceUnavailableError, PayloadTooLargeError, ResponseUnserializableError, QueryTimeoutError;
156606
156624
  var init_errors = __esm(() => {
156607
156625
  init_constants();
156626
+ init_logger();
156608
156627
  NotImplementedError = class NotImplementedError extends Error {
156609
156628
  constructor(message) {
156610
156629
  super(message);
@@ -156648,8 +156667,10 @@ var init_errors = __esm(() => {
156648
156667
  }
156649
156668
  };
156650
156669
  ConnectionError = class ConnectionError extends Error {
156651
- constructor(message) {
156670
+ callerSafe;
156671
+ constructor(message, options) {
156652
156672
  super(message);
156673
+ this.callerSafe = options?.callerSafe ?? false;
156653
156674
  }
156654
156675
  };
156655
156676
  DestinationNotFoundError = class DestinationNotFoundError extends Error {
@@ -213217,11 +213238,6 @@ var require_utils74 = __commonJS((exports, module) => {
213217
213238
  };
213218
213239
  });
213219
213240
 
213220
- // ../../node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
213221
- var require_sshcrypto = __commonJS((exports, module) => {
213222
- module.exports = __require("./sshcrypto-xqan60jb.node");
213223
- });
213224
-
213225
213241
  // ../../node_modules/ssh2/lib/protocol/crypto/poly1305.js
213226
213242
  var require_poly1305 = __commonJS((exports, module) => {
213227
213243
  var __dirname = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto", __filename = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto/poly1305.js";
@@ -213708,7 +213724,7 @@ var require_crypto = __commonJS((exports, module) => {
213708
213724
  var ChaChaPolyDecipher;
213709
213725
  var GenericDecipher;
213710
213726
  try {
213711
- binding = require_sshcrypto();
213727
+ binding = (()=>{throw new Error("Cannot require module "+"./crypto/build/Release/sshcrypto.node");})();
213712
213728
  ({
213713
213729
  AESGCMCipher,
213714
213730
  ChaChaPolyCipher,
@@ -254140,7 +254156,7 @@ function hydrateMarkdownOnlyCells(notebookCells) {
254140
254156
  return { type: "code", text: sc.text };
254141
254157
  });
254142
254158
  }
254143
- var MALLOY_VERSION, REQUEST_CHAIN_MAX_NAMES = 64, Model;
254159
+ var MALLOY_VERSION, REQUEST_CHAIN_MAX_NAMES = 64, NO_PREAGGREGATE_VIOLATIONS, Model;
254144
254160
  var init_model = __esm(() => {
254145
254161
  init_telemetry();
254146
254162
  init_materialization_metrics();
@@ -254176,6 +254192,7 @@ var init_model = __esm(() => {
254176
254192
  init_authorize_metrics();
254177
254193
  init_path_safety();
254178
254194
  MALLOY_VERSION = createRequire2(import.meta.url)("@malloydata/malloy/package.json").version;
254195
+ NO_PREAGGREGATE_VIOLATIONS = Object.freeze([]);
254179
254196
  Model = class Model {
254180
254197
  packageName;
254181
254198
  modelPath;
@@ -254199,6 +254216,7 @@ var init_model = __esm(() => {
254199
254216
  givens;
254200
254217
  declaredQueryMetadataMemo;
254201
254218
  declaredSourceQueryMetadataMemo;
254219
+ preaggregateViolationsMemo;
254202
254220
  authorizeReferencedGivenNames = new Set;
254203
254221
  discoveryCurationEnabled = false;
254204
254222
  queryBoundary = { mode: "all", exploresDeclared: false, isQueryEntryPoint: true };
@@ -255148,8 +255166,11 @@ var init_model = __esm(() => {
255148
255166
  }
255149
255167
  preaggregateViolations() {
255150
255168
  if (!this.modelDef)
255151
- return [];
255152
- return validateModelPreaggregation(this.modelDef.contents);
255169
+ return NO_PREAGGREGATE_VIOLATIONS;
255170
+ if (this.preaggregateViolationsMemo === undefined) {
255171
+ this.preaggregateViolationsMemo = validateModelPreaggregation(this.modelDef.contents);
255172
+ }
255173
+ return this.preaggregateViolationsMemo;
255153
255174
  }
255154
255175
  async buildPreaggregateServeModel(packagePath, malloyConfig, buildManifest) {
255155
255176
  this.preaggregateServeMaterializer = undefined;
@@ -277629,7 +277650,9 @@ class ConnectionController {
277629
277650
  if (packages.length === 1) {
277630
277651
  const onlyPackage = packages[0].name;
277631
277652
  if (!onlyPackage) {
277632
- throw new ConnectionError("Package name is undefined");
277653
+ throw new ConnectionError("Package name is undefined", {
277654
+ callerSafe: true
277655
+ });
277633
277656
  }
277634
277657
  const pkg = await environment.getPackage(onlyPackage);
277635
277658
  return await pkg.getMalloyConnection(connectionName);
@@ -278188,6 +278211,7 @@ init_errors();
278188
278211
  import { MalloyError as MalloyError2 } from "@malloydata/malloy";
278189
278212
 
278190
278213
  // src/mcp/error_messages.ts
278214
+ init_errors();
278191
278215
  function getNotFoundError(resourceUriOrContext) {
278192
278216
  const baseMessage = `Resource not found: ${resourceUriOrContext}`;
278193
278217
  const suggestions = [
@@ -278201,13 +278225,21 @@ function getNotFoundError(resourceUriOrContext) {
278201
278225
  }
278202
278226
  function getInternalError(operation, error) {
278203
278227
  const baseMessage = `An unexpected internal error occurred during ${operation}.`;
278228
+ const suggestions = [
278229
+ "Try the request again later.",
278230
+ "If the problem persists, check server logs or contact support."
278231
+ ];
278232
+ if (error instanceof ConnectionError && !error.callerSafe) {
278233
+ logInternalFailure(`Upstream connection error during ${operation}`, error, "warn");
278234
+ return {
278235
+ message: `${baseMessage}: Upstream connection error.`,
278236
+ suggestions
278237
+ };
278238
+ }
278204
278239
  const errorMessage = error instanceof Error ? error.message : String(error);
278205
278240
  return {
278206
278241
  message: error ? `${baseMessage}: ${errorMessage}` : baseMessage,
278207
- suggestions: [
278208
- "Try the request again later.",
278209
- "If the problem persists, check server logs or contact support."
278210
- ]
278242
+ suggestions
278211
278243
  };
278212
278244
  }
278213
278245
  var RESTRICTED_CONSTRUCTS = "raw SQL (duckdb.sql(...) / connection.sql(...)), direct SQL function calls (name!type(...)), the sql_* function family (sql_number, sql_string, sql_date, sql_timestamp, sql_boolean), import statements, ##! flags, or new sources from connection.table(...)";
@@ -292467,8 +292499,8 @@ class WatchModeController {
292467
292499
  assertSafePackageName(watchName);
292468
292500
  } catch (error) {
292469
292501
  logger.error(error);
292470
- const { status } = internalErrorToHttpError(error);
292471
- res.status(status).json({ error: error.message });
292502
+ const { status, json } = internalErrorToHttpError(error);
292503
+ res.status(status).json({ error: json.message });
292472
292504
  return;
292473
292505
  }
292474
292506
  const environmentManifest = await EnvironmentStore.reloadEnvironmentManifest(this.environmentStore.serverRootPath);
@@ -292483,8 +292515,8 @@ class WatchModeController {
292483
292515
  await this.ensureWatching(watchName);
292484
292516
  } catch (error) {
292485
292517
  logger.error(error);
292486
- const { status } = internalErrorToHttpError(error);
292487
- res.status(status).json({ error: error.message });
292518
+ const { status, json } = internalErrorToHttpError(error);
292519
+ res.status(status).json({ error: json.message });
292488
292520
  return;
292489
292521
  }
292490
292522
  res.json();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/server",
3
3
  "description": "Malloy Publisher Server",
4
- "version": "0.2.6",
4
+ "version": "0.2.7",
5
5
  "main": "dist/server.mjs",
6
6
  "bin": {
7
7
  "malloy-publisher": "dist/server.mjs"
Binary file