@malloy-publisher/server 0.9.2 → 0.9.4

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 (36) hide show
  1. package/README.docker.md +14 -0
  2. package/dist/app/api-doc.yaml +287 -45
  3. package/dist/app/assets/{EnvironmentPage-B-t_QP32.js → EnvironmentPage-uo3igcP9.js} +1 -1
  4. package/dist/app/assets/{HomePage-Cpu2zMy-.js → HomePage-0OQVchl4.js} +1 -1
  5. package/dist/app/assets/MainPage-D4SOACdm.js +1 -0
  6. package/dist/app/assets/ModelPage-B5CpdEDl.js +2 -0
  7. package/dist/app/assets/PackagePage-BzUfTQKy.js +1 -0
  8. package/dist/app/assets/{RenderedResult-CBLHr32C.es-DRr9Gmsz.js → RenderedResult-DwB0-9Yx.es-1MzhmBXH.js} +3 -3
  9. package/dist/app/assets/{RouteError-mgOkv7XK.js → RouteError-BXcrPjvD.js} +1 -1
  10. package/dist/app/assets/ThemeEditorPage-FpYg5NPt.js +1 -0
  11. package/dist/app/assets/ToggleButtonGroup-Vkkjsbxx.js +1 -0
  12. package/dist/app/assets/documentRoutes-BWHzvbNO.js +1 -0
  13. package/dist/app/assets/index-BZEvIUf-.js +484 -0
  14. package/dist/app/assets/index-CprgMbE_.js +1 -0
  15. package/dist/app/assets/{index-IC0jPCkR.js → index-DFvJYp4Q.js} +7 -7
  16. package/dist/app/assets/index-NVPzfbUZ.js +1317 -0
  17. package/dist/app/assets/{index-ireHUsv_.js → index-sKcoT1e_.js} +422 -427
  18. package/dist/app/assets/index.es-D2Ts8adM.js +195 -0
  19. package/dist/app/index.html +1 -1
  20. package/dist/cpufeatures-1yrn0vtw.node +0 -0
  21. package/dist/package_load_worker.mjs +368 -27
  22. package/dist/server.mjs +12986 -9250
  23. package/package.json +15 -15
  24. package/dist/app/assets/LightMode-DOBl6awB.js +0 -1
  25. package/dist/app/assets/MainPage-C7ySrclP.js +0 -1
  26. package/dist/app/assets/ModelPage-D8r_J-N9.js +0 -2
  27. package/dist/app/assets/PackagePage-CcVFBhnT.js +0 -1
  28. package/dist/app/assets/ThemeEditorPage-CIPDs2w9.js +0 -1
  29. package/dist/app/assets/ToggleButtonGroup-CEZThrzh.js +0 -1
  30. package/dist/app/assets/index-Bg-M8sZz.js +0 -1
  31. package/dist/app/assets/index-ByieDZlo.js +0 -23
  32. package/dist/app/assets/index-CGdwupPP.js +0 -457
  33. package/dist/app/assets/index-CeLTZAFW.js +0 -1322
  34. package/dist/app/assets/index-DoF8ihra.js +0 -1
  35. package/dist/app/assets/index.es-6eARPKZf.js +0 -198
  36. package/dist/sshcrypto-cqb52edz.node +0 -0
@@ -16,7 +16,7 @@ SPDX-License-Identifier: MIT
16
16
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
17
17
  />
18
18
  <title>Malloy Publisher</title>
19
- <script type="module" crossorigin src="/assets/index-CGdwupPP.js"></script>
19
+ <script type="module" crossorigin src="/assets/index-BZEvIUf-.js"></script>
20
20
  <link rel="stylesheet" crossorigin href="/assets/index-DUqldpo0.css">
21
21
  </head>
22
22
  <body>
Binary file
@@ -13195,7 +13195,7 @@ function toQueryTextProblem(problem) {
13195
13195
  ...problem.at ? { at: { range: problem.at.range } } : {}
13196
13196
  };
13197
13197
  }
