tplm-lang 0.2.6 → 0.3.0
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/README.md +11 -1
- package/dist/executor/index.d.ts +20 -8
- package/dist/executor/index.js +80 -33
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -6
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -40,9 +40,19 @@ The original TPL was a language developed by the U.S. Bureau of Labor Statistics
|
|
|
40
40
|
## Installation
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
|
|
43
|
+
# DuckDB (local CSV, Parquet files)
|
|
44
|
+
npm install tplm-lang @malloydata/db-duckdb
|
|
45
|
+
|
|
46
|
+
# BigQuery
|
|
47
|
+
npm install tplm-lang @malloydata/db-bigquery
|
|
48
|
+
|
|
49
|
+
# Both backends
|
|
50
|
+
npm install tplm-lang @malloydata/db-duckdb @malloydata/db-bigquery
|
|
44
51
|
```
|
|
45
52
|
|
|
53
|
+
Database backends are **optional peer dependencies** — install only the one(s) you need.
|
|
54
|
+
If you only need parsing and compilation (no query execution), `npm install tplm-lang` alone is sufficient.
|
|
55
|
+
|
|
46
56
|
## Quick Start - Query Your Data Immediately
|
|
47
57
|
|
|
48
58
|
For basic use, no Malloy knowledge is required; just use your local data and start querying.
|
package/dist/executor/index.d.ts
CHANGED
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
* TPL Executor
|
|
3
3
|
*
|
|
4
4
|
* Executes Malloy queries against BigQuery or DuckDB (local mode) and returns results.
|
|
5
|
+
*
|
|
6
|
+
* Database backends are loaded lazily via dynamic imports, so consumers only need
|
|
7
|
+
* to install the backend(s) they actually use:
|
|
8
|
+
* npm install @malloydata/db-duckdb # for DuckDB support
|
|
9
|
+
* npm install @malloydata/db-bigquery # for BigQuery support
|
|
5
10
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
11
|
+
import type { DuckDBConnection } from '@malloydata/db-duckdb';
|
|
12
|
+
import type { BigQueryConnection } from '@malloydata/db-bigquery';
|
|
8
13
|
import { Runtime, Connection } from '@malloydata/malloy';
|
|
9
14
|
export type ConnectionType = 'bigquery' | 'duckdb';
|
|
10
15
|
export interface BigQueryConnectionOptions {
|
|
@@ -24,20 +29,26 @@ export interface DuckDBConnectionOptions {
|
|
|
24
29
|
}[];
|
|
25
30
|
}
|
|
26
31
|
export type ConnectionOptions = BigQueryConnectionOptions | DuckDBConnectionOptions;
|
|
27
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Store connection options for deferred creation.
|
|
34
|
+
* The connection will be created lazily on first getConnection() call.
|
|
35
|
+
*/
|
|
36
|
+
export declare function setPendingConnection(options: ConnectionOptions): void;
|
|
37
|
+
export declare function createConnection(options: ConnectionOptions): Promise<Connection>;
|
|
28
38
|
/**
|
|
29
39
|
* Create a default local DuckDB connection with test data
|
|
30
40
|
*/
|
|
31
|
-
export declare function createLocalConnection():
|
|
41
|
+
export declare function createLocalConnection(): Promise<Connection>;
|
|
32
42
|
/**
|
|
33
43
|
* Get the current connection or create default connection.
|
|
34
44
|
*
|
|
35
45
|
* Connection priority:
|
|
36
46
|
* 1. If connection already exists, return it
|
|
37
|
-
* 2. If
|
|
38
|
-
* 3.
|
|
47
|
+
* 2. If pending options were set (via setPendingConnection), use those
|
|
48
|
+
* 3. If TPL_BIGQUERY=true env var is set and credentials exist, use BigQuery
|
|
49
|
+
* 4. Otherwise, use DuckDB (default)
|
|
39
50
|
*/
|
|
40
|
-
export declare function getConnection(): Connection
|
|
51
|
+
export declare function getConnection(): Promise<Connection>;
|
|
41
52
|
export declare function getConnectionType(): ConnectionType | null;
|
|
42
53
|
export declare function listDatasets(): Promise<string[]>;
|
|
43
54
|
export declare function listTables(dataset: string): Promise<{
|
|
@@ -68,4 +79,5 @@ export declare function executeMalloy(malloySource: string, options?: ExecuteOpt
|
|
|
68
79
|
* Execute raw SQL against the current connection
|
|
69
80
|
*/
|
|
70
81
|
export declare function executeSQL(sql: string): Promise<any[]>;
|
|
71
|
-
export {
|
|
82
|
+
export { Runtime };
|
|
83
|
+
export type { DuckDBConnection, BigQueryConnection };
|
package/dist/executor/index.js
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* TPL Executor
|
|
3
3
|
*
|
|
4
4
|
* Executes Malloy queries against BigQuery or DuckDB (local mode) and returns results.
|
|
5
|
+
*
|
|
6
|
+
* Database backends are loaded lazily via dynamic imports, so consumers only need
|
|
7
|
+
* to install the backend(s) they actually use:
|
|
8
|
+
* npm install @malloydata/db-duckdb # for DuckDB support
|
|
9
|
+
* npm install @malloydata/db-bigquery # for BigQuery support
|
|
5
10
|
*/
|
|
6
|
-
import { BigQueryConnection } from '@malloydata/db-bigquery';
|
|
7
|
-
import { DuckDBConnection } from '@malloydata/db-duckdb';
|
|
8
11
|
import { Runtime } from '@malloydata/malloy';
|
|
9
12
|
import * as fs from 'fs';
|
|
10
13
|
import * as path from 'path';
|
|
@@ -12,11 +15,49 @@ import { fileURLToPath } from 'url';
|
|
|
12
15
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
16
|
const PROJECT_ROOT = path.resolve(__dirname, '../..');
|
|
14
17
|
// ---
|
|
18
|
+
// LAZY MODULE LOADERS
|
|
19
|
+
// ---
|
|
20
|
+
let _DuckDBModule = null;
|
|
21
|
+
let _BigQueryModule = null;
|
|
22
|
+
async function loadDuckDB() {
|
|
23
|
+
if (!_DuckDBModule) {
|
|
24
|
+
try {
|
|
25
|
+
_DuckDBModule = await import('@malloydata/db-duckdb');
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new Error('@malloydata/db-duckdb is required for DuckDB support. Install it with: npm install @malloydata/db-duckdb');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return _DuckDBModule;
|
|
32
|
+
}
|
|
33
|
+
async function loadBigQuery() {
|
|
34
|
+
if (!_BigQueryModule) {
|
|
35
|
+
try {
|
|
36
|
+
_BigQueryModule = await import('@malloydata/db-bigquery');
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new Error('@malloydata/db-bigquery is required for BigQuery support. Install it with: npm install @malloydata/db-bigquery');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return _BigQueryModule;
|
|
43
|
+
}
|
|
44
|
+
// ---
|
|
15
45
|
// CONNECTION SETUP
|
|
16
46
|
// ---
|
|
17
47
|
let connectionInstance = null;
|
|
18
48
|
let currentConnectionType = null;
|
|
19
|
-
|
|
49
|
+
let pendingOptions = null;
|
|
50
|
+
/**
|
|
51
|
+
* Store connection options for deferred creation.
|
|
52
|
+
* The connection will be created lazily on first getConnection() call.
|
|
53
|
+
*/
|
|
54
|
+
export function setPendingConnection(options) {
|
|
55
|
+
pendingOptions = options;
|
|
56
|
+
// Clear any existing connection so the new options take effect
|
|
57
|
+
connectionInstance = null;
|
|
58
|
+
currentConnectionType = null;
|
|
59
|
+
}
|
|
60
|
+
export async function createConnection(options) {
|
|
20
61
|
if (options.type === 'bigquery') {
|
|
21
62
|
return createBigQueryConnection(options);
|
|
22
63
|
}
|
|
@@ -24,7 +65,8 @@ export function createConnection(options) {
|
|
|
24
65
|
return createDuckDBConnection(options);
|
|
25
66
|
}
|
|
26
67
|
}
|
|
27
|
-
function createBigQueryConnection(options) {
|
|
68
|
+
async function createBigQueryConnection(options) {
|
|
69
|
+
const { BigQueryConnection } = await loadBigQuery();
|
|
28
70
|
const credentialsPath = options.credentialsPath ?? './config/dev-credentials.json';
|
|
29
71
|
// Read credentials to get project ID if not provided
|
|
30
72
|
const credentials = JSON.parse(fs.readFileSync(credentialsPath, 'utf-8'));
|
|
@@ -36,7 +78,8 @@ function createBigQueryConnection(options) {
|
|
|
36
78
|
currentConnectionType = 'bigquery';
|
|
37
79
|
return connection;
|
|
38
80
|
}
|
|
39
|
-
function createDuckDBConnection(options) {
|
|
81
|
+
async function createDuckDBConnection(options) {
|
|
82
|
+
const { DuckDBConnection } = await loadDuckDB();
|
|
40
83
|
const connection = new DuckDBConnection('duckdb', options.databasePath ?? ':memory:', undefined);
|
|
41
84
|
connectionInstance = connection;
|
|
42
85
|
currentConnectionType = 'duckdb';
|
|
@@ -45,7 +88,7 @@ function createDuckDBConnection(options) {
|
|
|
45
88
|
/**
|
|
46
89
|
* Create a default local DuckDB connection with test data
|
|
47
90
|
*/
|
|
48
|
-
export function createLocalConnection() {
|
|
91
|
+
export async function createLocalConnection() {
|
|
49
92
|
return createDuckDBConnection({
|
|
50
93
|
type: 'duckdb',
|
|
51
94
|
});
|
|
@@ -55,37 +98,48 @@ export function createLocalConnection() {
|
|
|
55
98
|
*
|
|
56
99
|
* Connection priority:
|
|
57
100
|
* 1. If connection already exists, return it
|
|
58
|
-
* 2. If
|
|
59
|
-
* 3.
|
|
101
|
+
* 2. If pending options were set (via setPendingConnection), use those
|
|
102
|
+
* 3. If TPL_BIGQUERY=true env var is set and credentials exist, use BigQuery
|
|
103
|
+
* 4. Otherwise, use DuckDB (default)
|
|
60
104
|
*/
|
|
61
|
-
export function getConnection() {
|
|
105
|
+
export async function getConnection() {
|
|
62
106
|
if (!connectionInstance) {
|
|
107
|
+
// Use pending options if set
|
|
108
|
+
if (pendingOptions) {
|
|
109
|
+
const opts = pendingOptions;
|
|
110
|
+
pendingOptions = null;
|
|
111
|
+
return await createConnection(opts);
|
|
112
|
+
}
|
|
63
113
|
// BigQuery only if explicitly requested via env var AND credentials exist
|
|
64
114
|
const useBigQuery = process.env.TPL_BIGQUERY === 'true';
|
|
65
115
|
const credentialsPath = './config/dev-credentials.json';
|
|
66
116
|
if (useBigQuery && fs.existsSync(credentialsPath)) {
|
|
67
117
|
try {
|
|
68
|
-
return createConnection({ type: 'bigquery', credentialsPath });
|
|
118
|
+
return await createConnection({ type: 'bigquery', credentialsPath });
|
|
69
119
|
}
|
|
70
120
|
catch (e) {
|
|
71
121
|
console.log('BigQuery connection failed, falling back to DuckDB');
|
|
72
|
-
return createLocalConnection();
|
|
122
|
+
return await createLocalConnection();
|
|
73
123
|
}
|
|
74
124
|
}
|
|
75
125
|
// Default: DuckDB
|
|
76
|
-
return createLocalConnection();
|
|
126
|
+
return await createLocalConnection();
|
|
77
127
|
}
|
|
78
128
|
return connectionInstance;
|
|
79
129
|
}
|
|
80
130
|
export function getConnectionType() {
|
|
131
|
+
// Return pending type if connection hasn't been created yet
|
|
132
|
+
if (!currentConnectionType && pendingOptions) {
|
|
133
|
+
return pendingOptions.type;
|
|
134
|
+
}
|
|
81
135
|
return currentConnectionType;
|
|
82
136
|
}
|
|
83
137
|
// ---
|
|
84
138
|
// SCHEMA EXPLORATION (BigQuery only)
|
|
85
139
|
// ---
|
|
86
140
|
export async function listDatasets() {
|
|
87
|
-
const conn = getConnection();
|
|
88
|
-
if (
|
|
141
|
+
const conn = await getConnection();
|
|
142
|
+
if (currentConnectionType !== 'bigquery') {
|
|
89
143
|
throw new Error('listDatasets only supported for BigQuery');
|
|
90
144
|
}
|
|
91
145
|
const result = await conn.runSQL(`
|
|
@@ -96,8 +150,8 @@ export async function listDatasets() {
|
|
|
96
150
|
return result.rows.map((row) => row.schema_name);
|
|
97
151
|
}
|
|
98
152
|
export async function listTables(dataset) {
|
|
99
|
-
const conn = getConnection();
|
|
100
|
-
if (
|
|
153
|
+
const conn = await getConnection();
|
|
154
|
+
if (currentConnectionType !== 'bigquery') {
|
|
101
155
|
throw new Error('listTables only supported for BigQuery');
|
|
102
156
|
}
|
|
103
157
|
const result = await conn.runSQL(`
|
|
@@ -117,8 +171,8 @@ export async function listTables(dataset) {
|
|
|
117
171
|
}));
|
|
118
172
|
}
|
|
119
173
|
export async function describeTable(dataset, table) {
|
|
120
|
-
const conn = getConnection();
|
|
121
|
-
if (
|
|
174
|
+
const conn = await getConnection();
|
|
175
|
+
if (currentConnectionType !== 'bigquery') {
|
|
122
176
|
throw new Error('describeTable only supported for BigQuery');
|
|
123
177
|
}
|
|
124
178
|
const result = await conn.runSQL(`
|
|
@@ -133,8 +187,8 @@ export async function describeTable(dataset, table) {
|
|
|
133
187
|
}));
|
|
134
188
|
}
|
|
135
189
|
export async function sampleTable(dataset, table, limit = 10) {
|
|
136
|
-
const conn = getConnection();
|
|
137
|
-
if (
|
|
190
|
+
const conn = await getConnection();
|
|
191
|
+
if (currentConnectionType !== 'bigquery') {
|
|
138
192
|
throw new Error('sampleTable only supported for BigQuery');
|
|
139
193
|
}
|
|
140
194
|
const result = await conn.runSQL(`
|
|
@@ -150,7 +204,7 @@ export async function sampleTable(dataset, table, limit = 10) {
|
|
|
150
204
|
*/
|
|
151
205
|
export function getDefaultSource() {
|
|
152
206
|
const connType = getConnectionType();
|
|
153
|
-
if (connType === 'duckdb') {
|
|
207
|
+
if (connType === 'duckdb' || connType === null) {
|
|
154
208
|
const csvPath = path.join(PROJECT_ROOT, 'data/test_usa_names.csv');
|
|
155
209
|
return `
|
|
156
210
|
source: names is duckdb.table('${csvPath}') extend {
|
|
@@ -181,7 +235,7 @@ source: names is bigquery.table('slite-development.tpl_test.test_usa_names') ext
|
|
|
181
235
|
* Execute a Malloy query string
|
|
182
236
|
*/
|
|
183
237
|
export async function executeMalloy(malloySource, options = {}) {
|
|
184
|
-
const conn = getConnection();
|
|
238
|
+
const conn = await getConnection();
|
|
185
239
|
// Create a minimal URL reader (we're passing source directly)
|
|
186
240
|
const urlReader = {
|
|
187
241
|
readURL: async (url) => {
|
|
@@ -215,18 +269,11 @@ export async function executeMalloy(malloySource, options = {}) {
|
|
|
215
269
|
* Execute raw SQL against the current connection
|
|
216
270
|
*/
|
|
217
271
|
export async function executeSQL(sql) {
|
|
218
|
-
const conn = getConnection();
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return result.rows;
|
|
222
|
-
}
|
|
223
|
-
else if (conn instanceof DuckDBConnection) {
|
|
224
|
-
const result = await conn.runSQL(sql);
|
|
225
|
-
return result.rows;
|
|
226
|
-
}
|
|
227
|
-
throw new Error('Unknown connection type');
|
|
272
|
+
const conn = await getConnection();
|
|
273
|
+
const result = await conn.runSQL(sql);
|
|
274
|
+
return result.rows;
|
|
228
275
|
}
|
|
229
276
|
// ---
|
|
230
277
|
// EXPORTS
|
|
231
278
|
// ---
|
|
232
|
-
export {
|
|
279
|
+
export { Runtime };
|
package/dist/index.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export { buildTableSpec, generateQueryPlan, generateMalloyQueries, buildGridSpec
|
|
|
34
34
|
export type { TableSpec, QueryPlan, GridSpec, HeaderNode, QueryResults, MalloyQuerySpec, } from './compiler/index.js';
|
|
35
35
|
export { renderGridToHTML } from './renderer/index.js';
|
|
36
36
|
export type { GridRenderOptions } from './renderer/index.js';
|
|
37
|
-
export { createConnection, createLocalConnection, getConnection, getConnectionType, getDefaultSource, executeMalloy, executeSQL, } from './executor/index.js';
|
|
37
|
+
export { createConnection, createLocalConnection, getConnection, getConnectionType, getDefaultSource, executeMalloy, executeSQL, setPendingConnection, } from './executor/index.js';
|
|
38
38
|
export type { ConnectionType, ConnectionOptions, BigQueryConnectionOptions, DuckDBConnectionOptions, ExecuteOptions, } from './executor/index.js';
|
|
39
39
|
import type { TableSpec, QueryPlan, GridSpec, MalloyQuerySpec } from './compiler/index.js';
|
|
40
40
|
import type { DimensionOrderingProvider } from './compiler/dimension-utils.js';
|
|
@@ -134,10 +134,12 @@ export declare class TPL {
|
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
136
|
* Create a TPL instance with DuckDB connection (simplest way to start).
|
|
137
|
+
* Connection is created lazily on first execute() call.
|
|
137
138
|
*/
|
|
138
139
|
export declare function createTPL(options?: TPLOptions): TPL;
|
|
139
140
|
/**
|
|
140
141
|
* Create a TPL instance with BigQuery connection.
|
|
142
|
+
* Connection is created lazily on first execute() call.
|
|
141
143
|
*/
|
|
142
144
|
export declare function createBigQueryTPL(options?: TPLOptions & {
|
|
143
145
|
credentialsPath?: string;
|
package/dist/index.js
CHANGED
|
@@ -34,11 +34,11 @@ export { buildTableSpec, generateQueryPlan, generateMalloyQueries, buildGridSpec
|
|
|
34
34
|
// renderer
|
|
35
35
|
export { renderGridToHTML } from './renderer/index.js';
|
|
36
36
|
// executor
|
|
37
|
-
export { createConnection, createLocalConnection, getConnection, getConnectionType, getDefaultSource, executeMalloy, executeSQL, } from './executor/index.js';
|
|
37
|
+
export { createConnection, createLocalConnection, getConnection, getConnectionType, getDefaultSource, executeMalloy, executeSQL, setPendingConnection, } from './executor/index.js';
|
|
38
38
|
// --- internal imports ---
|
|
39
39
|
import { parse } from './parser/index.js';
|
|
40
40
|
import { buildTableSpec, generateQueryPlan, generateMalloyQueries, buildGridSpec } from './compiler/index.js';
|
|
41
|
-
import { executeMalloy,
|
|
41
|
+
import { executeMalloy, setPendingConnection, } from './executor/index.js';
|
|
42
42
|
import { renderGridToHTML } from './renderer/index.js';
|
|
43
43
|
/**
|
|
44
44
|
* High-level TPL API for parsing, compiling, executing, and rendering.
|
|
@@ -102,16 +102,18 @@ export class TPL {
|
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* Create a TPL instance with DuckDB connection (simplest way to start).
|
|
105
|
+
* Connection is created lazily on first execute() call.
|
|
105
106
|
*/
|
|
106
107
|
export function createTPL(options = {}) {
|
|
107
|
-
|
|
108
|
+
setPendingConnection({ type: 'duckdb' });
|
|
108
109
|
return new TPL(options);
|
|
109
110
|
}
|
|
110
111
|
/**
|
|
111
112
|
* Create a TPL instance with BigQuery connection.
|
|
113
|
+
* Connection is created lazily on first execute() call.
|
|
112
114
|
*/
|
|
113
115
|
export function createBigQueryTPL(options = {}) {
|
|
114
|
-
|
|
116
|
+
setPendingConnection({
|
|
115
117
|
type: 'bigquery',
|
|
116
118
|
credentialsPath: options.credentialsPath,
|
|
117
119
|
projectId: options.projectId,
|
|
@@ -134,7 +136,7 @@ import { analyzeAndGeneratePercentileConfig, postProcessMalloyForPercentiles, ge
|
|
|
134
136
|
* ```
|
|
135
137
|
*/
|
|
136
138
|
export function fromDuckDBTable(tablePath, options = {}) {
|
|
137
|
-
|
|
139
|
+
setPendingConnection({ type: 'duckdb' });
|
|
138
140
|
const sourceName = 'data';
|
|
139
141
|
const model = `source: ${sourceName} is duckdb.table('${tablePath}')`;
|
|
140
142
|
return new EasyTPL(model, sourceName, {
|
|
@@ -170,7 +172,7 @@ export function fromCSV(csvPath, options = {}) {
|
|
|
170
172
|
* ```
|
|
171
173
|
*/
|
|
172
174
|
export function fromBigQueryTable(options) {
|
|
173
|
-
|
|
175
|
+
setPendingConnection({
|
|
174
176
|
type: 'bigquery',
|
|
175
177
|
credentialsPath: options.credentialsPath,
|
|
176
178
|
projectId: options.projectId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tplm-lang",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "TPLm - Table Producing Language backed by Malloy.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,17 +29,29 @@
|
|
|
29
29
|
"docs:preview": "cd docs-site && npm run docs:preview"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
+
"@malloydata/db-bigquery": "^0.0.326",
|
|
33
|
+
"@malloydata/db-duckdb": "^0.0.326",
|
|
32
34
|
"@types/node": "^20.0.0",
|
|
33
35
|
"peggy": "^4.0.0",
|
|
34
36
|
"typescript": "^5.0.0",
|
|
35
37
|
"vitest": "^4.0.16"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
|
-
"@malloydata/db-bigquery": "^0.0.326",
|
|
39
|
-
"@malloydata/db-duckdb": "^0.0.326",
|
|
40
40
|
"@malloydata/malloy": "^0.0.326",
|
|
41
41
|
"chevrotain": "^11.0.3"
|
|
42
42
|
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@malloydata/db-bigquery": "^0.0.326",
|
|
45
|
+
"@malloydata/db-duckdb": "^0.0.326"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"@malloydata/db-bigquery": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"@malloydata/db-duckdb": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
43
55
|
"engines": {
|
|
44
56
|
"node": ">=18.0.0"
|
|
45
57
|
},
|