@flowblade/sqlduck 0.14.0 → 0.15.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/dist/{duck-reserved-keywords-CUmaTbrD.mjs → duck-reserved-keywords-B8XUjnaY.mjs} +14 -6
- package/dist/index.d.mts +9 -0
- package/dist/index.mjs +117 -4
- package/dist/validation/valibot/index.d.mts +10 -10
- package/dist/validation/valibot/index.mjs +3 -7
- package/dist/validation/zod/index.mjs +1 -1
- package/dist/{zod-BRUUIdGW.mjs → zod-CuPjTLv8.mjs} +4 -8
- package/package.json +7 -7
|
@@ -3,12 +3,20 @@ const duckIdentifierNameRegex = /^[a-z_]\w*$/i;
|
|
|
3
3
|
const duckStorageVersionRegexp = /^v?\d{1,4}\.\d{1,4}\.\d{1,4}$/;
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region src/validation/core/duck-connections-options.ts
|
|
6
|
-
const duckConnectionsOptions = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const duckConnectionsOptions = {
|
|
7
|
+
types: [
|
|
8
|
+
"DUCKDB",
|
|
9
|
+
"SQLITE",
|
|
10
|
+
"MYSQL",
|
|
11
|
+
"PostgreSQL"
|
|
12
|
+
],
|
|
13
|
+
accessModes: ["READ_ONLY", "READ_WRITE"],
|
|
14
|
+
encryptionCiphers: [
|
|
15
|
+
"CBC",
|
|
16
|
+
"CTR",
|
|
17
|
+
"GCM"
|
|
18
|
+
]
|
|
19
|
+
};
|
|
12
20
|
//#endregion
|
|
13
21
|
//#region src/validation/core/duck-reserved-keywords.ts
|
|
14
22
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -283,6 +283,15 @@ declare class DuckDatabaseManager {
|
|
|
283
283
|
analyze: () => Promise<boolean>;
|
|
284
284
|
checkpoint: (dbAlias: string) => Promise<boolean>;
|
|
285
285
|
vacuum: () => Promise<boolean>;
|
|
286
|
+
/**
|
|
287
|
+
* Helper to create an initial database file.
|
|
288
|
+
*/
|
|
289
|
+
createDatabaseFile: (params: {
|
|
290
|
+
path: string;
|
|
291
|
+
createDirectory?: boolean;
|
|
292
|
+
}) => Promise<{
|
|
293
|
+
status: "exists" | "created";
|
|
294
|
+
}>;
|
|
286
295
|
}
|
|
287
296
|
//#endregion
|
|
288
297
|
//#region src/config/flowblade-logtape-sqlduck.config.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { t as duckReservedKeywords } from "./duck-reserved-keywords-
|
|
2
|
-
import { n as assertValidAliasName, o as duckConnectionParamsZodSchema, s as duckValidatorsZod } from "./zod-
|
|
3
|
-
import { BIGINT, BOOLEAN, DOUBLE, DuckDBDataChunk, DuckDBTimestampValue, FLOAT, HUGEINT, INTEGER, SMALLINT, TIMESTAMP, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR } from "@duckdb/node-api";
|
|
1
|
+
import { t as duckReservedKeywords } from "./duck-reserved-keywords-B8XUjnaY.mjs";
|
|
2
|
+
import { n as assertValidAliasName, o as duckConnectionParamsZodSchema, s as duckValidatorsZod } from "./zod-CuPjTLv8.mjs";
|
|
3
|
+
import { BIGINT, BOOLEAN, DOUBLE, DuckDBDataChunk, DuckDBInstanceCache, DuckDBTimestampValue, FLOAT, HUGEINT, INTEGER, SMALLINT, TIMESTAMP, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR } from "@duckdb/node-api";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import { basename, dirname } from "node:path";
|
|
5
7
|
import * as z from "zod";
|
|
6
8
|
import { assertNever } from "@httpx/assert";
|
|
7
9
|
import { isPlainObject } from "@httpx/plain-object";
|
|
@@ -128,6 +130,81 @@ const flowbladeLogtapeSqlduckConfig = { categories: ["flowblade", "sqlduck"] };
|
|
|
128
130
|
//#region src/logger/sqlduck-default-logtape-logger.ts
|
|
129
131
|
const sqlduckDefaultLogtapeLogger = getLogger(flowbladeLogtapeSqlduckConfig.categories);
|
|
130
132
|
//#endregion
|
|
133
|
+
//#region src/filesystem/file-system-utils.ts
|
|
134
|
+
var FileSystemUtils = class {
|
|
135
|
+
#logger;
|
|
136
|
+
constructor(params) {
|
|
137
|
+
this.#logger = params?.logger ?? sqlduckDefaultLogtapeLogger.with({ source: "FileSystemUtils" });
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Create a directory recursively if it doesn't exist
|
|
141
|
+
*
|
|
142
|
+
* @throws Error if it can't be created
|
|
143
|
+
*/
|
|
144
|
+
createDirectory = (path) => {
|
|
145
|
+
try {
|
|
146
|
+
fs.mkdirSync(path, { recursive: true });
|
|
147
|
+
} catch (err) {
|
|
148
|
+
if (err.code !== "EEXIST") throw err;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Create a directory recursively if it doesn't exist and ensure it's writable
|
|
153
|
+
*/
|
|
154
|
+
createAndEnsureWritableDirectory = (label, path) => {
|
|
155
|
+
if (path === void 0) return;
|
|
156
|
+
if (!fs.existsSync(path)) try {
|
|
157
|
+
this.createDirectory(path);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
throw new Error(`Failed to create ${label} '${path}' - ${e?.message ?? ""}`);
|
|
160
|
+
}
|
|
161
|
+
if (!fs.statSync(path).isDirectory()) throw new Error(`${label} '${path}' must be a directory`);
|
|
162
|
+
try {
|
|
163
|
+
fs.accessSync(path, fs.constants.W_OK);
|
|
164
|
+
} catch {
|
|
165
|
+
throw new Error(`${label} '${path}' must be writable`);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Check if a path is a regular file and exists
|
|
170
|
+
*/
|
|
171
|
+
isFile = (path) => {
|
|
172
|
+
try {
|
|
173
|
+
return fs.statSync(path).isFile();
|
|
174
|
+
} catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Check if a path is a directory and exists
|
|
180
|
+
*/
|
|
181
|
+
isDirectory = (path) => {
|
|
182
|
+
try {
|
|
183
|
+
return fs.statSync(path).isDirectory();
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
parsePath = (path) => {
|
|
189
|
+
const dir = dirname(path);
|
|
190
|
+
if (dir.trim() === "") throw new Error(`Invalid path, missing directory '${path}'`);
|
|
191
|
+
const base = basename(path);
|
|
192
|
+
if (base.trim() === "") throw new Error(`Invalid path, missing filename '${path}'`);
|
|
193
|
+
return {
|
|
194
|
+
directory: dir,
|
|
195
|
+
filename: base
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
removeFileIfExists = (path) => {
|
|
199
|
+
try {
|
|
200
|
+
fs.rmSync(path);
|
|
201
|
+
return true;
|
|
202
|
+
} catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
//#endregion
|
|
131
208
|
//#region src/objects/database.ts
|
|
132
209
|
var Database = class {
|
|
133
210
|
#params;
|
|
@@ -207,6 +284,7 @@ var DuckDatabaseAttachCommand = class {
|
|
|
207
284
|
var DuckDatabaseManager = class {
|
|
208
285
|
#conn;
|
|
209
286
|
#logger;
|
|
287
|
+
#fs;
|
|
210
288
|
constructor(conn, params) {
|
|
211
289
|
this.#conn = conn;
|
|
212
290
|
this.#logger = params?.logger ?? sqlduckDefaultLogtapeLogger.with({ source: "DuckDatabaseManager" });
|
|
@@ -229,7 +307,12 @@ var DuckDatabaseManager = class {
|
|
|
229
307
|
attach = async (dbParams, options) => {
|
|
230
308
|
const params = duckConnectionParamsZodSchema.parse(dbParams);
|
|
231
309
|
const rawSql = new DuckDatabaseAttachCommand(params, options).getRawSql();
|
|
232
|
-
|
|
310
|
+
const { behaviour } = options ?? {};
|
|
311
|
+
await this.#executeRawSqlCommand([
|
|
312
|
+
`attach(${params.alias}`,
|
|
313
|
+
behaviour ?? null,
|
|
314
|
+
")"
|
|
315
|
+
].filter(Boolean).join(","), rawSql);
|
|
233
316
|
return new Database({ alias: params.alias });
|
|
234
317
|
};
|
|
235
318
|
attachOrReplace = async (dbParams) => {
|
|
@@ -272,6 +355,32 @@ var DuckDatabaseManager = class {
|
|
|
272
355
|
await this.#executeRawSqlCommand("vacuum()", "VACUUM");
|
|
273
356
|
return true;
|
|
274
357
|
};
|
|
358
|
+
/**
|
|
359
|
+
* Helper to create an initial database file.
|
|
360
|
+
*/
|
|
361
|
+
createDatabaseFile = async (params) => {
|
|
362
|
+
const startTime = Date.now();
|
|
363
|
+
const { path, createDirectory = true } = params;
|
|
364
|
+
const fs = this.#getFs();
|
|
365
|
+
if (fs.isFile(path)) return { status: "exists" };
|
|
366
|
+
if (createDirectory) {
|
|
367
|
+
const { directory } = fs.parsePath(path);
|
|
368
|
+
fs.createAndEnsureWritableDirectory("database file directory", directory);
|
|
369
|
+
}
|
|
370
|
+
const instanceCache = new DuckDBInstanceCache();
|
|
371
|
+
try {
|
|
372
|
+
(await (await instanceCache.getOrCreateInstance(path)).connect()).closeSync();
|
|
373
|
+
const timeMs = Math.round(Date.now() - startTime);
|
|
374
|
+
this.#logger.info(`DuckDatabaseManager.createDatabaseFile('${path}') in ${timeMs}ms`, {
|
|
375
|
+
timeMs,
|
|
376
|
+
path
|
|
377
|
+
});
|
|
378
|
+
} catch (e) {
|
|
379
|
+
this.#logger.error(`DuckDatabaseManager.createDatabaseFile('${path}') failed - ${e?.message ?? ""}`, { path });
|
|
380
|
+
throw e;
|
|
381
|
+
}
|
|
382
|
+
return { status: "created" };
|
|
383
|
+
};
|
|
275
384
|
#executeRawSqlCommand = async (name, rawSql) => {
|
|
276
385
|
const startTime = Date.now();
|
|
277
386
|
try {
|
|
@@ -291,6 +400,10 @@ var DuckDatabaseManager = class {
|
|
|
291
400
|
throw new Error(msg, { cause: e });
|
|
292
401
|
}
|
|
293
402
|
};
|
|
403
|
+
#getFs = () => {
|
|
404
|
+
if (this.#fs === void 0) this.#fs = new FileSystemUtils({ logger: this.#logger });
|
|
405
|
+
return this.#fs;
|
|
406
|
+
};
|
|
294
407
|
};
|
|
295
408
|
//#endregion
|
|
296
409
|
//#region src/table/get-duckdb-number-column-type.ts
|
|
@@ -2,42 +2,42 @@ import * as v from "valibot";
|
|
|
2
2
|
|
|
3
3
|
//#region src/validation/valibot/duck-connection-params-valibot-schema.d.ts
|
|
4
4
|
declare const duckAllConnectionOptionsValibotSchema: v.ObjectSchema<{
|
|
5
|
-
readonly accessMode: v.OptionalSchema<v.PicklistSchema<["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
5
|
+
readonly accessMode: v.OptionalSchema<v.PicklistSchema<readonly ["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
6
6
|
readonly compress: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
7
7
|
readonly type: v.OptionalSchema<v.PicklistSchema<readonly ["DUCKDB", "SQLITE", "MYSQL", "PostgreSQL"], undefined>, undefined>;
|
|
8
8
|
readonly blockSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 16384, undefined>, v.MaxValueAction<number, 262144, undefined>]>, undefined>;
|
|
9
9
|
readonly rowGroupSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
10
10
|
readonly storageVersion: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.StartsWithAction<string, "v", undefined>, v.RegexAction<string, undefined>]>, undefined>;
|
|
11
11
|
readonly encryptionKey: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 8, undefined>]>, undefined>;
|
|
12
|
-
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
12
|
+
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<readonly ["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
13
13
|
}, undefined>;
|
|
14
14
|
type DuckAllConnectionOptionsValibotSchema = v.InferOutput<typeof duckAllConnectionOptionsValibotSchema>;
|
|
15
15
|
declare const duckConnectionParamsValibotSchema: v.VariantSchema<"type", [v.ObjectSchema<{
|
|
16
16
|
readonly type: v.LiteralSchema<"memory", undefined>;
|
|
17
17
|
readonly alias: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 120, undefined>, v.RegexAction<string, "Identifier must start with a letter or underscore, and contain only letters, numbers and underscores">, v.CheckAction<string, "Identifier value is a DuckDB reserved keyword and cannot be used as an identifier">]>;
|
|
18
18
|
readonly options: v.OptionalSchema<v.ObjectSchema<{
|
|
19
|
-
readonly accessMode: v.OptionalSchema<v.PicklistSchema<["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
19
|
+
readonly accessMode: v.OptionalSchema<v.PicklistSchema<readonly ["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
20
20
|
readonly compress: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
21
21
|
readonly type: v.OptionalSchema<v.PicklistSchema<readonly ["DUCKDB", "SQLITE", "MYSQL", "PostgreSQL"], undefined>, undefined>;
|
|
22
22
|
readonly blockSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 16384, undefined>, v.MaxValueAction<number, 262144, undefined>]>, undefined>;
|
|
23
23
|
readonly rowGroupSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
24
24
|
readonly storageVersion: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.StartsWithAction<string, "v", undefined>, v.RegexAction<string, undefined>]>, undefined>;
|
|
25
25
|
readonly encryptionKey: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 8, undefined>]>, undefined>;
|
|
26
|
-
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
26
|
+
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<readonly ["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
27
27
|
}, undefined>, undefined>;
|
|
28
28
|
}, undefined>, v.ObjectSchema<{
|
|
29
29
|
readonly type: v.LiteralSchema<"filesystem", undefined>;
|
|
30
30
|
readonly path: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid database filename - it must be a safe filename (no path traversal, no absolute paths, no reserved names, etc.)">, v.CheckAction<string, "Invalid database pathname - it must be an absolute path with no traversal">]>;
|
|
31
31
|
readonly alias: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 120, undefined>, v.RegexAction<string, "Identifier must start with a letter or underscore, and contain only letters, numbers and underscores">, v.CheckAction<string, "Identifier value is a DuckDB reserved keyword and cannot be used as an identifier">]>;
|
|
32
32
|
readonly options: v.OptionalSchema<v.ObjectSchema<{
|
|
33
|
-
readonly accessMode: v.OptionalSchema<v.PicklistSchema<["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
33
|
+
readonly accessMode: v.OptionalSchema<v.PicklistSchema<readonly ["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
34
34
|
readonly compress: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
35
35
|
readonly type: v.OptionalSchema<v.PicklistSchema<readonly ["DUCKDB", "SQLITE", "MYSQL", "PostgreSQL"], undefined>, undefined>;
|
|
36
36
|
readonly blockSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 16384, undefined>, v.MaxValueAction<number, 262144, undefined>]>, undefined>;
|
|
37
37
|
readonly rowGroupSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
38
38
|
readonly storageVersion: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.StartsWithAction<string, "v", undefined>, v.RegexAction<string, undefined>]>, undefined>;
|
|
39
39
|
readonly encryptionKey: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 8, undefined>]>, undefined>;
|
|
40
|
-
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
40
|
+
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<readonly ["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
41
41
|
}, undefined>, undefined>;
|
|
42
42
|
}, undefined>], undefined>;
|
|
43
43
|
//#endregion
|
|
@@ -73,28 +73,28 @@ declare const duckDsnValibotSchema: v.SchemaWithPipe<readonly [v.StringSchema<un
|
|
|
73
73
|
readonly type: v.LiteralSchema<"memory", undefined>;
|
|
74
74
|
readonly alias: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 120, undefined>, v.RegexAction<string, "Identifier must start with a letter or underscore, and contain only letters, numbers and underscores">, v.CheckAction<string, "Identifier value is a DuckDB reserved keyword and cannot be used as an identifier">]>;
|
|
75
75
|
readonly options: v.OptionalSchema<v.ObjectSchema<{
|
|
76
|
-
readonly accessMode: v.OptionalSchema<v.PicklistSchema<["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
76
|
+
readonly accessMode: v.OptionalSchema<v.PicklistSchema<readonly ["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
77
77
|
readonly compress: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
78
78
|
readonly type: v.OptionalSchema<v.PicklistSchema<readonly ["DUCKDB", "SQLITE", "MYSQL", "PostgreSQL"], undefined>, undefined>;
|
|
79
79
|
readonly blockSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 16384, undefined>, v.MaxValueAction<number, 262144, undefined>]>, undefined>;
|
|
80
80
|
readonly rowGroupSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
81
81
|
readonly storageVersion: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.StartsWithAction<string, "v", undefined>, v.RegexAction<string, undefined>]>, undefined>;
|
|
82
82
|
readonly encryptionKey: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 8, undefined>]>, undefined>;
|
|
83
|
-
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
83
|
+
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<readonly ["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
84
84
|
}, undefined>, undefined>;
|
|
85
85
|
}, undefined>, v.ObjectSchema<{
|
|
86
86
|
readonly type: v.LiteralSchema<"filesystem", undefined>;
|
|
87
87
|
readonly path: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.CheckAction<string, "Invalid database filename - it must be a safe filename (no path traversal, no absolute paths, no reserved names, etc.)">, v.CheckAction<string, "Invalid database pathname - it must be an absolute path with no traversal">]>;
|
|
88
88
|
readonly alias: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 120, undefined>, v.RegexAction<string, "Identifier must start with a letter or underscore, and contain only letters, numbers and underscores">, v.CheckAction<string, "Identifier value is a DuckDB reserved keyword and cannot be used as an identifier">]>;
|
|
89
89
|
readonly options: v.OptionalSchema<v.ObjectSchema<{
|
|
90
|
-
readonly accessMode: v.OptionalSchema<v.PicklistSchema<["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
90
|
+
readonly accessMode: v.OptionalSchema<v.PicklistSchema<readonly ["READ_ONLY", "READ_WRITE"], undefined>, undefined>;
|
|
91
91
|
readonly compress: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
92
92
|
readonly type: v.OptionalSchema<v.PicklistSchema<readonly ["DUCKDB", "SQLITE", "MYSQL", "PostgreSQL"], undefined>, undefined>;
|
|
93
93
|
readonly blockSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 16384, undefined>, v.MaxValueAction<number, 262144, undefined>]>, undefined>;
|
|
94
94
|
readonly rowGroupSize: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
95
95
|
readonly storageVersion: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.StartsWithAction<string, "v", undefined>, v.RegexAction<string, undefined>]>, undefined>;
|
|
96
96
|
readonly encryptionKey: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 8, undefined>]>, undefined>;
|
|
97
|
-
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
97
|
+
readonly encryptionCipher: v.OptionalSchema<v.PicklistSchema<readonly ["CBC", "CTR", "GCM"], undefined>, undefined>;
|
|
98
98
|
}, undefined>, undefined>;
|
|
99
99
|
}, undefined>], undefined>]>;
|
|
100
100
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as duckStorageVersionRegexp, i as duckIdentifierNameRegex, n as duckdbReservedKeywordsSet, r as duckConnectionsOptions } from "../../duck-reserved-keywords-
|
|
1
|
+
import { a as duckStorageVersionRegexp, i as duckIdentifierNameRegex, n as duckdbReservedKeywordsSet, r as duckConnectionsOptions } from "../../duck-reserved-keywords-B8XUjnaY.mjs";
|
|
2
2
|
import isSafeFilename from "is-safe-filename";
|
|
3
3
|
import { parseDsn, parseDsnOrThrow } from "@httpx/dsn-parser";
|
|
4
4
|
import * as v from "valibot";
|
|
@@ -26,18 +26,14 @@ const duckValidatorsValibot = {
|
|
|
26
26
|
//#endregion
|
|
27
27
|
//#region src/validation/valibot/duck-connection-params-valibot-schema.ts
|
|
28
28
|
const duckAllConnectionOptionsValibotSchema = v.object({
|
|
29
|
-
accessMode: v.optional(v.picklist(
|
|
29
|
+
accessMode: v.optional(v.picklist(duckConnectionsOptions.accessModes)),
|
|
30
30
|
compress: v.optional(v.boolean()),
|
|
31
31
|
type: v.optional(v.picklist(duckConnectionsOptions.types)),
|
|
32
32
|
blockSize: v.optional(v.pipe(v.number(), v.integer(), v.minValue(16384), v.maxValue(262144))),
|
|
33
33
|
rowGroupSize: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1))),
|
|
34
34
|
storageVersion: v.optional(v.pipe(v.string(), v.startsWith("v"), v.regex(duckStorageVersionRegexp))),
|
|
35
35
|
encryptionKey: v.optional(v.pipe(v.string(), v.minLength(8))),
|
|
36
|
-
encryptionCipher: v.optional(v.picklist(
|
|
37
|
-
"CBC",
|
|
38
|
-
"CTR",
|
|
39
|
-
"GCM"
|
|
40
|
-
]))
|
|
36
|
+
encryptionCipher: v.optional(v.picklist(duckConnectionsOptions.encryptionCiphers))
|
|
41
37
|
});
|
|
42
38
|
const duckConnectionParamsValibotSchema = v.variant("type", [v.object({
|
|
43
39
|
type: v.literal("memory"),
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as duckAllConnectionOptionsZodSchema, i as assertValidTableName, n as assertValidAliasName, o as duckConnectionParamsZodSchema, r as assertValidSchemaName, s as duckValidatorsZod, t as duckDsnZodSchema } from "../../zod-
|
|
1
|
+
import { a as duckAllConnectionOptionsZodSchema, i as assertValidTableName, n as assertValidAliasName, o as duckConnectionParamsZodSchema, r as assertValidSchemaName, s as duckValidatorsZod, t as duckDsnZodSchema } from "../../zod-CuPjTLv8.mjs";
|
|
2
2
|
export { assertValidAliasName, assertValidSchemaName, assertValidTableName, duckAllConnectionOptionsZodSchema, duckConnectionParamsZodSchema, duckDsnZodSchema, duckValidatorsZod };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as duckStorageVersionRegexp, i as duckIdentifierNameRegex, n as duckdbReservedKeywordsSet, r as duckConnectionsOptions } from "./duck-reserved-keywords-
|
|
1
|
+
import { a as duckStorageVersionRegexp, i as duckIdentifierNameRegex, n as duckdbReservedKeywordsSet, r as duckConnectionsOptions } from "./duck-reserved-keywords-B8XUjnaY.mjs";
|
|
2
2
|
import isSafeFilename from "is-safe-filename";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import { parseDsn } from "@httpx/dsn-parser";
|
|
@@ -28,18 +28,14 @@ const duckValidatorsZod = {
|
|
|
28
28
|
//#endregion
|
|
29
29
|
//#region src/validation/zod/duck-connection-params-zod-schema.ts
|
|
30
30
|
const duckAllConnectionOptionsZodSchema = z.strictObject({
|
|
31
|
-
accessMode: z.optional(z.enum(
|
|
31
|
+
accessMode: z.optional(z.enum(duckConnectionsOptions.accessModes)),
|
|
32
32
|
compress: z.optional(z.boolean()),
|
|
33
33
|
type: z.optional(z.enum(duckConnectionsOptions.types)),
|
|
34
34
|
blockSize: z.optional(z.int32().min(16384).max(262144)),
|
|
35
35
|
rowGroupSize: z.optional(z.int32().positive()),
|
|
36
36
|
storageVersion: z.optional(z.string().startsWith("v").regex(duckStorageVersionRegexp)),
|
|
37
37
|
encryptionKey: z.optional(z.string().min(8)),
|
|
38
|
-
encryptionCipher: z.optional(z.enum(
|
|
39
|
-
"CBC",
|
|
40
|
-
"CTR",
|
|
41
|
-
"GCM"
|
|
42
|
-
]))
|
|
38
|
+
encryptionCipher: z.optional(z.enum(duckConnectionsOptions.encryptionCiphers))
|
|
43
39
|
});
|
|
44
40
|
const duckConnectionParamsZodSchema = z.discriminatedUnion("type", [z.strictObject({
|
|
45
41
|
type: z.literal("memory"),
|
|
@@ -52,7 +48,7 @@ const duckConnectionParamsZodSchema = z.discriminatedUnion("type", [z.strictObje
|
|
|
52
48
|
return typeof filename === "string" && isSafeFilename(filename);
|
|
53
49
|
}, { message: "Invalid database filename - it must be a safe filename (no path traversal, no absolute paths, no reserved names, etc.)" }).refine((path) => {
|
|
54
50
|
const pathname = "/" + path.replace("\\", "/").split("/").slice(0, -1).filter(Boolean).join("/");
|
|
55
|
-
return pathname.length > 0 && pathname.startsWith("/") && pathname.includes("..")
|
|
51
|
+
return pathname.length > 0 && pathname.startsWith("/") && !pathname.includes("..");
|
|
56
52
|
}, { message: "Invalid database pathname - it must be an absolute path with no traversal" }),
|
|
57
53
|
alias: duckValidatorsZod.aliasName,
|
|
58
54
|
options: z.optional(duckAllConnectionOptionsZodSchema)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowblade/sqlduck",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"fix-staged": "lint-staged --allow-empty",
|
|
54
54
|
"check-dist": "es-check --config=.escheckrc.json",
|
|
55
55
|
"check-pub": "publint",
|
|
56
|
-
"check-size": "size-limit"
|
|
56
|
+
"check-size-disabled": "size-limit"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@flowblade/core": "^0.2.26",
|
|
@@ -90,11 +90,11 @@
|
|
|
90
90
|
"@testcontainers/mssqlserver": "11.13.0",
|
|
91
91
|
"@total-typescript/ts-reset": "0.6.1",
|
|
92
92
|
"@types/node": "25.5.2",
|
|
93
|
-
"@typescript-eslint/eslint-plugin": "8.58.
|
|
94
|
-
"@typescript-eslint/parser": "8.58.
|
|
93
|
+
"@typescript-eslint/eslint-plugin": "8.58.1",
|
|
94
|
+
"@typescript-eslint/parser": "8.58.1",
|
|
95
95
|
"@typescript/native-preview": "7.0.0-dev.20260406.1",
|
|
96
|
-
"@vitest/coverage-v8": "4.1.
|
|
97
|
-
"@vitest/ui": "4.1.
|
|
96
|
+
"@vitest/coverage-v8": "4.1.3",
|
|
97
|
+
"@vitest/ui": "4.1.3",
|
|
98
98
|
"ansis": "4.2.0",
|
|
99
99
|
"browserslist-to-esbuild": "2.1.1",
|
|
100
100
|
"core-js": "3.49.0",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"typedoc-plugin-markdown": "4.11.0",
|
|
124
124
|
"typescript": "6.0.2",
|
|
125
125
|
"valibot": "1.3.1",
|
|
126
|
-
"vitest": "4.1.
|
|
126
|
+
"vitest": "4.1.3"
|
|
127
127
|
},
|
|
128
128
|
"files": [
|
|
129
129
|
"dist"
|