13198
- var GENERIC_INTERNAL_MESSAGE = "Internal server error.", GENERIC_UPSTREAM_MESSAGE = "Upstream connection error.", MAX_LOGGED_DETAIL_CHARS = 2000, FILESYSTEM_ACCESS_DESCRIPTIONS, NotImplementedError, BadRequestError, InvalidArgumentError, CompileRefusedError, EnvironmentNotFoundError, PackageNotFoundError, ModelNotFoundError, DashboardNotFoundError, ConnectionNotFoundError, TableNotFoundError, ConnectionError, DestinationNotFoundError, ConnectionAuthError, UnsupportedCatalogFormatError, ModelCompilationError, PackageManifestError, MaterializationEligibilityError, PublisherConfigError, ServerConfigurationError, FrozenConfigError, AccessDeniedError, QueryCompileError, NotQueryableError, OffSurfaceError, MaterializationNotFoundError, MaterializationConflictError, WriteConflictError, WriteRolledBackError, WriteVerifyError, InvalidStateTransitionError, ServiceUnavailableError, PayloadTooLargeError, ResponseUnserializableError, QueryTimeoutError;
13198
+ var GENERIC_INTERNAL_MESSAGE = "Internal server error.", GENERIC_UPSTREAM_MESSAGE = "Upstream connection error.", MAX_LOGGED_DETAIL_CHARS = 2000, FILESYSTEM_ACCESS_DESCRIPTIONS, NotImplementedError, BadRequestError, InvalidArgumentError, CompileRefusedError, RenderTagRefusedError, UnparseableTextError, EnvironmentNotFoundError, PackageNotFoundError, ModelNotFoundError, DashboardNotFoundError, ConnectionNotFoundError, TableNotFoundError, ConnectionError, DestinationNotFoundError, ConnectionAuthError, UnsupportedCatalogFormatError, ModelCompilationError, PackageManifestError, MaterializationEligibilityError, PublisherConfigError, ServerConfigurationError, FrozenConfigError, AccessDeniedError, QueryCompileError, NotQueryableError, OffSurfaceError, MaterializationNotFoundError, MaterializationConflictError, WriteConflictError, WriteRolledBackError, WriteVerifyError, InvalidStateTransitionError, ServiceUnavailableError, PayloadTooLargeError, ResponseUnserializableError, QueryTimeoutError;
13199
13199
  var init_errors = __esm(() => {
13200
13200
  init_constants();
13201
13201
  init_logger();
@@ -13218,6 +13218,15 @@ var init_errors = __esm(() => {
13218
13218
  };
13219
13219
  CompileRefusedError = class CompileRefusedError extends BadRequestError {
13220
13220
  };
13221
+ RenderTagRefusedError = class RenderTagRefusedError extends CompileRefusedError {
13222
+ };
13223
+ UnparseableTextError = class UnparseableTextError extends CompileRefusedError {
13224
+ detail;
13225
+ constructor(message, detail) {
13226
+ super(message);
13227
+ this.detail = detail;
13228
+ }
13229
+ };
13221
13230
  EnvironmentNotFoundError = class EnvironmentNotFoundError extends Error {
13222
13231
  constructor(message) {
13223
13232
  super(message);
@@ -16021,6 +16030,39 @@ function findArtifactLineOutsideBlocks(source, artifactLine) {
16021
16030
  if (artifactLine.test(trimmed))
16022
16031
  return trimmed;
16023
16032
  }
16033
+ return lexedArtifactNote(source, lines, artifactLine);
16034
+ }
16035
+ function lexedArtifactNote(source, lines, artifactLine) {
16036
+ if (!lines.some((line) => /\S.*##/.test(line) && !/^\s*##/.test(line))) {
16037
+ return;
16038
+ }
16039
+ const translator = new MalloyTranslator(NOTEBOOK_PARSE_URL, null, {
16040
+ urls: { [NOTEBOOK_PARSE_URL]: source }
16041
+ });
16042
+ const parse = translator.parseStep.step(translator).parse;
16043
+ const stream = parse?.tokenStream;
16044
+ const tokens = stream?.getTokens?.() ?? [];
16045
+ const name = (type) => stream?.tokenSource?.vocabulary?.getSymbolicName(type);
16046
+ const text = (i) => source.slice(tokens[i].startIndex, tokens[i].stopIndex + 1);
16047
+ for (let i = 0;i < tokens.length; i++) {
16048
+ const kind = name(tokens[i].type);
16049
+ if (kind === "DOC_ANNOTATION" && artifactLine.test(text(i))) {
16050
+ return text(i).trim();
16051
+ }
16052
+ if (kind !== "DOC_BLOCK_ANNOTATION_BEGIN")
16053
+ continue;
16054
+ let body = "";
16055
+ let j = i;
16056
+ while (name(tokens[j].type) !== "BLOCK_ANNOTATION_END") {
16057
+ body += text(j);
16058
+ if (++j >= tokens.length || name(tokens[j].type) === "EOF") {
16059
+ return;
16060
+ }
16061
+ }
16062
+ if (artifactLine.test(body))
16063
+ return body;
16064
+ i = j;
16065
+ }
16024
16066
  return;
16025
16067
  }
16026
16068
  function artifactKindOfNotes(notes) {
@@ -16296,7 +16338,9 @@ ${body}`;
16296
16338
  codeLine: (codeLineOf(span.startCp, span.stopCp) ?? span.startLine) - span.startLine,
16297
16339
  ...caption !== undefined && { caption },
16298
16340
  startLine: span.startLine,
16299
- endLine: span.endLine
16341
+ endLine: span.endLine,
16342
+ start: span.start,
16343
+ end: span.end
16300
16344
  });
16301
16345
  continue;
16302
16346
  }
@@ -16350,7 +16394,7 @@ ${body}`;
16350
16394
  });
16351
16395
  }
16352
16396
  const runCount = items.filter((item) => item.kind === "statement" && item.run).length;
16353
- if (runCount !== modelDef.queryList.length) {
16397
+ if (modelDef && runCount !== modelDef.queryList.length) {
16354
16398
  return refuse({
16355
16399
  line: 1,
16356
16400
  message: `Line 1: the notebook reader found ${runCount} run: statements where Malloy compiled ${modelDef.queryList.length}, so its query cells cannot be matched to their results. Fix: none in the file; report it, since the reader and the compiler disagree.`
@@ -16365,7 +16409,7 @@ ${body}`;
16365
16409
  if (item.kind === "statement") {
16366
16410
  lineRun = undefined;
16367
16411
  const queryIndex = item.run ? runsSeen++ : undefined;
16368
- cells.push(item.run && belowTag ? {
16412
+ const pushed = item.run && belowTag ? {
16369
16413
  kind: "query",
16370
16414
  type: "code",
16371
16415
  text: item.text,
@@ -16394,7 +16438,9 @@ ${body}`;
16394
16438
  },
16395
16439
  startLine: item.startLine,
16396
16440
  endLine: item.endLine
16397
- });
16441
+ };
16442
+ cellOffsets.set(pushed, [item.start, item.end]);
16443
+ cells.push(pushed);
16398
16444
  return;
16399
16445
  }
16400
16446
  const { note } = item;
@@ -16498,7 +16544,7 @@ function notebookReaderProblem(modelPath, text, modelDef, url) {
16498
16544
  }
16499
16545
  };
16500
16546
  }
