@flowblade/sqlduck 0.9.0 → 0.10.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/index.cjs CHANGED
@@ -21,9 +21,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
+ let _logtape_logtape = require("@logtape/logtape");
24
25
  let _duckdb_node_api = require("@duckdb/node-api");
25
26
  let zod = require("zod");
26
27
  zod = __toESM(zod);
28
+ //#region src/config/flowblade-logtape-sqlduck.config.ts
29
+ const flowbladeLogtapeSqlduckConfig = { categories: ["flowblade", "sqlduck"] };
30
+ //#endregion
27
31
  //#region src/helpers/duck-exec.ts
28
32
  var DuckExec = class {
29
33
  #conn;
@@ -120,6 +124,9 @@ var DuckMemory = class {
120
124
  };
121
125
  };
122
126
  //#endregion
127
+ //#region src/logger/sqlduck-default-logtape-logger.ts
128
+ const sqlduckDefaultLogtapeLogger = (0, _logtape_logtape.getLogger)(flowbladeLogtapeSqlduckConfig.categories);
129
+ //#endregion
123
130
  //#region src/appender/data-appender-callback.ts
124
131
  const isOnDataAppendedAsyncCb = (v) => {
125
132
  return v.constructor.name === "AsyncFunction";
@@ -174,6 +181,17 @@ const createOptions = {
174
181
  CREATE_OR_REPLACE: "CREATE OR REPLACE TABLE",
175
182
  IF_NOT_EXISTS: "CREATE TABLE IF NOT EXISTS"
176
183
  };
184
+ const duckDbTypes = [
185
+ ["VARCHAR", _duckdb_node_api.VARCHAR],
186
+ ["BIGINT", _duckdb_node_api.BIGINT],
187
+ ["TIMESTAMP", _duckdb_node_api.TIMESTAMP],
188
+ ["UUID", _duckdb_node_api.UUID],
189
+ ["BOOLEAN", _duckdb_node_api.BOOLEAN],
190
+ ["INTEGER", _duckdb_node_api.INTEGER],
191
+ ["DOUBLE", _duckdb_node_api.DOUBLE],
192
+ ["FLOAT", _duckdb_node_api.FLOAT]
193
+ ];
194
+ const duckDbTypesMap = new Map(duckDbTypes);
177
195
  const getTableCreateFromZod = (params) => {
178
196
  const { table, schema, options } = params;
179
197
  const { create = "CREATE" } = options ?? {};
@@ -186,9 +204,10 @@ const getTableCreateFromZod = (params) => {
186
204
  if (json.properties === void 0) throw new TypeError("Schema must have at least one property");
187
205
  const columnTypesMap = /* @__PURE__ */ new Map();
188
206
  for (const [columnName, def] of Object.entries(json.properties)) {
189
- const { type, nullable, format, primaryKey, minimum, maximum } = def;
207
+ const { type, duckdbType, nullable, format, primaryKey, minimum, maximum } = def;
190
208
  const c = { name: columnName };
191
- switch (type) {
209
+ if (duckdbType !== void 0 && duckDbTypesMap.has(duckdbType)) c.duckdbType = duckDbTypesMap.get(duckdbType);
210
+ else switch (type) {
192
211
  case "string":
193
212
  switch (format) {
194
213
  case "date-time":
@@ -244,15 +263,18 @@ const getTableCreateFromZod = (params) => {
244
263
  //#endregion
245
264
  //#region src/table/create-table-from-zod.ts
246
265
  const createTableFromZod = async (params) => {
247
- const { conn, table, schema, options } = params;
266
+ const { conn, table, schema, options, logger = sqlduckDefaultLogtapeLogger } = params;
248
267
  const { ddl, columnTypes } = getTableCreateFromZod({
249
268
  table,
250
269
  schema,
251
270
  options
252
271
  });
272
+ logger.debug(`Generate DDL for table '${table.getFullName()}'`, { ddl });
253
273
  try {
254
274
  await conn.run(ddl);
275
+ logger.info(`Table '${table.getFullName()}' successfully created`, { ddl });
255
276
  } catch (e) {
277
+ logger.error(`Failed to create table '${table.getFullName()}': ${e.message}`, { ddl });
256
278
  throw new Error(`Failed to create table '${table.getFullName()}': ${e.message}`, { cause: e });
257
279
  }
258
280
  return {
@@ -309,7 +331,7 @@ var SqlDuck = class {
309
331
  #logger;
310
332
  constructor(params) {
311
333
  this.#duck = params.conn;
312
- this.#logger = params.logger;
334
+ this.#logger = params.logger ?? sqlduckDefaultLogtapeLogger;
313
335
  }
314
336
  /**
315
337
  * Create a table from a Zod schema and fill it with data from a row stream.
@@ -366,25 +388,38 @@ var SqlDuck = class {
366
388
  rows: rowStream,
367
389
  chunkSize
368
390
  });
369
- for await (const dataChunk of columnStream) {
370
- const chunk = _duckdb_node_api.DuckDBDataChunk.create(chunkTypes);
371
- if (this.#logger) this.#logger(`Inserting chunk of ${dataChunk.length} rows`);
372
- totalRows += dataChunk?.[0]?.length ?? 0;
373
- chunk.setColumns(dataChunk);
374
- appender.appendDataChunk(chunk);
375
- appender.flushSync();
376
- if (onDataAppended !== void 0) {
377
- const payload = dataAppendedCollector(totalRows);
378
- if (isOnDataAppendedAsyncCb(onDataAppended)) await onDataAppended(payload);
379
- else onDataAppended(payload);
391
+ try {
392
+ for await (const dataChunk of columnStream) {
393
+ const chunk = _duckdb_node_api.DuckDBDataChunk.create(chunkTypes);
394
+ this.#logger.debug(`Inserting chunk of ${dataChunk.length} rows`, { table: table.getFullName() });
395
+ totalRows += dataChunk?.[0]?.length ?? 0;
396
+ chunk.setColumns(dataChunk);
397
+ appender.appendDataChunk(chunk);
398
+ appender.flushSync();
399
+ if (onDataAppended !== void 0) {
400
+ const payload = dataAppendedCollector(totalRows);
401
+ if (isOnDataAppendedAsyncCb(onDataAppended)) await onDataAppended(payload);
402
+ else onDataAppended(payload);
403
+ }
380
404
  }
405
+ appender.closeSync();
406
+ const timeMs = Math.round(Date.now() - timeStart);
407
+ this.#logger.info(`Successfully appended ${totalRows} rows into '${table.getFullName()}' in ${timeMs}ms`, {
408
+ table: table.getFullName(),
409
+ timeMs,
410
+ totalRows
411
+ });
412
+ return {
413
+ timeMs,
414
+ totalRows,
415
+ createTableDDL: ddl
416
+ };
417
+ } catch (e) {
418
+ appender.closeSync();
419
+ const msg = `Failed to append data into table '${table.getFullName()}' - ${e?.message ?? ""}`;
420
+ this.#logger.error(msg, { table: table.getFullName() });
421
+ throw new Error(msg, { cause: e });
381
422
  }
382
- appender.closeSync();
383
- return {
384
- timeMs: Math.round(Date.now() - timeStart),
385
- totalRows,
386
- createTableDDL: ddl
387
- };
388
423
  };
389
424
  };
390
425
  //#endregion
@@ -445,5 +480,7 @@ const zodCodecs = {
445
480
  exports.DuckMemory = DuckMemory;
446
481
  exports.SqlDuck = SqlDuck;
447
482
  exports.Table = Table;
483
+ exports.flowbladeLogtapeSqlduckConfig = flowbladeLogtapeSqlduckConfig;
448
484
  exports.getTableCreateFromZod = getTableCreateFromZod;
485
+ exports.sqlduckDefaultLogtapeLogger = sqlduckDefaultLogtapeLogger;
449
486
  exports.zodCodecs = zodCodecs;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { DuckDBConnection, DuckDBType } from "@duckdb/node-api";
2
+ import * as _logtape_logtape0 from "@logtape/logtape";
3
+ import { Logger } from "@logtape/logtape";
2
4
  import * as z from "zod";
3
5
  import { ZodObject } from "zod";
4
6
 
@@ -21,6 +23,11 @@ type OnDataAppendedSyncCb = (stats: OnDataAppendedStats) => void;
21
23
  type OnDataAppendedAsyncCb = (stats: OnDataAppendedStats) => Promise<void>;
22
24
  type OnDataAppendedCb = OnDataAppendedSyncCb | OnDataAppendedAsyncCb;
23
25
  //#endregion
26
+ //#region src/config/flowblade-logtape-sqlduck.config.d.ts
27
+ declare const flowbladeLogtapeSqlduckConfig: {
28
+ categories: string[];
29
+ };
30
+ //#endregion
24
31
  //#region src/helpers/duck-memory.d.ts
25
32
  declare const duckMemoryTags: readonly ["BASE_TABLE", "HASH_TABLE", "PARQUET_READER", "CSV_READER", "ORDER_BY", "ART_INDEX", "COLUMN_DATA", "METADATA", "OVERFLOW_STRINGS", "IN_MEMORY_TABLE", "ALLOCATOR", "EXTENSION", "TRANSACTION", "EXTERNAL_FILE_CACHE", "WINDOW", "OBJECT_CACHE"];
26
33
  type DuckMemoryTag = (typeof duckMemoryTags)[number];
@@ -49,6 +56,9 @@ declare class DuckMemory {
49
56
  getSummary: () => Promise<DuckMemorySummary>;
50
57
  }
51
58
  //#endregion
59
+ //#region src/logger/sqlduck-default-logtape-logger.d.ts
60
+ declare const sqlduckDefaultLogtapeLogger: _logtape_logtape0.Logger;
61
+ //#endregion
52
62
  //#region src/table/table.d.ts
53
63
  /**
54
64
  * Fully qualified table information
@@ -99,9 +109,14 @@ declare const getTableCreateFromZod: <TSchema extends TableSchemaZod>(params: Ge
99
109
  //#region src/sql-duck.d.ts
100
110
  type SqlDuckParams = {
101
111
  conn: DuckDBConnection;
102
- logger?: (msg: string) => void;
112
+ /**
113
+ * Optional logtape/logger to use for logging.
114
+ * If not provided, a default logger will be used.
115
+ * @see {@link https://github.com/logtape/logtape}
116
+ */
117
+ logger?: Logger;
103
118
  };
104
- type RowStream<T> = AsyncIterableIterator<T>;
119
+ type RowStream<T> = AsyncIterableIterator<T> | AsyncGenerator<T> | Generator<T>;
105
120
  type ToTableParams<TSchema extends TableSchemaZod> = {
106
121
  /**
107
122
  * Used to create and fill the data into the table
@@ -193,4 +208,4 @@ declare const zodCodecs: {
193
208
  readonly bigintToString: z.ZodCodec<z.ZodBigInt, z.ZodString>;
194
209
  };
195
210
  //#endregion
196
- export { DuckMemory, DuckMemoryTag, type OnDataAppendedCb, type OnDataAppendedStats, SqlDuck, type SqlDuckParams, Table, type ToTableParams, getTableCreateFromZod, zodCodecs };
211
+ export { DuckMemory, DuckMemoryTag, type OnDataAppendedCb, type OnDataAppendedStats, SqlDuck, type SqlDuckParams, Table, type ToTableParams, flowbladeLogtapeSqlduckConfig, getTableCreateFromZod, sqlduckDefaultLogtapeLogger, zodCodecs };
package/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as _logtape_logtape0 from "@logtape/logtape";
2
+ import { Logger } from "@logtape/logtape";
1
3
  import { DuckDBConnection, DuckDBType } from "@duckdb/node-api";
2
4
  import * as z from "zod";
3
5
  import { ZodObject } from "zod";
@@ -21,6 +23,11 @@ type OnDataAppendedSyncCb = (stats: OnDataAppendedStats) => void;
21
23
  type OnDataAppendedAsyncCb = (stats: OnDataAppendedStats) => Promise<void>;
22
24
  type OnDataAppendedCb = OnDataAppendedSyncCb | OnDataAppendedAsyncCb;
23
25
  //#endregion
26
+ //#region src/config/flowblade-logtape-sqlduck.config.d.ts
27
+ declare const flowbladeLogtapeSqlduckConfig: {
28
+ categories: string[];
29
+ };
30
+ //#endregion
24
31
  //#region src/helpers/duck-memory.d.ts
25
32
  declare const duckMemoryTags: readonly ["BASE_TABLE", "HASH_TABLE", "PARQUET_READER", "CSV_READER", "ORDER_BY", "ART_INDEX", "COLUMN_DATA", "METADATA", "OVERFLOW_STRINGS", "IN_MEMORY_TABLE", "ALLOCATOR", "EXTENSION", "TRANSACTION", "EXTERNAL_FILE_CACHE", "WINDOW", "OBJECT_CACHE"];
26
33
  type DuckMemoryTag = (typeof duckMemoryTags)[number];
@@ -49,6 +56,9 @@ declare class DuckMemory {
49
56
  getSummary: () => Promise<DuckMemorySummary>;
50
57
  }
51
58
  //#endregion
59
+ //#region src/logger/sqlduck-default-logtape-logger.d.ts
60
+ declare const sqlduckDefaultLogtapeLogger: _logtape_logtape0.Logger;
61
+ //#endregion
52
62
  //#region src/table/table.d.ts
53
63
  /**
54
64
  * Fully qualified table information
@@ -99,9 +109,14 @@ declare const getTableCreateFromZod: <TSchema extends TableSchemaZod>(params: Ge
99
109
  //#region src/sql-duck.d.ts
100
110
  type SqlDuckParams = {
101
111
  conn: DuckDBConnection;
102
- logger?: (msg: string) => void;
112
+ /**
113
+ * Optional logtape/logger to use for logging.
114
+ * If not provided, a default logger will be used.
115
+ * @see {@link https://github.com/logtape/logtape}
116
+ */
117
+ logger?: Logger;
103
118
  };
104
- type RowStream<T> = AsyncIterableIterator<T>;
119
+ type RowStream<T> = AsyncIterableIterator<T> | AsyncGenerator<T> | Generator<T>;
105
120
  type ToTableParams<TSchema extends TableSchemaZod> = {
106
121
  /**
107
122
  * Used to create and fill the data into the table
@@ -193,4 +208,4 @@ declare const zodCodecs: {
193
208
  readonly bigintToString: z.ZodCodec<z.ZodBigInt, z.ZodString>;
194
209
  };
195
210
  //#endregion
196
- export { DuckMemory, type DuckMemoryTag, type OnDataAppendedCb, type OnDataAppendedStats, SqlDuck, type SqlDuckParams, Table, type ToTableParams, getTableCreateFromZod, zodCodecs };
211
+ export { DuckMemory, type DuckMemoryTag, type OnDataAppendedCb, type OnDataAppendedStats, SqlDuck, type SqlDuckParams, Table, type ToTableParams, flowbladeLogtapeSqlduckConfig, getTableCreateFromZod, sqlduckDefaultLogtapeLogger, zodCodecs };
package/dist/index.mjs CHANGED
@@ -1,5 +1,9 @@
1
+ import { getLogger } from "@logtape/logtape";
1
2
  import { BIGINT, BOOLEAN, DOUBLE, DuckDBDataChunk, DuckDBTimestampValue, FLOAT, HUGEINT, INTEGER, SMALLINT, TIMESTAMP, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR } from "@duckdb/node-api";
2
3
  import * as z from "zod";
4
+ //#region src/config/flowblade-logtape-sqlduck.config.ts
5
+ const flowbladeLogtapeSqlduckConfig = { categories: ["flowblade", "sqlduck"] };
6
+ //#endregion
3
7
  //#region src/helpers/duck-exec.ts
4
8
  var DuckExec = class {
5
9
  #conn;
@@ -96,6 +100,9 @@ var DuckMemory = class {
96
100
  };
97
101
  };
98
102
  //#endregion
103
+ //#region src/logger/sqlduck-default-logtape-logger.ts
104
+ const sqlduckDefaultLogtapeLogger = getLogger(flowbladeLogtapeSqlduckConfig.categories);
105
+ //#endregion
99
106
  //#region src/appender/data-appender-callback.ts
100
107
  const isOnDataAppendedAsyncCb = (v) => {
101
108
  return v.constructor.name === "AsyncFunction";
@@ -150,6 +157,17 @@ const createOptions = {
150
157
  CREATE_OR_REPLACE: "CREATE OR REPLACE TABLE",
151
158
  IF_NOT_EXISTS: "CREATE TABLE IF NOT EXISTS"
152
159
  };
160
+ const duckDbTypes = [
161
+ ["VARCHAR", VARCHAR],
162
+ ["BIGINT", BIGINT],
163
+ ["TIMESTAMP", TIMESTAMP],
164
+ ["UUID", UUID],
165
+ ["BOOLEAN", BOOLEAN],
166
+ ["INTEGER", INTEGER],
167
+ ["DOUBLE", DOUBLE],
168
+ ["FLOAT", FLOAT]
169
+ ];
170
+ const duckDbTypesMap = new Map(duckDbTypes);
153
171
  const getTableCreateFromZod = (params) => {
154
172
  const { table, schema, options } = params;
155
173
  const { create = "CREATE" } = options ?? {};
@@ -162,9 +180,10 @@ const getTableCreateFromZod = (params) => {
162
180
  if (json.properties === void 0) throw new TypeError("Schema must have at least one property");
163
181
  const columnTypesMap = /* @__PURE__ */ new Map();
164
182
  for (const [columnName, def] of Object.entries(json.properties)) {
165
- const { type, nullable, format, primaryKey, minimum, maximum } = def;
183
+ const { type, duckdbType, nullable, format, primaryKey, minimum, maximum } = def;
166
184
  const c = { name: columnName };
167
- switch (type) {
185
+ if (duckdbType !== void 0 && duckDbTypesMap.has(duckdbType)) c.duckdbType = duckDbTypesMap.get(duckdbType);
186
+ else switch (type) {
168
187
  case "string":
169
188
  switch (format) {
170
189
  case "date-time":
@@ -220,15 +239,18 @@ const getTableCreateFromZod = (params) => {
220
239
  //#endregion
221
240
  //#region src/table/create-table-from-zod.ts
222
241
  const createTableFromZod = async (params) => {
223
- const { conn, table, schema, options } = params;
242
+ const { conn, table, schema, options, logger = sqlduckDefaultLogtapeLogger } = params;
224
243
  const { ddl, columnTypes } = getTableCreateFromZod({
225
244
  table,
226
245
  schema,
227
246
  options
228
247
  });
248
+ logger.debug(`Generate DDL for table '${table.getFullName()}'`, { ddl });
229
249
  try {
230
250
  await conn.run(ddl);
251
+ logger.info(`Table '${table.getFullName()}' successfully created`, { ddl });
231
252
  } catch (e) {
253
+ logger.error(`Failed to create table '${table.getFullName()}': ${e.message}`, { ddl });
232
254
  throw new Error(`Failed to create table '${table.getFullName()}': ${e.message}`, { cause: e });
233
255
  }
234
256
  return {
@@ -285,7 +307,7 @@ var SqlDuck = class {
285
307
  #logger;
286
308
  constructor(params) {
287
309
  this.#duck = params.conn;
288
- this.#logger = params.logger;
310
+ this.#logger = params.logger ?? sqlduckDefaultLogtapeLogger;
289
311
  }
290
312
  /**
291
313
  * Create a table from a Zod schema and fill it with data from a row stream.
@@ -342,25 +364,38 @@ var SqlDuck = class {
342
364
  rows: rowStream,
343
365
  chunkSize
344
366
  });
345
- for await (const dataChunk of columnStream) {
346
- const chunk = DuckDBDataChunk.create(chunkTypes);
347
- if (this.#logger) this.#logger(`Inserting chunk of ${dataChunk.length} rows`);
348
- totalRows += dataChunk?.[0]?.length ?? 0;
349
- chunk.setColumns(dataChunk);
350
- appender.appendDataChunk(chunk);
351
- appender.flushSync();
352
- if (onDataAppended !== void 0) {
353
- const payload = dataAppendedCollector(totalRows);
354
- if (isOnDataAppendedAsyncCb(onDataAppended)) await onDataAppended(payload);
355
- else onDataAppended(payload);
367
+ try {
368
+ for await (const dataChunk of columnStream) {
369
+ const chunk = DuckDBDataChunk.create(chunkTypes);
370
+ this.#logger.debug(`Inserting chunk of ${dataChunk.length} rows`, { table: table.getFullName() });
371
+ totalRows += dataChunk?.[0]?.length ?? 0;
372
+ chunk.setColumns(dataChunk);
373
+ appender.appendDataChunk(chunk);
374
+ appender.flushSync();
375
+ if (onDataAppended !== void 0) {
376
+ const payload = dataAppendedCollector(totalRows);
377
+ if (isOnDataAppendedAsyncCb(onDataAppended)) await onDataAppended(payload);
378
+ else onDataAppended(payload);
379
+ }
356
380
  }
381
+ appender.closeSync();
382
+ const timeMs = Math.round(Date.now() - timeStart);
383
+ this.#logger.info(`Successfully appended ${totalRows} rows into '${table.getFullName()}' in ${timeMs}ms`, {
384
+ table: table.getFullName(),
385
+ timeMs,
386
+ totalRows
387
+ });
388
+ return {
389
+ timeMs,
390
+ totalRows,
391
+ createTableDDL: ddl
392
+ };
393
+ } catch (e) {
394
+ appender.closeSync();
395
+ const msg = `Failed to append data into table '${table.getFullName()}' - ${e?.message ?? ""}`;
396
+ this.#logger.error(msg, { table: table.getFullName() });
397
+ throw new Error(msg, { cause: e });
357
398
  }
358
- appender.closeSync();
359
- return {
360
- timeMs: Math.round(Date.now() - timeStart),
361
- totalRows,
362
- createTableDDL: ddl
363
- };
364
399
  };
365
400
  };
366
401
  //#endregion
@@ -418,4 +453,4 @@ const zodCodecs = {
418
453
  })
419
454
  };
420
455
  //#endregion
421
- export { DuckMemory, SqlDuck, Table, getTableCreateFromZod, zodCodecs };
456
+ export { DuckMemory, SqlDuck, Table, flowbladeLogtapeSqlduckConfig, getTableCreateFromZod, sqlduckDefaultLogtapeLogger, zodCodecs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowblade/sqlduck",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -57,6 +57,7 @@
57
57
  "@flowblade/core": "^0.2.26",
58
58
  "@flowblade/source-duckdb": "^0.20.1",
59
59
  "@flowblade/sql-tag": "^0.3.2",
60
+ "@logtape/logtape": "2.0.4",
60
61
  "@standard-schema/spec": "^1.1.0",
61
62
  "p-mutex": "^1.0.0",
62
63
  "valibot": "^1.3.1",
@@ -67,7 +68,7 @@
67
68
  },
68
69
  "devDependencies": {
69
70
  "@belgattitude/eslint-config-bases": "8.10.0",
70
- "@dotenvx/dotenvx": "1.55.1",
71
+ "@dotenvx/dotenvx": "1.57.2",
71
72
  "@duckdb/node-api": "1.5.1-r.1",
72
73
  "@faker-js/faker": "10.3.0",
73
74
  "@flowblade/source-kysely": "^1.3.0",