@malloydata/malloy 0.0.372 → 0.0.374

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.
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.resolveConfig = resolveConfig;
8
+ const registry_1 = require("../../connection/registry");
9
+ /**
10
+ * Walk a compiled tree against the overlay dict and produce a plain
11
+ * resolved POJO.
12
+ *
13
+ * Two distinct "defaults" mechanisms, deliberately separated:
14
+ *
15
+ * 1. **Property defaults** (`applyPropertyDefaults`) — fill in missing
16
+ * properties on *every* connection entry, user-listed or fabricated.
17
+ * This is a uniform per-property rule; there is no asymmetry between
18
+ * explicit and auto-generated entries.
19
+ *
20
+ * 2. **`includeDefaultConnections`** (`fabricateMissingConnections`) —
21
+ * fabricate a bare `{is: typeName}` entry for each registered backend
22
+ * not already represented. Property defaults then fill in their
23
+ * properties via (1).
24
+ *
25
+ * Order matters: fabrication runs before property defaults so that
26
+ * fabricated entries pick up defaults in the same pass as user-listed
27
+ * ones.
28
+ *
29
+ * Three unresolved-reference cases, each with different handling:
30
+ * 1. Unknown overlay source → warning, drop property
31
+ * 2. Known overlay → undefined → silent drop
32
+ * 3. Property default → unresolved (either of the above inside a default)
33
+ * → silent drop (a default is a hint, not a requirement)
34
+ */
35
+ function resolveConfig(compiled, overlays, log) {
36
+ const resolved = { connections: {} };
37
+ let includeDefaultConnections = false;
38
+ for (const [key, node] of Object.entries(compiled.entries)) {
39
+ switch (key) {
40
+ case 'connections': {
41
+ if (node.kind !== 'dict')
42
+ break;
43
+ resolved.connections = resolveConnections(node, overlays, log);
44
+ break;
45
+ }
46
+ case 'manifestPath': {
47
+ const v = resolveNode(node, overlays, log);
48
+ if (typeof v === 'string')
49
+ resolved.manifestPath = v;
50
+ break;
51
+ }
52
+ case 'virtualMap': {
53
+ // virtualMap is literal data — the class body converts it to the
54
+ // runtime Map-of-Maps representation.
55
+ resolved.virtualMap = resolveNode(node, overlays, log);
56
+ break;
57
+ }
58
+ case 'includeDefaultConnections': {
59
+ const v = resolveNode(node, overlays, log);
60
+ if (typeof v === 'boolean')
61
+ includeDefaultConnections = v;
62
+ break;
63
+ }
64
+ }
65
+ }
66
+ if (includeDefaultConnections) {
67
+ fabricateMissingConnections(resolved.connections);
68
+ }
69
+ // Property defaults apply to every entry — user-listed and fabricated
70
+ // alike. This is the fix for the earlier bug where defaults only fired
71
+ // during fabrication, leaving explicit entries silently underconfigured.
72
+ applyPropertyDefaults(resolved.connections, overlays);
73
+ return resolved;
74
+ }
75
+ // =============================================================================
76
+ // Generic walk
77
+ // =============================================================================
78
+ /**
79
+ * Walk a single node and produce its resolved value. References that fail
80
+ * to resolve return `undefined`; the parent dict walker then drops the
81
+ * corresponding property.
82
+ */
83
+ function resolveNode(node, overlays, log) {
84
+ switch (node.kind) {
85
+ case 'value':
86
+ return node.value;
87
+ case 'reference':
88
+ return resolveReference(node, overlays, log);
89
+ case 'dict': {
90
+ const out = {};
91
+ for (const [k, child] of Object.entries(node.entries)) {
92
+ const r = resolveNode(child, overlays, log);
93
+ if (r !== undefined)
94
+ out[k] = r;
95
+ }
96
+ return out;
97
+ }
98
+ }
99
+ }
100
+ function resolveReference(ref, overlays, log) {
101
+ const overlay = overlays[ref.source];
102
+ if (!overlay) {
103
+ // Case 1: unknown overlay source — warn and drop.
104
+ log.push({
105
+ message: `unknown overlay source "${ref.source}" for reference path ${JSON.stringify(ref.path)}`,
106
+ severity: 'warn',
107
+ code: 'config-overlay',
108
+ });
109
+ return undefined;
110
+ }
111
+ // Case 2: overlay returns undefined — silent drop (no log push).
112
+ return overlay(ref.path);
113
+ }
114
+ // =============================================================================
115
+ // Connections
116
+ // =============================================================================
117
+ function resolveConnections(node, overlays, log) {
118
+ const result = {};
119
+ for (const [name, connNode] of Object.entries(node.entries)) {
120
+ if (connNode.kind !== 'dict')
121
+ continue;
122
+ const resolved = resolveNode(connNode, overlays, log);
123
+ // compileConnectionEntry guarantees `is` is a string value node, and
124
+ // resolveNode preserves it. Any connection without `is` is a bug in the
125
+ // compiler; skip it defensively.
126
+ if (typeof resolved['is'] !== 'string')
127
+ continue;
128
+ result[name] = resolved;
129
+ }
130
+ return result;
131
+ }
132
+ // =============================================================================
133
+ // Fabrication and property defaults
134
+ // =============================================================================
135
+ /**
136
+ * Fabricate a bare `{is: typeName}` entry for each registered connection
137
+ * type not already represented in `connections`. Only runs when the
138
+ * `includeDefaultConnections` flag is set on the config. Property values
139
+ * are *not* filled in here — that is the job of `applyPropertyDefaults`,
140
+ * which runs unconditionally on every entry in a later pass.
141
+ *
142
+ * A user-named connection that happens to share the type name but points
143
+ * at a different backend is left alone.
144
+ *
145
+ * Mutates `connections` in place. Called only by `resolveConfig` on its
146
+ * own freshly-built object.
147
+ */
148
+ function fabricateMissingConnections(connections) {
149
+ const presentTypes = new Set();
150
+ for (const entry of Object.values(connections)) {
151
+ if (typeof entry.is === 'string')
152
+ presentTypes.add(entry.is);
153
+ }
154
+ for (const typeName of (0, registry_1.getRegisteredConnectionTypes)()) {
155
+ // `presentTypes` catches {mydb: {is: 'duckdb'}}; the name check catches
156
+ // {duckdb: {is: 'jsondb'}}. Both cases leave the registered `duckdb`
157
+ // type alone — the first because it's already represented, the second
158
+ // because we can't use the obvious name without clobbering.
159
+ if (presentTypes.has(typeName))
160
+ continue;
161
+ if (connections[typeName])
162
+ continue;
163
+ connections[typeName] = { is: typeName };
164
+ }
165
+ }
166
+ /**
167
+ * For every connection entry, fill in any property that the user didn't
168
+ * specify and whose `ConnectionPropertyDefinition` declares a `default`.
169
+ * Runs uniformly on both user-listed and fabricated entries — the earlier
170
+ * behavior of only firing during fabrication was a bug that left explicit
171
+ * entries silently underconfigured (e.g. a user-listed `duckdb` never
172
+ * picked up `workingDirectory: {config: 'rootDirectory'}`).
173
+ *
174
+ * Defaults that are reference-shaped resolve through the overlays via
175
+ * `resolveDefault`. Unresolved defaults are silently dropped (case 3).
176
+ * User-specified values are never overwritten.
177
+ *
178
+ * Interaction with inline references: if the user specified a property
179
+ * as a reference-shaped value that failed to resolve (e.g. `{env: 'UNSET'}`),
180
+ * `resolveConnections` already dropped it before we see the entry. From
181
+ * our perspective the property is simply absent, so the default applies —
182
+ * effectively turning inline references into "try this first, else fall
183
+ * back to the default." This is almost always what users want.
184
+ *
185
+ * Mutates `connections` in place. Called only by `resolveConfig` on its
186
+ * own freshly-built object.
187
+ */
188
+ function applyPropertyDefaults(connections, overlays) {
189
+ var _a;
190
+ for (const entry of Object.values(connections)) {
191
+ const typeName = entry.is;
192
+ if (typeof typeName !== 'string')
193
+ continue;
194
+ const props = (_a = (0, registry_1.getConnectionProperties)(typeName)) !== null && _a !== void 0 ? _a : [];
195
+ for (const prop of props) {
196
+ if (prop.default === undefined)
197
+ continue;
198
+ if (entry[prop.name] !== undefined)
199
+ continue;
200
+ const v = resolveDefault(prop.default, overlays);
201
+ if (v !== undefined)
202
+ entry[prop.name] = v;
203
+ }
204
+ }
205
+ }
206
+ /**
207
+ * Resolve a property `default` field. Literals pass through; single-key
208
+ * reference-shaped objects are resolved through the overlays. Case 3:
209
+ * an unresolved default is a hint, not a requirement — always silent drop.
210
+ */
211
+ function resolveDefault(def, overlays) {
212
+ if (typeof def !== 'object')
213
+ return def;
214
+ const keys = Object.keys(def);
215
+ if (keys.length !== 1)
216
+ return undefined;
217
+ const source = keys[0];
218
+ const raw = def[source];
219
+ const path = typeof raw === 'string' ? [raw] : raw;
220
+ // The type says `raw` is `string | string[]`, but `default` comes from
221
+ // registered backend definitions which are runtime-dynamic — a
222
+ // malformed registration would blow up inside the overlay otherwise.
223
+ if (!Array.isArray(path))
224
+ return undefined;
225
+ const overlay = overlays[source];
226
+ if (!overlay)
227
+ return undefined;
228
+ return overlay(path);
229
+ }
230
+ //# sourceMappingURL=config_resolve.js.map
@@ -9,4 +9,7 @@ export { type ResultJSON, type DataColumn, type DataArrayOrRecord, Result, DataA
9
9
  export { type WriteStream, DataWriter, JSONWriter, CSVWriter } from './writers';
10
10
  export { Runtime, ConnectionRuntime, SingleConnectionRuntime, ModelMaterializer, QueryMaterializer, PreparedResultMaterializer, ExploreMaterializer, } from './runtime';
11
11
  export { Manifest, MalloyConfig } from './config';
12
+ export type { Overlay, ConfigOverlays } from './config_overlays';
13
+ export { envOverlay, contextOverlay, defaultConfigOverlays, } from './config_overlays';
14
+ export { discoverConfig } from './config_discover';
12
15
  export { Malloy, MalloyError, type MalloyCompileOptions, type MalloyRunOptions, } from './compile';
@@ -5,7 +5,7 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.ExploreMaterializer = exports.PreparedResultMaterializer = exports.QueryMaterializer = exports.ModelMaterializer = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.Runtime = exports.CSVWriter = exports.JSONWriter = exports.DataWriter = exports.DataRecord = exports.DataArray = exports.Result = exports.PreparedResult = exports.PreparedQuery = exports.PersistSource = exports.Model = exports.QueryField = exports.Query = exports.StringField = exports.UnsupportedField = exports.JSONField = exports.BooleanField = exports.NumberField = exports.TimestampField = exports.DateField = exports.AtomicField = exports.ExploreField = exports.Explore = exports.JoinRelationship = exports.TimestampTimeframe = exports.DateTimeframe = exports.AtomicFieldType = exports.SourceRelationship = exports.DocumentCompletion = exports.DocumentSymbol = exports.DocumentPosition = exports.DocumentRange = exports.DocumentTablePath = exports.Parse = exports.InMemoryModelCache = exports.CacheManager = exports.getInvalidationKey = exports.readURL = exports.isInternalURL = exports.hashForInvalidationKey = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.EMPTY_BUILD_MANIFEST = void 0;
8
- exports.MalloyError = exports.Malloy = exports.MalloyConfig = exports.Manifest = void 0;
8
+ exports.MalloyError = exports.Malloy = exports.discoverConfig = exports.defaultConfigOverlays = exports.contextOverlay = exports.envOverlay = exports.MalloyConfig = exports.Manifest = void 0;
9
9
  var types_1 = require("./types");
10
10
  Object.defineProperty(exports, "EMPTY_BUILD_MANIFEST", { enumerable: true, get: function () { return types_1.EMPTY_BUILD_MANIFEST; } });
11
11
  // URL readers and connection helpers
@@ -76,6 +76,12 @@ Object.defineProperty(exports, "ExploreMaterializer", { enumerable: true, get: f
76
76
  var config_1 = require("./config");
77
77
  Object.defineProperty(exports, "Manifest", { enumerable: true, get: function () { return config_1.Manifest; } });
78
78
  Object.defineProperty(exports, "MalloyConfig", { enumerable: true, get: function () { return config_1.MalloyConfig; } });
79
+ var config_overlays_1 = require("./config_overlays");
80
+ Object.defineProperty(exports, "envOverlay", { enumerable: true, get: function () { return config_overlays_1.envOverlay; } });
81
+ Object.defineProperty(exports, "contextOverlay", { enumerable: true, get: function () { return config_overlays_1.contextOverlay; } });
82
+ Object.defineProperty(exports, "defaultConfigOverlays", { enumerable: true, get: function () { return config_overlays_1.defaultConfigOverlays; } });
83
+ var config_discover_1 = require("./config_discover");
84
+ Object.defineProperty(exports, "discoverConfig", { enumerable: true, get: function () { return config_discover_1.discoverConfig; } });
79
85
  // Compile (Malloy static class)
80
86
  var compile_1 = require("./compile");
81
87
  Object.defineProperty(exports, "Malloy", { enumerable: true, get: function () { return compile_1.Malloy; } });
@@ -48,9 +48,11 @@ export declare class Runtime {
48
48
  private _cacheManager;
49
49
  private _config;
50
50
  private _buildManifest;
51
+ private _resolvedBuildManifestPromise;
51
52
  private _virtualMap;
52
- constructor({ urlReader, connections, connection, config, eventStream, cacheManager, }: {
53
+ constructor({ urlReader, connections, connection, config, buildManifest, eventStream, cacheManager, }: {
53
54
  urlReader?: URLReader;
55
+ buildManifest?: BuildManifest;
54
56
  eventStream?: EventStream;
55
57
  cacheManager?: CacheManager;
56
58
  } & Connectionable);
@@ -71,17 +73,47 @@ export declare class Runtime {
71
73
  */
72
74
  get eventStream(): EventStream | undefined;
73
75
  /**
74
- * The build manifest for persist source substitution.
75
- * When set, compiled queries automatically resolve persist sources
76
- * against this manifest. Can be overridden per-query via
77
- * CompileQueryOptions.buildManifest.
76
+ * Setter — install an explicit build manifest for persist source
77
+ * substitution. From this point on, compiled queries resolve persist
78
+ * sources against `manifest` (still overridable per-query via
79
+ * `CompileQueryOptions.buildManifest`). Pass `undefined` to clear.
78
80
  *
79
- * When constructed with a MalloyConfig, falls through to
80
- * config.manifest.buildManifest (a live reference — builder
81
- * mutations are visible automatically).
81
+ * This wins over the auto-read path: an explicit value here takes
82
+ * precedence over whatever `config.manifestURL` would have resolved
83
+ * to. Setting also drops any cached auto-read promise so a subsequent
84
+ * compile sees the new value rather than a stale soft-miss.
85
+ *
86
+ * No getter is provided — call `_resolveBuildManifest()` to materialize
87
+ * the value the next compile will actually use (explicit > auto-read >
88
+ * undefined).
82
89
  */
83
- get buildManifest(): BuildManifest | undefined;
84
90
  set buildManifest(manifest: BuildManifest | undefined);
91
+ /**
92
+ * Resolve the build manifest for the next compile. Called from inside
93
+ * the async query-compile path (`QueryMaterializer.loadPreparedResult`).
94
+ *
95
+ * Precedence:
96
+ * 1. Explicit `_buildManifest` (from constructor option or setter) → use it.
97
+ * 2. `config.manifestURL` present → lazily read it via `_urlReader`,
98
+ * parse as JSON, cache the promise.
99
+ * - Read failure (file not present, permission denied, etc.) →
100
+ * soft miss to `undefined`. The common "no manifest yet" case
101
+ * for projects that don't use persistence.
102
+ * - File present but unparseable → return `{entries: {}, loadError}`
103
+ * instead of `undefined`. Non-strict compiles still fall through
104
+ * to inline SQL (entries is empty), but strict compiles can
105
+ * include the load error in their "not found in manifest" throw,
106
+ * so the user sees *why* the manifest looks empty.
107
+ * 3. No URL → `undefined`.
108
+ *
109
+ * The cached promise means concurrent compiles share one IO round-trip;
110
+ * `buildManifest = ...` (setter) clears the cache so subsequent compiles
111
+ * see the new value.
112
+ *
113
+ * @internal Accessed from the Materializer classes in this file. Not part
114
+ * of the public API — the leading underscore + `@internal` marks intent.
115
+ */
116
+ _resolveBuildManifest(): Promise<BuildManifest | undefined>;
85
117
  /**
86
118
  * The virtual map for virtual source resolution.
87
119
  * When set, compiled queries automatically resolve virtual sources
@@ -93,6 +125,19 @@ export declare class Runtime {
93
125
  */
94
126
  get virtualMap(): VirtualMap | undefined;
95
127
  set virtualMap(map: VirtualMap | undefined);
128
+ /**
129
+ * Notify every connection this runtime's config has handed out that it
130
+ * is time to release its resources (pools, sockets, file handles,
131
+ * in-process databases). A no-op for runtimes constructed without a
132
+ * MalloyConfig — in that case the caller owns the connections they
133
+ * passed in and is responsible for closing them.
134
+ *
135
+ * The expected contract is one MalloyConfig per Runtime. Long-running
136
+ * hosts (Publisher, a VS Code extension tearing down a project) should
137
+ * call this when a runtime goes out of scope; one-shot CLIs can skip it
138
+ * and let process exit clean up.
139
+ */
140
+ releaseConnections(): Promise<void>;
96
141
  /**
97
142
  * Load a Malloy model by URL or contents.
98
143
  *
@@ -46,7 +46,7 @@ class FluentState {
46
46
  * An environment for compiling and running Malloy queries.
47
47
  */
48
48
  class Runtime {
49
- constructor({ urlReader, connections, connection, config, eventStream, cacheManager, }) {
49
+ constructor({ urlReader, connections, connection, config, buildManifest, eventStream, cacheManager, }) {
50
50
  this.isTestRuntime = false;
51
51
  if (config !== undefined) {
52
52
  this._config = config;
@@ -65,6 +65,7 @@ class Runtime {
65
65
  }
66
66
  this._urlReader = urlReader;
67
67
  this._connections = connections;
68
+ this._buildManifest = buildManifest;
68
69
  this._eventStream = eventStream;
69
70
  this._cacheManager = cacheManager;
70
71
  }
@@ -93,21 +94,87 @@ class Runtime {
93
94
  return this._eventStream;
94
95
  }
95
96
  /**
96
- * The build manifest for persist source substitution.
97
- * When set, compiled queries automatically resolve persist sources
98
- * against this manifest. Can be overridden per-query via
99
- * CompileQueryOptions.buildManifest.
97
+ * Setter — install an explicit build manifest for persist source
98
+ * substitution. From this point on, compiled queries resolve persist
99
+ * sources against `manifest` (still overridable per-query via
100
+ * `CompileQueryOptions.buildManifest`). Pass `undefined` to clear.
100
101
  *
101
- * When constructed with a MalloyConfig, falls through to
102
- * config.manifest.buildManifest (a live reference — builder
103
- * mutations are visible automatically).
102
+ * This wins over the auto-read path: an explicit value here takes
103
+ * precedence over whatever `config.manifestURL` would have resolved
104
+ * to. Setting also drops any cached auto-read promise so a subsequent
105
+ * compile sees the new value rather than a stale soft-miss.
106
+ *
107
+ * No getter is provided — call `_resolveBuildManifest()` to materialize
108
+ * the value the next compile will actually use (explicit > auto-read >
109
+ * undefined).
104
110
  */
105
- get buildManifest() {
106
- var _a, _b;
107
- return (_a = this._buildManifest) !== null && _a !== void 0 ? _a : (_b = this._config) === null || _b === void 0 ? void 0 : _b.manifest.buildManifest;
108
- }
109
111
  set buildManifest(manifest) {
110
112
  this._buildManifest = manifest;
113
+ this._resolvedBuildManifestPromise = undefined;
114
+ }
115
+ /**
116
+ * Resolve the build manifest for the next compile. Called from inside
117
+ * the async query-compile path (`QueryMaterializer.loadPreparedResult`).
118
+ *
119
+ * Precedence:
120
+ * 1. Explicit `_buildManifest` (from constructor option or setter) → use it.
121
+ * 2. `config.manifestURL` present → lazily read it via `_urlReader`,
122
+ * parse as JSON, cache the promise.
123
+ * - Read failure (file not present, permission denied, etc.) →
124
+ * soft miss to `undefined`. The common "no manifest yet" case
125
+ * for projects that don't use persistence.
126
+ * - File present but unparseable → return `{entries: {}, loadError}`
127
+ * instead of `undefined`. Non-strict compiles still fall through
128
+ * to inline SQL (entries is empty), but strict compiles can
129
+ * include the load error in their "not found in manifest" throw,
130
+ * so the user sees *why* the manifest looks empty.
131
+ * 3. No URL → `undefined`.
132
+ *
133
+ * The cached promise means concurrent compiles share one IO round-trip;
134
+ * `buildManifest = ...` (setter) clears the cache so subsequent compiles
135
+ * see the new value.
136
+ *
137
+ * @internal Accessed from the Materializer classes in this file. Not part
138
+ * of the public API — the leading underscore + `@internal` marks intent.
139
+ */
140
+ _resolveBuildManifest() {
141
+ var _a;
142
+ if (this._buildManifest) {
143
+ return Promise.resolve(this._buildManifest);
144
+ }
145
+ const url = (_a = this._config) === null || _a === void 0 ? void 0 : _a.manifestURL;
146
+ if (!url)
147
+ return Promise.resolve(undefined);
148
+ if (!this._resolvedBuildManifestPromise) {
149
+ this._resolvedBuildManifestPromise = (async () => {
150
+ let text;
151
+ try {
152
+ const result = await this._urlReader.readURL(url);
153
+ text = typeof result === 'string' ? result : result.contents;
154
+ }
155
+ catch {
156
+ // Read failure (no file, permission, etc.) — treat as "no
157
+ // manifest, no substitution." Strict mode is silently inactive
158
+ // here, matching the soloist case where there's nothing to
159
+ // strict-check against.
160
+ return undefined;
161
+ }
162
+ try {
163
+ return JSON.parse(text);
164
+ }
165
+ catch (e) {
166
+ // File was present but couldn't be parsed. Return an empty
167
+ // manifest carrying the load error so a strict-mode compile can
168
+ // surface the real reason in its throw.
169
+ const msg = e instanceof Error ? e.message : String(e);
170
+ return {
171
+ entries: {},
172
+ loadError: `Manifest file at ${url.toString()} could not be parsed: ${msg}`,
173
+ };
174
+ }
175
+ })();
176
+ }
177
+ return this._resolvedBuildManifestPromise;
111
178
  }
112
179
  /**
113
180
  * The virtual map for virtual source resolution.
@@ -125,6 +192,22 @@ class Runtime {
125
192
  set virtualMap(map) {
126
193
  this._virtualMap = map;
127
194
  }
195
+ /**
196
+ * Notify every connection this runtime's config has handed out that it
197
+ * is time to release its resources (pools, sockets, file handles,
198
+ * in-process databases). A no-op for runtimes constructed without a
199
+ * MalloyConfig — in that case the caller owns the connections they
200
+ * passed in and is responsible for closing them.
201
+ *
202
+ * The expected contract is one MalloyConfig per Runtime. Long-running
203
+ * hosts (Publisher, a VS Code extension tearing down a project) should
204
+ * call this when a runtime goes out of scope; one-shot CLIs can skip it
205
+ * and let process exit clean up.
206
+ */
207
+ async releaseConnections() {
208
+ var _a;
209
+ await ((_a = this._config) === null || _a === void 0 ? void 0 : _a.releaseConnections());
210
+ }
128
211
  /**
129
212
  * Load a Malloy model by URL or contents.
130
213
  *
@@ -598,10 +681,11 @@ class QueryMaterializer extends FluentState {
598
681
  ...this.compileQueryOptions,
599
682
  ...options,
600
683
  };
601
- // Use manifest from options if provided, otherwise fall back to Runtime's manifest.
684
+ // Use manifest from options if provided, otherwise fall back to
685
+ // Runtime's manifest (explicit or lazily-read from config.manifestURL).
602
686
  // Pass EMPTY_BUILD_MANIFEST in options to explicitly suppress manifest substitution.
603
687
  const explicitManifest = mergedOptions.buildManifest !== undefined;
604
- let buildManifest = (_a = mergedOptions.buildManifest) !== null && _a !== void 0 ? _a : this.runtime.buildManifest;
688
+ let buildManifest = (_a = mergedOptions.buildManifest) !== null && _a !== void 0 ? _a : (await this.runtime._resolveBuildManifest());
605
689
  // If we have a manifest with entries, compute connectionDigests for lookups.
606
690
  // TODO: This is inefficient - we call getBuildPlan just to find connection names.
607
691
  // Consider adding a listConnections() method to LookupConnection, or caching this.
@@ -9,13 +9,22 @@ export type ConnectionTypeFactory = (config: ConnectionConfig) => Promise<Connec
9
9
  export type ConnectionPropertyType = 'string' | 'number' | 'boolean' | 'password' | 'secret' | 'file' | 'json' | 'text';
10
10
  /**
11
11
  * Describes a single configuration property for a connection type.
12
+ *
13
+ * `default` accepts either a literal value or a single-key object that names an
14
+ * overlay source: e.g. `{config: 'rootDirectory'}` or `{env: 'HOME'}`. Defaults
15
+ * apply uniformly to every connection entry that doesn't specify the property —
16
+ * both user-listed entries and entries fabricated by `includeDefaultConnections`.
17
+ * Reference-shaped defaults are resolved through the same config overlays as
18
+ * inline references.
12
19
  */
13
20
  export interface ConnectionPropertyDefinition {
14
21
  name: string;
15
22
  displayName: string;
16
23
  type: ConnectionPropertyType;
17
24
  optional?: true;
18
- default?: string;
25
+ default?: string | number | boolean | {
26
+ [source: string]: string | string[];
27
+ };
19
28
  description?: string;
20
29
  /** For type 'file': extension filters for picker dialogs. */
21
30
  fileFilters?: Record<string, string[]>;
@@ -28,12 +37,6 @@ export interface ConnectionTypeDef {
28
37
  factory: ConnectionTypeFactory;
29
38
  properties: ConnectionPropertyDefinition[];
30
39
  }
31
- /**
32
- * An environment variable reference in a config file.
33
- */
34
- export type ValueRef = {
35
- env: string;
36
- };
37
40
  /**
38
41
  * A JSON-compatible value for structured config properties (e.g. SSL options).
39
42
  */
@@ -41,25 +44,13 @@ export type JsonConfigValue = string | number | boolean | null | JsonConfigValue
41
44
  [key: string]: JsonConfigValue;
42
45
  };
43
46
  /**
44
- * The type of a config property value: a literal, an env reference, a JSON
45
- * object, or undefined.
46
- */
47
- export type ConfigValue = string | number | boolean | ValueRef | JsonConfigValue | undefined;
48
- /**
49
- * Type guard for ValueRef.
50
- */
51
- export declare function isValueRef(value: unknown): value is ValueRef;
52
- /**
53
- * Resolve a ValueRef to a string by looking up the environment variable.
54
- * Returns undefined if the env var is not set.
55
- */
56
- export declare function resolveValue(vr: ValueRef): string | undefined;
57
- /**
58
- * A single connection entry in a JSON config.
47
+ * A single connection entry passed to `createConnectionsFromConfig`. All
48
+ * values here are already fully resolved — overlay references are expanded
49
+ * by the config compiler/resolver before the registry ever sees them.
59
50
  */
60
51
  export interface ConnectionConfigEntry {
61
52
  is: string;
62
- [key: string]: ConfigValue;
53
+ [key: string]: unknown;
63
54
  }
64
55
  /**
65
56
  * Type guard for ConnectionConfigEntry.
@@ -4,8 +4,6 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.isValueRef = isValueRef;
8
- exports.resolveValue = resolveValue;
9
7
  exports.isConnectionConfigEntry = isConnectionConfigEntry;
10
8
  exports.registerConnectionType = registerConnectionType;
11
9
  exports.getConnectionProperties = getConnectionProperties;
@@ -14,24 +12,6 @@ exports.getRegisteredConnectionTypes = getRegisteredConnectionTypes;
14
12
  exports.readConnectionsConfig = readConnectionsConfig;
15
13
  exports.writeConnectionsConfig = writeConnectionsConfig;
16
14
  exports.createConnectionsFromConfig = createConnectionsFromConfig;
17
- /**
18
- * Type guard for ValueRef.
19
- */
20
- function isValueRef(value) {
21
- return (typeof value === 'object' &&
22
- value !== null &&
23
- !Array.isArray(value) &&
24
- Object.keys(value).length === 1 &&
25
- 'env' in value &&
26
- typeof value.env === 'string');
27
- }
28
- /**
29
- * Resolve a ValueRef to a string by looking up the environment variable.
30
- * Returns undefined if the env var is not set.
31
- */
32
- function resolveValue(vr) {
33
- return process.env[vr.env];
34
- }
35
15
  /**
36
16
  * Type guard for ConnectionConfigEntry.
37
17
  */
@@ -126,21 +106,16 @@ function createConnectionsFromConfig(config, onConnectionCreated) {
126
106
  throw new Error(`No registered connection type "${entry.is}" for connection "${connectionName}". ` +
127
107
  'Did you forget to import the connection package?');
128
108
  }
129
- const jsonKeys = new Set(typeDef.properties.filter(p => p.type === 'json').map(p => p.name));
109
+ // Values are already resolved the config compiler/resolver handles
110
+ // overlay references, property defaults, and entry fabrication via
111
+ // `includeDefaultConnections`. The registry's only job is to hand them
112
+ // to the factory.
130
113
  const connConfig = { name: connectionName };
131
114
  for (const [key, value] of Object.entries(entry)) {
132
115
  if (key === 'is')
133
116
  continue;
134
117
  if (value !== undefined && value !== null) {
135
- if (!jsonKeys.has(key) && isValueRef(value)) {
136
- const resolved = resolveValue(value);
137
- if (resolved !== undefined) {
138
- connConfig[key] = resolved;
139
- }
140
- }
141
- else {
142
- connConfig[key] = value;
143
- }
118
+ connConfig[key] = value;
144
119
  }
145
120
  }
146
121
  const connection = await typeDef.factory(connConfig);
package/dist/index.d.ts CHANGED
@@ -4,13 +4,14 @@ export type { QueryRecord, StructDef, TableSourceDef, SQLSourceDef, SourceDef, J
4
4
  export { isSourceDef, isAtomic, isBasicAtomic, isCompoundArrayData, isJoined, isJoinedSource, isSamplingEnable, isSamplingPercent, isSamplingRows, isRepeatedRecord, isBasicArray, mkArrayDef, mkFieldDef, expressionIsAggregate, expressionIsAnalytic, expressionIsCalculation, expressionIsScalar, expressionIsUngroupedAggregate, indent, composeSQLExpr, isTimestampUnit, isDateUnit, constantExprToSQL, } from './model';
5
5
  export { malloyToQuery, MalloyTranslator, } from './lang';
6
6
  export type { LogMessage, TranslateResponse } from './lang';
7
- export { Model, Malloy, Runtime, AtomicFieldType, ConnectionRuntime, SingleConnectionRuntime, EmptyURLReader, InMemoryURLReader, FixedConnectionMap, MalloyError, JoinRelationship, SourceRelationship, DateTimeframe, TimestampTimeframe, PreparedResult, Result, QueryMaterializer, CSVWriter, JSONWriter, Parse, DataWriter, Explore, InMemoryModelCache, CacheManager, Manifest, MalloyConfig, } from './api/foundation';
7
+ export { Model, Malloy, Runtime, AtomicFieldType, ConnectionRuntime, SingleConnectionRuntime, EmptyURLReader, InMemoryURLReader, FixedConnectionMap, MalloyError, JoinRelationship, SourceRelationship, DateTimeframe, TimestampTimeframe, PreparedResult, Result, QueryMaterializer, CSVWriter, JSONWriter, Parse, DataWriter, Explore, InMemoryModelCache, CacheManager, Manifest, MalloyConfig, envOverlay, contextOverlay, defaultConfigOverlays, discoverConfig, } from './api/foundation';
8
8
  export type { PreparedQuery, Field, AtomicField, ExploreField, QueryField, SortableField, DataArray, DataRecord, DataColumn, DataArrayOrRecord, Loggable, ModelMaterializer, DocumentTablePath, DocumentSymbol, ResultJSON, PreparedResultJSON, PreparedResultMaterializer, ExploreMaterializer, WriteStream, SerializedExplore, ModelCache, CachedModel, DateField, TimestampField, } from './api/foundation';
9
+ export type { Overlay, ConfigOverlays } from './api/foundation';
9
10
  export type { QueryOptionsReader, RunSQLOptions } from './run_sql_options';
10
11
  export type { EventStream, ModelString, ModelURL, QueryString, QueryURL, URLReader, InvalidationKey, } from './runtime_types';
11
12
  export type { Connection, ConnectionConfig, ConnectionParameterValue, FetchSchemaOptions, InfoConnection, LookupConnection, PersistSQLResults, PooledConnection, TestableConnection, StreamingConnection, } from './connection/types';
12
- export { registerConnectionType, getConnectionProperties, getConnectionTypeDisplayName, getRegisteredConnectionTypes, createConnectionsFromConfig, isValueRef, resolveValue, } from './connection/registry';
13
- export type { ConnectionTypeFactory, ConnectionPropertyType, ConnectionPropertyDefinition, ConnectionTypeDef, ConnectionConfigEntry, ConnectionsConfig, ConfigValue, JsonConfigValue, ValueRef, ManagedConnectionLookup, } from './connection/registry';
13
+ export { registerConnectionType, getConnectionProperties, getConnectionTypeDisplayName, getRegisteredConnectionTypes, createConnectionsFromConfig, } from './connection/registry';
14
+ export type { ConnectionTypeFactory, ConnectionPropertyType, ConnectionPropertyDefinition, ConnectionTypeDef, ConnectionConfigEntry, ConnectionsConfig, JsonConfigValue, ManagedConnectionLookup, } from './connection/registry';
14
15
  export { toAsyncGenerator } from './connection_utils';
15
16
  export { modelDefToModelInfo, sourceDefToSourceInfo } from './to_stable';
16
17
  export * as API from './api';