16501
- var NOTEBOOKS_DIR = "notebooks", DASHBOARDS_DIR = "dashboards", ARTIFACT_NOTE, ANY_ARTIFACT_NOTE, MARKDOWN_ROUTE = "markdown", attachedNowhereFix = (block) => block ? "write it as a floating `##|(markdown)` block closed by `|##` for prose that stands on its own, or move it directly above the statement it describes." : "write it as a floating `##(markdown)` line for prose that stands on its own, or move it directly above the statement it describes.", isRuleNode = (node) => node !== undefined && node.ruleIndex !== undefined && (node.childCount ?? 0) > 0, callAccessor = (node, name) => {
16547
+ var NOTEBOOKS_DIR = "notebooks", DASHBOARDS_DIR = "dashboards", ARTIFACT_NOTE, ANY_ARTIFACT_NOTE, MARKDOWN_ROUTE = "markdown", attachedNowhereFix = (block) => block ? "write it as a floating `##|(markdown)` block closed by `|##` for prose that stands on its own, or move it directly above the statement it describes." : "write it as a floating `##(markdown)` line for prose that stands on its own, or move it directly above the statement it describes.", cellOffsets, isRuleNode = (node) => node !== undefined && node.ruleIndex !== undefined && (node.childCount ?? 0) > 0, callAccessor = (node, name) => {
16502
16548
  const accessor = node[name];
16503
16549
  return typeof accessor === "function" ? accessor.call(node) : undefined;
16504
16550
  }, STATEMENT_ACCESSORS, NOTEBOOK_PARSE_URL = "file:///publisher-notebook-reader/notebook.malloy", NOTE_TOKENS, TEXT_BLOCK_NAME;
@@ -16563,6 +16609,7 @@ var init_notebook = __esm(() => {
16563
16609
  }
16564
16610
  };
16565
16611
  ANY_ARTIFACT_NOTE = /^#{1,2}\|?(?=[ \t\r\n])(?:[^"'\\]|\\.|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')*?(?<![\w\u00C0-\u024F\u1E00-\u1EFF.$-])artifact(?![\w\u00C0-\u024F\u1E00-\u1EFF])/;
16612
+ cellOffsets = new WeakMap;
16566
16613
  STATEMENT_ACCESSORS = [
16567
16614
  ["runStatement", "run"],
16568
16615
  ["docAnnotations", "notes"],
@@ -23267,6 +23314,7 @@ function malloyGivenToApi(given) {
23267
23314
  type: renderedType,
23268
23315
  annotations: allNotes.filter((note) => !isReservedRoute(note.route) && note.route !== MARKDOWN_ROUTE).map((note) => note.text),
23269
23316
  ...readGivenControlSpec(allNotes.map((note) => note.text)),
23317
+ ...allNotes.some((note) => note.route === "secure") && { secure: true },
23270
23318
  default: given._internal?.defaultText
23271
23319
  };
23272
23320
  }
@@ -23443,6 +23491,13 @@ function readArtifactTag(tag, declaredType) {
23443
23491
  autorun
23444
23492
  };
23445
23493
  }
