@malloydata/db-duckdb 0.0.377 → 0.0.379
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/MAINTENANCE_NOTES.md +487 -0
- package/dist/duckdb_config.d.ts +48 -0
- package/dist/duckdb_config.js +375 -0
- package/dist/duckdb_connection.d.ts +27 -6
- package/dist/duckdb_connection.js +181 -120
- package/dist/native.js +86 -7
- package/dist/path_security.d.ts +7 -0
- package/dist/path_security.js +83 -0
- package/package.json +2 -2
|
@@ -30,160 +30,207 @@ const duckdb_common_1 = require("./duckdb_common");
|
|
|
30
30
|
const node_api_1 = require("@duckdb/node-api");
|
|
31
31
|
const malloy_1 = require("@malloydata/malloy");
|
|
32
32
|
const package_json_1 = __importDefault(require("@malloydata/malloy/package.json"));
|
|
33
|
+
const duckdb_config_1 = require("./duckdb_config");
|
|
33
34
|
class DuckDBConnection extends duckdb_common_1.DuckDBCommon {
|
|
34
35
|
constructor(arg, arg2, workingDirectory, queryOptions) {
|
|
36
|
+
var _a;
|
|
35
37
|
super();
|
|
36
|
-
this.additionalExtensions = [];
|
|
37
|
-
this.databasePath = ':memory:';
|
|
38
|
-
this.workingDirectory = '.';
|
|
39
|
-
this.readOnly = false;
|
|
40
38
|
this.connection = null;
|
|
39
|
+
const options = typeof arg === 'string'
|
|
40
|
+
? buildLegacyOptions(arg, arg2, workingDirectory)
|
|
41
|
+
: arg;
|
|
42
|
+
this.name = options.name;
|
|
41
43
|
if (typeof arg === 'string') {
|
|
42
|
-
this.name = arg;
|
|
43
|
-
if (typeof arg2 === 'string') {
|
|
44
|
-
this.databasePath = arg2;
|
|
45
|
-
}
|
|
46
|
-
if (typeof workingDirectory === 'string') {
|
|
47
|
-
this.workingDirectory = workingDirectory;
|
|
48
|
-
}
|
|
49
44
|
if (queryOptions) {
|
|
50
45
|
this.queryOptions = queryOptions;
|
|
51
46
|
}
|
|
52
47
|
}
|
|
53
|
-
else {
|
|
54
|
-
this.
|
|
55
|
-
if (arg2) {
|
|
56
|
-
this.queryOptions = arg2;
|
|
57
|
-
}
|
|
58
|
-
if (typeof arg.readOnly === 'boolean') {
|
|
59
|
-
this.readOnly = arg.readOnly;
|
|
60
|
-
}
|
|
61
|
-
if (typeof arg.databasePath === 'string') {
|
|
62
|
-
this.databasePath = arg.databasePath;
|
|
63
|
-
}
|
|
64
|
-
if (typeof arg.workingDirectory === 'string') {
|
|
65
|
-
this.workingDirectory = arg.workingDirectory;
|
|
66
|
-
}
|
|
67
|
-
if (typeof arg.motherDuckToken === 'string') {
|
|
68
|
-
this.motherDuckToken = arg.motherDuckToken;
|
|
69
|
-
}
|
|
70
|
-
if (Array.isArray(arg.additionalExtensions)) {
|
|
71
|
-
this.additionalExtensions = arg.additionalExtensions;
|
|
72
|
-
}
|
|
73
|
-
if (typeof arg.setupSQL === 'string') {
|
|
74
|
-
this.setupSQL = arg.setupSQL;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (this.databasePath === ':memory:') {
|
|
78
|
-
this.readOnly = false;
|
|
48
|
+
else if (arg2) {
|
|
49
|
+
this.queryOptions = arg2;
|
|
79
50
|
}
|
|
51
|
+
this.digestDatabasePath = (_a = options.databasePath) !== null && _a !== void 0 ? _a : ':memory:';
|
|
52
|
+
this.digestWorkingDirectory = options.workingDirectory;
|
|
53
|
+
this.digestSetupSQL =
|
|
54
|
+
typeof options.setupSQL === 'string' ? options.setupSQL : undefined;
|
|
55
|
+
this.normalized = (0, duckdb_config_1.normalizeDuckDBConfig)(options);
|
|
56
|
+
this.shareKey = (0, duckdb_config_1.buildDuckDBShareKey)(this.normalized);
|
|
80
57
|
this.isMotherDuck =
|
|
81
|
-
this.databasePath.startsWith('md:') ||
|
|
82
|
-
this.databasePath.startsWith('motherduck:');
|
|
58
|
+
this.normalized.databasePath.startsWith('md:') ||
|
|
59
|
+
this.normalized.databasePath.startsWith('motherduck:');
|
|
60
|
+
this.motherDuckToken = this.normalized.motherDuckToken;
|
|
61
|
+
this.setupSQL = this.normalized.setupSQL;
|
|
83
62
|
this.connecting = this.init();
|
|
84
63
|
}
|
|
85
64
|
getDigest() {
|
|
86
|
-
return (0, malloy_1.makeDigest)('duckdb', this.
|
|
65
|
+
return (0, malloy_1.makeDigest)('duckdb', this.digestDatabasePath, this.digestWorkingDirectory, this.digestSetupSQL);
|
|
87
66
|
}
|
|
88
67
|
async init() {
|
|
89
68
|
try {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.connection = await
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
else {
|
|
96
|
-
const config = {
|
|
97
|
-
custom_user_agent: `Malloy/${package_json_1.default.version}`,
|
|
98
|
-
};
|
|
99
|
-
if (this.isMotherDuck) {
|
|
100
|
-
if (!this.motherDuckToken &&
|
|
101
|
-
!process.env['motherduck_token'] &&
|
|
102
|
-
!process.env['MOTHERDUCK_TOKEN']) {
|
|
103
|
-
this.setupError = new Error('Please set your MotherDuck Token');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (this.motherDuckToken) {
|
|
107
|
-
config['motherduck_token'] = this.motherDuckToken;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (this.readOnly) {
|
|
111
|
-
config['access_mode'] = 'READ_ONLY';
|
|
112
|
-
}
|
|
113
|
-
const instance = await node_api_1.DuckDBInstance.create(this.databasePath, config);
|
|
114
|
-
this.connection = await instance.connect();
|
|
115
|
-
const activeDB = {
|
|
116
|
-
instance,
|
|
117
|
-
connections: [this.connection],
|
|
118
|
-
};
|
|
119
|
-
DuckDBConnection.activeDBs[this.databasePath] = activeDB;
|
|
69
|
+
const cached = DuckDBConnection.activeDBs[this.shareKey];
|
|
70
|
+
if (cached) {
|
|
71
|
+
this.connection = await cached.instance.connect();
|
|
72
|
+
cached.connections.push(this.connection);
|
|
73
|
+
return;
|
|
120
74
|
}
|
|
75
|
+
const instance = await node_api_1.DuckDBInstance.create(this.normalized.databasePath, this.buildInstanceOptions());
|
|
76
|
+
this.connection = await instance.connect();
|
|
77
|
+
DuckDBConnection.activeDBs[this.shareKey] = {
|
|
78
|
+
instance,
|
|
79
|
+
connections: [this.connection],
|
|
80
|
+
};
|
|
121
81
|
}
|
|
122
82
|
catch (err) {
|
|
123
83
|
this.setupError = err instanceof Error ? err : new Error(String(err));
|
|
124
84
|
}
|
|
125
85
|
}
|
|
126
|
-
async loadExtension(ext) {
|
|
127
|
-
try {
|
|
128
|
-
await this.runDuckDBQuery(`INSTALL '${ext}'`);
|
|
129
|
-
await this.runDuckDBQuery(`LOAD '${ext}'`);
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
// eslint-disable-next-line no-console
|
|
133
|
-
console.error('Unable to load ${ext} extension', error);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
86
|
async setup() {
|
|
137
87
|
if (this.setupError) {
|
|
138
88
|
throw this.setupError;
|
|
139
89
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
await this.loadExtension(ext);
|
|
90
|
+
await this.connecting;
|
|
91
|
+
if (this.setupError) {
|
|
92
|
+
throw this.setupError;
|
|
93
|
+
}
|
|
94
|
+
if (!this.isSetup) {
|
|
95
|
+
this.isSetup = this.setupOnce();
|
|
96
|
+
}
|
|
97
|
+
await this.isSetup;
|
|
98
|
+
}
|
|
99
|
+
async setupOnce() {
|
|
100
|
+
await this.applyFinalBaseline();
|
|
101
|
+
if (this.normalized.setupSQL) {
|
|
102
|
+
for (const statement of splitSetupSQL(this.normalized.setupSQL)) {
|
|
103
|
+
await this.runDuckDBQuery(statement);
|
|
155
104
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
105
|
+
}
|
|
106
|
+
if (this.normalized.lockConfiguration) {
|
|
107
|
+
await this.runDuckDBQuery('SET lock_configuration=true');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
buildInstanceOptions() {
|
|
111
|
+
const options = {
|
|
112
|
+
custom_user_agent: `Malloy/${package_json_1.default.version}`,
|
|
113
|
+
};
|
|
114
|
+
if (this.normalized.motherDuckToken !== undefined) {
|
|
115
|
+
options['motherduck_token'] = this.normalized.motherDuckToken;
|
|
116
|
+
}
|
|
117
|
+
if (this.normalized.readOnly) {
|
|
118
|
+
options['access_mode'] = 'READ_ONLY';
|
|
119
|
+
}
|
|
120
|
+
if (this.normalized.autoloadKnownExtensions !== undefined) {
|
|
121
|
+
options['autoload_known_extensions'] = (0, duckdb_config_1.stringifyDuckDBOption)(this.normalized.autoloadKnownExtensions);
|
|
122
|
+
}
|
|
123
|
+
if (this.normalized.autoinstallKnownExtensions !== undefined) {
|
|
124
|
+
options['autoinstall_known_extensions'] = (0, duckdb_config_1.stringifyDuckDBOption)(this.normalized.autoinstallKnownExtensions);
|
|
125
|
+
}
|
|
126
|
+
if (this.normalized.allowCommunityExtensions !== undefined) {
|
|
127
|
+
options['allow_community_extensions'] = (0, duckdb_config_1.stringifyDuckDBOption)(this.normalized.allowCommunityExtensions);
|
|
128
|
+
}
|
|
129
|
+
if (this.normalized.allowUnsignedExtensions !== undefined) {
|
|
130
|
+
options['allow_unsigned_extensions'] = (0, duckdb_config_1.stringifyDuckDBOption)(this.normalized.allowUnsignedExtensions);
|
|
131
|
+
}
|
|
132
|
+
if (this.normalized.tempFileEncryption !== undefined) {
|
|
133
|
+
options['temp_file_encryption'] = (0, duckdb_config_1.stringifyDuckDBOption)(this.normalized.tempFileEncryption);
|
|
134
|
+
}
|
|
135
|
+
if (this.normalized.threads !== undefined) {
|
|
136
|
+
options['threads'] = (0, duckdb_config_1.stringifyDuckDBOption)(this.normalized.threads);
|
|
137
|
+
}
|
|
138
|
+
if (this.normalized.memoryLimit !== undefined) {
|
|
139
|
+
options['memory_limit'] = this.normalized.memoryLimit;
|
|
140
|
+
}
|
|
141
|
+
if (this.normalized.tempDirectory !== undefined) {
|
|
142
|
+
options['temp_directory'] = this.normalized.tempDirectory;
|
|
143
|
+
}
|
|
144
|
+
if (this.normalized.extensionDirectory !== undefined) {
|
|
145
|
+
options['extension_directory'] = this.normalized.extensionDirectory;
|
|
146
|
+
}
|
|
147
|
+
if (this.shouldApplyEnableExternalAccessAtOpenTime()) {
|
|
148
|
+
options['enable_external_access'] = (0, duckdb_config_1.stringifyDuckDBOption)(this.normalized.enableExternalAccess);
|
|
149
|
+
}
|
|
150
|
+
return options;
|
|
151
|
+
}
|
|
152
|
+
shouldApplyEnableExternalAccessAtOpenTime() {
|
|
153
|
+
// DuckDB's Node API does not currently accept allowed_directories as an
|
|
154
|
+
// open-time option, and DuckDB rejects SET allowed_directories after
|
|
155
|
+
// enable_external_access=false. Apply the disable at open time unless a
|
|
156
|
+
// post-connect allowlist SET is required.
|
|
157
|
+
return (this.normalized.enableExternalAccess !== undefined &&
|
|
158
|
+
this.normalized.allowedDirectories === undefined);
|
|
159
|
+
}
|
|
160
|
+
async applyFinalBaseline() {
|
|
161
|
+
if (this.normalized.allowedDirectories !== undefined) {
|
|
162
|
+
await this.runDuckDBQuery(`SET allowed_directories=${(0, duckdb_config_1.sqlStringListLiteral)(this.normalized.allowedDirectories)}`);
|
|
163
|
+
}
|
|
164
|
+
if (this.normalized.enableExternalAccess !== undefined &&
|
|
165
|
+
!this.shouldApplyEnableExternalAccessAtOpenTime()) {
|
|
166
|
+
await this.runDuckDBQuery(`SET enable_external_access=${this.normalized.enableExternalAccess}`);
|
|
167
|
+
}
|
|
168
|
+
if (this.normalized.workingDirectory !== undefined) {
|
|
169
|
+
await this.runDuckDBQuery(`SET FILE_SEARCH_PATH=${(0, duckdb_config_1.sqlStringLiteral)(this.normalized.workingDirectory)}`);
|
|
170
|
+
}
|
|
171
|
+
if (this.normalized.secretDirectory !== undefined) {
|
|
172
|
+
await this.runDuckDBQuery(`SET secret_directory=${(0, duckdb_config_1.sqlStringLiteral)(this.normalized.secretDirectory)}`);
|
|
173
|
+
}
|
|
174
|
+
await this.runDuckDBQuery("SET TimeZone='UTC'");
|
|
175
|
+
await this.loadBaselineExtensions();
|
|
176
|
+
}
|
|
177
|
+
async loadBaselineExtensions() {
|
|
178
|
+
if (this.normalized.networkPolicy === 'closed') {
|
|
179
|
+
await this.loadExtension('json', { allowInstall: false, required: true });
|
|
180
|
+
await this.loadExtension('icu', { allowInstall: false, required: true });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const allowInstall = this.normalized.enableExternalAccess !== false;
|
|
184
|
+
await this.loadExtension('json', { allowInstall, required: false });
|
|
185
|
+
await this.loadExtension('icu', { allowInstall, required: false });
|
|
186
|
+
if (this.shouldLoadHttpfs()) {
|
|
187
|
+
await this.loadExtension('httpfs', { allowInstall, required: false });
|
|
188
|
+
}
|
|
189
|
+
for (const extension of this.normalized.additionalExtensions) {
|
|
190
|
+
await this.loadExtension(extension, { allowInstall, required: false });
|
|
191
|
+
}
|
|
192
|
+
if (this.isMotherDuck) {
|
|
193
|
+
await this.loadExtension('motherduck', { allowInstall, required: false });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
shouldLoadHttpfs() {
|
|
197
|
+
if (this.normalized.networkPolicy === 'closed') {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return this.normalized.enableExternalAccess !== false;
|
|
201
|
+
}
|
|
202
|
+
async loadExtension(extension, options) {
|
|
203
|
+
try {
|
|
204
|
+
await this.runDuckDBQuery(`LOAD ${(0, duckdb_config_1.sqlStringLiteral)(extension)}`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
catch (loadError) {
|
|
208
|
+
if (!options.allowInstall) {
|
|
209
|
+
if (options.required) {
|
|
210
|
+
throw loadError;
|
|
164
211
|
}
|
|
212
|
+
// eslint-disable-next-line no-console
|
|
213
|
+
console.error(`Unable to load DuckDB extension "${extension}"`, loadError);
|
|
214
|
+
return;
|
|
165
215
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
await this.runDuckDBQuery(`INSTALL ${(0, duckdb_config_1.sqlStringLiteral)(extension)}`);
|
|
219
|
+
await this.runDuckDBQuery(`LOAD ${(0, duckdb_config_1.sqlStringLiteral)(extension)}`);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (options.required) {
|
|
223
|
+
throw error;
|
|
173
224
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (!this.isSetup) {
|
|
177
|
-
this.isSetup = doSetup();
|
|
225
|
+
// eslint-disable-next-line no-console
|
|
226
|
+
console.error(`Unable to load DuckDB extension "${extension}"`, error);
|
|
178
227
|
}
|
|
179
|
-
await this.isSetup;
|
|
180
228
|
}
|
|
181
229
|
async runDuckDBQuery(sql) {
|
|
182
230
|
if (!this.connection) {
|
|
183
231
|
throw new Error('Connection not open');
|
|
184
232
|
}
|
|
185
233
|
const result = await this.connection.run(sql);
|
|
186
|
-
// getRowObjectsJson() converts nested types (LIST, STRUCT) to JS arrays/objects
|
|
187
234
|
const rows = (await result.getRowObjectsJson());
|
|
188
235
|
return {
|
|
189
236
|
rows,
|
|
@@ -216,12 +263,12 @@ class DuckDBConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
216
263
|
}
|
|
217
264
|
}
|
|
218
265
|
async close() {
|
|
219
|
-
const activeDB = DuckDBConnection.activeDBs[this.
|
|
266
|
+
const activeDB = DuckDBConnection.activeDBs[this.shareKey];
|
|
220
267
|
if (activeDB) {
|
|
221
268
|
activeDB.connections = activeDB.connections.filter(connection => connection !== this.connection);
|
|
222
269
|
if (activeDB.connections.length === 0) {
|
|
223
270
|
activeDB.instance.closeSync();
|
|
224
|
-
delete DuckDBConnection.activeDBs[this.
|
|
271
|
+
delete DuckDBConnection.activeDBs[this.shareKey];
|
|
225
272
|
}
|
|
226
273
|
}
|
|
227
274
|
}
|
|
@@ -230,9 +277,9 @@ class DuckDBConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
230
277
|
* to release file locks between test runs.
|
|
231
278
|
*/
|
|
232
279
|
static closeAllInstances() {
|
|
233
|
-
for (const
|
|
280
|
+
for (const key of Object.keys(DuckDBConnection.activeDBs)) {
|
|
234
281
|
try {
|
|
235
|
-
DuckDBConnection.activeDBs[
|
|
282
|
+
DuckDBConnection.activeDBs[key].instance.closeSync();
|
|
236
283
|
}
|
|
237
284
|
catch {
|
|
238
285
|
// Ignore errors during cleanup
|
|
@@ -243,4 +290,18 @@ class DuckDBConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
243
290
|
}
|
|
244
291
|
exports.DuckDBConnection = DuckDBConnection;
|
|
245
292
|
DuckDBConnection.activeDBs = {};
|
|
293
|
+
function buildLegacyOptions(name, databasePathOrQueryOptions, workingDirectory) {
|
|
294
|
+
const options = { name };
|
|
295
|
+
if (typeof databasePathOrQueryOptions === 'string') {
|
|
296
|
+
options.databasePath = databasePathOrQueryOptions;
|
|
297
|
+
}
|
|
298
|
+
options.workingDirectory = workingDirectory !== null && workingDirectory !== void 0 ? workingDirectory : '.';
|
|
299
|
+
return options;
|
|
300
|
+
}
|
|
301
|
+
function splitSetupSQL(setupSQL) {
|
|
302
|
+
return setupSQL
|
|
303
|
+
.split(';\n')
|
|
304
|
+
.map(statement => statement.trim())
|
|
305
|
+
.filter(statement => statement !== '');
|
|
306
|
+
}
|
|
246
307
|
//# sourceMappingURL=duckdb_connection.js.map
|
package/dist/native.js
CHANGED
|
@@ -15,13 +15,6 @@ const duckdb_connection_1 = require("./duckdb_connection");
|
|
|
15
15
|
options['databasePath'] = options['path'];
|
|
16
16
|
delete options['path'];
|
|
17
17
|
}
|
|
18
|
-
// Parse comma-separated extensions string into array
|
|
19
|
-
if (typeof options['additionalExtensions'] === 'string') {
|
|
20
|
-
options['additionalExtensions'] = options['additionalExtensions']
|
|
21
|
-
.split(',')
|
|
22
|
-
.map(s => s.trim())
|
|
23
|
-
.filter(s => s.length > 0);
|
|
24
|
-
}
|
|
25
18
|
return new duckdb_connection_1.DuckDBConnection(options);
|
|
26
19
|
},
|
|
27
20
|
properties: [
|
|
@@ -44,6 +37,92 @@ const duckdb_connection_1 = require("./duckdb_connection");
|
|
|
44
37
|
// happens to live inside the project.
|
|
45
38
|
default: { config: 'rootDirectory' },
|
|
46
39
|
},
|
|
40
|
+
{
|
|
41
|
+
name: 'filesystemPolicy',
|
|
42
|
+
displayName: 'Filesystem Policy',
|
|
43
|
+
type: 'string',
|
|
44
|
+
optional: true,
|
|
45
|
+
requireLiteralString: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'networkPolicy',
|
|
49
|
+
displayName: 'Network Policy',
|
|
50
|
+
type: 'string',
|
|
51
|
+
optional: true,
|
|
52
|
+
requireLiteralString: true,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'allowedDirectories',
|
|
56
|
+
displayName: 'Allowed Directories',
|
|
57
|
+
type: 'json',
|
|
58
|
+
optional: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'enableExternalAccess',
|
|
62
|
+
displayName: 'Enable External Access',
|
|
63
|
+
type: 'boolean',
|
|
64
|
+
optional: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'lockConfiguration',
|
|
68
|
+
displayName: 'Lock Configuration',
|
|
69
|
+
type: 'boolean',
|
|
70
|
+
optional: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'autoloadKnownExtensions',
|
|
74
|
+
displayName: 'Autoload Known Extensions',
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
optional: true,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'autoinstallKnownExtensions',
|
|
80
|
+
displayName: 'Autoinstall Known Extensions',
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
optional: true,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'allowCommunityExtensions',
|
|
86
|
+
displayName: 'Allow Community Extensions',
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
optional: true,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'allowUnsignedExtensions',
|
|
92
|
+
displayName: 'Allow Unsigned Extensions',
|
|
93
|
+
type: 'boolean',
|
|
94
|
+
optional: true,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'tempFileEncryption',
|
|
98
|
+
displayName: 'Temp File Encryption',
|
|
99
|
+
type: 'boolean',
|
|
100
|
+
optional: true,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'threads',
|
|
104
|
+
displayName: 'Threads',
|
|
105
|
+
type: 'number',
|
|
106
|
+
optional: true,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'memoryLimit',
|
|
110
|
+
displayName: 'Memory Limit',
|
|
111
|
+
type: 'string',
|
|
112
|
+
optional: true,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'tempDirectory',
|
|
116
|
+
displayName: 'Temp Directory',
|
|
117
|
+
type: 'file',
|
|
118
|
+
optional: true,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'extensionDirectory',
|
|
122
|
+
displayName: 'Extension Directory',
|
|
123
|
+
type: 'file',
|
|
124
|
+
optional: true,
|
|
125
|
+
},
|
|
47
126
|
{
|
|
48
127
|
name: 'motherDuckToken',
|
|
49
128
|
displayName: 'MotherDuck Token',
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface CanonicalPathOptions {
|
|
2
|
+
mustExist?: boolean;
|
|
3
|
+
}
|
|
4
|
+
export declare function isPosixHost(): boolean;
|
|
5
|
+
export declare function canonicalizePath(input: string, { mustExist }?: CanonicalPathOptions): string;
|
|
6
|
+
export declare function canonicalizePathList(paths: string[]): string[];
|
|
7
|
+
export declare function isContainedPath(parent: string, child: string): boolean;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.isPosixHost = isPosixHost;
|
|
11
|
+
exports.canonicalizePath = canonicalizePath;
|
|
12
|
+
exports.canonicalizePathList = canonicalizePathList;
|
|
13
|
+
exports.isContainedPath = isContainedPath;
|
|
14
|
+
const fs_1 = __importDefault(require("fs"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
function isPosixHost() {
|
|
17
|
+
return path_1.default.sep === '/';
|
|
18
|
+
}
|
|
19
|
+
function canonicalizePath(input, { mustExist = false } = {}) {
|
|
20
|
+
if (input.trim() === '') {
|
|
21
|
+
throw new Error('path must not be empty');
|
|
22
|
+
}
|
|
23
|
+
const resolved = path_1.default.resolve(input);
|
|
24
|
+
const canonical = (() => {
|
|
25
|
+
try {
|
|
26
|
+
return fs_1.default.realpathSync.native(resolved);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (mustExist || !isENOENT(error)) {
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
return canonicalizeMissingPath(resolved);
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
return stripTrailingSeparator(path_1.default.normalize(canonical));
|
|
36
|
+
}
|
|
37
|
+
function canonicalizePathList(paths) {
|
|
38
|
+
return Array.from(new Set(paths.map(p => canonicalizePath(p)).sort()));
|
|
39
|
+
}
|
|
40
|
+
function isContainedPath(parent, child) {
|
|
41
|
+
const relative = path_1.default.relative(parent, child);
|
|
42
|
+
return (relative === '' ||
|
|
43
|
+
(!relative.startsWith('..') && !path_1.default.isAbsolute(relative)));
|
|
44
|
+
}
|
|
45
|
+
function canonicalizeMissingPath(resolved) {
|
|
46
|
+
const parent = path_1.default.dirname(resolved);
|
|
47
|
+
if (parent === resolved) {
|
|
48
|
+
return resolved;
|
|
49
|
+
}
|
|
50
|
+
const realParent = (() => {
|
|
51
|
+
try {
|
|
52
|
+
return fs_1.default.realpathSync.native(parent);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (!isENOENT(error)) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
return canonicalizeMissingPath(parent);
|
|
59
|
+
}
|
|
60
|
+
})();
|
|
61
|
+
return path_1.default.join(realParent, path_1.default.basename(resolved));
|
|
62
|
+
}
|
|
63
|
+
function isENOENT(error) {
|
|
64
|
+
return (typeof error === 'object' &&
|
|
65
|
+
error !== null &&
|
|
66
|
+
'code' in error &&
|
|
67
|
+
error.code === 'ENOENT');
|
|
68
|
+
}
|
|
69
|
+
function stripTrailingSeparator(value) {
|
|
70
|
+
const root = path_1.default.parse(value).root;
|
|
71
|
+
if (value === root) {
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
let end = value.length;
|
|
75
|
+
while (end > root.length && isPathSeparator(value.charCodeAt(end - 1))) {
|
|
76
|
+
end -= 1;
|
|
77
|
+
}
|
|
78
|
+
return end === value.length ? value : value.slice(0, end);
|
|
79
|
+
}
|
|
80
|
+
function isPathSeparator(charCode) {
|
|
81
|
+
return charCode === 47 || charCode === 92;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=path_security.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/db-duckdb",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.379",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@duckdb/duckdb-wasm": "1.33.1-dev13.0",
|
|
62
62
|
"@duckdb/node-api": "1.4.4-r.1",
|
|
63
|
-
"@malloydata/malloy": "0.0.
|
|
63
|
+
"@malloydata/malloy": "0.0.379",
|
|
64
64
|
"@motherduck/wasm-client": "^0.6.6",
|
|
65
65
|
"apache-arrow": "^17.0.0",
|
|
66
66
|
"web-worker": "^1.3.0"
|