@malloydata/malloy 0.0.391 → 0.0.392

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 (127) hide show
  1. package/dist/api/asynchronous.js +0 -3
  2. package/dist/api/foundation/compile.d.ts +1 -1
  3. package/dist/api/foundation/config.d.ts +80 -8
  4. package/dist/api/foundation/config.js +151 -69
  5. package/dist/api/foundation/config_compile.js +27 -35
  6. package/dist/api/foundation/config_discover.js +5 -9
  7. package/dist/api/foundation/config_overlays.d.ts +6 -0
  8. package/dist/api/foundation/config_overlays.js +12 -0
  9. package/dist/api/foundation/config_resolve.d.ts +4 -1
  10. package/dist/api/foundation/config_resolve.js +64 -4
  11. package/dist/api/foundation/core.d.ts +75 -2
  12. package/dist/api/foundation/core.js +104 -6
  13. package/dist/api/foundation/index.d.ts +2 -0
  14. package/dist/api/foundation/readers.js +1 -1
  15. package/dist/api/foundation/runtime.d.ts +68 -2
  16. package/dist/api/foundation/runtime.js +212 -10
  17. package/dist/api/foundation/types.d.ts +2 -1
  18. package/dist/index.d.ts +3 -1
  19. package/dist/lang/ast/ast-utils.js +0 -1
  20. package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +1 -1
  21. package/dist/lang/ast/expressions/expr-aggregate-function.js +9 -8
  22. package/dist/lang/ast/expressions/expr-coalesce.d.ts +1 -1
  23. package/dist/lang/ast/expressions/expr-coalesce.js +2 -3
  24. package/dist/lang/ast/expressions/expr-count-distinct.js +1 -1
  25. package/dist/lang/ast/expressions/expr-count.js +6 -4
  26. package/dist/lang/ast/expressions/expr-filter-expr.js +0 -1
  27. package/dist/lang/ast/expressions/expr-func.js +9 -4
  28. package/dist/lang/ast/expressions/expr-given.d.ts +18 -0
  29. package/dist/lang/ast/expressions/expr-given.js +69 -0
  30. package/dist/lang/ast/expressions/expr-granular-time.d.ts +1 -1
  31. package/dist/lang/ast/expressions/expr-id-reference.js +3 -2
  32. package/dist/lang/ast/expressions/expr-now.js +0 -1
  33. package/dist/lang/ast/expressions/expr-props.d.ts +132 -132
  34. package/dist/lang/ast/expressions/expr-props.js +2 -2
  35. package/dist/lang/ast/expressions/expr-ungroup.d.ts +1 -1
  36. package/dist/lang/ast/expressions/expr-ungroup.js +4 -4
  37. package/dist/lang/ast/expressions/for-range.d.ts +1 -1
  38. package/dist/lang/ast/expressions/function-ordering.d.ts +1 -1
  39. package/dist/lang/ast/expressions/function-ordering.js +2 -2
  40. package/dist/lang/ast/expressions/time-literal.d.ts +3 -3
  41. package/dist/lang/ast/field-space/include-utils.js +2 -2
  42. package/dist/lang/ast/field-space/index-field-space.js +18 -23
  43. package/dist/lang/ast/field-space/passthrough-space.d.ts +1 -1
  44. package/dist/lang/ast/field-space/query-spaces.d.ts +6 -2
  45. package/dist/lang/ast/field-space/query-spaces.js +29 -19
  46. package/dist/lang/ast/field-space/reference-field.js +1 -1
  47. package/dist/lang/ast/field-space/rename-space-field.d.ts +1 -1
  48. package/dist/lang/ast/field-space/rename-space-field.js +2 -2
  49. package/dist/lang/ast/field-space/struct-space-field-base.js +2 -3
  50. package/dist/lang/ast/index.d.ts +2 -0
  51. package/dist/lang/ast/index.js +2 -0
  52. package/dist/lang/ast/query-builders/index-builder.d.ts +1 -1
  53. package/dist/lang/ast/query-builders/index-builder.js +4 -3
  54. package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -2
  55. package/dist/lang/ast/query-builders/reduce-builder.js +4 -5
  56. package/dist/lang/ast/query-elements/query-arrow.js +3 -2
  57. package/dist/lang/ast/query-elements/query-base.d.ts +1 -1
  58. package/dist/lang/ast/query-elements/query-base.js +1 -1
  59. package/dist/lang/ast/query-elements/query-refine.js +3 -1
  60. package/dist/lang/ast/query-items/field-declaration.js +2 -2
  61. package/dist/lang/ast/query-properties/drill.js +6 -6
  62. package/dist/lang/ast/query-properties/filters.js +2 -2
  63. package/dist/lang/ast/query-properties/nest.js +3 -3
  64. package/dist/lang/ast/source-elements/composite-source.js +5 -3
  65. package/dist/lang/ast/source-elements/named-source.js +4 -0
  66. package/dist/lang/ast/source-elements/sql-source.js +2 -2
  67. package/dist/lang/ast/source-elements/table-source.js +3 -1
  68. package/dist/lang/ast/source-properties/join.js +4 -4
  69. package/dist/lang/ast/source-query-elements/sq-reference.js +2 -1
  70. package/dist/lang/ast/statements/define-given.d.ts +29 -0
  71. package/dist/lang/ast/statements/define-given.js +163 -0
  72. package/dist/lang/ast/statements/import-statement.js +72 -9
  73. package/dist/lang/ast/typedesc-utils.d.ts +3 -1
  74. package/dist/lang/ast/typedesc-utils.js +4 -47
  75. package/dist/lang/ast/types/expr-value.js +2 -3
  76. package/dist/lang/ast/types/expression-def.d.ts +2 -2
  77. package/dist/lang/ast/types/expression-def.js +2 -2
  78. package/dist/lang/ast/types/malloy-element.d.ts +5 -15
  79. package/dist/lang/ast/types/malloy-element.js +113 -1
  80. package/dist/lang/ast/types/space-field.js +7 -9
  81. package/dist/lang/ast/view-elements/reference-view.js +6 -5
  82. package/dist/lang/ast/view-elements/refine-utils.js +1 -1
  83. package/dist/lang/composite-source-utils.d.ts +30 -15
  84. package/dist/lang/composite-source-utils.js +234 -64
  85. package/dist/lang/lib/Malloy/MalloyLexer.d.ts +171 -169
  86. package/dist/lang/lib/Malloy/MalloyLexer.js +1194 -1178
  87. package/dist/lang/lib/Malloy/MalloyParser.d.ts +408 -334
  88. package/dist/lang/lib/Malloy/MalloyParser.js +3062 -2561
  89. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +68 -0
  90. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +43 -0
  91. package/dist/lang/malloy-to-ast.d.ts +13 -1
  92. package/dist/lang/malloy-to-ast.js +90 -11
  93. package/dist/lang/parse-log.d.ts +8 -0
  94. package/dist/lang/prettify/filter-type.d.ts +3 -0
  95. package/dist/lang/prettify/filter-type.js +38 -0
  96. package/dist/lang/prettify/formatter.js +6 -0
  97. package/dist/lang/prettify/inline-renderer.js +20 -0
  98. package/dist/lang/prettify/rules.d.ts +1 -1
  99. package/dist/lang/prettify/rules.js +1 -0
  100. package/dist/lang/prettify/sections.js +2 -0
  101. package/dist/lang/prettify/tokens.js +2 -0
  102. package/dist/lang/test/expr-to-str.js +2 -0
  103. package/dist/lang/test/parse-expects.d.ts +1 -0
  104. package/dist/lang/test/parse-expects.js +27 -10
  105. package/dist/model/constant_expression_compiler.js +1 -0
  106. package/dist/model/expression_compiler.d.ts +2 -1
  107. package/dist/model/expression_compiler.js +41 -1
  108. package/dist/model/given_binding.d.ts +2 -0
  109. package/dist/model/given_binding.js +204 -0
  110. package/dist/model/index.d.ts +1 -1
  111. package/dist/model/index.js +2 -1
  112. package/dist/model/malloy_types.d.ts +163 -36
  113. package/dist/model/malloy_types.js +97 -0
  114. package/dist/model/query_model_contract.d.ts +2 -1
  115. package/dist/model/query_model_impl.d.ts +2 -1
  116. package/dist/model/query_model_impl.js +7 -0
  117. package/dist/model/query_node.d.ts +2 -1
  118. package/dist/model/source_def_utils.d.ts +2 -1
  119. package/dist/model/source_def_utils.js +4 -0
  120. package/dist/model/utils.d.ts +14 -1
  121. package/dist/model/utils.js +41 -0
  122. package/dist/to_stable.js +1 -1
  123. package/dist/util/closest_match.d.ts +9 -0
  124. package/dist/util/closest_match.js +47 -0
  125. package/dist/version.d.ts +1 -1
  126. package/dist/version.js +1 -1
  127. package/package.json +4 -4
