@aiquants/duckdb-helper 1.5.0 → 1.6.2
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.d.mts +164 -5
- package/dist/index.d.ts +164 -5
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/docs/specs/2026.04.11 [AI] 00-overview.md +32 -11
- package/docs/specs/2026.04.11 [AI] 02-service.md +114 -240
- package/docs/specs/2026.04.11 [AI] 03-hooks.md +10 -21
- package/docs/specs/2026.04.11 [AI] 05-logger.md +3 -5
- package/docs/specs/2026.04.11 [AI] 06-build-config.md +22 -4
- package/docs/specs/2026.07.05 [AI] 07-testing.md +184 -0
- package/package.json +6 -1
package/dist/index.d.mts
CHANGED
|
@@ -35,9 +35,21 @@ declare const useDuckDB: (autoInitialize?: boolean) => UseDuckDBResult;
|
|
|
35
35
|
* Hook for simple DuckDB query execution.
|
|
36
36
|
* シンプルな DuckDB クエリ実行のためのフック。
|
|
37
37
|
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。
|
|
40
|
+
* `.length` や `.map` は持たないため、行配列として扱う場合は必ず
|
|
41
|
+
* {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく
|
|
42
|
+
* Arrow Table 相当の型を想定)。
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const { data } = useDuckDBQuery("SELECT * FROM t")
|
|
47
|
+
* const rows = duckdbTableToArray<Row>(data)
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
38
50
|
* @param sql - 実行する SQL クエリ
|
|
39
51
|
* @param dependencies - クエリを再実行するための依存配列
|
|
40
|
-
* @returns
|
|
52
|
+
* @returns クエリ結果(生の Arrow Table)と状態
|
|
41
53
|
*/
|
|
42
54
|
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
43
55
|
data: T | null;
|
|
@@ -62,9 +74,18 @@ declare class DuckDBService {
|
|
|
62
74
|
private operationFlags;
|
|
63
75
|
private workerInstance;
|
|
64
76
|
private connectionInstance;
|
|
77
|
+
private connectionPromise;
|
|
65
78
|
private isInitializing;
|
|
66
79
|
private initPromise;
|
|
67
80
|
private hasError;
|
|
81
|
+
/** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */
|
|
82
|
+
private pendingWorker;
|
|
83
|
+
private pendingWorkerUrl;
|
|
84
|
+
/**
|
|
85
|
+
* 共有コネクション上での操作直列化キュー。
|
|
86
|
+
* createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。
|
|
87
|
+
*/
|
|
88
|
+
private operationQueue;
|
|
68
89
|
private listeners;
|
|
69
90
|
/**
|
|
70
91
|
* Get singleton instance of DuckDBService.
|
|
@@ -91,6 +112,12 @@ declare class DuckDBService {
|
|
|
91
112
|
* 現在のステータスを取得します。
|
|
92
113
|
*/
|
|
93
114
|
getStatus(): "not-initialized" | "initializing" | "ready" | "error";
|
|
115
|
+
/** Enqueue a task on the single-connection serialization queue. */
|
|
116
|
+
private enqueue;
|
|
117
|
+
/** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */
|
|
118
|
+
private assertNotAborted;
|
|
119
|
+
/** 初期化中の Worker / Blob URL を確実に破棄する。 */
|
|
120
|
+
private abortPendingWorker;
|
|
94
121
|
/**
|
|
95
122
|
* Initialize DuckDB Worker instance.
|
|
96
123
|
* DuckDB Worker インスタンスを初期化します。
|
|
@@ -99,13 +126,26 @@ declare class DuckDBService {
|
|
|
99
126
|
/**
|
|
100
127
|
* Internal worker initialization method.
|
|
101
128
|
* 内部的な Worker 初期化メソッド。
|
|
129
|
+
*
|
|
130
|
+
* workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は
|
|
131
|
+
* ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。
|
|
102
132
|
*/
|
|
103
133
|
private internalInitializeWorker;
|
|
104
134
|
/**
|
|
105
135
|
* Get or create DuckDB connection.
|
|
106
136
|
* DuckDB 接続を取得または作成します。
|
|
137
|
+
*
|
|
138
|
+
* 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。
|
|
107
139
|
*/
|
|
108
140
|
getConnection(): Promise<duckdb.AsyncDuckDBConnection>;
|
|
141
|
+
/**
|
|
142
|
+
* Run a single query with a timeout, discarding the connection on timeout.
|
|
143
|
+
* 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。
|
|
144
|
+
*
|
|
145
|
+
* タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して
|
|
146
|
+
* 後続クエリが同じ(詰まった)コネクションで待たされないようにする。
|
|
147
|
+
*/
|
|
148
|
+
private runQueryWithTimeout;
|
|
109
149
|
/**
|
|
110
150
|
* Execute SQL query using DuckDB Worker.
|
|
111
151
|
* DuckDB Worker を使用して SQL クエリを実行します。
|
|
@@ -113,12 +153,24 @@ declare class DuckDBService {
|
|
|
113
153
|
executeQuery(sql: string): Promise<unknown>;
|
|
114
154
|
/**
|
|
115
155
|
* Execute multiple SQL queries.
|
|
116
|
-
* 複数の SQL
|
|
156
|
+
* 複数の SQL クエリを実行します(逐次実行)。
|
|
157
|
+
*
|
|
158
|
+
* バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。
|
|
117
159
|
*/
|
|
118
160
|
executeQueries(queries: string[]): Promise<unknown[]>;
|
|
119
161
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
162
|
+
* Collect the union of column names across all rows (first-seen order).
|
|
163
|
+
* 全行を走査して列名の和集合を取得します(初出順を保持)。
|
|
164
|
+
* 先頭行に存在しないキーを取りこぼさないため全行を対象とする。
|
|
165
|
+
*/
|
|
166
|
+
private collectColumnNames;
|
|
167
|
+
/** Check whether a string is a genuine (strict) date/timestamp literal. */
|
|
168
|
+
private looksLikeTimestamp;
|
|
169
|
+
/**
|
|
170
|
+
* Infer column types from a data sample for the given columns.
|
|
171
|
+
* 指定した列について、データサンプルから DuckDB の型を推定します。
|
|
172
|
+
*
|
|
173
|
+
* 整数は桁あふれを避けるため一律 BIGINT を採用する。
|
|
122
174
|
*/
|
|
123
175
|
private inferColumnTypes;
|
|
124
176
|
/**
|
|
@@ -126,15 +178,24 @@ declare class DuckDBService {
|
|
|
126
178
|
* SQL 挿入用に値をフォーマットします。
|
|
127
179
|
*/
|
|
128
180
|
private formatValueForSQL;
|
|
181
|
+
/**
|
|
182
|
+
* Quote a SQL identifier (table / column / primary key) safely.
|
|
183
|
+
* SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。
|
|
184
|
+
*/
|
|
185
|
+
private quoteIdentifier;
|
|
129
186
|
/**
|
|
130
187
|
* Create table from JavaScript array of objects.
|
|
131
188
|
* JavaScript オブジェクトの配列からテーブルを作成します。
|
|
189
|
+
*
|
|
190
|
+
* 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。
|
|
132
191
|
*/
|
|
133
192
|
createTableFromData<T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
134
193
|
dropIfExists?: boolean;
|
|
135
194
|
primaryKey?: string;
|
|
136
195
|
verbose?: boolean;
|
|
137
196
|
}): Promise<void>;
|
|
197
|
+
/** Actual (serialized, transactional) table build. */
|
|
198
|
+
private buildTable;
|
|
138
199
|
/**
|
|
139
200
|
* Get table information.
|
|
140
201
|
* テーブル情報を取得します。
|
|
@@ -225,4 +286,102 @@ declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
|
225
286
|
*/
|
|
226
287
|
declare const isDuckDBTable: (result: unknown) => result is DuckDBQueryResult;
|
|
227
288
|
|
|
228
|
-
|
|
289
|
+
/**
|
|
290
|
+
* @module utils/logger
|
|
291
|
+
* @description Logger utility for consistent log management across the library.
|
|
292
|
+
* @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。
|
|
293
|
+
*/
|
|
294
|
+
/**
|
|
295
|
+
* @enum LogLevel
|
|
296
|
+
* @description Log levels for filtering output.
|
|
297
|
+
* @description 出力をフィルタリングするためのログレベル。
|
|
298
|
+
*/
|
|
299
|
+
declare enum LogLevel {
|
|
300
|
+
DEBUG = 0,
|
|
301
|
+
INFO = 1,
|
|
302
|
+
WARN = 2,
|
|
303
|
+
ERROR = 3,
|
|
304
|
+
NONE = 4
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* @interface ILogger
|
|
308
|
+
* @description Interface for a logger object compatible with Console.
|
|
309
|
+
* @description Console と互換性のあるロガーオブジェクトのインターフェース。
|
|
310
|
+
*/
|
|
311
|
+
interface ILogger {
|
|
312
|
+
debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
313
|
+
info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
314
|
+
warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
315
|
+
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* @class Logger
|
|
319
|
+
* @description A wrapper class for handling logging with levels and prefixes.
|
|
320
|
+
* @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。
|
|
321
|
+
*/
|
|
322
|
+
declare class Logger implements ILogger {
|
|
323
|
+
private level;
|
|
324
|
+
private prefix;
|
|
325
|
+
private impl;
|
|
326
|
+
/**
|
|
327
|
+
* @constructor
|
|
328
|
+
* @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.
|
|
329
|
+
* @param {string} [prefix="[duckdb-helper]"] - The prefix to add to all log messages.
|
|
330
|
+
* @param {ILogger} [impl=console] - The implementation to use for logging.
|
|
331
|
+
*/
|
|
332
|
+
constructor(level?: LogLevel, prefix?: string, impl?: ILogger);
|
|
333
|
+
private static instance;
|
|
334
|
+
/**
|
|
335
|
+
* @method setLevel
|
|
336
|
+
* @description Updates the current log level for the static instance.
|
|
337
|
+
* @description 静的インスタンスの現在のログレベルを更新します。
|
|
338
|
+
* @param {LogLevel} level - The new log level.
|
|
339
|
+
*/
|
|
340
|
+
static setLevel(level: LogLevel): void;
|
|
341
|
+
/**
|
|
342
|
+
* @method setLevel
|
|
343
|
+
* @description Updates the current log level.
|
|
344
|
+
* @description 現在のログレベルを更新します。
|
|
345
|
+
* @param {LogLevel} level - The new log level.
|
|
346
|
+
*/
|
|
347
|
+
setLevel(level: LogLevel): void;
|
|
348
|
+
/**
|
|
349
|
+
* @method setImplementation
|
|
350
|
+
* @description Updates the logger implementation for the static instance.
|
|
351
|
+
* @description 静的インスタンスのロガーの実装を更新します。
|
|
352
|
+
* @param {ILogger} impl - The new logger implementation.
|
|
353
|
+
*/
|
|
354
|
+
static setImplementation(impl: ILogger): void;
|
|
355
|
+
/**
|
|
356
|
+
* @method setImplementation
|
|
357
|
+
* @description Updates the logger implementation.
|
|
358
|
+
* @description ロガーの実装を更新します。
|
|
359
|
+
* @param {ILogger} impl - The new logger implementation.
|
|
360
|
+
*/
|
|
361
|
+
setImplementation(impl: ILogger): void;
|
|
362
|
+
/**
|
|
363
|
+
* @method setPrefix
|
|
364
|
+
* @description Updates the log prefix for the static instance.
|
|
365
|
+
* @description 静的インスタンスのログのプレフィックスを更新します。
|
|
366
|
+
* @param {string} prefix - The new prefix.
|
|
367
|
+
*/
|
|
368
|
+
static setPrefix(prefix: string): void;
|
|
369
|
+
/**
|
|
370
|
+
* @method setPrefix
|
|
371
|
+
* @description Updates the log prefix.
|
|
372
|
+
* @description ログのプレフィックスを更新します。
|
|
373
|
+
* @param {string} prefix - The new prefix.
|
|
374
|
+
*/
|
|
375
|
+
setPrefix(prefix: string): void;
|
|
376
|
+
private formatMessage;
|
|
377
|
+
static debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
378
|
+
debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
379
|
+
static info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
380
|
+
info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
381
|
+
static warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
382
|
+
warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
383
|
+
static error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
384
|
+
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export { type DuckDBQueryResult, type DuckDBRow, DuckDBService, type ILogger, LogLevel, Logger, type UseDuckDBResult, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, useDuckDB, useDuckDBQuery };
|
package/dist/index.d.ts
CHANGED
|
@@ -35,9 +35,21 @@ declare const useDuckDB: (autoInitialize?: boolean) => UseDuckDBResult;
|
|
|
35
35
|
* Hook for simple DuckDB query execution.
|
|
36
36
|
* シンプルな DuckDB クエリ実行のためのフック。
|
|
37
37
|
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。
|
|
40
|
+
* `.length` や `.map` は持たないため、行配列として扱う場合は必ず
|
|
41
|
+
* {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく
|
|
42
|
+
* Arrow Table 相当の型を想定)。
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const { data } = useDuckDBQuery("SELECT * FROM t")
|
|
47
|
+
* const rows = duckdbTableToArray<Row>(data)
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
38
50
|
* @param sql - 実行する SQL クエリ
|
|
39
51
|
* @param dependencies - クエリを再実行するための依存配列
|
|
40
|
-
* @returns
|
|
52
|
+
* @returns クエリ結果(生の Arrow Table)と状態
|
|
41
53
|
*/
|
|
42
54
|
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
43
55
|
data: T | null;
|
|
@@ -62,9 +74,18 @@ declare class DuckDBService {
|
|
|
62
74
|
private operationFlags;
|
|
63
75
|
private workerInstance;
|
|
64
76
|
private connectionInstance;
|
|
77
|
+
private connectionPromise;
|
|
65
78
|
private isInitializing;
|
|
66
79
|
private initPromise;
|
|
67
80
|
private hasError;
|
|
81
|
+
/** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */
|
|
82
|
+
private pendingWorker;
|
|
83
|
+
private pendingWorkerUrl;
|
|
84
|
+
/**
|
|
85
|
+
* 共有コネクション上での操作直列化キュー。
|
|
86
|
+
* createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。
|
|
87
|
+
*/
|
|
88
|
+
private operationQueue;
|
|
68
89
|
private listeners;
|
|
69
90
|
/**
|
|
70
91
|
* Get singleton instance of DuckDBService.
|
|
@@ -91,6 +112,12 @@ declare class DuckDBService {
|
|
|
91
112
|
* 現在のステータスを取得します。
|
|
92
113
|
*/
|
|
93
114
|
getStatus(): "not-initialized" | "initializing" | "ready" | "error";
|
|
115
|
+
/** Enqueue a task on the single-connection serialization queue. */
|
|
116
|
+
private enqueue;
|
|
117
|
+
/** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */
|
|
118
|
+
private assertNotAborted;
|
|
119
|
+
/** 初期化中の Worker / Blob URL を確実に破棄する。 */
|
|
120
|
+
private abortPendingWorker;
|
|
94
121
|
/**
|
|
95
122
|
* Initialize DuckDB Worker instance.
|
|
96
123
|
* DuckDB Worker インスタンスを初期化します。
|
|
@@ -99,13 +126,26 @@ declare class DuckDBService {
|
|
|
99
126
|
/**
|
|
100
127
|
* Internal worker initialization method.
|
|
101
128
|
* 内部的な Worker 初期化メソッド。
|
|
129
|
+
*
|
|
130
|
+
* workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は
|
|
131
|
+
* ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。
|
|
102
132
|
*/
|
|
103
133
|
private internalInitializeWorker;
|
|
104
134
|
/**
|
|
105
135
|
* Get or create DuckDB connection.
|
|
106
136
|
* DuckDB 接続を取得または作成します。
|
|
137
|
+
*
|
|
138
|
+
* 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。
|
|
107
139
|
*/
|
|
108
140
|
getConnection(): Promise<duckdb.AsyncDuckDBConnection>;
|
|
141
|
+
/**
|
|
142
|
+
* Run a single query with a timeout, discarding the connection on timeout.
|
|
143
|
+
* 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。
|
|
144
|
+
*
|
|
145
|
+
* タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して
|
|
146
|
+
* 後続クエリが同じ(詰まった)コネクションで待たされないようにする。
|
|
147
|
+
*/
|
|
148
|
+
private runQueryWithTimeout;
|
|
109
149
|
/**
|
|
110
150
|
* Execute SQL query using DuckDB Worker.
|
|
111
151
|
* DuckDB Worker を使用して SQL クエリを実行します。
|
|
@@ -113,12 +153,24 @@ declare class DuckDBService {
|
|
|
113
153
|
executeQuery(sql: string): Promise<unknown>;
|
|
114
154
|
/**
|
|
115
155
|
* Execute multiple SQL queries.
|
|
116
|
-
* 複数の SQL
|
|
156
|
+
* 複数の SQL クエリを実行します(逐次実行)。
|
|
157
|
+
*
|
|
158
|
+
* バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。
|
|
117
159
|
*/
|
|
118
160
|
executeQueries(queries: string[]): Promise<unknown[]>;
|
|
119
161
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
162
|
+
* Collect the union of column names across all rows (first-seen order).
|
|
163
|
+
* 全行を走査して列名の和集合を取得します(初出順を保持)。
|
|
164
|
+
* 先頭行に存在しないキーを取りこぼさないため全行を対象とする。
|
|
165
|
+
*/
|
|
166
|
+
private collectColumnNames;
|
|
167
|
+
/** Check whether a string is a genuine (strict) date/timestamp literal. */
|
|
168
|
+
private looksLikeTimestamp;
|
|
169
|
+
/**
|
|
170
|
+
* Infer column types from a data sample for the given columns.
|
|
171
|
+
* 指定した列について、データサンプルから DuckDB の型を推定します。
|
|
172
|
+
*
|
|
173
|
+
* 整数は桁あふれを避けるため一律 BIGINT を採用する。
|
|
122
174
|
*/
|
|
123
175
|
private inferColumnTypes;
|
|
124
176
|
/**
|
|
@@ -126,15 +178,24 @@ declare class DuckDBService {
|
|
|
126
178
|
* SQL 挿入用に値をフォーマットします。
|
|
127
179
|
*/
|
|
128
180
|
private formatValueForSQL;
|
|
181
|
+
/**
|
|
182
|
+
* Quote a SQL identifier (table / column / primary key) safely.
|
|
183
|
+
* SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。
|
|
184
|
+
*/
|
|
185
|
+
private quoteIdentifier;
|
|
129
186
|
/**
|
|
130
187
|
* Create table from JavaScript array of objects.
|
|
131
188
|
* JavaScript オブジェクトの配列からテーブルを作成します。
|
|
189
|
+
*
|
|
190
|
+
* 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。
|
|
132
191
|
*/
|
|
133
192
|
createTableFromData<T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
134
193
|
dropIfExists?: boolean;
|
|
135
194
|
primaryKey?: string;
|
|
136
195
|
verbose?: boolean;
|
|
137
196
|
}): Promise<void>;
|
|
197
|
+
/** Actual (serialized, transactional) table build. */
|
|
198
|
+
private buildTable;
|
|
138
199
|
/**
|
|
139
200
|
* Get table information.
|
|
140
201
|
* テーブル情報を取得します。
|
|
@@ -225,4 +286,102 @@ declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
|
225
286
|
*/
|
|
226
287
|
declare const isDuckDBTable: (result: unknown) => result is DuckDBQueryResult;
|
|
227
288
|
|
|
228
|
-
|
|
289
|
+
/**
|
|
290
|
+
* @module utils/logger
|
|
291
|
+
* @description Logger utility for consistent log management across the library.
|
|
292
|
+
* @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。
|
|
293
|
+
*/
|
|
294
|
+
/**
|
|
295
|
+
* @enum LogLevel
|
|
296
|
+
* @description Log levels for filtering output.
|
|
297
|
+
* @description 出力をフィルタリングするためのログレベル。
|
|
298
|
+
*/
|
|
299
|
+
declare enum LogLevel {
|
|
300
|
+
DEBUG = 0,
|
|
301
|
+
INFO = 1,
|
|
302
|
+
WARN = 2,
|
|
303
|
+
ERROR = 3,
|
|
304
|
+
NONE = 4
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* @interface ILogger
|
|
308
|
+
* @description Interface for a logger object compatible with Console.
|
|
309
|
+
* @description Console と互換性のあるロガーオブジェクトのインターフェース。
|
|
310
|
+
*/
|
|
311
|
+
interface ILogger {
|
|
312
|
+
debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
313
|
+
info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
314
|
+
warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
315
|
+
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* @class Logger
|
|
319
|
+
* @description A wrapper class for handling logging with levels and prefixes.
|
|
320
|
+
* @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。
|
|
321
|
+
*/
|
|
322
|
+
declare class Logger implements ILogger {
|
|
323
|
+
private level;
|
|
324
|
+
private prefix;
|
|
325
|
+
private impl;
|
|
326
|
+
/**
|
|
327
|
+
* @constructor
|
|
328
|
+
* @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.
|
|
329
|
+
* @param {string} [prefix="[duckdb-helper]"] - The prefix to add to all log messages.
|
|
330
|
+
* @param {ILogger} [impl=console] - The implementation to use for logging.
|
|
331
|
+
*/
|
|
332
|
+
constructor(level?: LogLevel, prefix?: string, impl?: ILogger);
|
|
333
|
+
private static instance;
|
|
334
|
+
/**
|
|
335
|
+
* @method setLevel
|
|
336
|
+
* @description Updates the current log level for the static instance.
|
|
337
|
+
* @description 静的インスタンスの現在のログレベルを更新します。
|
|
338
|
+
* @param {LogLevel} level - The new log level.
|
|
339
|
+
*/
|
|
340
|
+
static setLevel(level: LogLevel): void;
|
|
341
|
+
/**
|
|
342
|
+
* @method setLevel
|
|
343
|
+
* @description Updates the current log level.
|
|
344
|
+
* @description 現在のログレベルを更新します。
|
|
345
|
+
* @param {LogLevel} level - The new log level.
|
|
346
|
+
*/
|
|
347
|
+
setLevel(level: LogLevel): void;
|
|
348
|
+
/**
|
|
349
|
+
* @method setImplementation
|
|
350
|
+
* @description Updates the logger implementation for the static instance.
|
|
351
|
+
* @description 静的インスタンスのロガーの実装を更新します。
|
|
352
|
+
* @param {ILogger} impl - The new logger implementation.
|
|
353
|
+
*/
|
|
354
|
+
static setImplementation(impl: ILogger): void;
|
|
355
|
+
/**
|
|
356
|
+
* @method setImplementation
|
|
357
|
+
* @description Updates the logger implementation.
|
|
358
|
+
* @description ロガーの実装を更新します。
|
|
359
|
+
* @param {ILogger} impl - The new logger implementation.
|
|
360
|
+
*/
|
|
361
|
+
setImplementation(impl: ILogger): void;
|
|
362
|
+
/**
|
|
363
|
+
* @method setPrefix
|
|
364
|
+
* @description Updates the log prefix for the static instance.
|
|
365
|
+
* @description 静的インスタンスのログのプレフィックスを更新します。
|
|
366
|
+
* @param {string} prefix - The new prefix.
|
|
367
|
+
*/
|
|
368
|
+
static setPrefix(prefix: string): void;
|
|
369
|
+
/**
|
|
370
|
+
* @method setPrefix
|
|
371
|
+
* @description Updates the log prefix.
|
|
372
|
+
* @description ログのプレフィックスを更新します。
|
|
373
|
+
* @param {string} prefix - The new prefix.
|
|
374
|
+
*/
|
|
375
|
+
setPrefix(prefix: string): void;
|
|
376
|
+
private formatMessage;
|
|
377
|
+
static debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
378
|
+
debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
379
|
+
static info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
380
|
+
info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
381
|
+
static warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
382
|
+
warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
383
|
+
static error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
384
|
+
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export { type DuckDBQueryResult, type DuckDBRow, DuckDBService, type ILogger, LogLevel, Logger, type UseDuckDBResult, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, useDuckDB, useDuckDBQuery };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var M=Object.create;var v=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var W=(i,e)=>{for(var n in e)v(i,n,{get:e[n],enumerable:!0})},B=(i,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Q(e))!U.call(i,s)&&s!==n&&v(i,s,{get:()=>e[s],enumerable:!(o=O(e,s))||o.enumerable});return i};var N=(i,e,n)=>(n=i!=null?M(F(i)):{},B(e||!i||!i.__esModule?v(n,"default",{value:i,enumerable:!0}):n,i)),j=i=>B(v({},"__esModule",{value:!0}),i);var q={};W(q,{DuckDBService:()=>b,duckDBService:()=>R,duckdbTableToArray:()=>x,getDuckDBColumnCount:()=>L,getDuckDBRowCount:()=>P,isDuckDBTable:()=>z,useDuckDB:()=>E,useDuckDBQuery:()=>S});module.exports=j(q);var d=require("react");var y=N(require("@duckdb/duckdb-wasm"));var p=class p{constructor(e=2,n="[duckdb-helper]",o=console){this.level=e,this.prefix=n,this.impl=o}static setLevel(e){p.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){p.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){p.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){p.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){p.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){p.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){p.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};p.instance=new p(2,"[duckdb-helper]");var r=p;var g=class g{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.listeners=new Set}static getInstance(){return g.instance||(g.instance=new g),g.instance}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){r.error("\u26A0\uFE0F Error in status listener:",n)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}async initialize(){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let e=12e4,n=new Promise((o,s)=>{setTimeout(()=>s(new Error(`DuckDB initialization timed out after ${e}ms`)),e)});this.initPromise=(async()=>await Promise.race([this.internalInitializeWorker(),n]))();try{let o=await this.initPromise;return this.isInitializing=!1,this.notifyListeners(),o}catch(o){throw this.isInitializing=!1,this.hasError=!0,this.notifyListeners(),this.initPromise=null,o}}async internalInitializeWorker(){try{r.info("\u{1F527} Starting DuckDB Worker initialization..."),r.info("\u{1F4E6} Getting DuckDB bundles from CDN...");let e=y.getJsDelivrBundles();r.info("\u{1F50D} Selecting appropriate bundle for browser...");let n=await y.selectBundle(e);r.info("\u2705 Bundle selected:",{mainWorker:n.mainWorker,mainModule:n.mainModule,pthreadWorker:n.pthreadWorker}),r.info("\u{1F680} Creating Worker with Blob URL...");let o=URL.createObjectURL(new Blob([`importScripts("${n.mainWorker}");`],{type:"text/javascript"}));r.info("\u{1F477} Creating Worker instance...");let s=new Worker(o),t=new y.ConsoleLogger;r.info("\u{1F5C4}\uFE0F Creating DuckDB instance..."),this.workerInstance=new y.AsyncDuckDB(t,s),r.info("\u26A1 Instantiating DuckDB with modules..."),await this.workerInstance.instantiate(n.mainModule,n.pthreadWorker),r.info("\u{1F4E6} Installing splink_udfs from community...");let a=await this.workerInstance.connect();try{await a.query("INSTALL splink_udfs FROM community;"),r.info("\u2705 splink_udfs installed successfully"),await a.query("LOAD splink_udfs;"),r.info("\u2705 splink_udfs loaded successfully")}catch(u){r.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",u)}finally{await a.close()}return URL.revokeObjectURL(o),r.info("\u2705 DuckDB Worker initialized successfully"),this.workerInstance}catch(e){throw r.error("\u274C Failed to initialize DuckDB Worker:",e),e instanceof Error&&(r.error("\u274C Error name:",e.name),r.error("\u274C Error message:",e.message),r.error("\u274C Error stack:",e.stack)),e}}async getConnection(){if(!this.connectionInstance){let e=await this.initialize();this.connectionInstance=await e.connect()}return this.connectionInstance}async executeQuery(e){let o=new Promise((s,t)=>{setTimeout(()=>t(new Error("DuckDB query execution timed out after 60000ms")),6e4)});try{let s=(async()=>await(await this.getConnection()).query(e))();return await Promise.race([s,o])}catch(s){throw r.error("\u274C Failed to execute SQL query:",s),s}}async executeQueries(e){let n=[];for(let o of e){let s=await this.executeQuery(o);n.push(s)}return n}inferColumnTypes(e){let n={},o=e.slice(0,Math.min(e.length,10)),s=Object.keys(o[0]||{});for(let t of s){let a=o.map(f=>f[t]).filter(f=>f!=null);if(a.length===0){n[t]="VARCHAR";continue}let u={number:0,integer:0,bigint:0,boolean:0,date:0,string:0};for(let f of a)if(typeof f=="number")u.number+=1,Number.isInteger(f)&&(u.integer+=1,(f<-2147483648||f>2147483647)&&(u.bigint+=1));else if(typeof f=="boolean")u.boolean+=1;else if(f instanceof Date)u.date+=1;else if(typeof f=="string"){let w=new Date(f);!Number.isNaN(w.getTime())&&f.includes("-")?u.date+=1:u.string+=1}else u.string+=1;u.number===a.length?u.integer===a.length?u.bigint>0?n[t]="BIGINT":n[t]="INTEGER":n[t]="DOUBLE":u.boolean===a.length?n[t]="BOOLEAN":u.date===a.length?n[t]="TIMESTAMP":n[t]="VARCHAR"}return n}formatValueForSQL(e){return e==null?"NULL":typeof e=="string"?`'${e.replace(/'/g,"''")}'`:e instanceof Date?`'${e.toISOString()}'`:typeof e=="boolean"?e?"TRUE":"FALSE":(typeof e=="number"&&Number.isInteger(e)&&(e<-2147483648||e>2147483647),String(e))}async createTableFromData(e,n,o={}){if(!n||n.length===0)throw new Error("Data array is empty");let s=`create_table_${e}`,t=o.verbose??!0;if(this.operationFlags.has(s)){t&&r.info(`\u23F3 Table creation for '${e}' is already in progress, skipping...`);return}this.operationFlags.set(s,!0);try{let a=await this.getConnection();o.dropIfExists&&(t&&r.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await a.query(`DROP TABLE IF EXISTS ${e}`));try{let l=await a.query("SHOW TABLES");t&&r.info("\u{1F4CB} Existing tables before creation:",l)}catch(l){t&&r.warn("\u26A0\uFE0F Could not list existing tables:",l)}let u=Math.min(n.length,100),f=this.inferColumnTypes(n.slice(0,u)),w=Object.keys(f).map(l=>`"${l}" ${f[l]}`),h=o.primaryKey?`, PRIMARY KEY ("${o.primaryKey}")`:"",k=`CREATE TABLE ${e} (${w.join(", ")}${h})`;t&&r.info("\u{1F528} Creating table with SQL:",k),await a.query(k);try{let l=await a.query("SHOW TABLES");t&&r.info("\u{1F4CB} Tables after creation:",l);let m=await a.query(`SELECT COUNT(*) as count FROM information_schema.tables WHERE table_name = '${e}'`);t&&r.info(`\u2705 Table '${e}' exists check:`,m)}catch(l){t&&r.warn("\u26A0\uFE0F Could not verify table creation:",l)}t&&r.info(`\u{1F4E5} Inserting ${n.length} rows into table '${e}'`);let D=Object.keys(n[0]),c=1e3;for(let l=0;l<n.length;l+=c){let m=n.slice(l,l+c);try{let I=m.map(T=>`(${D.map(C=>{let $=T[C];return this.formatValueForSQL($)}).join(", ")})`),A=`INSERT INTO ${e} (${D.map(T=>`"${T}"`).join(", ")}) VALUES ${I.join(", ")}`;await a.query(A),t&&r.info(`\u{1F4CA} Inserted batch ${Math.floor(l/c)+1} (${m.length} rows)`)}catch(I){throw r.error(`\u274C Failed to insert batch ${Math.floor(l/c)+1}:`,I),I}}t&&r.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(a){throw r.error(`\u274C Failed to create table '${e}':`,a),a}finally{this.operationFlags.delete(s)}}async getTableInfo(e){return await(await this.getConnection()).query(`DESCRIBE ${e}`)}async listTables(){return await(await this.getConnection()).query("SHOW TABLES")}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.listeners.clear(),this.operationFlags.clear()}catch(e){r.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){g.instance&&(g.instance.cleanup(),g.instance=null)}};g.instance=null;var b=g;var R=b.getInstance();var E=(i=!0)=>{let[e,n]=(0,d.useState)("not-initialized"),[o,s]=(0,d.useState)(null),[t]=(0,d.useState)(()=>b.getInstance()),a=(0,d.useCallback)(()=>{let c=t.getStatus();r.info("\u{1F504} DuckDB status updated:",c),n(c),c==="error"?s("DuckDB initialization failed"):c==="ready"&&s(null)},[t]);(0,d.useEffect)(()=>(t.addStatusListener(a),a(),()=>{t.removeStatusListener(a)}),[t,a]);let u=(0,d.useCallback)(async()=>{try{await t.initialize()}catch(c){let l=c instanceof Error?c.message:"Unknown error occurred";s(l),r.error("\u274C DuckDB initialization failed:",c)}},[t]);(0,d.useEffect)(()=>{i&&e==="not-initialized"&&(r.info("\u{1F680} Auto-initializing DuckDB..."),u())},[i,e,u]);let f=(0,d.useCallback)(async c=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQuery(c)},[t,e]),w=(0,d.useCallback)(async c=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQueries(c)},[t,e]),h=(0,d.useCallback)(async(c,l,m)=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.createTableFromData(c,l,m)},[t,e]),k=(0,d.useCallback)(async c=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.getTableInfo(c)},[t,e]),D=(0,d.useCallback)(async()=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.listTables()},[t,e]);return(0,d.useEffect)(()=>{let c=()=>{let m=t.getStatus();m!==e&&m!=="initializing"&&n(m)};if(typeof window>"u"){c();return}let l=window.setInterval(c,1e3);return c(),()=>window.clearInterval(l)},[t,e]),{status:e,error:o,executeQuery:f,executeQueries:w,createTableFromData:h,getTableInfo:k,listTables:D,isReady:e==="ready",initialize:u}},S=(i,e=[])=>{let{executeQuery:n,isReady:o}=E(),[s,t]=(0,d.useState)(null),[a,u]=(0,d.useState)(!1),[f,w]=(0,d.useState)(null),h=(0,d.useCallback)(async()=>{if(o&&i.trim())try{u(!0),w(null);let k=await n(i);t(k)}catch(k){let D=k instanceof Error?k.message:"Query execution failed";w(D),t(null)}finally{u(!1)}},[n,o,i]);return(0,d.useEffect)(()=>{h()},[h,...e]),{data:s,loading:a,error:f,refetch:h}};var x=i=>!i||typeof i!="object"?[]:"toArray"in i&&typeof i.toArray=="function"?i.toArray():[],P=i=>!i||typeof i!="object"?0:"numRows"in i&&typeof i.numRows=="number"?i.numRows:0,L=i=>!i||typeof i!="object"?0:"numCols"in i&&typeof i.numCols=="number"?i.numCols:0,z=i=>!i||typeof i!="object"?!1:"toArray"in i&&typeof i.toArray=="function"&&"numRows"in i&&typeof i.numRows=="number";0&&(module.exports={DuckDBService,duckDBService,duckdbTableToArray,getDuckDBColumnCount,getDuckDBRowCount,isDuckDBTable,useDuckDB,useDuckDBQuery});
|
|
1
|
+
"use strict";var q=Object.create;var v=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var K=(i,e)=>{for(var n in e)v(i,n,{get:e[n],enumerable:!0})},L=(i,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of O(e))!j.call(i,r)&&r!==n&&v(i,r,{get:()=>e[r],enumerable:!(o=M(e,r))||o.enumerable});return i};var V=(i,e,n)=>(n=i!=null?q(F(i)):{},L(e||!i||!i.__esModule?v(n,"default",{value:i,enumerable:!0}):n,i)),G=i=>L(v({},"__esModule",{value:!0}),i);var Z={};K(Z,{DuckDBService:()=>w,LogLevel:()=>I,Logger:()=>a,duckDBService:()=>S,duckdbTableToArray:()=>A,getDuckDBColumnCount:()=>C,getDuckDBRowCount:()=>W,isDuckDBTable:()=>z,useDuckDB:()=>P,useDuckDBQuery:()=>x});module.exports=G(Z);var u=require("react");var k=V(require("@duckdb/duckdb-wasm"));var I=(t=>(t[t.DEBUG=0]="DEBUG",t[t.INFO=1]="INFO",t[t.WARN=2]="WARN",t[t.ERROR=3]="ERROR",t[t.NONE=4]="NONE",t))(I||{}),d=class d{constructor(e=2,n="[duckdb-helper]",o=console){this.level=e,this.prefix=n,this.impl=o}static setLevel(e){d.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){d.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){d.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){d.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){d.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){d.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){d.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};d.instance=new d(2,"[duckdb-helper]");var a=d;var E=12e4,R=6e4,T=1e3,H=100,Y=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/,B=()=>{},p=class p{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.connectionPromise=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.pendingWorker=null;this.pendingWorkerUrl=null;this.operationQueue=Promise.resolve();this.listeners=new Set}static getInstance(){return p.instance||(p.instance=new p),p.instance}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){a.error("\u26A0\uFE0F Error in status listener:",n)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}enqueue(e){let n=this.operationQueue.then(e,e);return this.operationQueue=n.then(B,B),n}assertNotAborted(e){if(e.aborted)throw new Error("DuckDB initialization aborted")}abortPendingWorker(){if(this.pendingWorker){try{this.pendingWorker.terminate()}catch{}this.pendingWorker=null}if(this.pendingWorkerUrl){try{URL.revokeObjectURL(this.pendingWorkerUrl)}catch{}this.pendingWorkerUrl=null}}async initialize(){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let e={aborted:!1},n,o=new Promise((r,t)=>{n=setTimeout(()=>{e.aborted=!0,t(new Error(`DuckDB initialization timed out after ${E}ms`))},E)});this.initPromise=Promise.race([this.internalInitializeWorker(e),o]);try{let r=await this.initPromise;return clearTimeout(n),this.isInitializing=!1,this.notifyListeners(),r}catch(r){throw clearTimeout(n),e.aborted=!0,this.isInitializing=!1,this.hasError=!0,this.workerInstance=null,this.connectionInstance=null,this.connectionPromise=null,this.initPromise=null,this.abortPendingWorker(),this.notifyListeners(),r}}async internalInitializeWorker(e){let n=null,o=null;try{a.info("\u{1F527} Starting DuckDB Worker initialization..."),a.info("\u{1F4E6} Getting DuckDB bundles from CDN...");let r=k.getJsDelivrBundles();a.info("\u{1F50D} Selecting appropriate bundle for browser...");let t=await k.selectBundle(r);this.assertNotAborted(e),a.info("\u2705 Bundle selected:",{mainWorker:t.mainWorker,mainModule:t.mainModule,pthreadWorker:t.pthreadWorker}),a.info("\u{1F680} Creating Worker with Blob URL..."),n=URL.createObjectURL(new Blob([`importScripts("${t.mainWorker}");`],{type:"text/javascript"})),a.info("\u{1F477} Creating Worker instance..."),o=new Worker(n),this.pendingWorker=o,this.pendingWorkerUrl=n;let c=new k.ConsoleLogger;a.info("\u{1F5C4}\uFE0F Creating DuckDB instance...");let s=new k.AsyncDuckDB(c,o);a.info("\u26A1 Instantiating DuckDB with modules..."),await s.instantiate(t.mainModule,t.pthreadWorker),this.assertNotAborted(e),a.info("\u{1F4E6} Installing splink_udfs from community...");let m=await s.connect();try{await m.query("INSTALL splink_udfs FROM community;"),a.info("\u2705 splink_udfs installed successfully"),await m.query("LOAD splink_udfs;"),a.info("\u2705 splink_udfs loaded successfully")}catch(h){a.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",h)}finally{await m.close()}return this.assertNotAborted(e),this.workerInstance=s,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(n),a.info("\u2705 DuckDB Worker initialized successfully"),s}catch(r){if(o)try{o.terminate()}catch{}if(n)try{URL.revokeObjectURL(n)}catch{}throw this.pendingWorker===o&&(this.pendingWorker=null),this.pendingWorkerUrl===n&&(this.pendingWorkerUrl=null),a.error("\u274C Failed to initialize DuckDB Worker:",r),r instanceof Error&&(a.error("\u274C Error name:",r.name),a.error("\u274C Error message:",r.message),a.error("\u274C Error stack:",r.stack)),r}}async getConnection(){if(this.connectionInstance)return this.connectionInstance;this.connectionPromise||(this.connectionPromise=(async()=>{let n=await(await this.initialize()).connect();return this.connectionInstance=n,n})());try{return await this.connectionPromise}catch(e){throw this.connectionPromise=null,e}}async runQueryWithTimeout(e){let n=!1,o,r=new Promise((c,s)=>{o=setTimeout(()=>{n=!0,s(new Error(`DuckDB query execution timed out after ${R}ms`))},R)}),t=(async()=>(await this.getConnection()).query(e))();try{let c=await Promise.race([t,r]);return clearTimeout(o),c}catch(c){if(clearTimeout(o),n){let s=this.connectionInstance;this.connectionInstance=null,this.connectionPromise=null,s&&s.close().catch(()=>{})}throw a.error("\u274C Failed to execute SQL query:",c),c}}async executeQuery(e){return this.enqueue(()=>this.runQueryWithTimeout(e))}async executeQueries(e){return this.enqueue(async()=>{let n=[];for(let o of e)n.push(await this.runQueryWithTimeout(o));return n})}collectColumnNames(e){let n=new Set,o=[];for(let r of e)if(r)for(let t of Object.keys(r))n.has(t)||(n.add(t),o.push(t));return o}looksLikeTimestamp(e){return Y.test(e)?!Number.isNaN(new Date(e).getTime()):!1}inferColumnTypes(e,n){let o={};for(let r of n){let t=e.map(s=>s?.[r]).filter(s=>s!=null);if(t.length===0){o[r]="VARCHAR";continue}let c={number:0,integer:0,boolean:0,date:0,string:0};for(let s of t)typeof s=="number"?Number.isFinite(s)?(c.number+=1,Number.isInteger(s)&&(c.integer+=1)):c.string+=1:typeof s=="bigint"?(c.number+=1,c.integer+=1):typeof s=="boolean"?c.boolean+=1:s instanceof Date||typeof s=="string"&&this.looksLikeTimestamp(s)?c.date+=1:c.string+=1;c.number===t.length?o[r]=c.integer===t.length?"BIGINT":"DOUBLE":c.boolean===t.length?o[r]="BOOLEAN":c.date===t.length?o[r]="TIMESTAMP":o[r]="VARCHAR"}return o}formatValueForSQL(e){if(e==null)return"NULL";if(typeof e=="string")return`'${e.replace(/'/g,"''")}'`;if(e instanceof Date)return`'${e.toISOString()}'`;if(typeof e=="boolean")return e?"TRUE":"FALSE";if(typeof e=="bigint")return e.toString();if(typeof e=="number")return Number.isFinite(e)?String(e):"NULL";try{let n=JSON.stringify(e);return n===void 0?"NULL":`'${n.replace(/'/g,"''")}'`}catch{return"NULL"}}quoteIdentifier(e){if(typeof e!="string"||e.length===0)throw new Error("Invalid SQL identifier");return`"${e.replace(/"/g,'""')}"`}async createTableFromData(e,n,o={}){if(!n||n.length===0)throw new Error("Data array is empty");let r=`create_table_${e}`,t=o.verbose??!0,c=this.operationFlags.get(r);if(c)return t&&a.info(`\u23F3 Table creation for '${e}' is already in progress, awaiting existing operation...`),c;let s=this.enqueue(()=>this.buildTable(e,n,o,t));this.operationFlags.set(r,s);try{await s}finally{this.operationFlags.delete(r)}}async buildTable(e,n,o,r){try{let t=await this.getConnection(),c=this.quoteIdentifier(e),s=this.collectColumnNames(n);if(s.length===0)throw new Error("Data rows have no columns");let m=Math.min(n.length,H),h=this.inferColumnTypes(n.slice(0,m),s),y=s.map(f=>`${this.quoteIdentifier(f)} ${h[f]}`),D=o.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(o.primaryKey)})`:"",g=`CREATE TABLE ${c} (${y.join(", ")}${D})`,l=s.map(f=>this.quoteIdentifier(f)).join(", ");r&&a.info(`\u{1F4E5} Building table '${e}' with ${n.length} rows`),await t.query("BEGIN TRANSACTION");try{o.dropIfExists&&(r&&a.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await t.query(`DROP TABLE IF EXISTS ${c}`)),r&&a.info("\u{1F528} Creating table with SQL:",g),await t.query(g);for(let f=0;f<n.length;f+=T){let b=n.slice(f,f+T),N=b.map($=>`(${s.map(Q=>this.formatValueForSQL($?.[Q])).join(", ")})`),U=`INSERT INTO ${c} (${l}) VALUES ${N.join(", ")}`;await t.query(U),r&&a.info(`\u{1F4CA} Inserted batch ${Math.floor(f/T)+1} (${b.length} rows)`)}await t.query("COMMIT")}catch(f){try{await t.query("ROLLBACK")}catch(b){a.warn("\u26A0\uFE0F Rollback failed:",b)}throw f}r&&a.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(t){throw a.error(`\u274C Failed to create table '${e}':`,t),t}}async getTableInfo(e){return this.enqueue(async()=>(await this.getConnection()).query(`DESCRIBE ${this.quoteIdentifier(e)}`))}async listTables(){return this.enqueue(async()=>(await this.getConnection()).query("SHOW TABLES"))}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.abortPendingWorker(),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.connectionPromise=null,this.operationQueue=Promise.resolve(),this.operationFlags.clear(),this.notifyListeners(),this.listeners.clear()}catch(e){a.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){p.instance&&(p.instance.cleanup(),p.instance=null)}};p.instance=null;var w=p;var S=w.getInstance();var J=i=>{let e=(0,u.useRef)({deps:i,version:0}),n=e.current.deps;return(n.length!==i.length||i.some((r,t)=>!Object.is(r,n[t])))&&(e.current={deps:i,version:e.current.version+1}),e.current.version},P=(i=!0)=>{let[e,n]=(0,u.useState)("not-initialized"),[o,r]=(0,u.useState)(null),[t]=(0,u.useState)(()=>w.getInstance()),c=(0,u.useCallback)(()=>{let l=t.getStatus();a.info("\u{1F504} DuckDB status updated:",l),n(l),l==="error"?r("DuckDB initialization failed"):l==="ready"&&r(null)},[t]);(0,u.useEffect)(()=>(t.addStatusListener(c),c(),()=>{t.removeStatusListener(c)}),[t,c]);let s=(0,u.useCallback)(async()=>{try{await t.initialize()}catch(l){let f=l instanceof Error?l.message:"Unknown error occurred";r(f),a.error("\u274C DuckDB initialization failed:",l)}},[t]);(0,u.useEffect)(()=>{i&&e==="not-initialized"&&(a.info("\u{1F680} Auto-initializing DuckDB..."),s())},[i,e,s]);let m=(0,u.useCallback)(async l=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQuery(l)},[t,e]),h=(0,u.useCallback)(async l=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQueries(l)},[t,e]),y=(0,u.useCallback)(async(l,f,b)=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.createTableFromData(l,f,b)},[t,e]),D=(0,u.useCallback)(async l=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.getTableInfo(l)},[t,e]),g=(0,u.useCallback)(async()=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.listTables()},[t,e]);return{status:e,error:o,executeQuery:m,executeQueries:h,createTableFromData:y,getTableInfo:D,listTables:g,isReady:e==="ready",initialize:s}},x=(i,e=[])=>{let{executeQuery:n,isReady:o}=P(),[r,t]=(0,u.useState)(null),[c,s]=(0,u.useState)(!1),[m,h]=(0,u.useState)(null),y=(0,u.useCallback)(async()=>{if(o&&i.trim())try{s(!0),h(null);let g=await n(i);t(g)}catch(g){let l=g instanceof Error?g.message:"Query execution failed";h(l),t(null)}finally{s(!1)}},[n,o,i]),D=J(e);return(0,u.useEffect)(()=>{y()},[y,D]),{data:r,loading:c,error:m,refetch:y}};var A=i=>!i||typeof i!="object"?[]:"toArray"in i&&typeof i.toArray=="function"?i.toArray():[],W=i=>!i||typeof i!="object"?0:"numRows"in i&&typeof i.numRows=="number"?i.numRows:0,C=i=>!i||typeof i!="object"?0:"numCols"in i&&typeof i.numCols=="number"?i.numCols:0,z=i=>!i||typeof i!="object"?!1:"toArray"in i&&typeof i.toArray=="function"&&"numRows"in i&&typeof i.numRows=="number";0&&(module.exports={DuckDBService,LogLevel,Logger,duckDBService,duckdbTableToArray,getDuckDBColumnCount,getDuckDBRowCount,isDuckDBTable,useDuckDB,useDuckDBQuery});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|