23494
+ function readDocumentTileExpressions(notes) {
23495
+ const tag = motlyTag(notes);
23496
+ const composite = tag ? readArtifactTag(tag, () => {
23497
+ return;
23498
+ }) : undefined;
23499
+ return (composite?.tiles ?? []).flatMap((entry) => entry.kind === "text" ? [] : [entry.expression]);
23500
+ }
23446
23501
  function readTextBlocks(notes) {
23447
23502
  return notes.flatMap((note) => {
23448
23503
  const newline = note.indexOf(`
@@ -23626,6 +23681,9 @@ function givenSpec(declaration, facts) {
23626
23681
  type: declaration.type,
23627
23682
  default: declaration.type.startsWith("filter<") && declaration.default !== undefined ? unwrapFilterLiteral(declaration.default) : declaration.default,
23628
23683
  annotations: declaration.annotations.filter((text) => /^##?\(/.test(text) && !isMarkdownNote(text)),
23684
+ ...declaration.annotations.some((text) => /^#\(secure\)/.test(text)) && {
23685
+ secure: true
23686
+ },
23629
23687
  ...control
23630
23688
  };
23631
23689
  }
@@ -23634,7 +23692,7 @@ function factsCarryArtifactTag(facts) {
23634
23692
  return true;
23635
23693
  return facts.queries.some((query) => Boolean(motlyTag(query.annotations)?.tag("artifact")));
23636
23694
  }
23637
- function buildDashboardManifest(facts) {
23695
+ function buildDashboardManifest(facts, options) {
23638
23696
  const name = dashboardSlug(facts.modelPath);
23639
23697
  const base = {
23640
23698
  name,
@@ -23657,6 +23715,9 @@ function buildDashboardManifest(facts) {
23657
23715
  };
23658
23716
  }
23659
23717
  const query = entry.expression;
23718
+ if (options?.restrictedTiles?.has(normalizeTileExpression(query))) {
23719
+ return { kind: "query", query, restricted: true };
23720
+ }
23660
23721
  const givenNames = resolveTileGivens(query, facts);
23661
23722
  const layout = resolveTileLayout(query, facts);
23662
23723
  return {
@@ -23666,7 +23727,7 @@ function buildDashboardManifest(facts) {
23666
23727
  ...layout
23667
23728
  };
23668
23729
  });
23669
- const queries = queryTiles(tiles);
23730
+ const queries = queryTiles(tiles).filter((tile) => !tile.restricted);
23670
23731
  const doc = docCommentTitleAndDescription(facts.descriptionNotes ?? facts.modelAnnotations, composite.title);
23671
23732
  return {
23672
23733
  ...base,
@@ -23959,9 +24020,40 @@ function lintSelfDrills(facts) {
23959
24020
  }
23960
24021
  return Array.from(findings.values());
23961
24022
  }
24023
+ async function compileTileGivens(tiles, materializer, fallbackRegistry, gatesOf, skip, onPrepared, onCompileError) {
24024
+ const compiled = new Map;
24025
+ for (const tile of tiles) {
24026
+ const key = normalizeTileExpression(tile);
24027
+ if (compiled.has(key) || skip?.has(key))
24028
+ continue;
24029
+ let prepared;
24030
+ try {
24031
+ prepared = await materializer.loadQuery(`run: ${tile}`).getPreparedQuery();
24032
+ } catch (error) {
24033
+ onCompileError?.(tile, error);
24034
+ continue;
24035
+ }
24036
+ await onPrepared?.(tile, prepared);
24037
+ const usage = prepared._query?.givenUsage;
24038
+ if (!usage)
24039
+ continue;
24040
+ const registry = prepared._modelDef?.givens ?? fallbackRegistry ?? {};
24041
+ const reads = usage.map((given) => registry[given.id]?.name).filter((name) => name !== undefined);
24042
+ const target = prepared._query?.structRef;
24043
+ const sourceName = typeof target === "string" ? target : target?.as || target?.name;
24044
+ const gateReads = new Set;
24045
+ for (const expr of sourceName ? gatesOf(sourceName) ?? [] : []) {
24046
+ for (const name of referencedGivenNames(expr))
24047
+ gateReads.add(name);
24048
+ }
24049
+ compiled.set(key, { reads, gateReads: Array.from(gateReads) });
24050
+ }
24051
+ return compiled;
24052
+ }
23962
24053
  var MAX_BUILDER_COLUMNS = 24, COMPONENT_FILE_SUFFIXES, MODEL_FILE_SUFFIX2 = ".malloy", PUBLISHER_OWNED_TAGS, RENDERER_CHILD_TAGS, UNKNOWN_RENDER_TAG, COMPOSITE_ARTIFACT_PROPERTIES, QUERY_ARTIFACT_PROPERTIES, ARTIFACT_PROPERTY_REPLACEMENTS, NAME_THE_TILES = `Name the views to show: tiles=["source -> view", …].`;
23963
24054
  var init_dashboard = __esm(() => {
23964
24055
  init_annotations();
24056
+ init_authorize();
23965
24057
  init_notebook();
23966
24058
  init_given();
23967
24059
  init_motly();
@@ -24414,11 +24506,11 @@ var init_source_extraction = __esm(() => {
24414
24506
  });
24415
24507
 
24416
24508
  // src/utils.ts
24417
- import * as fs from "fs";
24418
- import * as path from "path";
24509
+ import * as fs2 from "fs";
24510
+ import * as path2 from "path";
24419
24511
  import { fileURLToPath } from "url";
24420
24512
  function ignoreDotfiles(file) {
24421
- return path.basename(file).startsWith(".");
24513
+ return path2.basename(file).startsWith(".");
24422
24514
  }
24423
24515
  function errMessage(err) {
24424
24516
  return err instanceof Error ? err.message : String(err);
@@ -24427,11 +24519,11 @@ var URL_READER;
24427
24519
  var init_utils = __esm(() => {
24428
24520
  URL_READER = {
24429
24521
  readURL: (url) => {
24430
- let path2 = url.toString();
24522
+ let path3 = url.toString();
24431
24523
  if (url.protocol == "file:") {
24432
- path2 = fileURLToPath(url);
24524
+ path3 = fileURLToPath(url);
24433
24525
  }
24434
- return fs.promises.readFile(path2, "utf8");
24526
+ return fs2.promises.readFile(path3, "utf8");
24435
24527
  }
24436
24528
  };
24437
24529
  });
@@ -24457,8 +24549,8 @@ import {
24457
24549
  MalloySQLParser,
24458
24550
  MalloySQLStatementType
24459
24551
  } from "@malloydata/malloy-sql";
24460
- import * as fs2 from "fs";
24461
- import * as path2 from "path";
24552
+ import * as fs3 from "fs";
24553
+ import * as path3 from "path";
24462
24554
  import { parentPort, threadId } from "node:worker_threads";
24463
24555
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
24464
24556
 
@@ -24645,6 +24737,254 @@ var EXPLORES_EMPTY_SUPPRESSES_CONVENTION = `"explores" in publisher.json is depr
24645
24737
  var EXPLORES_EMPTY_IS_UNCURATED = `"explores": [] in publisher.json does nothing. Fix: delete it.`;
24646
24738
  var QUERYABLE_SOURCES_DEPRECATION = `"queryableSources" in publisher.json does nothing. Fix: delete it.`;
24647
24739
 
24740
+ // src/service/package_retrieval.ts
24741
+ init_errors();
24742
+ import { createHash } from "crypto";
24743
+ import * as fs from "fs";
24744
+ import * as path from "path";
24745
+ var PACKAGE_REPRESENTATIONS = ["single", "facets"];
24746
+ var KEYPHRASE_MODES = ["auto", "never", "always"];
24747
+ var REFINE_LEVEL_NAMES = ["LOW", "MEDIUM", "HIGH"];
24748
+ var DEFAULT_RERANK_TOP_SOURCES = 8;
24749
+ var DEFAULT_REFINE_MIN_LEVEL = "MEDIUM";
24750
+ var RETRIEVAL_KEYS = [
24751
+ "representation",
24752
+ "keyphrases",
24753
+ "refine",
24754
+ "rerank",
24755
+ "sourceMatch",
24756
+ "sourceSummary",
24757
+ "prompts"
24758
+ ];
24759
+ var PROMPT_KEYS = [
24760
+ "keyphrase",
24761
+ "refine",
24762
+ "rerank",
24763
+ "sourceMatch",
24764
+ "sourceSummary"
24765
+ ];
24766
+ var REFINE_KEYS = ["enabled", "minLevel"];
24767
+ var RERANK_KEYS = ["enabled", "topSources"];
24768
+ var SOURCE_MATCH_KEYS = ["enabled"];
24769
+ var SOURCE_SUMMARY_KEYS = ["enabled"];
24770
+ var DEFAULT_PACKAGE_RETRIEVAL = {
24771
+ representation: "single",
24772
+ keyphrases: "auto",
24773
+ prompts: {}
24774
+ };
24775
+ function refineSettingsOf(retrieval) {
24776
+ return retrieval.refine ?? {
24777
+ enabled: "auto",
24778
+ minLevel: DEFAULT_REFINE_MIN_LEVEL
24779
+ };
24780
+ }
24781
+ function rerankSettingsOf(retrieval) {
24782
+ return retrieval.rerank ?? {
24783
+ enabled: "auto",
24784
+ topSources: DEFAULT_RERANK_TOP_SOURCES
24785
+ };
24786
+ }
24787
+ function sourceMatchSettingsOf(retrieval) {
24788
+ return retrieval.sourceMatch ?? { enabled: "auto" };
24789
+ }
24790
+ function sourceSummarySettingsOf(retrieval) {
24791
+ return retrieval.sourceSummary ?? { enabled: "auto" };
24792
+ }
24793
+ function fail(message) {
24794
+ throw new PackageManifestError(message);
24795
+ }
24796
+ function describe(value) {
24797
+ return JSON.stringify(value) ?? "nothing";
24798
+ }
24799
+ function stepsOutside(within) {
24800
+ return within === ".." || within.startsWith(`..${path.sep}`) || path.isAbsolute(within);
24801
+ }
24802
+ function resolvePromptPath(packageRoot, rel, promptKey = "keyphrase") {
24803
+ const key = `retrieval.prompts.${promptKey}`;
24804
+ const fix = `Fix: use a path inside the package, e.g. "prompts/${promptKey}.md".`;
24805
+ if (rel.includes("\x00") || path.isAbsolute(rel) || /^[A-Za-z]:[\\/]/.test(rel) || rel.startsWith("\\")) {
24806
+ fail(`Invalid publisher.json ${key}: ${describe(rel)} is an absolute path. ${fix}`);
24807
+ }
24808
+ const root = path.resolve(packageRoot);
24809
+ const resolved = path.resolve(root, rel);
24810
+ const within = path.relative(root, resolved);
24811
+ if (within === "" || stepsOutside(within)) {
24812
+ fail(`Invalid publisher.json ${key}: ${describe(rel)} resolves outside the package directory. ${fix}`);
24813
+ }
24814
+ return resolved;
24815
+ }
24816
+ function objectBlock(raw, key, valid, fix) {
24817
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
24818
+ fail(`Invalid publisher.json ${key}: expected an object, got ${describe(raw)}. Fix: ${fix}`);
24819
+ }
24820
+ const obj = raw;
24821
+ const unknown = Object.keys(obj).filter((k) => !valid.includes(k));
24822
+ if (unknown.length > 0) {
24823
+ fail(`Invalid publisher.json ${key}: unknown key ${unknown.map((k) => `'${k}'`).join(", ")}. ` + `Valid keys: ${valid.join(", ")}.`);
24824
+ }
24825
+ return obj;
24826
+ }
24827
+ function parseEnabled(value, key) {
24828
+ if (value === undefined || value === null)
24829
+ return "auto";
24830
+ if (value === "auto" || value === true || value === false)
24831
+ return value;
24832
+ return fail(`Invalid publisher.json ${key}: expected "auto", true or false, got ${describe(value)}. ` + `Fix: "enabled": "auto" (on when the server has an LLM), true (require one) or false.`);
24833
+ }
24834
+ function parseRefine(raw) {
24835
+ const obj = objectBlock(raw, "retrieval.refine", REFINE_KEYS, `"refine": { "enabled": "auto", "minLevel": "MEDIUM" }`);
24836
+ const minLevel = obj.minLevel ?? DEFAULT_REFINE_MIN_LEVEL;
24837
+ if (typeof minLevel !== "string" || !REFINE_LEVEL_NAMES.includes(minLevel)) {
24838
+ fail(`Invalid publisher.json retrieval.refine.minLevel: expected one of ${REFINE_LEVEL_NAMES.join(", ")}, got ${describe(obj.minLevel)}. ` + `Fix: "minLevel": "MEDIUM" (drop candidates the model rates below it).`);
24839
+ }
24840
+ return {
24841
+ enabled: parseEnabled(obj.enabled, "retrieval.refine.enabled"),
24842
+ minLevel
24843
+ };
24844
+ }
24845
+ function parseRerank(raw) {
24846
+ const obj = objectBlock(raw, "retrieval.rerank", RERANK_KEYS, `"rerank": { "enabled": "auto", "topSources": ${DEFAULT_RERANK_TOP_SOURCES} }`);
24847
+ const top = obj.topSources ?? DEFAULT_RERANK_TOP_SOURCES;
24848
+ if (typeof top !== "number" || !Number.isSafeInteger(top) || top <= 0) {
24849
+ fail(`Invalid publisher.json retrieval.rerank.topSources: expected a positive integer, got ${describe(obj.topSources)}. ` + `Fix: "topSources": ${DEFAULT_RERANK_TOP_SOURCES}`);
24850
+ }
24851
+ return {
24852
+ enabled: parseEnabled(obj.enabled, "retrieval.rerank.enabled"),
24853
+ topSources: top
24854
+ };
24855
+ }
24856
+ function parseSourceMatch(raw) {
24857
+ const obj = objectBlock(raw, "retrieval.sourceMatch", SOURCE_MATCH_KEYS, `"sourceMatch": { "enabled": "auto" }`);
24858
+ return {
24859
+ enabled: parseEnabled(obj.enabled, "retrieval.sourceMatch.enabled")
24860
+ };
24861
+ }
24862
+ function parseSourceSummary(raw) {
24863
+ const obj = objectBlock(raw, "retrieval.sourceSummary", SOURCE_SUMMARY_KEYS, `"sourceSummary": { "enabled": "auto" }`);
24864
+ return {
24865
+ enabled: parseEnabled(obj.enabled, "retrieval.sourceSummary.enabled")
24866
+ };
24867
+ }
24868
+ function parsePackageRetrieval(raw) {
24869
+ if (raw === undefined || raw === null) {
24870
+ return {
24871
+ representation: DEFAULT_PACKAGE_RETRIEVAL.representation,
24872
+ keyphrases: DEFAULT_PACKAGE_RETRIEVAL.keyphrases,
24873
+ promptPaths: {}
24874
+ };
24875
+ }
24876
+ if (typeof raw !== "object" || Array.isArray(raw)) {
24877
+ fail(`Invalid publisher.json retrieval: expected an object, got ${describe(raw)}. ` + `Fix: "retrieval": { "representation": "single" }`);
24878
+ }
24879
+ const obj = raw;
24880
+ const unknown = Object.keys(obj).filter((k) => !RETRIEVAL_KEYS.includes(k));
24881
+ if (unknown.length > 0) {
24882
+ fail(`Invalid publisher.json retrieval: unknown key ${unknown.map((k) => `'${k}'`).join(", ")}. ` + `Valid keys: ${RETRIEVAL_KEYS.join(", ")}.`);
24883
+ }
24884
+ const representation = obj.representation ?? "single";
24885
+ if (typeof representation !== "string" || !PACKAGE_REPRESENTATIONS.includes(representation)) {
24886
+ fail(`Invalid publisher.json retrieval.representation: expected one of ${PACKAGE_REPRESENTATIONS.join(", ")}, got ${describe(obj.representation)}. ` + `Fix: "representation": "single" (one row per entity) or "facets" (a name row plus doc chunks).`);
24887
+ }
24888
+ const keyphrases = obj.keyphrases ?? "auto";
24889
+ if (typeof keyphrases !== "string" || !KEYPHRASE_MODES.includes(keyphrases)) {
24890
+ fail(`Invalid publisher.json retrieval.keyphrases: expected one of ${KEYPHRASE_MODES.join(", ")}, got ${describe(obj.keyphrases)}. ` + `Fix: "keyphrases": "auto".`);
24891
+ }
24892
+ const refine = obj.refine === undefined || obj.refine === null ? undefined : parseRefine(obj.refine);
24893
+ const rerank = obj.rerank === undefined || obj.rerank === null ? undefined : parseRerank(obj.rerank);
24894
+ const sourceMatch = obj.sourceMatch === undefined || obj.sourceMatch === null ? undefined : parseSourceMatch(obj.sourceMatch);
24895
+ const sourceSummary = obj.sourceSummary === undefined || obj.sourceSummary === null ? undefined : parseSourceSummary(obj.sourceSummary);
24896
+ const promptPaths = {};
24897
+ if (obj.prompts !== undefined && obj.prompts !== null) {
24898
+ if (typeof obj.prompts !== "object" || Array.isArray(obj.prompts)) {
24899
+ fail(`Invalid publisher.json retrieval.prompts: expected an object, got ${describe(obj.prompts)}. ` + `Fix: "prompts": { "keyphrase": "prompts/keyphrase.md" }`);
24900
+ }
24901
+ const prompts = obj.prompts;
24902
+ const badPrompt = Object.keys(prompts).filter((k) => !PROMPT_KEYS.includes(k));
24903
+ if (badPrompt.length > 0) {
24904
+ fail(`Invalid publisher.json retrieval.prompts: unknown key ${badPrompt.map((k) => `'${k}'`).join(", ")}. ` + `Valid keys: ${PROMPT_KEYS.join(", ")}.`);
24905
+ }
24906
+ for (const key of PROMPT_KEYS) {
24907
+ const value = prompts[key];
24908
+ if (value === undefined || value === null)
24909
+ continue;
24910
+ if (typeof value !== "string" || value.trim() === "") {
24911
+ fail(`Invalid publisher.json retrieval.prompts.${key}: expected a file path inside the package, got ${describe(value)}. ` + `Fix: "${key}": "prompts/${key}.md"`);
24912
+ }
24913
+ promptPaths[key] = value;
24914
+ }
24915
+ }
24916
+ return {
24917
+ representation,
24918
+ keyphrases,
24919
+ ...refine ? { refine } : {},
24920
+ ...rerank ? { rerank } : {},
24921
+ ...sourceMatch ? { sourceMatch } : {},
24922
+ ...sourceSummary ? { sourceSummary } : {},
24923
+ promptPaths
24924
+ };
24925
+ }
24926
+ async function readPromptFile(packageRoot, key, rel) {
24927
+ const file = resolvePromptPath(packageRoot, rel, key);
24928
+ const at = `retrieval.prompts.${key}`;
24929
+ let text;
24930
+ try {
24931
+ const [realRoot, realFile] = await Promise.all([
24932
+ fs.promises.realpath(packageRoot),
24933
+ fs.promises.realpath(file)
24934
+ ]);
24935
+ const within = path.relative(realRoot, realFile);
24936
+ if (stepsOutside(within)) {
24937
+ fail(`Invalid publisher.json ${at}: ${describe(rel)} resolves outside the package directory through a link. ` + `Fix: copy the file into the package.`);
24938
+ }
24939
+ text = await fs.promises.readFile(realFile, "utf8");
24940
+ } catch (error) {
24941
+ if (error instanceof PackageManifestError)
24942
+ throw error;
24943
+ return fail(`Invalid publisher.json ${at}: cannot read ${describe(rel)} (${error.message}). ` + `Fix: create the file inside the package or remove the key.`);
24944
+ }
24945
+ if (text.trim() === "") {
24946
+ fail(`Invalid publisher.json ${at}: ${describe(rel)} is empty. ` + `Fix: write the prompt, or remove the key to use the built-in one.`);
24947
+ }
24948
+ return {
24949
+ path: rel,
24950
+ text,
24951
+ hash: createHash("sha256").update(text).digest("hex")
24952
+ };
24953
+ }
24954
+ async function readPackageRetrieval(packageRoot, raw) {
24955
+ const parsed = parsePackageRetrieval(raw);
24956
+ const prompts = {};
24957
+ for (const key of PROMPT_KEYS) {
24958
+ const rel = parsed.promptPaths[key];
24959
+ if (rel !== undefined) {
24960
+ prompts[key] = await readPromptFile(packageRoot, key, rel);
24961
+ }
24962
+ }
24963
+ return {
24964
+ representation: parsed.representation,
24965
+ keyphrases: parsed.keyphrases,
24966
+ ...parsed.refine ? { refine: parsed.refine } : {},
24967
+ ...parsed.rerank ? { rerank: parsed.rerank } : {},
24968
+ ...parsed.sourceMatch ? { sourceMatch: parsed.sourceMatch } : {},
24969
+ ...parsed.sourceSummary ? { sourceSummary: parsed.sourceSummary } : {},
24970
+ prompts
24971
+ };
24972
+ }
24973
+ function assertRequiredStagesAvailable(retrieval, llmConfigured) {
24974
+ if (llmConfigured)
24975
+ return;
24976
+ for (const [key, stage] of [
24977
+ ["refine", retrieval.refine],
24978
+ ["rerank", retrieval.rerank],
24979
+ ["sourceMatch", retrieval.sourceMatch],
24980
+ ["sourceSummary", retrieval.sourceSummary]
24981
+ ]) {
24982
+ if (stage?.enabled === true) {
24983
+ fail(`Invalid publisher.json retrieval.${key}.enabled: true needs an LLM, and this server has none configured. ` + `Fix: set retrieval.llm in publisher.config.json and LLM_API_KEY in the server's environment, ` + `or set "enabled": "auto" (on only when an LLM is configured) or false.`);
24984
+ }
24985
+ }
24986
+ }
24987
+
24648
24988
  // src/package_load/package_load_worker.ts
24649
24989
  init_source_extraction();
24650
24990
  init_given();
@@ -24704,9 +25044,9 @@ function newRpcId() {
24704
25044
  }
24705
25045
  function callMain(send) {
24706
25046
  const requestId = newRpcId();
24707
- return new Promise((resolve2, reject2) => {
25047
+ return new Promise((resolve3, reject2) => {
24708
25048
  pendingRpc.set(requestId, {
24709
- resolve: (value) => resolve2(value),
25049
+ resolve: (value) => resolve3(value),
24710
25050
  reject: reject2
24711
25051
  });
24712
25052
  send(requestId);
@@ -24826,10 +25166,10 @@ function makeWorkerUrlReader(job) {
24826
25166
  readURL: async (url) => {
24827
25167
  if (url.protocol === "file:") {
24828
25168
  const filePath = fileURLToPath2(url);
24829
- if (job.replacement && path2.resolve(filePath) === path2.resolve(job.packagePath, job.replacement.modelPath)) {
25169
+ if (job.replacement && path3.resolve(filePath) === path3.resolve(job.packagePath, job.replacement.modelPath)) {
24830
25170
  return job.replacement.source;
24831
25171
  }
24832
- return fs2.promises.readFile(filePath, "utf8");
25172
+ return fs3.promises.readFile(filePath, "utf8");
24833
25173
  }
24834
25174
  const response = await callMain((requestId) => {
24835
25175
  const req = {
@@ -24879,8 +25219,8 @@ function buildWorkerMalloyConfig(job) {
24879
25219
  return config;
24880
25220
  }
24881
25221
  async function readPackageMetadata(packagePath, modelPaths) {
24882
- const manifestPath = path2.join(packagePath, PACKAGE_MANIFEST_NAME);
24883
- const contents = await fs2.promises.readFile(manifestPath, "utf8");
25222
+ const manifestPath = path3.join(packagePath, PACKAGE_MANIFEST_NAME);
25223
+ const contents = await fs3.promises.readFile(manifestPath, "utf8");
24884
25224
  let parsed;
24885
25225
  try {
24886
25226
  parsed = JSON.parse(contents);
@@ -24911,12 +25251,13 @@ async function readPackageMetadata(packagePath, modelPaths) {
24911
25251
  manifestLocation: typeof parsed.manifestLocation === "string" ? parsed.manifestLocation : null,
24912
25252
  materialization: materializationWithQueryMetadata(parsePackageMaterialization(parsed.materialization), queryMetadata.queryMetadata),
24913
25253
  scope: scope.scope,
24914
- manifestWarnings: manifestWarnings.length > 0 ? manifestWarnings : undefined
25254
+ manifestWarnings: manifestWarnings.length > 0 ? manifestWarnings : undefined,
25255
+ retrieval: await readPackageRetrieval(packagePath, parsed.retrieval)
24915
25256
  };
24916
25257
  }
24917
25258
  async function listPackageFiles(packagePath) {
24918
25259
  const files = await import_recursive_readdir.default(packagePath, [ignoreDotfiles]);
24919
- return files.map((full) => path2.relative(packagePath, full).replace(/\\/g, "/")).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
25260
+ return files.map((full) => path3.relative(packagePath, full).replace(/\\/g, "/")).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
24920
25261
  }
24921
25262
  function filterModelPaths(allRelative) {
24922
25263
  return allRelative.filter((p) => p.endsWith(MODEL_FILE_SUFFIX) || p.endsWith(NOTEBOOK_FILE_SUFFIX));
@@ -24985,7 +25326,7 @@ function buildRuntimeForModel(job, malloyConfig) {
24985
25326
  }
24986
25327
  async function compileMalloyModel(job, malloyConfig, modelPath) {
24987
25328
  const compileStart = performance.now();
24988
- const fullPath = path2.join(job.packagePath, modelPath);
25329
+ const fullPath = path3.join(job.packagePath, modelPath);
24989
25330
  const modelURL = pathToFileURL(fullPath);
24990
25331
  const importBaseURL = new URL(".", modelURL);
24991
25332
  const { runtime, urlReader, textFor } = buildRuntimeForModel(job, malloyConfig);
@@ -25050,12 +25391,12 @@ async function compileMalloyModel(job, malloyConfig, modelPath) {
25050
25391
  }
25051
25392
  async function compileNotebookModel(job, malloyConfig, modelPath) {
25052
25393
  const compileStart = performance.now();
25053
- const fullPath = path2.join(job.packagePath, modelPath);
25394
+ const fullPath = path3.join(job.packagePath, modelPath);
25054
25395
  const modelURL = pathToFileURL(fullPath);
25055
25396
  const importBaseURL = new URL(".", modelURL);
25056
25397
  const { runtime, urlReader } = buildRuntimeForModel(job, malloyConfig);
25057
25398
  const authorizeWarningCollection = authorizeWarningCollector();
25058
- const fileContents = await fs2.promises.readFile(modelURL, "utf8");
25399
+ const fileContents = await fs3.promises.readFile(modelURL, "utf8");
25059
25400
  const parse = MalloySQLParser.parse(fileContents, modelPath);
25060
25401
  let mm;
25061
25402
  const perCellMM = parse.statements.map((stmt) => {