@malloydata/malloy 0.0.371 → 0.0.373
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/foundation/config.d.ts +109 -108
- package/dist/api/foundation/config.js +205 -364
- package/dist/api/foundation/config_compile.d.ts +34 -0
- package/dist/api/foundation/config_compile.js +247 -0
- package/dist/api/foundation/config_discover.d.ts +37 -0
- package/dist/api/foundation/config_discover.js +154 -0
- package/dist/api/foundation/config_overlays.d.ts +54 -0
- package/dist/api/foundation/config_overlays.js +51 -0
- package/dist/api/foundation/config_resolve.d.ts +49 -0
- package/dist/api/foundation/config_resolve.js +230 -0
- package/dist/api/foundation/index.d.ts +3 -0
- package/dist/api/foundation/index.js +7 -1
- package/dist/api/foundation/runtime.d.ts +54 -9
- package/dist/api/foundation/runtime.js +98 -14
- package/dist/connection/registry.d.ts +14 -23
- package/dist/connection/registry.js +5 -30
- package/dist/index.d.ts +4 -3
- package/dist/index.js +5 -3
- package/dist/model/malloy_types.d.ts +8 -0
- package/dist/model/query_query.js +4 -1
- package/dist/model/sql_compiled.js +4 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -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
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* against
|
|
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
|
-
*
|
|
80
|
-
* config.
|
|
81
|
-
*
|
|
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
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* against
|
|
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
|
-
*
|
|
102
|
-
* config.
|
|
103
|
-
*
|
|
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
|
|
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.
|
|
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
|
-
*
|
|
45
|
-
*
|
|
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]:
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
13
|
-
export type { ConnectionTypeFactory, ConnectionPropertyType, ConnectionPropertyDefinition, ConnectionTypeDef, ConnectionConfigEntry, ConnectionsConfig,
|
|
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';
|