@@ -147,7 +147,6 @@ async function fetchNeeds(needs, fetchers) {
147
147
  async function compileModel(request, fetchers) {
148
148
  const timer = new timing_1.Timer('compile_model');
149
149
  const state = Core.newCompileModelState(request);
150
- // eslint-disable-next-line no-constant-condition
151
150
  while (true) {
152
151
  const result = Core.statedCompileModel(state);
153
152
  timer.incorporate(result.timing_info);
@@ -162,7 +161,6 @@ async function compileModel(request, fetchers) {
162
161
  async function compileSource(request, fetchers) {
163
162
  const timer = new timing_1.Timer('compile_source');
164
163
  const state = Core.newCompileSourceState(request);
165
- // eslint-disable-next-line no-constant-condition
166
164
  while (true) {
167
165
  const result = Core.statedCompileSource(state, request.name);
168
166
  timer.incorporate(result.timing_info);
@@ -177,7 +175,6 @@ async function compileSource(request, fetchers) {
177
175
  async function compileQuery(request, fetchers) {
178
176
  const timer = new timing_1.Timer('compile_query');
179
177
  const state = Core.newCompileQueryState(request);
180
- // eslint-disable-next-line no-constant-condition
181
178
  while (true) {
182
179
  const result = Core.statedCompileQuery(state);
183
180
  timer.incorporate(result.timing_info);
@@ -121,7 +121,7 @@ export declare class Malloy {
121
121
  * instead of looping forever, but the fix is to correct the dialect.
122
122
  */
123
123
  static safelyFetchTableSchema(connection: InfoConnection, toFetch: Record<string, string>, opts: FetchSchemaOptions): Promise<{
124
- schemas: Record<string, import("../../model").TableSourceDef>;
124
+ schemas: Record<string, import("../..").TableSourceDef>;
125
125
  errors: Record<string, string>;
126
126
  }>;
127
127
  /**
@@ -100,23 +100,95 @@ export declare class Manifest {
100
100
  * `urlReader.readURL()` / `JSON.parse()` and pass the POJO, or use the
101
101
  * sync string form after reading the file.
102
102
  */
103
+ /**
104
+ * Filesystem context for a `MalloyConfig`: where the config file lives
105
+ * (`configURL`) and where the project root is (`rootDirectory`).
106
+ *
107
+ * Both are URL-shaped strings (e.g. `'file:///path/to/config.json'`).
108
+ * Hosts in Node convert their paths to file URLs (`url.pathToFileURL(path)
109
+ * .toString()`); browser hosts pass `https://...` or whatever scheme they
110
+ * serve from. Foundation never reaches for `process.cwd` — that decision
111
+ * lives in the host.
112
+ *
113
+ * - `configURL` — anchor for `manifestPath` and `givensPath` resolution.
114
+ * Set by `discoverConfig` to the URL of the matched config file. POJO
115
+ * callers pass it directly when their config carries any path setting.
116
+ * - `rootDirectory` — project root. Consumed by connection-property
117
+ * defaults via the `{config: 'rootDirectory'}` overlay-reference shape
118
+ * (e.g. DuckDB's `databasePath` default). Set by `discoverConfig` to
119
+ * the ceiling of the discovery range; POJO callers pass it when they
120
+ * need a project-rooted anchor.
121
+ */
122
+ export type FilesystemContext = {
123
+ configURL?: string;
124
+ rootDirectory?: string;
125
+ };
126
+ /**
127
+ * Second-arg options for `MalloyConfig`. Combines the filesystem context
128
+ * with non-`config` overlays. The `config` slot in `overlays` is reserved
129
+ * — pass `configURL` / `rootDirectory` directly instead. A reserved-slot
130
+ * collision is warned and dropped, not silently merged.
131
+ */
132
+ export interface MalloyConfigOptions extends FilesystemContext {
133
+ overlays?: ConfigOverlays;
134
+ }
103
135
  export declare class MalloyConfig {
104
136
  readonly virtualMap?: VirtualMap;
137
+ /** URL-shaped string of the config file, or undefined if not supplied. */
138
+ readonly configURL?: string;
139
+ /** URL-shaped string of the project root, or undefined if not supplied. */
140
+ readonly rootDirectory?: string;
105
141
  readonly manifestPath?: string;
106
142
  /**
107
- * Resolved URL of the manifest file (`malloy-manifest.json`), if a
108
- * `configURL` was provided in the `config` overlay. Computed once in the
109
- * constructor from `manifestPath` (default `'MANIFESTS'`) joined to
110
- * `configURL`. Stays `undefined` for callers that didn't supply a
111
- * `configURL` — Runtime treats that as "no auto-read."
143
+ * Resolved URL string of the manifest file (`malloy-manifest.json`), if a
144
+ * `configURL` was supplied. Computed once in the constructor from
145
+ * `manifestPath` (default `'MANIFESTS'`) joined to `configURL`. Stays
146
+ * `undefined` when no `configURL` is available Runtime treats that as
147
+ * "no auto-read."
148
+ */
149
+ readonly manifestURL?: string;
150
+ readonly givensPath?: string;
151
+ /**
152
+ * Resolved URL string of the givens-values JSON file, if `givensPath`
153
+ * was set and a `configURL` was supplied. Resolved against `configURL`
154
+ * so relative paths in the project's `malloy-config.json` work the same
155
+ * way `manifestPath` does. Stays `undefined` if `givensPath` was not
156
+ * configured; Runtime treats that as "no per-runtime givens layer."
157
+ */
158
+ readonly givensURL?: string;
159
+ /**
160
+ * Surface names of givens that the runtime locks. Locked givens cannot
161
+ * be overridden via per-query supply (`.run({givens: {X: ...}})` for a
162
+ * locked X throws at API entry) and are filtered out of `Model.givens`
163
+ * / `PreparedQuery.givens` introspection so UIs don't render editors
164
+ * for them. Security primitive — multi-tenant deployments lock the
165
+ * tenant identifier so a downstream endpoint that accidentally accepts
166
+ * caller-controlled overrides can't leak across tenants.
167
+ *
168
+ * Validation that every locked name has a resolved value (file +
169
+ * constructor) happens lazily at the first compile that needs givens,
170
+ * since the `givensURL` file read is async.
112
171
  */
113
- readonly manifestURL?: URL;
172
+ readonly finalizeGivens?: ReadonlyArray<string>;
114
173
  readonly log: readonly LogMessage[];
115
174
  private _connections;
116
175
  private readonly _managedLookup;
117
176
  private readonly _overlays;
118
- constructor(source: string);
119
- constructor(pojo: object, overlays?: ConfigOverlays);
177
+ /**
178
+ * @deprecated Pass `MalloyConfigOptions` (`{configURL, rootDirectory,
179
+ * overlays}`) instead of a raw `ConfigOverlays` dict. The old shape is
180
+ * detected at runtime and adapted automatically; remove the
181
+ * `contextOverlay({...})` wrapping when convenient.
182
+ */
183
+ constructor(source: string, overlays: ConfigOverlays);
184
+ /** Build a `MalloyConfig` from a JSON config string. */
185
+ constructor(source: string, options?: MalloyConfigOptions);
186
+ /**
187
+ * @deprecated See the source-string overload's deprecation note.
188
+ */
189
+ constructor(pojo: object, overlays: ConfigOverlays);
190
+ /** Build a `MalloyConfig` from a parsed POJO. */
191
+ constructor(pojo: object, options?: MalloyConfigOptions);
120
192
  /**
121
193
  * The live `LookupConnection` consumed by Runtime. Stable until
122
194
  * `wrapConnections()` is called; after that, returns the wrapped lookup.
@@ -109,50 +109,8 @@ class Manifest {
109
109
  }
110
110
  }
111
111
  exports.Manifest = Manifest;
112
- /**
113
- * A resolved Malloy runtime configuration. The constructor takes either a
114
- * JSON string (legacy/compat) or a POJO plus an optional `ConfigOverlays`
115
- * dict, compiles the input into a typed tree, resolves all overlay
116
- * references, and builds the connection lookup via the connection registry.
117
- *
118
- * After construction:
119
- * - `connections` — the `LookupConnection` runtime consumes. Starts as the
120
- * lookup created from the resolved connections; `wrapConnections()` replaces
121
- * it with a wrapped version for host-specific behavior.
122
- * - `virtualMap` — extracted from the POJO as-is.
123
- * - `manifestPath` — extracted from the POJO as-is (the raw string).
124
- * - `manifestURL` — resolved URL of `malloy-manifest.json`, computed from
125
- * `manifestPath` + the `configURL` carried on the `config` overlay (if
126
- * any). `undefined` when no `configURL` is available. Runtime uses this
127
- * to lazily read the manifest on the first persistence query. Builders
128
- * that want to construct or mutate manifest state use the standalone
129
- * `Manifest` class directly — they don't go through MalloyConfig.
130
- * - `log` — validation warnings and overlay-resolution warnings.
131
- *
132
- * Typical usage:
133
- *
134
- * // From a pre-loaded POJO (e.g. from `discoverConfig` or Publisher):
135
- * const config = new MalloyConfig(pojo);
136
- * const runtime = new Runtime({config, urlReader});
137
- *
138
- * // From a JSON string:
139
- * const config = new MalloyConfig(configText);
140
- *
141
- * // With host-supplied overlays (local hosts after discovery):
142
- * const config = new MalloyConfig(pojo, {
143
- * config: contextOverlay({rootDirectory: ceilingURL}),
144
- * });
145
- *
146
- * // Wrap the connection lookup for host-specific behavior:
147
- * config.wrapConnections(base => name => base(name) ?? fallback(name));
148
- *
149
- * The old async `new MalloyConfig(urlReader, configURL)` form has been
150
- * removed. Callers that need to load from a URL should pre-load via
151
- * `urlReader.readURL()` / `JSON.parse()` and pass the POJO, or use the
152
- * sync string form after reading the file.
153
- */
154
112
  class MalloyConfig {
155
- constructor(sourceOrPojo, overlays) {
113
+ constructor(sourceOrPojo, optsOrOverlays) {
156
114
  const log = [];
157
115
  // Normalize input to a POJO.
158
116
  let pojo;
@@ -175,11 +133,28 @@ class MalloyConfig {
175
133
  // Compile → typed tree + validation warnings.
176
134
  const compiled = (0, config_compile_1.compileConfig)(pojo);
177
135
  log.push(...compiled.log);
178
- // Merge the host-supplied overlays onto the defaults. Same-named
179
- // entries replace defaults; new keys are added.
136
+ // Distinguish the new typed-options form from the legacy
137
+ // ConfigOverlays-dict form. Legacy dicts have function-valued slots
138
+ // (`config: () => ...`); new options have string/object fields.
139
+ const options = normalizeOptions(optsOrOverlays, log);
140
+ // configURL must parse as a URL — foundation joins paths against it
141
+ // (manifestURL, givensURL). rootDirectory is opaque; foundation just
142
+ // forwards it via the `config` overlay to whoever consumes it (e.g.
143
+ // DuckDB's databasePath default), so its shape is the consumer's call.
144
+ const configURL = validateURLString(options.configURL, 'configURL', log);
145
+ const rootDirectory = options.rootDirectory;
146
+ // Build the internal `config` overlay from the typed FS context. Other
147
+ // overlay slots (env, host-defined session/secret) ride alongside as
148
+ // host-supplied. The `config` slot is reserved here; if the host
149
+ // supplied one in `options.overlays`, normalizeOptions already warned
150
+ // and dropped it.
180
151
  const mergedOverlays = {
181
152
  ...(0, config_overlays_1.defaultConfigOverlays)(),
182
- ...overlays,
153
+ ...options.overlays,
154
+ config: (0, config_overlays_1.contextOverlay)({
155
+ configURL,
156
+ rootDirectory,
157
+ }),
183
158
  };
184
159
  // Synchronous prep: extract literal sections (manifestPath, virtualMap),
185
160
  // pull out compiled connection subtrees, fabricate bare entries for
@@ -187,13 +162,18 @@ class MalloyConfig {
187
162
  // Reference resolution for connection properties is *not* done here —
188
163
  // it happens async at `lookupConnection` time, so overlays that touch
189
164
  // IO (secret stores, session reads) have a natural async seam.
190
- const prepared = (0, config_resolve_1.prepareConfig)(compiled.compiled, log);
165
+ const prepared = (0, config_resolve_1.prepareConfig)(compiled.compiled, mergedOverlays, log);
191
166
  this._managedLookup = (0, config_lookup_1.buildManagedLookup)(prepared.compiledConnections, mergedOverlays, log);
192
167
  this._connections = this._managedLookup;
193
168
  this._overlays = mergedOverlays;
194
169
  this.virtualMap = toVirtualMap(prepared.virtualMap);
170
+ this.configURL = configURL;
171
+ this.rootDirectory = rootDirectory;
195
172
  this.manifestPath = prepared.manifestPath;
196
- this.manifestURL = computeManifestURL(prepared.manifestPath, mergedOverlays, log);
173
+ this.manifestURL = computeManifestURL(prepared.manifestPath, configURL);
174
+ this.givensPath = prepared.givensPath;
175
+ this.givensURL = computeGivensURL(prepared.givensPath, configURL, log);
176
+ this.finalizeGivens = prepared.finalizeGivens;
197
177
  this.log = log;
198
178
  }
199
179
  /**
@@ -318,22 +298,12 @@ const MANIFEST_FILENAME = 'malloy-manifest.json';
318
298
  * - A trailing slash is forced onto the directory portion so that the
319
299
  * final `new URL(MANIFEST_FILENAME, dir)` joins correctly.
320
300
  */
321
- function computeManifestURL(manifestPath, overlays, log) {
322
- const configOverlay = overlays['config'];
323
- const configURLValue = configOverlay === null || configOverlay === void 0 ? void 0 : configOverlay(['configURL']);
324
- if (isThenable(configURLValue)) {
325
- log.push({
326
- message: 'the `config` overlay returned a Promise for `configURL`; `configURL` must be resolved synchronously. manifestURL will be undefined and persistence will not work.',
327
- severity: 'warn',
328
- code: 'config-overlay',
329
- });
301
+ function computeManifestURL(manifestPath, configURL) {
302
+ if (!configURL)
330
303
  return undefined;
331
- }
332
- if (typeof configURLValue !== 'string')
333
- return undefined;
334
- let configURL;
304
+ let baseURL;
335
305
  try {
336
- configURL = new URL(configURLValue);
306
+ baseURL = new URL(configURL);
337
307
  }
338
308
  catch {
339
309
  return undefined;
@@ -341,7 +311,7 @@ function computeManifestURL(manifestPath, overlays, log) {
341
311
  const path = manifestPath !== null && manifestPath !== void 0 ? manifestPath : DEFAULT_MANIFEST_PATH;
342
312
  let manifestRoot;
343
313
  try {
344
- manifestRoot = new URL(path, configURL);
314
+ manifestRoot = new URL(path, baseURL);
345
315
  }
346
316
  catch {
347
317
  return undefined;
@@ -350,7 +320,124 @@ function computeManifestURL(manifestPath, overlays, log) {
350
320
  const dirURL = asString.endsWith('/')
351
321
  ? manifestRoot
352
322
  : new URL(asString + '/');
353
- return new URL(MANIFEST_FILENAME, dirURL);
323
+ return new URL(MANIFEST_FILENAME, dirURL).toString();
324
+ }
325
+ /**
326
+ * Resolve `givensPath` to a full URL string, joined against `configURL`
327
+ * so relative paths anchor at the project's `malloy-config.json`
328
+ * directory. Returns `undefined` and warns when `givensPath` was set but
329
+ * resolution can't proceed — silent drop here is unfriendly because
330
+ * unlike `manifestPath`, `givensPath` has no graceful-fallback story.
331
+ */
332
+ function computeGivensURL(givensPath, configURL, log) {
333
+ if (!givensPath)
334
+ return undefined;
335
+ if (!configURL) {
336
+ log.push({
337
+ message: 'givensPath is set but no configURL is available; per-runtime givens will not be loaded. Pass `configURL` (or use an absolute URL such as `file:///...`).',
338
+ severity: 'warn',
339
+ code: 'config-overlay',
340
+ });
341
+ return undefined;
342
+ }
343
+ let baseURL;
344
+ try {
345
+ baseURL = new URL(configURL);
346
+ }
347
+ catch {
348
+ return undefined;
349
+ }
350
+ try {
351
+ return new URL(givensPath, baseURL).toString();
352
+ }
353
+ catch (e) {
354
+ const msg = e instanceof Error ? e.message : String(e);
355
+ log.push({
356
+ message: `givensPath '${givensPath}' could not be resolved against configURL '${configURL}': ${msg}`,
357
+ severity: 'warn',
358
+ code: 'config-validation',
359
+ });
360
+ return undefined;
361
+ }
362
+ }
363
+ /**
364
+ * Distinguish the new typed-options form (`{configURL?, rootDirectory?,
365
+ * overlays?}`) from the legacy `ConfigOverlays`-dict form (`{config: fn,
366
+ * env: fn, ...}`) by checking for function-valued top-level slots. Legacy
367
+ * inputs are adapted: extract `configURL` and `rootDirectory` from
368
+ * `overlays.config?.(['name'])` (sync only — async overlays warn and
369
+ * drop), preserve the rest as `options.overlays`. New inputs pass
370
+ * through, but a non-empty `overlays.config` is reserved-slot collision —
371
+ * warn and drop.
372
+ */
373
+ function normalizeOptions(optsOrOverlays, log) {
374
+ if (optsOrOverlays === undefined)
375
+ return {};
376
+ if (isLegacyOverlays(optsOrOverlays)) {
377
+ return adaptLegacyOverlays(optsOrOverlays, log);
378
+ }
379
+ const opts = optsOrOverlays;
380
+ if (opts.overlays && 'config' in opts.overlays) {
381
+ log.push({
382
+ message: '`config` is reserved by MalloyConfig; pass `configURL` and `rootDirectory` directly instead. Supplied `overlays.config` ignored.',
383
+ severity: 'warn',
384
+ code: 'config-overlay',
385
+ });
386
+ const { config: _config, ...rest } = opts.overlays;
387
+ return { ...opts, overlays: rest };
388
+ }
389
+ return opts;
390
+ }
391
+ function isLegacyOverlays(arg) {
392
+ for (const v of Object.values(arg)) {
393
+ if (typeof v === 'function')
394
+ return true;
395
+ }
396
+ return false;
397
+ }
398
+ function adaptLegacyOverlays(overlays, log) {
399
+ const cfg = overlays['config'];
400
+ if (!cfg)
401
+ return { overlays };
402
+ const configURL = readSyncString(cfg, 'configURL', log);
403
+ const rootDirectory = readSyncString(cfg, 'rootDirectory', log);
404
+ // Strip the legacy `config` overlay; its values are now typed fields.
405
+ // Any other slots (env, host-defined) ride through.
406
+ const { config: _config, ...rest } = overlays;
407
+ return { configURL, rootDirectory, overlays: rest };
408
+ }
409
+ function readSyncString(overlay, key, log) {
410
+ const v = overlay([key]);
411
+ if ((0, config_overlays_1.isThenable)(v)) {
412
+ log.push({
413
+ message: `the legacy \`config\` overlay returned a Promise for \`${key}\`; top-level FS context must resolve synchronously. ${key} will be undefined.`,
414
+ severity: 'warn',
415
+ code: 'config-overlay',
416
+ });
417
+ return undefined;
418
+ }
419
+ return typeof v === 'string' ? v : undefined;
420
+ }
421
+ /**
422
+ * Diagnose a URL-shaped string. Returns the string back when valid,
423
+ * undefined (with a warning) when not. Empty/undefined inputs return
424
+ * undefined silently — those are the "not supplied" case.
425
+ */
426
+ function validateURLString(value, fieldName, log) {
427
+ if (value === undefined)
428
+ return undefined;
429
+ try {
430
+ new URL(value);
431
+ return value;
432
+ }
433
+ catch {
434
+ log.push({
435
+ message: `${fieldName} must be a URL-shaped string (e.g. 'file:///path/to/...'), got '${value}'. In Node, use \`url.pathToFileURL(path).toString()\`.`,
436
+ severity: 'warn',
437
+ code: 'config-validation',
438
+ });
439
+ return undefined;
440
+ }
354
441
  }
355
442
  /**
356
443
  * Convert the raw virtualMap POJO shape (a dict of dicts of strings) into
@@ -377,11 +464,6 @@ function toVirtualMap(raw) {
377
464
  function isRecord(value) {
378
465
  return typeof value === 'object' && value !== null && !Array.isArray(value);
379
466
  }
380
- function isThenable(value) {
381
- return (typeof value === 'object' &&
382
- value !== null &&
383
- typeof value.then === 'function');
384
- }
385
467
  function isBuildManifestEntry(value) {
386
468
  return isRecord(value) && typeof value['tableName'] === 'string';
387
469
  }
@@ -6,9 +6,12 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.compileConfig = compileConfig;
8
8
  const registry_1 = require("../../connection/registry");
9
+ const closest_match_1 = require("../../util/closest_match");
9
10
  const TOP_LEVEL_SECTIONS = {
10
11
  connections: compileConnections,
11
12
  manifestPath: compileManifestPath,
13
+ givensPath: compileGivensPath,
14
+ finalizeGivens: compileFinalizeGivens,
12
15
  virtualMap: compileVirtualMap,
13
16
  includeDefaultConnections: compileIncludeDefaultConnections,
14
17
  };
@@ -34,7 +37,7 @@ function compileConfig(pojo) {
34
37
  for (const [key, value] of Object.entries(pojo)) {
35
38
  const compiler = TOP_LEVEL_SECTIONS[key];
36
39
  if (!compiler) {
37
- const suggestion = closestMatch(key, [...KNOWN_TOP_LEVEL_KEYS]);
40
+ const suggestion = (0, closest_match_1.closestMatch)(key, [...KNOWN_TOP_LEVEL_KEYS]);
38
41
  const hint = suggestion ? `. Did you mean "${suggestion}"?` : '';
39
42
  log.push(makeWarning(key, `unknown config key "${key}"${hint}`));
40
43
  continue;
@@ -72,7 +75,7 @@ function compileConnections(value, log) {
72
75
  continue;
73
76
  }
74
77
  if (!registeredTypes.has(is)) {
75
- const suggestion = closestMatch(is, [...registeredTypes]);
78
+ const suggestion = (0, closest_match_1.closestMatch)(is, [...registeredTypes]);
76
79
  const hint = suggestion ? ` Did you mean "${suggestion}"?` : '';
77
80
  log.push(makeWarning(`${prefix}.is`, `unknown connection type "${is}".${hint} Available types: ${[...registeredTypes].join(', ')}`));
78
81
  continue;
@@ -94,7 +97,7 @@ function compileConnectionEntry(prefix, typeName, rawEntry, log) {
94
97
  continue;
95
98
  const propDef = propMap.get(key);
96
99
  if (!propDef) {
97
- const suggestion = closestMatch(key, [...propMap.keys()]);
100
+ const suggestion = (0, closest_match_1.closestMatch)(key, [...propMap.keys()]);
98
101
  const hint = suggestion ? `. Did you mean "${suggestion}"?` : '';
99
102
  log.push(makeWarning(`${prefix}.${key}`, `unknown property "${key}" for connection type "${typeName}"${hint}`));
100
103
  continue;
@@ -142,8 +145,28 @@ function compileConnectionProperty(path, propDef, value, log) {
142
145
  // Pass-through sections
143
146
  // =============================================================================
144
147
  function compileManifestPath(value, log) {
148
+ const ref = asReferenceShape(value);
149
+ if (ref !== undefined)
150
+ return ref;
151
+ if (typeof value !== 'string') {
152
+ log.push(makeWarning('manifestPath', '"manifestPath" should be a string or an overlay reference'));
153
+ return undefined;
154
+ }
155
+ return { kind: 'value', value };
156
+ }
157
+ function compileGivensPath(value, log) {
158
+ const ref = asReferenceShape(value);
159
+ if (ref !== undefined)
160
+ return ref;
145
161
  if (typeof value !== 'string') {
146
- log.push(makeWarning('manifestPath', '"manifestPath" should be a string'));
162
+ log.push(makeWarning('givensPath', '"givensPath" should be a string or an overlay reference'));
163
+ return undefined;
164
+ }
165
+ return { kind: 'value', value };
166
+ }
167
+ function compileFinalizeGivens(value, log) {
168
+ if (!Array.isArray(value) || !value.every(v => typeof v === 'string')) {
169
+ log.push(makeWarning('finalizeGivens', '"finalizeGivens" should be an array of given names'));
147
170
  return undefined;
148
171
  }
149
172
  return { kind: 'value', value };
@@ -226,35 +249,4 @@ function checkValueType(value, expectedType) {
226
249
  }
227
250
  return undefined;
228
251
  }
229
- function levenshtein(a, b) {
230
- const m = a.length;
231
- const n = b.length;
232
- const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
233
- for (let i = 0; i <= m; i++)
234
- dp[i][0] = i;
235
- for (let j = 0; j <= n; j++)
236
- dp[0][j] = j;
237
- for (let i = 1; i <= m; i++) {
238
- for (let j = 1; j <= n; j++) {
239
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
240
- dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
241
- }
242
- }
243
- return dp[m][n];
244
- }
245
- function closestMatch(input, candidates) {
246
- if (candidates.length === 0)
247
- return undefined;
248
- let best = candidates[0];
249
- let bestDist = Infinity;
250
- for (const c of candidates) {
251
- const dist = levenshtein(input.toLowerCase(), c.toLowerCase());
252
- if (dist < bestDist) {
253
- bestDist = dist;
254
- best = c;
255
- }
256
- }
257
- const maxDist = Math.max(1, Math.floor(Math.max(input.length, best.length) / 3));
258
- return bestDist <= maxDist ? best : undefined;
259
- }
260
252
  //# sourceMappingURL=config_compile.js.map
@@ -6,7 +6,6 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.discoverConfig = discoverConfig;
8
8
  const config_1 = require("./config");
9
- const config_overlays_1 = require("./config_overlays");
10
9
  const SHARED_FILENAME = 'malloy-config.json';
11
10
  const LOCAL_FILENAME = 'malloy-config-local.json';
12
11
  /**
@@ -66,14 +65,11 @@ async function discoverConfig(startURL, ceilingURL, urlReader, extraOverlays) {
66
65
  return null;
67
66
  }
68
67
  function buildConfig(hit, ceilingURL, extraOverlays) {
69
- const discoveryOverlays = {
70
- config: (0, config_overlays_1.contextOverlay)({
71
- rootDirectory: ceilingURL.toString(),
72
- configURL: hit.configURL.toString(),
73
- }),
74
- };
75
- const merged = { ...discoveryOverlays, ...extraOverlays };
76
- return new config_1.MalloyConfig(hit.pojo, merged);
68
+ return new config_1.MalloyConfig(hit.pojo, {
69
+ configURL: hit.configURL.toString(),
70
+ rootDirectory: ceilingURL.toString(),
71
+ overlays: extraOverlays,
72
+ });
77
73
  }
78
74
  async function tryReadAtLevel(dirURL, urlReader) {
79
75
  const sharedURL = new URL(SHARED_FILENAME, dirURL);
@@ -59,3 +59,9 @@ export declare function contextOverlay(dict: Record<string, unknown>): Overlay;
59
59
  * resolve to "not present".
60
60
  */
61
61
  export declare function defaultConfigOverlays(): ConfigOverlays;
62
+ /**
63
+ * Type guard for "looks like a Promise." Used by sync-peek call sites
64
+ * (`computeManifestURL`, `resolveSyncStringSetting`) to detect when a host
65
+ * mistakenly wired an async overlay into a slot that's read synchronously.
66
+ */
67
+ export declare function isThenable(value: unknown): value is PromiseLike<unknown>;
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.envOverlay = envOverlay;
8
8
  exports.contextOverlay = contextOverlay;
9
9
  exports.defaultConfigOverlays = defaultConfigOverlays;
10
+ exports.isThenable = isThenable;
10
11
  /**
11
12
  * Built-in `env` overlay: reads `process.env[path[0]]`.
12
13
  *
@@ -48,4 +49,15 @@ function defaultConfigOverlays() {
48
49
  config: () => undefined,
49
50
  };
50
51
  }
52
+ /**
53
+ * Type guard for "looks like a Promise." Used by sync-peek call sites
54
+ * (`computeManifestURL`, `resolveSyncStringSetting`) to detect when a host
55
+ * mistakenly wired an async overlay into a slot that's read synchronously.
56
+ */
57
+ function isThenable(value) {
58
+ return (typeof value === 'object' &&
59
+ value !== null &&
60
+ 'then' in value &&
61
+ typeof value.then === 'function');
62
+ }
51
63
  //# sourceMappingURL=config_overlays.js.map
@@ -1,5 +1,6 @@
1
1
  import type { LogMessage } from '../../lang/parse-log';
2
2
  import type { ConfigDict } from './config_compile';
3
+ import type { ConfigOverlays } from './config_overlays';
3
4
  /**
4
5
  * The synchronous slice of config preparation. What the `MalloyConfig`
5
6
  * constructor needs *before* any overlay IO happens:
@@ -18,6 +19,8 @@ import type { ConfigDict } from './config_compile';
18
19
  export interface PreparedConfig {
19
20
  compiledConnections: Record<string, ConfigDict>;
20
21
  manifestPath?: string;
22
+ givensPath?: string;
23
+ finalizeGivens?: ReadonlyArray<string>;
21
24
  virtualMap?: unknown;
22
25
  }
23
26
  /**
@@ -36,4 +39,4 @@ export interface PreparedConfig {
36
39
  * via `includeDefaultConnections`. Property defaults are filled in at
37
40
  * lookup time alongside reference resolution.
38
41
  */
39
- export declare function prepareConfig(compiled: ConfigDict, _log: LogMessage[]): PreparedConfig;
42
+ export declare function prepareConfig(compiled: ConfigDict, overlays: ConfigOverlays, log: LogMessage[]): PreparedConfig;