@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.
- package/dist/api/asynchronous.js +0 -3
- package/dist/api/foundation/compile.d.ts +1 -1
- package/dist/api/foundation/config.d.ts +80 -8
- package/dist/api/foundation/config.js +151 -69
- package/dist/api/foundation/config_compile.js +27 -35
- package/dist/api/foundation/config_discover.js +5 -9
- package/dist/api/foundation/config_overlays.d.ts +6 -0
- package/dist/api/foundation/config_overlays.js +12 -0
- package/dist/api/foundation/config_resolve.d.ts +4 -1
- package/dist/api/foundation/config_resolve.js +64 -4
- package/dist/api/foundation/core.d.ts +75 -2
- package/dist/api/foundation/core.js +104 -6
- package/dist/api/foundation/index.d.ts +2 -0
- package/dist/api/foundation/readers.js +1 -1
- package/dist/api/foundation/runtime.d.ts +68 -2
- package/dist/api/foundation/runtime.js +212 -10
- package/dist/api/foundation/types.d.ts +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/lang/ast/ast-utils.js +0 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.js +9 -8
- package/dist/lang/ast/expressions/expr-coalesce.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-coalesce.js +2 -3
- package/dist/lang/ast/expressions/expr-count-distinct.js +1 -1
- package/dist/lang/ast/expressions/expr-count.js +6 -4
- package/dist/lang/ast/expressions/expr-filter-expr.js +0 -1
- package/dist/lang/ast/expressions/expr-func.js +9 -4
- package/dist/lang/ast/expressions/expr-given.d.ts +18 -0
- package/dist/lang/ast/expressions/expr-given.js +69 -0
- package/dist/lang/ast/expressions/expr-granular-time.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-id-reference.js +3 -2
- package/dist/lang/ast/expressions/expr-now.js +0 -1
- package/dist/lang/ast/expressions/expr-props.d.ts +132 -132
- package/dist/lang/ast/expressions/expr-props.js +2 -2
- package/dist/lang/ast/expressions/expr-ungroup.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-ungroup.js +4 -4
- package/dist/lang/ast/expressions/for-range.d.ts +1 -1
- package/dist/lang/ast/expressions/function-ordering.d.ts +1 -1
- package/dist/lang/ast/expressions/function-ordering.js +2 -2
- package/dist/lang/ast/expressions/time-literal.d.ts +3 -3
- package/dist/lang/ast/field-space/include-utils.js +2 -2
- package/dist/lang/ast/field-space/index-field-space.js +18 -23
- package/dist/lang/ast/field-space/passthrough-space.d.ts +1 -1
- package/dist/lang/ast/field-space/query-spaces.d.ts +6 -2
- package/dist/lang/ast/field-space/query-spaces.js +29 -19
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/field-space/rename-space-field.d.ts +1 -1
- package/dist/lang/ast/field-space/rename-space-field.js +2 -2
- package/dist/lang/ast/field-space/struct-space-field-base.js +2 -3
- package/dist/lang/ast/index.d.ts +2 -0
- package/dist/lang/ast/index.js +2 -0
- package/dist/lang/ast/query-builders/index-builder.d.ts +1 -1
- package/dist/lang/ast/query-builders/index-builder.js +4 -3
- package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -2
- package/dist/lang/ast/query-builders/reduce-builder.js +4 -5
- package/dist/lang/ast/query-elements/query-arrow.js +3 -2
- package/dist/lang/ast/query-elements/query-base.d.ts +1 -1
- package/dist/lang/ast/query-elements/query-base.js +1 -1
- package/dist/lang/ast/query-elements/query-refine.js +3 -1
- package/dist/lang/ast/query-items/field-declaration.js +2 -2
- package/dist/lang/ast/query-properties/drill.js +6 -6
- package/dist/lang/ast/query-properties/filters.js +2 -2
- package/dist/lang/ast/query-properties/nest.js +3 -3
- package/dist/lang/ast/source-elements/composite-source.js +5 -3
- package/dist/lang/ast/source-elements/named-source.js +4 -0
- package/dist/lang/ast/source-elements/sql-source.js +2 -2
- package/dist/lang/ast/source-elements/table-source.js +3 -1
- package/dist/lang/ast/source-properties/join.js +4 -4
- package/dist/lang/ast/source-query-elements/sq-reference.js +2 -1
- package/dist/lang/ast/statements/define-given.d.ts +29 -0
- package/dist/lang/ast/statements/define-given.js +163 -0
- package/dist/lang/ast/statements/import-statement.js +72 -9
- package/dist/lang/ast/typedesc-utils.d.ts +3 -1
- package/dist/lang/ast/typedesc-utils.js +4 -47
- package/dist/lang/ast/types/expr-value.js +2 -3
- package/dist/lang/ast/types/expression-def.d.ts +2 -2
- package/dist/lang/ast/types/expression-def.js +2 -2
- package/dist/lang/ast/types/malloy-element.d.ts +5 -15
- package/dist/lang/ast/types/malloy-element.js +113 -1
- package/dist/lang/ast/types/space-field.js +7 -9
- package/dist/lang/ast/view-elements/reference-view.js +6 -5
- package/dist/lang/ast/view-elements/refine-utils.js +1 -1
- package/dist/lang/composite-source-utils.d.ts +30 -15
- package/dist/lang/composite-source-utils.js +234 -64
- package/dist/lang/lib/Malloy/MalloyLexer.d.ts +171 -169
- package/dist/lang/lib/Malloy/MalloyLexer.js +1194 -1178
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +408 -334
- package/dist/lang/lib/Malloy/MalloyParser.js +3062 -2561
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +68 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +43 -0
- package/dist/lang/malloy-to-ast.d.ts +13 -1
- package/dist/lang/malloy-to-ast.js +90 -11
- package/dist/lang/parse-log.d.ts +8 -0
- package/dist/lang/prettify/filter-type.d.ts +3 -0
- package/dist/lang/prettify/filter-type.js +38 -0
- package/dist/lang/prettify/formatter.js +6 -0
- package/dist/lang/prettify/inline-renderer.js +20 -0
- package/dist/lang/prettify/rules.d.ts +1 -1
- package/dist/lang/prettify/rules.js +1 -0
- package/dist/lang/prettify/sections.js +2 -0
- package/dist/lang/prettify/tokens.js +2 -0
- package/dist/lang/test/expr-to-str.js +2 -0
- package/dist/lang/test/parse-expects.d.ts +1 -0
- package/dist/lang/test/parse-expects.js +27 -10
- package/dist/model/constant_expression_compiler.js +1 -0
- package/dist/model/expression_compiler.d.ts +2 -1
- package/dist/model/expression_compiler.js +41 -1
- package/dist/model/given_binding.d.ts +2 -0
- package/dist/model/given_binding.js +204 -0
- package/dist/model/index.d.ts +1 -1
- package/dist/model/index.js +2 -1
- package/dist/model/malloy_types.d.ts +163 -36
- package/dist/model/malloy_types.js +97 -0
- package/dist/model/query_model_contract.d.ts +2 -1
- package/dist/model/query_model_impl.d.ts +2 -1
- package/dist/model/query_model_impl.js +7 -0
- package/dist/model/query_node.d.ts +2 -1
- package/dist/model/source_def_utils.d.ts +2 -1
- package/dist/model/source_def_utils.js +4 -0
- package/dist/model/utils.d.ts +14 -1
- package/dist/model/utils.js +41 -0
- package/dist/to_stable.js +1 -1
- package/dist/util/closest_match.d.ts +9 -0
- package/dist/util/closest_match.js +47 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
package/dist/api/asynchronous.js
CHANGED
|
@@ -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("
|
|
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
|
|
109
|
-
*
|
|
110
|
-
* `
|
|
111
|
-
*
|
|
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
|
|
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
|
-
|
|
119
|
-
|
|
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,
|
|
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
|
-
//
|
|
179
|
-
//
|
|
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,
|
|
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,
|
|
322
|
-
|
|
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
|
-
|
|
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,
|
|
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('
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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,
|
|
42
|
+
export declare function prepareConfig(compiled: ConfigDict, overlays: ConfigOverlays, log: LogMessage[]): PreparedConfig;
|