@aiquants/duckdb-helper 1.6.8 → 1.7.1
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 +2 -295
- package/dist/index.d.ts +2 -295
- package/dist/index.js +0 -1
- package/dist/index.mjs +0 -1
- package/package.json +2 -2
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,39 +1,14 @@
|
|
|
1
|
-
import { DependencyList } from
|
|
2
|
-
import * as duckdb from
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* DuckDB Web Worker Service
|
|
6
|
-
*
|
|
7
|
-
* Provides reusable DuckDB-wasm functionality across the application.
|
|
8
|
-
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Options for opening a persistent database (e.g. OPFS-backed).
|
|
13
|
-
* 永続データベース (OPFS など) を開くためのオプション。
|
|
14
|
-
*/
|
|
1
|
+
import { DependencyList } from "react";
|
|
2
|
+
import * as duckdb from "@duckdb/duckdb-wasm";
|
|
15
3
|
interface DuckDBDatabaseOptions {
|
|
16
|
-
/** Database path. Use an `opfs://` path for a persistent OPFS-backed database. `opfs://` パスで OPFS 永続 DB を開く。 */
|
|
17
4
|
path: string;
|
|
18
|
-
/** Access mode (defaults to READ_WRITE). アクセスモード (既定 READ_WRITE)。 */
|
|
19
5
|
accessMode?: duckdb.DuckDBAccessMode;
|
|
20
|
-
/** How `opfs://` files are handled during SQL execution (defaults to "auto"). SQL 実行時の `opfs://` ファイル登録の扱い (既定 "auto")。 */
|
|
21
6
|
opfsFileHandling?: "auto" | "manual";
|
|
22
7
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Options accepted by {@link DuckDBService.initialize}.
|
|
25
|
-
* initialize() が受け取るオプション。
|
|
26
|
-
*/
|
|
27
8
|
interface DuckDBInitializeOptions {
|
|
28
|
-
/** Custom bundles (defaults to jsDelivr bundles). カスタムバンドル (既定は jsDelivr)。 */
|
|
29
9
|
bundles?: duckdb.DuckDBBundles;
|
|
30
|
-
/** Persistent database to open after instantiation. インスタンス化後に開く永続データベース。 */
|
|
31
10
|
database?: DuckDBDatabaseOptions;
|
|
32
11
|
}
|
|
33
|
-
/**
|
|
34
|
-
* DuckDB service for browser-based SQL operations.
|
|
35
|
-
* ブラウザベースの SQL 操作のための DuckDB サービス。
|
|
36
|
-
*/
|
|
37
12
|
declare class DuckDBService {
|
|
38
13
|
private static instance;
|
|
39
14
|
private operationFlags;
|
|
@@ -43,165 +18,43 @@ declare class DuckDBService {
|
|
|
43
18
|
private isInitializing;
|
|
44
19
|
private initPromise;
|
|
45
20
|
private hasError;
|
|
46
|
-
/** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */
|
|
47
21
|
private pendingWorker;
|
|
48
22
|
private pendingWorkerUrl;
|
|
49
|
-
/**
|
|
50
|
-
* 共有コネクション上での操作直列化キュー。
|
|
51
|
-
* createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。
|
|
52
|
-
*/
|
|
53
23
|
private operationQueue;
|
|
54
24
|
private listeners;
|
|
55
|
-
/**
|
|
56
|
-
* Get singleton instance of DuckDBService.
|
|
57
|
-
* DuckDBService のシングルトンインスタンスを取得します。
|
|
58
|
-
*/
|
|
59
25
|
static getInstance(): DuckDBService;
|
|
60
|
-
/**
|
|
61
|
-
* Creates a dedicated (non-shared) service instance, e.g. for a persistent OPFS database.
|
|
62
|
-
* 専用 (非共有) のサービスインスタンスを生成します (OPFS 永続 DB を共有シングルトンと分離して持つ用途)。
|
|
63
|
-
*/
|
|
64
26
|
static create(): DuckDBService;
|
|
65
|
-
/**
|
|
66
|
-
* Add status change listener.
|
|
67
|
-
* 状態変更リスナーを追加します。
|
|
68
|
-
*/
|
|
69
27
|
addStatusListener(listener: () => void): void;
|
|
70
|
-
/**
|
|
71
|
-
* Remove status change listener.
|
|
72
|
-
* 状態変更リスナーを削除します。
|
|
73
|
-
*/
|
|
74
28
|
removeStatusListener(listener: () => void): void;
|
|
75
|
-
/**
|
|
76
|
-
* Notify all listeners of status change.
|
|
77
|
-
* すべてのリスナーに状態変更を通知します。
|
|
78
|
-
*/
|
|
79
29
|
private notifyListeners;
|
|
80
|
-
/**
|
|
81
|
-
* Get current status.
|
|
82
|
-
* 現在のステータスを取得します。
|
|
83
|
-
*/
|
|
84
30
|
getStatus(): "not-initialized" | "initializing" | "ready" | "error";
|
|
85
|
-
/** Enqueue a task on the single-connection serialization queue. */
|
|
86
31
|
private enqueue;
|
|
87
|
-
/** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */
|
|
88
32
|
private assertNotAborted;
|
|
89
|
-
/** 初期化中の Worker / Blob URL を確実に破棄する。 */
|
|
90
33
|
private abortPendingWorker;
|
|
91
|
-
/**
|
|
92
|
-
* Initialize DuckDB Worker instance.
|
|
93
|
-
* DuckDB Worker インスタンスを初期化します。
|
|
94
|
-
*/
|
|
95
34
|
initialize(options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB>;
|
|
96
|
-
/**
|
|
97
|
-
* Internal worker initialization method.
|
|
98
|
-
* 内部的な Worker 初期化メソッド。
|
|
99
|
-
*
|
|
100
|
-
* workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は
|
|
101
|
-
* ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。
|
|
102
|
-
*/
|
|
103
35
|
private internalInitializeWorker;
|
|
104
|
-
/**
|
|
105
|
-
* Get or create DuckDB connection.
|
|
106
|
-
* DuckDB 接続を取得または作成します。
|
|
107
|
-
*
|
|
108
|
-
* 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。
|
|
109
|
-
*/
|
|
110
36
|
getConnection(): Promise<duckdb.AsyncDuckDBConnection>;
|
|
111
|
-
/**
|
|
112
|
-
* Run a single query with a timeout, discarding the connection on timeout.
|
|
113
|
-
* 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。
|
|
114
|
-
*
|
|
115
|
-
* タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して
|
|
116
|
-
* 後続クエリが同じ(詰まった)コネクションで待たされないようにする。
|
|
117
|
-
*/
|
|
118
37
|
private runQueryWithTimeout;
|
|
119
|
-
/**
|
|
120
|
-
* Execute SQL query using DuckDB Worker.
|
|
121
|
-
* DuckDB Worker を使用して SQL クエリを実行します。
|
|
122
|
-
*/
|
|
123
38
|
executeQuery(sql: string): Promise<unknown>;
|
|
124
|
-
/**
|
|
125
|
-
* Execute multiple SQL queries.
|
|
126
|
-
* 複数の SQL クエリを実行します(逐次実行)。
|
|
127
|
-
*
|
|
128
|
-
* バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。
|
|
129
|
-
*/
|
|
130
39
|
executeQueries(queries: string[]): Promise<unknown[]>;
|
|
131
|
-
/**
|
|
132
|
-
* Collect the union of column names across all rows (first-seen order).
|
|
133
|
-
* 全行を走査して列名の和集合を取得します(初出順を保持)。
|
|
134
|
-
* 先頭行に存在しないキーを取りこぼさないため全行を対象とする。
|
|
135
|
-
*/
|
|
136
40
|
private collectColumnNames;
|
|
137
|
-
/** Check whether a string is a genuine (strict) date/timestamp literal. */
|
|
138
41
|
private looksLikeTimestamp;
|
|
139
|
-
/**
|
|
140
|
-
* Infer column types from a data sample for the given columns.
|
|
141
|
-
* 指定した列について、データサンプルから DuckDB の型を推定します。
|
|
142
|
-
*
|
|
143
|
-
* 整数は桁あふれを避けるため一律 BIGINT を採用する。
|
|
144
|
-
*/
|
|
145
42
|
private inferColumnTypes;
|
|
146
|
-
/**
|
|
147
|
-
* Format value for SQL insertion.
|
|
148
|
-
* SQL 挿入用に値をフォーマットします。
|
|
149
|
-
*/
|
|
150
43
|
private formatValueForSQL;
|
|
151
|
-
/**
|
|
152
|
-
* Quote a SQL identifier (table / column / primary key) safely.
|
|
153
|
-
* SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。
|
|
154
|
-
*/
|
|
155
44
|
private quoteIdentifier;
|
|
156
|
-
/**
|
|
157
|
-
* Create table from JavaScript array of objects.
|
|
158
|
-
* JavaScript オブジェクトの配列からテーブルを作成します。
|
|
159
|
-
*
|
|
160
|
-
* 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。
|
|
161
|
-
*/
|
|
162
45
|
createTableFromData<T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
163
46
|
dropIfExists?: boolean;
|
|
164
47
|
primaryKey?: string;
|
|
165
48
|
verbose?: boolean;
|
|
166
49
|
}): Promise<void>;
|
|
167
|
-
/** Actual (serialized, transactional) table build. */
|
|
168
50
|
private buildTable;
|
|
169
|
-
/**
|
|
170
|
-
* Get table information.
|
|
171
|
-
* テーブル情報を取得します。
|
|
172
|
-
*/
|
|
173
51
|
getTableInfo(tableName: string): Promise<unknown>;
|
|
174
|
-
/**
|
|
175
|
-
* List all tables.
|
|
176
|
-
* すべてのテーブルを一覧表示します。
|
|
177
|
-
*/
|
|
178
52
|
listTables(): Promise<unknown>;
|
|
179
|
-
/**
|
|
180
|
-
* Check if DuckDB is ready.
|
|
181
|
-
* DuckDB が準備完了かどうかを確認します。
|
|
182
|
-
*/
|
|
183
53
|
isReady(): boolean;
|
|
184
|
-
/**
|
|
185
|
-
* Cleanup DuckDB Worker resources.
|
|
186
|
-
* DuckDB Worker のリソースをクリーンアップします。
|
|
187
|
-
*/
|
|
188
54
|
cleanup(): Promise<void>;
|
|
189
|
-
/**
|
|
190
|
-
* Reset the service instance (useful for testing).
|
|
191
|
-
* サービスインスタンスをリセットします(テスト用)。
|
|
192
|
-
*/
|
|
193
55
|
static resetInstance(): void;
|
|
194
56
|
}
|
|
195
|
-
|
|
196
57
|
declare const _default: DuckDBService;
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* DuckDB React Hook
|
|
200
|
-
*
|
|
201
|
-
* React hook for using DuckDB functionality in components.
|
|
202
|
-
* コンポーネントで DuckDB 機能を使用するための React フック。
|
|
203
|
-
*/
|
|
204
|
-
|
|
205
58
|
interface UseDuckDBResult {
|
|
206
59
|
status: "not-initialized" | "initializing" | "ready" | "error";
|
|
207
60
|
error: string | null;
|
|
@@ -217,117 +70,24 @@ interface UseDuckDBResult {
|
|
|
217
70
|
isReady: boolean;
|
|
218
71
|
initialize: () => Promise<void>;
|
|
219
72
|
}
|
|
220
|
-
/**
|
|
221
|
-
* DuckDB functionality hook.
|
|
222
|
-
* DuckDB 機能を提供するフック。
|
|
223
|
-
*
|
|
224
|
-
* @param autoInitialize - 自動初期化を行うかどうか
|
|
225
|
-
* @returns DuckDB の状態と操作関数
|
|
226
|
-
*/
|
|
227
73
|
declare const useDuckDB: (autoInitialize?: boolean, options?: DuckDBInitializeOptions) => UseDuckDBResult;
|
|
228
|
-
/**
|
|
229
|
-
* Hook for simple DuckDB query execution.
|
|
230
|
-
* シンプルな DuckDB クエリ実行のためのフック。
|
|
231
|
-
*
|
|
232
|
-
* @remarks
|
|
233
|
-
* `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。
|
|
234
|
-
* `.length` や `.map` は持たないため、行配列として扱う場合は必ず
|
|
235
|
-
* {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく
|
|
236
|
-
* Arrow Table 相当の型を想定)。
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* ```tsx
|
|
240
|
-
* const { data } = useDuckDBQuery("SELECT * FROM t")
|
|
241
|
-
* const rows = duckdbTableToArray<Row>(data)
|
|
242
|
-
* ```
|
|
243
|
-
*
|
|
244
|
-
* @param sql - 実行する SQL クエリ
|
|
245
|
-
* @param dependencies - クエリを再実行するための依存配列
|
|
246
|
-
* @returns クエリ結果(生の Arrow Table)と状態
|
|
247
|
-
*/
|
|
248
74
|
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
249
75
|
data: T | null;
|
|
250
76
|
loading: boolean;
|
|
251
77
|
error: string | null;
|
|
252
78
|
refetch: () => Promise<void>;
|
|
253
79
|
};
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* DuckDB Type Definitions
|
|
257
|
-
*
|
|
258
|
-
* Type definitions for DuckDB query results and data structures.
|
|
259
|
-
* DuckDB クエリ結果とデータ構造の型定義。
|
|
260
|
-
*/
|
|
261
|
-
/**
|
|
262
|
-
* DuckDB query result type (Apache Arrow Table)
|
|
263
|
-
* DuckDB クエリ結果の型(Apache Arrow テーブル)
|
|
264
|
-
*/
|
|
265
80
|
interface DuckDBQueryResult<T = unknown> {
|
|
266
81
|
toArray(): T[];
|
|
267
82
|
numRows: number;
|
|
268
83
|
numCols: number;
|
|
269
84
|
}
|
|
270
|
-
/**
|
|
271
|
-
* Helper type to extract row data from DuckDB table
|
|
272
|
-
* DuckDB テーブルから行データを抽出するヘルパー型
|
|
273
|
-
*/
|
|
274
85
|
type DuckDBRow<T> = T;
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* DuckDB Helper Functions
|
|
278
|
-
*
|
|
279
|
-
* Utility functions for working with DuckDB query results.
|
|
280
|
-
* DuckDB クエリ結果を扱うためのユーティリティ関数。
|
|
281
|
-
*/
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Safely convert DuckDB table to typed array
|
|
285
|
-
* DuckDB テーブルを型付き配列に安全に変換します
|
|
286
|
-
*
|
|
287
|
-
* @param result - DuckDB query result
|
|
288
|
-
* @returns Typed array of results
|
|
289
|
-
*/
|
|
290
86
|
declare const duckdbTableToArray: <T>(result: unknown) => T[];
|
|
291
|
-
/**
|
|
292
|
-
* Get table row count
|
|
293
|
-
* テーブルの行数を取得します
|
|
294
|
-
*
|
|
295
|
-
* @param result - DuckDB query result
|
|
296
|
-
* @returns Number of rows in the table
|
|
297
|
-
*/
|
|
298
87
|
declare const getDuckDBRowCount: (result: unknown) => number;
|
|
299
|
-
/**
|
|
300
|
-
* Get table column count
|
|
301
|
-
* テーブルの列数を取得します
|
|
302
|
-
*
|
|
303
|
-
* @param result - DuckDB query result
|
|
304
|
-
* @returns Number of columns in the table
|
|
305
|
-
*/
|
|
306
88
|
declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
307
|
-
/**
|
|
308
|
-
* Check if result is a valid DuckDB table
|
|
309
|
-
* 結果が有効な DuckDB テーブルかどうかをチェックします
|
|
310
|
-
*
|
|
311
|
-
* @param result - DuckDB query result
|
|
312
|
-
* @returns True if result is a valid DuckDB table
|
|
313
|
-
*/
|
|
314
89
|
declare const isDuckDBTable: (result: unknown) => result is DuckDBQueryResult;
|
|
315
|
-
/**
|
|
316
|
-
* Returns whether the current environment can host an OPFS-backed persistent database.
|
|
317
|
-
* 現在の環境が OPFS 永続データベースを利用できるか (navigator.storage.getDirectory の有無) を返します。
|
|
318
|
-
*/
|
|
319
90
|
declare const isOpfsSupported: () => boolean;
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* @module utils/logger
|
|
323
|
-
* @description Logger utility for consistent log management across the library.
|
|
324
|
-
* @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。
|
|
325
|
-
*/
|
|
326
|
-
/**
|
|
327
|
-
* @enum LogLevel
|
|
328
|
-
* @description Log levels for filtering output.
|
|
329
|
-
* @description 出力をフィルタリングするためのログレベル。
|
|
330
|
-
*/
|
|
331
91
|
declare enum LogLevel {
|
|
332
92
|
DEBUG = 0,
|
|
333
93
|
INFO = 1,
|
|
@@ -335,75 +95,23 @@ declare enum LogLevel {
|
|
|
335
95
|
ERROR = 3,
|
|
336
96
|
NONE = 4
|
|
337
97
|
}
|
|
338
|
-
/**
|
|
339
|
-
* @interface ILogger
|
|
340
|
-
* @description Interface for a logger object compatible with Console.
|
|
341
|
-
* @description Console と互換性のあるロガーオブジェクトのインターフェース。
|
|
342
|
-
*/
|
|
343
98
|
interface ILogger {
|
|
344
99
|
debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
345
100
|
info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
346
101
|
warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
347
102
|
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
348
103
|
}
|
|
349
|
-
/**
|
|
350
|
-
* @class Logger
|
|
351
|
-
* @description A wrapper class for handling logging with levels and prefixes.
|
|
352
|
-
* @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。
|
|
353
|
-
*/
|
|
354
104
|
declare class Logger implements ILogger {
|
|
355
105
|
private level;
|
|
356
106
|
private prefix;
|
|
357
107
|
private impl;
|
|
358
|
-
/**
|
|
359
|
-
* @constructor
|
|
360
|
-
* @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.
|
|
361
|
-
* @param {string} [prefix="[duckdb-helper]"] - The prefix to add to all log messages.
|
|
362
|
-
* @param {ILogger} [impl=console] - The implementation to use for logging.
|
|
363
|
-
*/
|
|
364
108
|
constructor(level?: LogLevel, prefix?: string, impl?: ILogger);
|
|
365
109
|
private static instance;
|
|
366
|
-
/**
|
|
367
|
-
* @method setLevel
|
|
368
|
-
* @description Updates the current log level for the static instance.
|
|
369
|
-
* @description 静的インスタンスの現在のログレベルを更新します。
|
|
370
|
-
* @param {LogLevel} level - The new log level.
|
|
371
|
-
*/
|
|
372
110
|
static setLevel(level: LogLevel): void;
|
|
373
|
-
/**
|
|
374
|
-
* @method setLevel
|
|
375
|
-
* @description Updates the current log level.
|
|
376
|
-
* @description 現在のログレベルを更新します。
|
|
377
|
-
* @param {LogLevel} level - The new log level.
|
|
378
|
-
*/
|
|
379
111
|
setLevel(level: LogLevel): void;
|
|
380
|
-
/**
|
|
381
|
-
* @method setImplementation
|
|
382
|
-
* @description Updates the logger implementation for the static instance.
|
|
383
|
-
* @description 静的インスタンスのロガーの実装を更新します。
|
|
384
|
-
* @param {ILogger} impl - The new logger implementation.
|
|
385
|
-
*/
|
|
386
112
|
static setImplementation(impl: ILogger): void;
|
|
387
|
-
/**
|
|
388
|
-
* @method setImplementation
|
|
389
|
-
* @description Updates the logger implementation.
|
|
390
|
-
* @description ロガーの実装を更新します。
|
|
391
|
-
* @param {ILogger} impl - The new logger implementation.
|
|
392
|
-
*/
|
|
393
113
|
setImplementation(impl: ILogger): void;
|
|
394
|
-
/**
|
|
395
|
-
* @method setPrefix
|
|
396
|
-
* @description Updates the log prefix for the static instance.
|
|
397
|
-
* @description 静的インスタンスのログのプレフィックスを更新します。
|
|
398
|
-
* @param {string} prefix - The new prefix.
|
|
399
|
-
*/
|
|
400
114
|
static setPrefix(prefix: string): void;
|
|
401
|
-
/**
|
|
402
|
-
* @method setPrefix
|
|
403
|
-
* @description Updates the log prefix.
|
|
404
|
-
* @description ログのプレフィックスを更新します。
|
|
405
|
-
* @param {string} prefix - The new prefix.
|
|
406
|
-
*/
|
|
407
115
|
setPrefix(prefix: string): void;
|
|
408
116
|
private formatMessage;
|
|
409
117
|
static debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
@@ -415,5 +123,4 @@ declare class Logger implements ILogger {
|
|
|
415
123
|
static error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
416
124
|
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
417
125
|
}
|
|
418
|
-
|
|
419
126
|
export { type DuckDBDatabaseOptions, type DuckDBInitializeOptions, type DuckDBQueryResult, type DuckDBRow, DuckDBService, type ILogger, LogLevel, Logger, type UseDuckDBResult, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, isOpfsSupported, useDuckDB, useDuckDBQuery };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,39 +1,14 @@
|
|
|
1
|
-
import { DependencyList } from
|
|
2
|
-
import * as duckdb from
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* DuckDB Web Worker Service
|
|
6
|
-
*
|
|
7
|
-
* Provides reusable DuckDB-wasm functionality across the application.
|
|
8
|
-
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Options for opening a persistent database (e.g. OPFS-backed).
|
|
13
|
-
* 永続データベース (OPFS など) を開くためのオプション。
|
|
14
|
-
*/
|
|
1
|
+
import { DependencyList } from "react";
|
|
2
|
+
import * as duckdb from "@duckdb/duckdb-wasm";
|
|
15
3
|
interface DuckDBDatabaseOptions {
|
|
16
|
-
/** Database path. Use an `opfs://` path for a persistent OPFS-backed database. `opfs://` パスで OPFS 永続 DB を開く。 */
|
|
17
4
|
path: string;
|
|
18
|
-
/** Access mode (defaults to READ_WRITE). アクセスモード (既定 READ_WRITE)。 */
|
|
19
5
|
accessMode?: duckdb.DuckDBAccessMode;
|
|
20
|
-
/** How `opfs://` files are handled during SQL execution (defaults to "auto"). SQL 実行時の `opfs://` ファイル登録の扱い (既定 "auto")。 */
|
|
21
6
|
opfsFileHandling?: "auto" | "manual";
|
|
22
7
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Options accepted by {@link DuckDBService.initialize}.
|
|
25
|
-
* initialize() が受け取るオプション。
|
|
26
|
-
*/
|
|
27
8
|
interface DuckDBInitializeOptions {
|
|
28
|
-
/** Custom bundles (defaults to jsDelivr bundles). カスタムバンドル (既定は jsDelivr)。 */
|
|
29
9
|
bundles?: duckdb.DuckDBBundles;
|
|
30
|
-
/** Persistent database to open after instantiation. インスタンス化後に開く永続データベース。 */
|
|
31
10
|
database?: DuckDBDatabaseOptions;
|
|
32
11
|
}
|
|
33
|
-
/**
|
|
34
|
-
* DuckDB service for browser-based SQL operations.
|
|
35
|
-
* ブラウザベースの SQL 操作のための DuckDB サービス。
|
|
36
|
-
*/
|
|
37
12
|
declare class DuckDBService {
|
|
38
13
|
private static instance;
|
|
39
14
|
private operationFlags;
|
|
@@ -43,165 +18,43 @@ declare class DuckDBService {
|
|
|
43
18
|
private isInitializing;
|
|
44
19
|
private initPromise;
|
|
45
20
|
private hasError;
|
|
46
|
-
/** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */
|
|
47
21
|
private pendingWorker;
|
|
48
22
|
private pendingWorkerUrl;
|
|
49
|
-
/**
|
|
50
|
-
* 共有コネクション上での操作直列化キュー。
|
|
51
|
-
* createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。
|
|
52
|
-
*/
|
|
53
23
|
private operationQueue;
|
|
54
24
|
private listeners;
|
|
55
|
-
/**
|
|
56
|
-
* Get singleton instance of DuckDBService.
|
|
57
|
-
* DuckDBService のシングルトンインスタンスを取得します。
|
|
58
|
-
*/
|
|
59
25
|
static getInstance(): DuckDBService;
|
|
60
|
-
/**
|
|
61
|
-
* Creates a dedicated (non-shared) service instance, e.g. for a persistent OPFS database.
|
|
62
|
-
* 専用 (非共有) のサービスインスタンスを生成します (OPFS 永続 DB を共有シングルトンと分離して持つ用途)。
|
|
63
|
-
*/
|
|
64
26
|
static create(): DuckDBService;
|
|
65
|
-
/**
|
|
66
|
-
* Add status change listener.
|
|
67
|
-
* 状態変更リスナーを追加します。
|
|
68
|
-
*/
|
|
69
27
|
addStatusListener(listener: () => void): void;
|
|
70
|
-
/**
|
|
71
|
-
* Remove status change listener.
|
|
72
|
-
* 状態変更リスナーを削除します。
|
|
73
|
-
*/
|
|
74
28
|
removeStatusListener(listener: () => void): void;
|
|
75
|
-
/**
|
|
76
|
-
* Notify all listeners of status change.
|
|
77
|
-
* すべてのリスナーに状態変更を通知します。
|
|
78
|
-
*/
|
|
79
29
|
private notifyListeners;
|
|
80
|
-
/**
|
|
81
|
-
* Get current status.
|
|
82
|
-
* 現在のステータスを取得します。
|
|
83
|
-
*/
|
|
84
30
|
getStatus(): "not-initialized" | "initializing" | "ready" | "error";
|
|
85
|
-
/** Enqueue a task on the single-connection serialization queue. */
|
|
86
31
|
private enqueue;
|
|
87
|
-
/** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */
|
|
88
32
|
private assertNotAborted;
|
|
89
|
-
/** 初期化中の Worker / Blob URL を確実に破棄する。 */
|
|
90
33
|
private abortPendingWorker;
|
|
91
|
-
/**
|
|
92
|
-
* Initialize DuckDB Worker instance.
|
|
93
|
-
* DuckDB Worker インスタンスを初期化します。
|
|
94
|
-
*/
|
|
95
34
|
initialize(options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB>;
|
|
96
|
-
/**
|
|
97
|
-
* Internal worker initialization method.
|
|
98
|
-
* 内部的な Worker 初期化メソッド。
|
|
99
|
-
*
|
|
100
|
-
* workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は
|
|
101
|
-
* ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。
|
|
102
|
-
*/
|
|
103
35
|
private internalInitializeWorker;
|
|
104
|
-
/**
|
|
105
|
-
* Get or create DuckDB connection.
|
|
106
|
-
* DuckDB 接続を取得または作成します。
|
|
107
|
-
*
|
|
108
|
-
* 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。
|
|
109
|
-
*/
|
|
110
36
|
getConnection(): Promise<duckdb.AsyncDuckDBConnection>;
|
|
111
|
-
/**
|
|
112
|
-
* Run a single query with a timeout, discarding the connection on timeout.
|
|
113
|
-
* 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。
|
|
114
|
-
*
|
|
115
|
-
* タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して
|
|
116
|
-
* 後続クエリが同じ(詰まった)コネクションで待たされないようにする。
|
|
117
|
-
*/
|
|
118
37
|
private runQueryWithTimeout;
|
|
119
|
-
/**
|
|
120
|
-
* Execute SQL query using DuckDB Worker.
|
|
121
|
-
* DuckDB Worker を使用して SQL クエリを実行します。
|
|
122
|
-
*/
|
|
123
38
|
executeQuery(sql: string): Promise<unknown>;
|
|
124
|
-
/**
|
|
125
|
-
* Execute multiple SQL queries.
|
|
126
|
-
* 複数の SQL クエリを実行します(逐次実行)。
|
|
127
|
-
*
|
|
128
|
-
* バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。
|
|
129
|
-
*/
|
|
130
39
|
executeQueries(queries: string[]): Promise<unknown[]>;
|
|
131
|
-
/**
|
|
132
|
-
* Collect the union of column names across all rows (first-seen order).
|
|
133
|
-
* 全行を走査して列名の和集合を取得します(初出順を保持)。
|
|
134
|
-
* 先頭行に存在しないキーを取りこぼさないため全行を対象とする。
|
|
135
|
-
*/
|
|
136
40
|
private collectColumnNames;
|
|
137
|
-
/** Check whether a string is a genuine (strict) date/timestamp literal. */
|
|
138
41
|
private looksLikeTimestamp;
|
|
139
|
-
/**
|
|
140
|
-
* Infer column types from a data sample for the given columns.
|
|
141
|
-
* 指定した列について、データサンプルから DuckDB の型を推定します。
|
|
142
|
-
*
|
|
143
|
-
* 整数は桁あふれを避けるため一律 BIGINT を採用する。
|
|
144
|
-
*/
|
|
145
42
|
private inferColumnTypes;
|
|
146
|
-
/**
|
|
147
|
-
* Format value for SQL insertion.
|
|
148
|
-
* SQL 挿入用に値をフォーマットします。
|
|
149
|
-
*/
|
|
150
43
|
private formatValueForSQL;
|
|
151
|
-
/**
|
|
152
|
-
* Quote a SQL identifier (table / column / primary key) safely.
|
|
153
|
-
* SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。
|
|
154
|
-
*/
|
|
155
44
|
private quoteIdentifier;
|
|
156
|
-
/**
|
|
157
|
-
* Create table from JavaScript array of objects.
|
|
158
|
-
* JavaScript オブジェクトの配列からテーブルを作成します。
|
|
159
|
-
*
|
|
160
|
-
* 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。
|
|
161
|
-
*/
|
|
162
45
|
createTableFromData<T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
163
46
|
dropIfExists?: boolean;
|
|
164
47
|
primaryKey?: string;
|
|
165
48
|
verbose?: boolean;
|
|
166
49
|
}): Promise<void>;
|
|
167
|
-
/** Actual (serialized, transactional) table build. */
|
|
168
50
|
private buildTable;
|
|
169
|
-
/**
|
|
170
|
-
* Get table information.
|
|
171
|
-
* テーブル情報を取得します。
|
|
172
|
-
*/
|
|
173
51
|
getTableInfo(tableName: string): Promise<unknown>;
|
|
174
|
-
/**
|
|
175
|
-
* List all tables.
|
|
176
|
-
* すべてのテーブルを一覧表示します。
|
|
177
|
-
*/
|
|
178
52
|
listTables(): Promise<unknown>;
|
|
179
|
-
/**
|
|
180
|
-
* Check if DuckDB is ready.
|
|
181
|
-
* DuckDB が準備完了かどうかを確認します。
|
|
182
|
-
*/
|
|
183
53
|
isReady(): boolean;
|
|
184
|
-
/**
|
|
185
|
-
* Cleanup DuckDB Worker resources.
|
|
186
|
-
* DuckDB Worker のリソースをクリーンアップします。
|
|
187
|
-
*/
|
|
188
54
|
cleanup(): Promise<void>;
|
|
189
|
-
/**
|
|
190
|
-
* Reset the service instance (useful for testing).
|
|
191
|
-
* サービスインスタンスをリセットします(テスト用)。
|
|
192
|
-
*/
|
|
193
55
|
static resetInstance(): void;
|
|
194
56
|
}
|
|
195
|
-
|
|
196
57
|
declare const _default: DuckDBService;
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* DuckDB React Hook
|
|
200
|
-
*
|
|
201
|
-
* React hook for using DuckDB functionality in components.
|
|
202
|
-
* コンポーネントで DuckDB 機能を使用するための React フック。
|
|
203
|
-
*/
|
|
204
|
-
|
|
205
58
|
interface UseDuckDBResult {
|
|
206
59
|
status: "not-initialized" | "initializing" | "ready" | "error";
|
|
207
60
|
error: string | null;
|
|
@@ -217,117 +70,24 @@ interface UseDuckDBResult {
|
|
|
217
70
|
isReady: boolean;
|
|
218
71
|
initialize: () => Promise<void>;
|
|
219
72
|
}
|
|
220
|
-
/**
|
|
221
|
-
* DuckDB functionality hook.
|
|
222
|
-
* DuckDB 機能を提供するフック。
|
|
223
|
-
*
|
|
224
|
-
* @param autoInitialize - 自動初期化を行うかどうか
|
|
225
|
-
* @returns DuckDB の状態と操作関数
|
|
226
|
-
*/
|
|
227
73
|
declare const useDuckDB: (autoInitialize?: boolean, options?: DuckDBInitializeOptions) => UseDuckDBResult;
|
|
228
|
-
/**
|
|
229
|
-
* Hook for simple DuckDB query execution.
|
|
230
|
-
* シンプルな DuckDB クエリ実行のためのフック。
|
|
231
|
-
*
|
|
232
|
-
* @remarks
|
|
233
|
-
* `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。
|
|
234
|
-
* `.length` や `.map` は持たないため、行配列として扱う場合は必ず
|
|
235
|
-
* {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく
|
|
236
|
-
* Arrow Table 相当の型を想定)。
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* ```tsx
|
|
240
|
-
* const { data } = useDuckDBQuery("SELECT * FROM t")
|
|
241
|
-
* const rows = duckdbTableToArray<Row>(data)
|
|
242
|
-
* ```
|
|
243
|
-
*
|
|
244
|
-
* @param sql - 実行する SQL クエリ
|
|
245
|
-
* @param dependencies - クエリを再実行するための依存配列
|
|
246
|
-
* @returns クエリ結果(生の Arrow Table)と状態
|
|
247
|
-
*/
|
|
248
74
|
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
249
75
|
data: T | null;
|
|
250
76
|
loading: boolean;
|
|
251
77
|
error: string | null;
|
|
252
78
|
refetch: () => Promise<void>;
|
|
253
79
|
};
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* DuckDB Type Definitions
|
|
257
|
-
*
|
|
258
|
-
* Type definitions for DuckDB query results and data structures.
|
|
259
|
-
* DuckDB クエリ結果とデータ構造の型定義。
|
|
260
|
-
*/
|
|
261
|
-
/**
|
|
262
|
-
* DuckDB query result type (Apache Arrow Table)
|
|
263
|
-
* DuckDB クエリ結果の型(Apache Arrow テーブル)
|
|
264
|
-
*/
|
|
265
80
|
interface DuckDBQueryResult<T = unknown> {
|
|
266
81
|
toArray(): T[];
|
|
267
82
|
numRows: number;
|
|
268
83
|
numCols: number;
|
|
269
84
|
}
|
|
270
|
-
/**
|
|
271
|
-
* Helper type to extract row data from DuckDB table
|
|
272
|
-
* DuckDB テーブルから行データを抽出するヘルパー型
|
|
273
|
-
*/
|
|
274
85
|
type DuckDBRow<T> = T;
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* DuckDB Helper Functions
|
|
278
|
-
*
|
|
279
|
-
* Utility functions for working with DuckDB query results.
|
|
280
|
-
* DuckDB クエリ結果を扱うためのユーティリティ関数。
|
|
281
|
-
*/
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Safely convert DuckDB table to typed array
|
|
285
|
-
* DuckDB テーブルを型付き配列に安全に変換します
|
|
286
|
-
*
|
|
287
|
-
* @param result - DuckDB query result
|
|
288
|
-
* @returns Typed array of results
|
|
289
|
-
*/
|
|
290
86
|
declare const duckdbTableToArray: <T>(result: unknown) => T[];
|
|
291
|
-
/**
|
|
292
|
-
* Get table row count
|
|
293
|
-
* テーブルの行数を取得します
|
|
294
|
-
*
|
|
295
|
-
* @param result - DuckDB query result
|
|
296
|
-
* @returns Number of rows in the table
|
|
297
|
-
*/
|
|
298
87
|
declare const getDuckDBRowCount: (result: unknown) => number;
|
|
299
|
-
/**
|
|
300
|
-
* Get table column count
|
|
301
|
-
* テーブルの列数を取得します
|
|
302
|
-
*
|
|
303
|
-
* @param result - DuckDB query result
|
|
304
|
-
* @returns Number of columns in the table
|
|
305
|
-
*/
|
|
306
88
|
declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
307
|
-
/**
|
|
308
|
-
* Check if result is a valid DuckDB table
|
|
309
|
-
* 結果が有効な DuckDB テーブルかどうかをチェックします
|
|
310
|
-
*
|
|
311
|
-
* @param result - DuckDB query result
|
|
312
|
-
* @returns True if result is a valid DuckDB table
|
|
313
|
-
*/
|
|
314
89
|
declare const isDuckDBTable: (result: unknown) => result is DuckDBQueryResult;
|
|
315
|
-
/**
|
|
316
|
-
* Returns whether the current environment can host an OPFS-backed persistent database.
|
|
317
|
-
* 現在の環境が OPFS 永続データベースを利用できるか (navigator.storage.getDirectory の有無) を返します。
|
|
318
|
-
*/
|
|
319
90
|
declare const isOpfsSupported: () => boolean;
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* @module utils/logger
|
|
323
|
-
* @description Logger utility for consistent log management across the library.
|
|
324
|
-
* @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。
|
|
325
|
-
*/
|
|
326
|
-
/**
|
|
327
|
-
* @enum LogLevel
|
|
328
|
-
* @description Log levels for filtering output.
|
|
329
|
-
* @description 出力をフィルタリングするためのログレベル。
|
|
330
|
-
*/
|
|
331
91
|
declare enum LogLevel {
|
|
332
92
|
DEBUG = 0,
|
|
333
93
|
INFO = 1,
|
|
@@ -335,75 +95,23 @@ declare enum LogLevel {
|
|
|
335
95
|
ERROR = 3,
|
|
336
96
|
NONE = 4
|
|
337
97
|
}
|
|
338
|
-
/**
|
|
339
|
-
* @interface ILogger
|
|
340
|
-
* @description Interface for a logger object compatible with Console.
|
|
341
|
-
* @description Console と互換性のあるロガーオブジェクトのインターフェース。
|
|
342
|
-
*/
|
|
343
98
|
interface ILogger {
|
|
344
99
|
debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
345
100
|
info(message?: unknown, ...optionalParams: unknown[]): void;
|
|
346
101
|
warn(message?: unknown, ...optionalParams: unknown[]): void;
|
|
347
102
|
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
348
103
|
}
|
|
349
|
-
/**
|
|
350
|
-
* @class Logger
|
|
351
|
-
* @description A wrapper class for handling logging with levels and prefixes.
|
|
352
|
-
* @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。
|
|
353
|
-
*/
|
|
354
104
|
declare class Logger implements ILogger {
|
|
355
105
|
private level;
|
|
356
106
|
private prefix;
|
|
357
107
|
private impl;
|
|
358
|
-
/**
|
|
359
|
-
* @constructor
|
|
360
|
-
* @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.
|
|
361
|
-
* @param {string} [prefix="[duckdb-helper]"] - The prefix to add to all log messages.
|
|
362
|
-
* @param {ILogger} [impl=console] - The implementation to use for logging.
|
|
363
|
-
*/
|
|
364
108
|
constructor(level?: LogLevel, prefix?: string, impl?: ILogger);
|
|
365
109
|
private static instance;
|
|
366
|
-
/**
|
|
367
|
-
* @method setLevel
|
|
368
|
-
* @description Updates the current log level for the static instance.
|
|
369
|
-
* @description 静的インスタンスの現在のログレベルを更新します。
|
|
370
|
-
* @param {LogLevel} level - The new log level.
|
|
371
|
-
*/
|
|
372
110
|
static setLevel(level: LogLevel): void;
|
|
373
|
-
/**
|
|
374
|
-
* @method setLevel
|
|
375
|
-
* @description Updates the current log level.
|
|
376
|
-
* @description 現在のログレベルを更新します。
|
|
377
|
-
* @param {LogLevel} level - The new log level.
|
|
378
|
-
*/
|
|
379
111
|
setLevel(level: LogLevel): void;
|
|
380
|
-
/**
|
|
381
|
-
* @method setImplementation
|
|
382
|
-
* @description Updates the logger implementation for the static instance.
|
|
383
|
-
* @description 静的インスタンスのロガーの実装を更新します。
|
|
384
|
-
* @param {ILogger} impl - The new logger implementation.
|
|
385
|
-
*/
|
|
386
112
|
static setImplementation(impl: ILogger): void;
|
|
387
|
-
/**
|
|
388
|
-
* @method setImplementation
|
|
389
|
-
* @description Updates the logger implementation.
|
|
390
|
-
* @description ロガーの実装を更新します。
|
|
391
|
-
* @param {ILogger} impl - The new logger implementation.
|
|
392
|
-
*/
|
|
393
113
|
setImplementation(impl: ILogger): void;
|
|
394
|
-
/**
|
|
395
|
-
* @method setPrefix
|
|
396
|
-
* @description Updates the log prefix for the static instance.
|
|
397
|
-
* @description 静的インスタンスのログのプレフィックスを更新します。
|
|
398
|
-
* @param {string} prefix - The new prefix.
|
|
399
|
-
*/
|
|
400
114
|
static setPrefix(prefix: string): void;
|
|
401
|
-
/**
|
|
402
|
-
* @method setPrefix
|
|
403
|
-
* @description Updates the log prefix.
|
|
404
|
-
* @description ログのプレフィックスを更新します。
|
|
405
|
-
* @param {string} prefix - The new prefix.
|
|
406
|
-
*/
|
|
407
115
|
setPrefix(prefix: string): void;
|
|
408
116
|
private formatMessage;
|
|
409
117
|
static debug(message?: unknown, ...optionalParams: unknown[]): void;
|
|
@@ -415,5 +123,4 @@ declare class Logger implements ILogger {
|
|
|
415
123
|
static error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
416
124
|
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
417
125
|
}
|
|
418
|
-
|
|
419
126
|
export { type DuckDBDatabaseOptions, type DuckDBInitializeOptions, type DuckDBQueryResult, type DuckDBRow, DuckDBService, type ILogger, LogLevel, Logger, type UseDuckDBResult, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, isOpfsSupported, useDuckDB, useDuckDBQuery };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
1
|
"use strict";var q=Object.create;var I=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var V=(r,e)=>{for(var n in e)I(r,n,{get:e[n],enumerable:!0})},E=(r,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of j(e))!H.call(r,s)&&s!==n&&I(r,s,{get:()=>e[s],enumerable:!(o=F(e,s))||o.enumerable});return r};var _=(r,e,n)=>(n=r!=null?q(K(r)):{},E(e||!r||!r.__esModule?I(n,"default",{value:r,enumerable:!0}):n,r)),G=r=>E(I({},"__esModule",{value:!0}),r);var X={};V(X,{DuckDBService:()=>b,LogLevel:()=>T,Logger:()=>c,duckDBService:()=>A,duckdbTableToArray:()=>z,getDuckDBColumnCount:()=>C,getDuckDBRowCount:()=>O,isDuckDBTable:()=>M,isOpfsSupported:()=>N,useDuckDB:()=>P,useDuckDBQuery:()=>W});module.exports=G(X);var l=require("react");var y=_(require("@duckdb/duckdb-wasm"));var T=(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))(T||{}),k=class k{constructor(e=2,n="[duckdb-helper]",o=console){this.level=e,this.prefix=n,this.impl=o}static setLevel(e){k.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){k.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){k.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){k.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){k.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){k.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){k.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};k.instance=new k(2,"[duckdb-helper]");var c=k;var R=12e4,L=6e4,S=1e4,B=1e3,Y=100,Z=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/,x=()=>{},h=class h{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 h.instance||(h.instance=new h),h.instance}static create(){return new h}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){c.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(x,x),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(e){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let n={aborted:!1},o,s=new Promise((t,i)=>{o=setTimeout(()=>{n.aborted=!0,i(new Error(`DuckDB initialization timed out after ${R}ms`))},R)});this.initPromise=Promise.race([this.internalInitializeWorker(n,e),s]);try{let t=await this.initPromise;return clearTimeout(o),this.isInitializing=!1,this.notifyListeners(),t}catch(t){throw clearTimeout(o),n.aborted=!0,this.isInitializing=!1,this.hasError=!0,this.workerInstance=null,this.connectionInstance=null,this.connectionPromise=null,this.initPromise=null,this.abortPendingWorker(),this.notifyListeners(),t}}async internalInitializeWorker(e,n){let o=null,s=null;try{c.info("\u{1F527} Starting DuckDB Worker initialization..."),c.info("\u{1F4E6} Getting DuckDB bundles...");let t=n?.bundles||y.getJsDelivrBundles();c.info("\u{1F50D} Selecting appropriate bundle for browser...");let i=await y.selectBundle(t);this.assertNotAborted(e);let a=f=>f.startsWith("/")&&typeof window<"u"&&window.location?new URL(f,window.location.origin).toString():f,d={mainWorker:i.mainWorker?a(i.mainWorker):"",mainModule:i.mainModule?a(i.mainModule):"",pthreadWorker:i.pthreadWorker?a(i.pthreadWorker):void 0};c.info("\u2705 Bundle selected:",{mainWorker:d.mainWorker,mainModule:d.mainModule,pthreadWorker:d.pthreadWorker}),c.info("\u{1F680} Creating Worker with Blob URL..."),o=URL.createObjectURL(new Blob([`importScripts("${d.mainWorker}");`],{type:"text/javascript"})),c.info("\u{1F477} Creating Worker instance..."),s=new Worker(o),this.assertNotAborted(e),this.pendingWorker=s,this.pendingWorkerUrl=o,c.info("\u{1F91D} Connecting to AsyncDuckDB instance...");let p=new y.AsyncDuckDB(new y.ConsoleLogger,s);await p.instantiate(d.mainModule,d.pthreadWorker),this.assertNotAborted(e),n?.database&&(c.info("\u{1F5C4}\uFE0F Opening persistent database:",n.database.path),await p.open({path:n.database.path,accessMode:n.database.accessMode??y.DuckDBAccessMode.READ_WRITE,opfs:{fileHandling:n.database.opfsFileHandling??"auto"}}),this.assertNotAborted(e)),c.info("\u{1F4E6} Installing splink_udfs from community...");let m=await p.connect();try{let f,g=new Promise((D,u)=>{f=setTimeout(()=>{u(new Error(`splink_udfs installation timed out after ${S}ms`))},S)});await Promise.race([(async()=>{await m.query("INSTALL splink_udfs FROM community;"),c.info("\u2705 splink_udfs installed successfully"),await m.query("LOAD splink_udfs;"),c.info("\u2705 splink_udfs loaded successfully")})(),g]),clearTimeout(f)}catch(f){c.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",f)}finally{await m.close()}return this.assertNotAborted(e),this.workerInstance=p,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(o),c.info("\u2705 DuckDB Worker initialized successfully"),p}catch(t){if(s)try{s.terminate()}catch{}if(o)try{URL.revokeObjectURL(o)}catch{}throw this.pendingWorker===s&&(this.pendingWorker=null),this.pendingWorkerUrl===o&&(this.pendingWorkerUrl=null),c.error("\u274C Failed to initialize DuckDB Worker:",t),t instanceof Error&&(c.error("\u274C Error name:",t.name),c.error("\u274C Error message:",t.message),c.error("\u274C Error stack:",t.stack)),t}}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,s=new Promise((i,a)=>{o=setTimeout(()=>{n=!0,a(new Error(`DuckDB query execution timed out after ${L}ms`))},L)}),t=(async()=>(await this.getConnection()).query(e))();try{let i=await Promise.race([t,s]);return clearTimeout(o),i}catch(i){if(clearTimeout(o),n){let a=this.connectionInstance;this.connectionInstance=null,this.connectionPromise=null,a&&a.close().catch(()=>{})}throw c.error("\u274C Failed to execute SQL query:",i),i}}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 s of e)if(s)for(let t of Object.keys(s))n.has(t)||(n.add(t),o.push(t));return o}looksLikeTimestamp(e){return Z.test(e)?!Number.isNaN(new Date(e).getTime()):!1}inferColumnTypes(e,n){let o={};for(let s of n){let t=e.map(a=>a?.[s]).filter(a=>a!=null);if(t.length===0){o[s]="VARCHAR";continue}let i={number:0,integer:0,boolean:0,date:0,string:0};for(let a of t)typeof a=="number"?Number.isFinite(a)?(i.number+=1,Number.isInteger(a)&&(i.integer+=1)):i.string+=1:typeof a=="bigint"?(i.number+=1,i.integer+=1):typeof a=="boolean"?i.boolean+=1:a instanceof Date||typeof a=="string"&&this.looksLikeTimestamp(a)?i.date+=1:i.string+=1;i.number===t.length?o[s]=i.integer===t.length?"BIGINT":"DOUBLE":i.boolean===t.length?o[s]="BOOLEAN":i.date===t.length?o[s]="TIMESTAMP":o[s]="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 s=`create_table_${e}`,t=o.verbose??!0,i=this.operationFlags.get(s);if(i)return t&&c.info(`\u23F3 Table creation for '${e}' is already in progress, awaiting existing operation...`),i;let a=this.enqueue(()=>this.buildTable(e,n,o,t));this.operationFlags.set(s,a);try{await a}finally{this.operationFlags.delete(s)}}async buildTable(e,n,o,s){try{let t=await this.getConnection(),i=this.quoteIdentifier(e),a=this.collectColumnNames(n);if(a.length===0)throw new Error("Data rows have no columns");let d=Math.min(n.length,Y),p=this.inferColumnTypes(n.slice(0,d),a),m=a.map(u=>`${this.quoteIdentifier(u)} ${p[u]}`),f=o.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(o.primaryKey)})`:"",g=`CREATE TABLE ${i} (${m.join(", ")}${f})`,D=a.map(u=>this.quoteIdentifier(u)).join(", ");s&&c.info(`\u{1F4E5} Building table '${e}' with ${n.length} rows`),await t.query("BEGIN TRANSACTION");try{o.dropIfExists&&(s&&c.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await t.query(`DROP TABLE IF EXISTS ${i}`)),s&&c.info("\u{1F528} Creating table with SQL:",g),await t.query(g);for(let u=0;u<n.length;u+=B){let w=n.slice(u,u+B),v=w.map($=>`(${a.map(Q=>this.formatValueForSQL($?.[Q])).join(", ")})`),U=`INSERT INTO ${i} (${D}) VALUES ${v.join(", ")}`;await t.query(U),s&&c.info(`\u{1F4CA} Inserted batch ${Math.floor(u/B)+1} (${w.length} rows)`)}await t.query("COMMIT")}catch(u){try{await t.query("ROLLBACK")}catch(w){c.warn("\u26A0\uFE0F Rollback failed:",w)}throw u}s&&c.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(t){throw c.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){c.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){h.instance&&(h.instance.cleanup(),h.instance=null)}};h.instance=null;var b=h;var A=b.getInstance();var J=r=>{let e=(0,l.useRef)({deps:r,version:0}),n=e.current.deps;return(n.length!==r.length||r.some((s,t)=>!Object.is(s,n[t])))&&(e.current={deps:r,version:e.current.version+1}),e.current.version},P=(r=!0,e)=>{let[n,o]=(0,l.useState)("not-initialized"),[s,t]=(0,l.useState)(null),[i]=(0,l.useState)(()=>b.getInstance()),a=(0,l.useCallback)(()=>{let u=i.getStatus();c.info("\u{1F504} DuckDB status updated:",u),o(u),u==="error"?t("DuckDB initialization failed"):u==="ready"&&t(null)},[i]);(0,l.useEffect)(()=>(i.addStatusListener(a),a(),()=>{i.removeStatusListener(a)}),[i,a]);let d=(0,l.useCallback)(async()=>{try{await i.initialize(e)}catch(u){let w=u instanceof Error?u.message:"Unknown error occurred";t(w),c.error("\u274C DuckDB initialization failed:",u)}},[i,e]);(0,l.useEffect)(()=>{r&&n==="not-initialized"&&(c.info("\u{1F680} Auto-initializing DuckDB..."),d())},[r,n,d]);let p=(0,l.useCallback)(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQuery(u)},[i,n]),m=(0,l.useCallback)(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQueries(u)},[i,n]),f=(0,l.useCallback)(async(u,w,v)=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.createTableFromData(u,w,v)},[i,n]),g=(0,l.useCallback)(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.getTableInfo(u)},[i,n]),D=(0,l.useCallback)(async()=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.listTables()},[i,n]);return{status:n,error:s,executeQuery:p,executeQueries:m,createTableFromData:f,getTableInfo:g,listTables:D,isReady:n==="ready",initialize:d}},W=(r,e=[])=>{let{executeQuery:n,isReady:o}=P(),[s,t]=(0,l.useState)(null),[i,a]=(0,l.useState)(!1),[d,p]=(0,l.useState)(null),m=(0,l.useCallback)(async()=>{if(o&&r.trim())try{a(!0),p(null);let g=await n(r);t(g)}catch(g){let D=g instanceof Error?g.message:"Query execution failed";p(D),t(null)}finally{a(!1)}},[n,o,r]),f=J(e);return(0,l.useEffect)(()=>{m()},[m,f]),{data:s,loading:i,error:d,refetch:m}};var z=r=>!r||typeof r!="object"?[]:"toArray"in r&&typeof r.toArray=="function"?r.toArray():[],O=r=>!r||typeof r!="object"?0:"numRows"in r&&typeof r.numRows=="number"?r.numRows:0,C=r=>!r||typeof r!="object"?0:"numCols"in r&&typeof r.numCols=="number"?r.numCols:0,M=r=>!r||typeof r!="object"?!1:"toArray"in r&&typeof r.toArray=="function"&&"numRows"in r&&typeof r.numRows=="number",N=()=>typeof navigator<"u"&&typeof navigator.storage<"u"&&typeof navigator.storage.getDirectory=="function";0&&(module.exports={DuckDBService,LogLevel,Logger,duckDBService,duckdbTableToArray,getDuckDBColumnCount,getDuckDBRowCount,isDuckDBTable,isOpfsSupported,useDuckDB,useDuckDBQuery});
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
1
|
import{useCallback as w,useEffect as B,useRef as N,useState as I}from"react";import*as h from"@duckdb/duckdb-wasm";var P=(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))(P||{}),g=class g{constructor(e=2,n="[duckdb-helper]",r=console){this.level=e,this.prefix=n,this.impl=r}static setLevel(e){g.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){g.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){g.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){g.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){g.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){g.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){g.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};g.instance=new g(2,"[duckdb-helper]");var c=g;var E=12e4,R=6e4,L=1e4,T=1e3,O=100,C=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/,S=()=>{},k=class k{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 k.instance||(k.instance=new k),k.instance}static create(){return new k}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){c.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(S,S),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(e){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let n={aborted:!1},r,a=new Promise((t,i)=>{r=setTimeout(()=>{n.aborted=!0,i(new Error(`DuckDB initialization timed out after ${E}ms`))},E)});this.initPromise=Promise.race([this.internalInitializeWorker(n,e),a]);try{let t=await this.initPromise;return clearTimeout(r),this.isInitializing=!1,this.notifyListeners(),t}catch(t){throw clearTimeout(r),n.aborted=!0,this.isInitializing=!1,this.hasError=!0,this.workerInstance=null,this.connectionInstance=null,this.connectionPromise=null,this.initPromise=null,this.abortPendingWorker(),this.notifyListeners(),t}}async internalInitializeWorker(e,n){let r=null,a=null;try{c.info("\u{1F527} Starting DuckDB Worker initialization..."),c.info("\u{1F4E6} Getting DuckDB bundles...");let t=n?.bundles||h.getJsDelivrBundles();c.info("\u{1F50D} Selecting appropriate bundle for browser...");let i=await h.selectBundle(t);this.assertNotAborted(e);let o=d=>d.startsWith("/")&&typeof window<"u"&&window.location?new URL(d,window.location.origin).toString():d,l={mainWorker:i.mainWorker?o(i.mainWorker):"",mainModule:i.mainModule?o(i.mainModule):"",pthreadWorker:i.pthreadWorker?o(i.pthreadWorker):void 0};c.info("\u2705 Bundle selected:",{mainWorker:l.mainWorker,mainModule:l.mainModule,pthreadWorker:l.pthreadWorker}),c.info("\u{1F680} Creating Worker with Blob URL..."),r=URL.createObjectURL(new Blob([`importScripts("${l.mainWorker}");`],{type:"text/javascript"})),c.info("\u{1F477} Creating Worker instance..."),a=new Worker(r),this.assertNotAborted(e),this.pendingWorker=a,this.pendingWorkerUrl=r,c.info("\u{1F91D} Connecting to AsyncDuckDB instance...");let f=new h.AsyncDuckDB(new h.ConsoleLogger,a);await f.instantiate(l.mainModule,l.pthreadWorker),this.assertNotAborted(e),n?.database&&(c.info("\u{1F5C4}\uFE0F Opening persistent database:",n.database.path),await f.open({path:n.database.path,accessMode:n.database.accessMode??h.DuckDBAccessMode.READ_WRITE,opfs:{fileHandling:n.database.opfsFileHandling??"auto"}}),this.assertNotAborted(e)),c.info("\u{1F4E6} Installing splink_udfs from community...");let p=await f.connect();try{let d,m=new Promise((b,u)=>{d=setTimeout(()=>{u(new Error(`splink_udfs installation timed out after ${L}ms`))},L)});await Promise.race([(async()=>{await p.query("INSTALL splink_udfs FROM community;"),c.info("\u2705 splink_udfs installed successfully"),await p.query("LOAD splink_udfs;"),c.info("\u2705 splink_udfs loaded successfully")})(),m]),clearTimeout(d)}catch(d){c.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",d)}finally{await p.close()}return this.assertNotAborted(e),this.workerInstance=f,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(r),c.info("\u2705 DuckDB Worker initialized successfully"),f}catch(t){if(a)try{a.terminate()}catch{}if(r)try{URL.revokeObjectURL(r)}catch{}throw this.pendingWorker===a&&(this.pendingWorker=null),this.pendingWorkerUrl===r&&(this.pendingWorkerUrl=null),c.error("\u274C Failed to initialize DuckDB Worker:",t),t instanceof Error&&(c.error("\u274C Error name:",t.name),c.error("\u274C Error message:",t.message),c.error("\u274C Error stack:",t.stack)),t}}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,r,a=new Promise((i,o)=>{r=setTimeout(()=>{n=!0,o(new Error(`DuckDB query execution timed out after ${R}ms`))},R)}),t=(async()=>(await this.getConnection()).query(e))();try{let i=await Promise.race([t,a]);return clearTimeout(r),i}catch(i){if(clearTimeout(r),n){let o=this.connectionInstance;this.connectionInstance=null,this.connectionPromise=null,o&&o.close().catch(()=>{})}throw c.error("\u274C Failed to execute SQL query:",i),i}}async executeQuery(e){return this.enqueue(()=>this.runQueryWithTimeout(e))}async executeQueries(e){return this.enqueue(async()=>{let n=[];for(let r of e)n.push(await this.runQueryWithTimeout(r));return n})}collectColumnNames(e){let n=new Set,r=[];for(let a of e)if(a)for(let t of Object.keys(a))n.has(t)||(n.add(t),r.push(t));return r}looksLikeTimestamp(e){return C.test(e)?!Number.isNaN(new Date(e).getTime()):!1}inferColumnTypes(e,n){let r={};for(let a of n){let t=e.map(o=>o?.[a]).filter(o=>o!=null);if(t.length===0){r[a]="VARCHAR";continue}let i={number:0,integer:0,boolean:0,date:0,string:0};for(let o of t)typeof o=="number"?Number.isFinite(o)?(i.number+=1,Number.isInteger(o)&&(i.integer+=1)):i.string+=1:typeof o=="bigint"?(i.number+=1,i.integer+=1):typeof o=="boolean"?i.boolean+=1:o instanceof Date||typeof o=="string"&&this.looksLikeTimestamp(o)?i.date+=1:i.string+=1;i.number===t.length?r[a]=i.integer===t.length?"BIGINT":"DOUBLE":i.boolean===t.length?r[a]="BOOLEAN":i.date===t.length?r[a]="TIMESTAMP":r[a]="VARCHAR"}return r}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,r={}){if(!n||n.length===0)throw new Error("Data array is empty");let a=`create_table_${e}`,t=r.verbose??!0,i=this.operationFlags.get(a);if(i)return t&&c.info(`\u23F3 Table creation for '${e}' is already in progress, awaiting existing operation...`),i;let o=this.enqueue(()=>this.buildTable(e,n,r,t));this.operationFlags.set(a,o);try{await o}finally{this.operationFlags.delete(a)}}async buildTable(e,n,r,a){try{let t=await this.getConnection(),i=this.quoteIdentifier(e),o=this.collectColumnNames(n);if(o.length===0)throw new Error("Data rows have no columns");let l=Math.min(n.length,O),f=this.inferColumnTypes(n.slice(0,l),o),p=o.map(u=>`${this.quoteIdentifier(u)} ${f[u]}`),d=r.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(r.primaryKey)})`:"",m=`CREATE TABLE ${i} (${p.join(", ")}${d})`,b=o.map(u=>this.quoteIdentifier(u)).join(", ");a&&c.info(`\u{1F4E5} Building table '${e}' with ${n.length} rows`),await t.query("BEGIN TRANSACTION");try{r.dropIfExists&&(a&&c.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await t.query(`DROP TABLE IF EXISTS ${i}`)),a&&c.info("\u{1F528} Creating table with SQL:",m),await t.query(m);for(let u=0;u<n.length;u+=T){let y=n.slice(u,u+T),v=y.map(W=>`(${o.map(z=>this.formatValueForSQL(W?.[z])).join(", ")})`),A=`INSERT INTO ${i} (${b}) VALUES ${v.join(", ")}`;await t.query(A),a&&c.info(`\u{1F4CA} Inserted batch ${Math.floor(u/T)+1} (${y.length} rows)`)}await t.query("COMMIT")}catch(u){try{await t.query("ROLLBACK")}catch(y){c.warn("\u26A0\uFE0F Rollback failed:",y)}throw u}a&&c.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(t){throw c.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){c.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){k.instance&&(k.instance.cleanup(),k.instance=null)}};k.instance=null;var D=k;var M=D.getInstance();var U=s=>{let e=N({deps:s,version:0}),n=e.current.deps;return(n.length!==s.length||s.some((a,t)=>!Object.is(a,n[t])))&&(e.current={deps:s,version:e.current.version+1}),e.current.version},x=(s=!0,e)=>{let[n,r]=I("not-initialized"),[a,t]=I(null),[i]=I(()=>D.getInstance()),o=w(()=>{let u=i.getStatus();c.info("\u{1F504} DuckDB status updated:",u),r(u),u==="error"?t("DuckDB initialization failed"):u==="ready"&&t(null)},[i]);B(()=>(i.addStatusListener(o),o(),()=>{i.removeStatusListener(o)}),[i,o]);let l=w(async()=>{try{await i.initialize(e)}catch(u){let y=u instanceof Error?u.message:"Unknown error occurred";t(y),c.error("\u274C DuckDB initialization failed:",u)}},[i,e]);B(()=>{s&&n==="not-initialized"&&(c.info("\u{1F680} Auto-initializing DuckDB..."),l())},[s,n,l]);let f=w(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQuery(u)},[i,n]),p=w(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQueries(u)},[i,n]),d=w(async(u,y,v)=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.createTableFromData(u,y,v)},[i,n]),m=w(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.getTableInfo(u)},[i,n]),b=w(async()=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.listTables()},[i,n]);return{status:n,error:a,executeQuery:f,executeQueries:p,createTableFromData:d,getTableInfo:m,listTables:b,isReady:n==="ready",initialize:l}},$=(s,e=[])=>{let{executeQuery:n,isReady:r}=x(),[a,t]=I(null),[i,o]=I(!1),[l,f]=I(null),p=w(async()=>{if(r&&s.trim())try{o(!0),f(null);let m=await n(s);t(m)}catch(m){let b=m instanceof Error?m.message:"Query execution failed";f(b),t(null)}finally{o(!1)}},[n,r,s]),d=U(e);return B(()=>{p()},[p,d]),{data:a,loading:i,error:l,refetch:p}};var Q=s=>!s||typeof s!="object"?[]:"toArray"in s&&typeof s.toArray=="function"?s.toArray():[],q=s=>!s||typeof s!="object"?0:"numRows"in s&&typeof s.numRows=="number"?s.numRows:0,F=s=>!s||typeof s!="object"?0:"numCols"in s&&typeof s.numCols=="number"?s.numCols:0,j=s=>!s||typeof s!="object"?!1:"toArray"in s&&typeof s.toArray=="function"&&"numRows"in s&&typeof s.numRows=="number",K=()=>typeof navigator<"u"&&typeof navigator.storage<"u"&&typeof navigator.storage.getDirectory=="function";export{D as DuckDBService,P as LogLevel,c as Logger,M as duckDBService,Q as duckdbTableToArray,F as getDuckDBColumnCount,q as getDuckDBRowCount,j as isDuckDBTable,K as isOpfsSupported,x as useDuckDB,$ as useDuckDBQuery};
|
|
2
|
-
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiquants/duckdb-helper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "DuckDB helper utilities with React hooks, worker initialization, and typed query helpers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
|
-
"build": "tsup && node scripts/copy-assets.js",
|
|
73
|
+
"build": "tsup && node scripts/copy-assets.js && node ../../.config/scripts/strip-dts-comments.mjs dist",
|
|
74
74
|
"build:watch": "tsup --watch",
|
|
75
75
|
"dev": "tsup --watch",
|
|
76
76
|
"watch": "tsup --watch",
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/hooks/useDuckDB.ts","../src/services/duckdb.ts","../src/utils/simple-logger.ts","../src/utils/duckdb-helpers.ts"],"sourcesContent":["/**\n * DuckDB Helper Library Entry Point\n *\n * Provides React hooks, services, types, and utilities for working with DuckDB in web applications.\n * Web アプリケーションで DuckDB を扱うための React フック、サービス、型、ユーティリティを提供します。\n */\n\nexport type { UseDuckDBResult } from \"./hooks/useDuckDB\"\nexport { useDuckDB, useDuckDBQuery } from \"./hooks/useDuckDB\"\nexport type { DuckDBDatabaseOptions, DuckDBInitializeOptions } from \"./services/duckdb\"\nexport { DuckDBService, default as duckDBService } from \"./services/duckdb\"\nexport type {\n DuckDBQueryResult,\n DuckDBRow,\n} from \"./types/duckdb\"\nexport {\n duckdbTableToArray,\n getDuckDBColumnCount,\n getDuckDBRowCount,\n isDuckDBTable,\n isOpfsSupported,\n} from \"./utils/duckdb-helpers\"\nexport type { ILogger } from \"./utils/simple-logger\"\nexport { Logger, LogLevel } from \"./utils/simple-logger\"\n","/**\n * DuckDB React Hook\n *\n * React hook for using DuckDB functionality in components.\n * コンポーネントで DuckDB 機能を使用するための React フック。\n */\n\nimport { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\"\nimport { DuckDBService, type DuckDBInitializeOptions } from \"../services/duckdb\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/**\n * Derive a constant-size version number that increments whenever the given\n * dependency list changes (by React's own Object.is comparison).\n * 依存配列の変化(React と同じ Object.is 比較)で増える固定サイズのバージョン番号を返す。\n *\n * これにより可変長の `dependencies` を effect の依存配列に直接スプレッドせずに済み、\n * 配列サイズがレンダー間で変わっても React の「依存配列サイズ変更」エラーを起こさない。\n */\nconst useDependencyVersion = (dependencies: DependencyList): number => {\n const ref = useRef<{ deps: DependencyList; version: number }>({ deps: dependencies, version: 0 })\n const previous = ref.current.deps\n const changed = previous.length !== dependencies.length || dependencies.some((dep, index) => !Object.is(dep, previous[index]))\n if (changed) {\n ref.current = { deps: dependencies, version: ref.current.version + 1 }\n }\n return ref.current.version\n}\n\nexport interface UseDuckDBResult {\n status: \"not-initialized\" | \"initializing\" | \"ready\" | \"error\"\n error: string | null\n executeQuery: (sql: string) => Promise<unknown>\n executeQueries: (queries: string[]) => Promise<unknown[]>\n createTableFromData: <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ) => Promise<void>\n getTableInfo: (tableName: string) => Promise<unknown>\n listTables: () => Promise<unknown>\n isReady: boolean\n initialize: () => Promise<void>\n}\n\n/**\n * DuckDB functionality hook.\n * DuckDB 機能を提供するフック。\n *\n * @param autoInitialize - 自動初期化を行うかどうか\n * @returns DuckDB の状態と操作関数\n */\nexport const useDuckDB = (autoInitialize = true, options?: DuckDBInitializeOptions): UseDuckDBResult => {\n const [status, setStatus] = useState<\"not-initialized\" | \"initializing\" | \"ready\" | \"error\">(\"not-initialized\")\n const [error, setError] = useState<string | null>(null)\n const [duckDBService] = useState(() => DuckDBService.getInstance())\n\n const updateStatus = useCallback(() => {\n const serviceStatus = duckDBService.getStatus()\n Logger.info(\"🔄 DuckDB status updated:\", serviceStatus)\n setStatus(serviceStatus)\n\n if (serviceStatus === \"error\") {\n setError(\"DuckDB initialization failed\")\n } else if (serviceStatus === \"ready\") {\n setError(null)\n }\n }, [duckDBService])\n\n useEffect(() => {\n duckDBService.addStatusListener(updateStatus)\n updateStatus()\n\n return () => {\n duckDBService.removeStatusListener(updateStatus)\n }\n }, [duckDBService, updateStatus])\n\n const initialize = useCallback(async () => {\n try {\n await duckDBService.initialize(options)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Unknown error occurred\"\n setError(errorMessage)\n Logger.error(\"❌ DuckDB initialization failed:\", err)\n }\n }, [duckDBService, options])\n\n useEffect(() => {\n if (autoInitialize && status === \"not-initialized\") {\n Logger.info(\"🚀 Auto-initializing DuckDB...\")\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(\n async (sql: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQuery(sql)\n },\n [duckDBService, status],\n )\n\n const executeQueries = useCallback(\n async (queries: string[]): Promise<unknown[]> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQueries(queries)\n },\n [duckDBService, status],\n )\n\n const createTableFromData = useCallback(\n async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ): Promise<void> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.createTableFromData(tableName, data, options)\n },\n [duckDBService, status],\n )\n\n const getTableInfo = useCallback(\n async (tableName: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.getTableInfo(tableName)\n },\n [duckDBService, status],\n )\n\n const listTables = useCallback(async (): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.listTables()\n }, [duckDBService, status])\n\n return {\n status,\n error,\n executeQuery,\n executeQueries,\n createTableFromData,\n getTableInfo,\n listTables,\n isReady: status === \"ready\",\n initialize,\n }\n}\n\n/**\n * Hook for simple DuckDB query execution.\n * シンプルな DuckDB クエリ実行のためのフック。\n *\n * @remarks\n * `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。\n * `.length` や `.map` は持たないため、行配列として扱う場合は必ず\n * {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく\n * Arrow Table 相当の型を想定)。\n *\n * @example\n * ```tsx\n * const { data } = useDuckDBQuery(\"SELECT * FROM t\")\n * const rows = duckdbTableToArray<Row>(data)\n * ```\n *\n * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果(生の Arrow Table)と状態\n */\nexport const useDuckDBQuery = <T = unknown>(\n sql: string,\n dependencies: DependencyList = [],\n): {\n data: T | null\n loading: boolean\n error: string | null\n refetch: () => Promise<void>\n} => {\n const { executeQuery, isReady } = useDuckDB()\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refetch = useCallback(async () => {\n if (!(isReady && sql.trim())) {\n return\n }\n\n try {\n setLoading(true)\n setError(null)\n const result = await executeQuery(sql)\n setData(result as T)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Query execution failed\"\n setError(errorMessage)\n setData(null)\n } finally {\n setLoading(false)\n }\n }, [executeQuery, isReady, sql])\n\n // 可変長の dependencies を直接スプレッドせず、変化を検知する固定サイズのバージョンに変換する。\n const dependenciesVersion = useDependencyVersion(dependencies)\n\n useEffect(() => {\n void refetch()\n }, [refetch, dependenciesVersion])\n\n return {\n data,\n loading,\n error,\n refetch,\n }\n}\n","/**\n * DuckDB Web Worker Service\n *\n * Provides reusable DuckDB-wasm functionality across the application.\n * アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。\n */\n\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/** 初期化タイムアウト(WASM ダウンロードを考慮した長め設定)。 */\nconst INIT_TIMEOUT_MS = 120000\n/** クエリ実行タイムアウト。 */\nconst QUERY_TIMEOUT_MS = 60000\n/** splink_udfs インストールの個別タイムアウト。 */\nconst SPLINK_TIMEOUT_MS = 10000\n/** バッチ INSERT の1回あたり行数。 */\nconst BATCH_SIZE = 1000\n/** 型推定に使用する先頭サンプル行数。 */\nconst TYPE_INFERENCE_SAMPLE_SIZE = 100\n/**\n * 文字列を TIMESTAMP とみなすための厳密パターン。\n * `YYYY-MM-DD` または `YYYY-MM-DD[ T]HH:MM(:SS(.sss)?)?(Z|±HH:MM)?` のみ許容し、\n * `ABC-123` のような「ハイフンを含むが日付ではない」文字列を誤って日付扱いしない。\n */\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n\n/** 内部初期化のアボート指示を運ぶシグナル。 */\ninterface InitSignal {\n aborted: boolean\n}\n\n/**\n * Options for opening a persistent database (e.g. OPFS-backed).\n * 永続データベース (OPFS など) を開くためのオプション。\n */\nexport interface DuckDBDatabaseOptions {\n /** Database path. Use an `opfs://` path for a persistent OPFS-backed database. `opfs://` パスで OPFS 永続 DB を開く。 */\n path: string\n /** Access mode (defaults to READ_WRITE). アクセスモード (既定 READ_WRITE)。 */\n accessMode?: duckdb.DuckDBAccessMode\n /** How `opfs://` files are handled during SQL execution (defaults to \"auto\"). SQL 実行時の `opfs://` ファイル登録の扱い (既定 \"auto\")。 */\n opfsFileHandling?: \"auto\" | \"manual\"\n}\n\n/**\n * Options accepted by {@link DuckDBService.initialize}.\n * initialize() が受け取るオプション。\n */\nexport interface DuckDBInitializeOptions {\n /** Custom bundles (defaults to jsDelivr bundles). カスタムバンドル (既定は jsDelivr)。 */\n bundles?: duckdb.DuckDBBundles\n /** Persistent database to open after instantiation. インスタンス化後に開く永続データベース。 */\n database?: DuckDBDatabaseOptions\n}\n\nconst noop = (): void => {\n // 直列化キューの継続用。前段の解決/拒否を握りつぶす。\n}\n\n/**\n * DuckDB service for browser-based SQL operations.\n * ブラウザベースの SQL 操作のための DuckDB サービス。\n */\nclass DuckDBService {\n private static instance: DuckDBService | null = null\n private operationFlags: Map<string, Promise<void>> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private connectionPromise: Promise<duckdb.AsyncDuckDBConnection> | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\n\n /** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */\n private pendingWorker: Worker | null = null\n private pendingWorkerUrl: string | null = null\n\n /**\n * 共有コネクション上での操作直列化キュー。\n * createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。\n */\n private operationQueue: Promise<unknown> = Promise.resolve()\n\n private listeners: Set<() => void> = new Set()\n\n /**\n * Get singleton instance of DuckDBService.\n * DuckDBService のシングルトンインスタンスを取得します。\n */\n public static getInstance(): DuckDBService {\n if (!DuckDBService.instance) {\n DuckDBService.instance = new DuckDBService()\n }\n return DuckDBService.instance\n }\n\n /**\n * Creates a dedicated (non-shared) service instance, e.g. for a persistent OPFS database.\n * 専用 (非共有) のサービスインスタンスを生成します (OPFS 永続 DB を共有シングルトンと分離して持つ用途)。\n */\n public static create(): DuckDBService {\n return new DuckDBService()\n }\n\n /**\n * Add status change listener.\n * 状態変更リスナーを追加します。\n */\n public addStatusListener(listener: () => void): void {\n this.listeners.add(listener)\n }\n\n /**\n * Remove status change listener.\n * 状態変更リスナーを削除します。\n */\n public removeStatusListener(listener: () => void): void {\n this.listeners.delete(listener)\n }\n\n /**\n * Notify all listeners of status change.\n * すべてのリスナーに状態変更を通知します。\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener()\n } catch (error) {\n Logger.error(\"⚠️ Error in status listener:\", error)\n }\n }\n }\n\n /**\n * Get current status.\n * 現在のステータスを取得します。\n */\n public getStatus(): \"not-initialized\" | \"initializing\" | \"ready\" | \"error\" {\n if (this.hasError) {\n return \"error\"\n }\n if (this.isInitializing) {\n return \"initializing\"\n }\n if (this.workerInstance) {\n return \"ready\"\n }\n return \"not-initialized\"\n }\n\n /** Enqueue a task on the single-connection serialization queue. */\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.operationQueue.then(task, task)\n this.operationQueue = run.then(noop, noop)\n return run\n }\n\n /** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */\n private assertNotAborted(signal: InitSignal): void {\n if (signal.aborted) {\n throw new Error(\"DuckDB initialization aborted\")\n }\n }\n\n /** 初期化中の Worker / Blob URL を確実に破棄する。 */\n private abortPendingWorker(): void {\n if (this.pendingWorker) {\n try {\n this.pendingWorker.terminate()\n } catch {\n // terminate は冪等でなくても良い。失敗は無視する。\n }\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl) {\n try {\n URL.revokeObjectURL(this.pendingWorkerUrl)\n } catch {\n // revoke 失敗は無視する。\n }\n this.pendingWorkerUrl = null\n }\n }\n\n /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB> {\n if (this.workerInstance) {\n return this.workerInstance\n }\n\n if (this.isInitializing && this.initPromise) {\n return this.initPromise\n }\n\n this.isInitializing = true\n this.hasError = false\n this.notifyListeners()\n\n // タイムアウト設定 (120秒) - WASMのダウンロードなどを考慮して長めに設定\n const signal: InitSignal = { aborted: false }\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n signal.aborted = true\n reject(new Error(`DuckDB initialization timed out after ${INIT_TIMEOUT_MS}ms`))\n }, INIT_TIMEOUT_MS)\n })\n\n this.initPromise = Promise.race([this.internalInitializeWorker(signal, options), timeoutPromise])\n\n try {\n const result = await this.initPromise\n clearTimeout(timeoutId)\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n // 失敗/タイムアウト時は状態を完全にリセットして再試行可能にする。\n signal.aborted = true\n this.isInitializing = false\n this.hasError = true\n this.workerInstance = null\n this.connectionInstance = null\n this.connectionPromise = null\n this.initPromise = null\n this.abortPendingWorker()\n this.notifyListeners()\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n *\n * workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は\n * ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。\n */\n private async internalInitializeWorker(signal: InitSignal, options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB> {\n let workerUrl: string | null = null\n let worker: Worker | null = null\n try {\n Logger.info(\"🔧 Starting DuckDB Worker initialization...\")\n Logger.info(\"📦 Getting DuckDB bundles...\")\n const baseBundles = options?.bundles || duckdb.getJsDelivrBundles()\n\n Logger.info(\"🔍 Selecting appropriate bundle for browser...\")\n const bundle = await duckdb.selectBundle(baseBundles)\n this.assertNotAborted(signal)\n\n // Blob Worker 内の importScripts/fetch でパス解決できるように、相対パス(スラッシュ始まり)は絶対 URL に変換する\n const toAbsoluteUrl = (urlPath: string): string => {\n if (urlPath.startsWith(\"/\") && typeof window !== \"undefined\" && window.location) {\n return new URL(urlPath, window.location.origin).toString()\n }\n return urlPath\n }\n\n const normalizedBundle = {\n mainWorker: bundle.mainWorker ? toAbsoluteUrl(bundle.mainWorker) : \"\",\n mainModule: bundle.mainModule ? toAbsoluteUrl(bundle.mainModule) : \"\",\n pthreadWorker: bundle.pthreadWorker ? toAbsoluteUrl(bundle.pthreadWorker) : undefined,\n }\n\n Logger.info(\"✅ Bundle selected:\", {\n mainWorker: normalizedBundle.mainWorker,\n mainModule: normalizedBundle.mainModule,\n pthreadWorker: normalizedBundle.pthreadWorker,\n })\n\n Logger.info(\"🚀 Creating Worker with Blob URL...\")\n workerUrl = URL.createObjectURL(new Blob([`importScripts(\"${normalizedBundle.mainWorker}\");`], { type: \"text/javascript\" }))\n\n Logger.info(\"👷 Creating Worker instance...\")\n worker = new Worker(workerUrl)\n\n this.assertNotAborted(signal)\n this.pendingWorker = worker\n this.pendingWorkerUrl = workerUrl\n\n Logger.info(\"🤝 Connecting to AsyncDuckDB instance...\")\n const db = new duckdb.AsyncDuckDB(new duckdb.ConsoleLogger(), worker)\n await db.instantiate(normalizedBundle.mainModule, normalizedBundle.pthreadWorker)\n this.assertNotAborted(signal)\n\n // 永続データベース指定時はインスタンス化直後に開く (opfs:// パスでリロードを跨いで保持される)\n if (options?.database) {\n Logger.info(\"🗄️ Opening persistent database:\", options.database.path)\n await db.open({\n path: options.database.path,\n accessMode: options.database.accessMode ?? duckdb.DuckDBAccessMode.READ_WRITE,\n opfs: { fileHandling: options.database.opfsFileHandling ?? \"auto\" },\n })\n this.assertNotAborted(signal)\n }\n\n Logger.info(\"📦 Installing splink_udfs from community...\")\n const connection = await db.connect()\n try {\n // splink_udfs のインストール/ロードはネットワークアクセスを伴うため、\n // 個別の短いタイムアウトを設けてハングを防止する。\n let splinkTimeoutId!: ReturnType<typeof setTimeout>\n const splinkTimeoutPromise = new Promise<never>((_, reject) => {\n splinkTimeoutId = setTimeout(() => {\n reject(new Error(`splink_udfs installation timed out after ${SPLINK_TIMEOUT_MS}ms`))\n }, SPLINK_TIMEOUT_MS)\n })\n\n await Promise.race([\n (async () => {\n await connection.query(\"INSTALL splink_udfs FROM community;\")\n Logger.info(\"✅ splink_udfs installed successfully\")\n\n await connection.query(\"LOAD splink_udfs;\")\n Logger.info(\"✅ splink_udfs loaded successfully\")\n })(),\n splinkTimeoutPromise,\n ])\n clearTimeout(splinkTimeoutId)\n } catch (error) {\n // タイムアウトやネットワークエラーの場合は警告ログを出して続行\n Logger.warn(\"⚠️ Failed to install/load splink_udfs:\", error)\n } finally {\n await connection.close()\n }\n this.assertNotAborted(signal)\n\n // すべて成功したときのみ公開する。\n this.workerInstance = db\n this.pendingWorker = null\n this.pendingWorkerUrl = null\n URL.revokeObjectURL(workerUrl)\n\n Logger.info(\"✅ DuckDB Worker initialized successfully\")\n return db\n } catch (error) {\n // 途中生成物を必ず破棄する(Worker スレッド / Blob URL のリーク防止)。\n if (worker) {\n try {\n worker.terminate()\n } catch {\n // 無視する。\n }\n }\n if (workerUrl) {\n try {\n URL.revokeObjectURL(workerUrl)\n } catch {\n // 無視する。\n }\n }\n if (this.pendingWorker === worker) {\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl === workerUrl) {\n this.pendingWorkerUrl = null\n }\n\n Logger.error(\"❌ Failed to initialize DuckDB Worker:\", error)\n if (error instanceof Error) {\n Logger.error(\"❌ Error name:\", error.name)\n Logger.error(\"❌ Error message:\", error.message)\n Logger.error(\"❌ Error stack:\", error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n *\n * 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (this.connectionInstance) {\n return this.connectionInstance\n }\n if (!this.connectionPromise) {\n this.connectionPromise = (async () => {\n const db = await this.initialize()\n const connection = await db.connect()\n this.connectionInstance = connection\n return connection\n })()\n }\n try {\n return await this.connectionPromise\n } catch (error) {\n // 失敗時はリセットして次回リトライ可能にする。\n this.connectionPromise = null\n throw error\n }\n }\n\n /**\n * Run a single query with a timeout, discarding the connection on timeout.\n * 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。\n *\n * タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して\n * 後続クエリが同じ(詰まった)コネクションで待たされないようにする。\n */\n private async runQueryWithTimeout(sql: string): Promise<unknown> {\n let timedOut = false\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n timedOut = true\n reject(new Error(`DuckDB query execution timed out after ${QUERY_TIMEOUT_MS}ms`))\n }, QUERY_TIMEOUT_MS)\n })\n\n // コネクション取得とクエリ実行の両方をタイムアウトの対象にする。\n const run = (async () => {\n const connection = await this.getConnection()\n return connection.query(sql)\n })()\n\n try {\n const result = await Promise.race([run, timeoutPromise])\n clearTimeout(timeoutId)\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n if (timedOut) {\n const stale = this.connectionInstance\n this.connectionInstance = null\n this.connectionPromise = null\n if (stale) {\n void stale.close().catch(() => {\n // 詰まったコネクションの close 失敗は無視する。\n })\n }\n }\n Logger.error(\"❌ Failed to execute SQL query:\", error)\n throw error\n }\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n return this.enqueue(() => this.runQueryWithTimeout(sql))\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します(逐次実行)。\n *\n * バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n return this.enqueue(async () => {\n const results: unknown[] = []\n for (const query of queries) {\n results.push(await this.runQueryWithTimeout(query))\n }\n return results\n })\n }\n\n /**\n * Collect the union of column names across all rows (first-seen order).\n * 全行を走査して列名の和集合を取得します(初出順を保持)。\n * 先頭行に存在しないキーを取りこぼさないため全行を対象とする。\n */\n private collectColumnNames(rows: Record<string, unknown>[]): string[] {\n const seen = new Set<string>()\n const names: string[] = []\n for (const row of rows) {\n if (!row) {\n continue\n }\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key)\n names.push(key)\n }\n }\n }\n return names\n }\n\n /** Check whether a string is a genuine (strict) date/timestamp literal. */\n private looksLikeTimestamp(value: string): boolean {\n if (!TIMESTAMP_PATTERN.test(value)) {\n return false\n }\n return !Number.isNaN(new Date(value).getTime())\n }\n\n /**\n * Infer column types from a data sample for the given columns.\n * 指定した列について、データサンプルから DuckDB の型を推定します。\n *\n * 整数は桁あふれを避けるため一律 BIGINT を採用する。\n */\n private inferColumnTypes(sample: Record<string, unknown>[], columnNames: string[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n\n for (const key of columnNames) {\n const values = sample.map((row) => row?.[key]).filter((val) => val !== null && val !== undefined)\n\n if (values.length === 0) {\n columnTypes[key] = \"VARCHAR\"\n continue\n }\n\n const typeInfo = {\n number: 0,\n integer: 0,\n boolean: 0,\n date: 0,\n string: 0,\n }\n\n for (const value of values) {\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n }\n } else {\n // NaN / Infinity は数値列にできないため文字列扱い(実挿入時は NULL 化)。\n typeInfo.string += 1\n }\n } else if (typeof value === \"bigint\") {\n typeInfo.number += 1\n typeInfo.integer += 1\n } else if (typeof value === \"boolean\") {\n typeInfo.boolean += 1\n } else if (value instanceof Date) {\n typeInfo.date += 1\n } else if (typeof value === \"string\" && this.looksLikeTimestamp(value)) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n columnTypes[key] = typeInfo.integer === values.length ? \"BIGINT\" : \"DOUBLE\"\n } else if (typeInfo.boolean === values.length) {\n columnTypes[key] = \"BOOLEAN\"\n } else if (typeInfo.date === values.length) {\n columnTypes[key] = \"TIMESTAMP\"\n } else {\n columnTypes[key] = \"VARCHAR\"\n }\n }\n\n return columnTypes\n }\n\n /**\n * Format value for SQL insertion.\n * SQL 挿入用に値をフォーマットします。\n */\n private formatValueForSQL(value: unknown): string {\n if (value === null || value === undefined) {\n return \"NULL\"\n }\n if (typeof value === \"string\") {\n return `'${value.replace(/'/g, \"''\")}'`\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`\n }\n if (typeof value === \"boolean\") {\n return value ? \"TRUE\" : \"FALSE\"\n }\n if (typeof value === \"bigint\") {\n return value.toString()\n }\n if (typeof value === \"number\") {\n // NaN / Infinity / -Infinity は有効な SQL 数値リテラルではないため NULL 化する。\n if (!Number.isFinite(value)) {\n return \"NULL\"\n }\n return String(value)\n }\n // オブジェクト / 配列などは JSON 文字列としてクォート・エスケープする。\n try {\n const json = JSON.stringify(value)\n if (json === undefined) {\n return \"NULL\"\n }\n return `'${json.replace(/'/g, \"''\")}'`\n } catch {\n return \"NULL\"\n }\n }\n\n /**\n * Quote a SQL identifier (table / column / primary key) safely.\n * SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。\n */\n private quoteIdentifier(name: string): string {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new Error(\"Invalid SQL identifier\")\n }\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\n *\n * 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。\n */\n public async createTableFromData<T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n } = {},\n ): Promise<void> {\n if (!data || data.length === 0) {\n throw new Error(\"Data array is empty\")\n }\n\n const operationKey = `create_table_${tableName}`\n const verbose = options.verbose ?? true\n\n const inFlight = this.operationFlags.get(operationKey)\n if (inFlight) {\n if (verbose) {\n Logger.info(`⏳ Table creation for '${tableName}' is already in progress, awaiting existing operation...`)\n }\n return inFlight\n }\n\n const operation = this.enqueue(() => this.buildTable(tableName, data, options, verbose))\n this.operationFlags.set(operationKey, operation)\n try {\n await operation\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /** Actual (serialized, transactional) table build. */\n private async buildTable<T extends Record<string, unknown>>(tableName: string, data: T[], options: { dropIfExists?: boolean; primaryKey?: string }, verbose: boolean): Promise<void> {\n try {\n const connection = await this.getConnection()\n const quotedTable = this.quoteIdentifier(tableName)\n\n const columnNames = this.collectColumnNames(data)\n if (columnNames.length === 0) {\n throw new Error(\"Data rows have no columns\")\n }\n\n const sampleSize = Math.min(data.length, TYPE_INFERENCE_SAMPLE_SIZE)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize), columnNames)\n\n const columnDefs = columnNames.map((key) => `${this.quoteIdentifier(key)} ${columnTypes[key]}`)\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (${this.quoteIdentifier(options.primaryKey)})` : \"\"\n const createTableSQL = `CREATE TABLE ${quotedTable} (${columnDefs.join(\", \")}${primaryKeyClause})`\n const columnList = columnNames.map((col) => this.quoteIdentifier(col)).join(\", \")\n\n if (verbose) {\n Logger.info(`📥 Building table '${tableName}' with ${data.length} rows`)\n }\n\n // DROP / CREATE / INSERT を1トランザクションにまとめ、途中失敗時はロールバックして\n // 半端に作成されたテーブルを残さない。\n await connection.query(\"BEGIN TRANSACTION\")\n try {\n if (options.dropIfExists) {\n if (verbose) {\n Logger.info(`🗑️ Dropping table '${tableName}' if exists`)\n }\n await connection.query(`DROP TABLE IF EXISTS ${quotedTable}`)\n }\n\n if (verbose) {\n Logger.info(\"🔨 Creating table with SQL:\", createTableSQL)\n }\n await connection.query(createTableSQL)\n\n for (let i = 0; i < data.length; i += BATCH_SIZE) {\n const batch = data.slice(i, i + BATCH_SIZE)\n const valueRows = batch.map((row) => {\n // null/undefined 行はすべて NULL として扱う(不揃いなデータへの耐性)。\n const values = columnNames.map((col) => this.formatValueForSQL(row?.[col]))\n return `(${values.join(\", \")})`\n })\n\n const insertSQL = `INSERT INTO ${quotedTable} (${columnList}) VALUES ${valueRows.join(\", \")}`\n await connection.query(insertSQL)\n if (verbose) {\n Logger.info(`📊 Inserted batch ${Math.floor(i / BATCH_SIZE) + 1} (${batch.length} rows)`)\n }\n }\n\n await connection.query(\"COMMIT\")\n } catch (txError) {\n try {\n await connection.query(\"ROLLBACK\")\n } catch (rollbackError) {\n Logger.warn(\"⚠️ Rollback failed:\", rollbackError)\n }\n throw txError\n }\n\n if (verbose) {\n Logger.info(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n }\n } catch (error) {\n Logger.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(`DESCRIBE ${this.quoteIdentifier(tableName)}`)\n })\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(\"SHOW TABLES\")\n })\n }\n\n /**\n * Check if DuckDB is ready.\n * DuckDB が準備完了かどうかを確認します。\n */\n public isReady(): boolean {\n return this.workerInstance !== null\n }\n\n /**\n * Cleanup DuckDB Worker resources.\n * DuckDB Worker のリソースをクリーンアップします。\n */\n public async cleanup(): Promise<void> {\n try {\n if (this.connectionInstance) {\n await this.connectionInstance.close()\n this.connectionInstance = null\n }\n if (this.workerInstance) {\n await this.workerInstance.terminate()\n this.workerInstance = null\n }\n this.abortPendingWorker()\n this.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.connectionPromise = null\n this.operationQueue = Promise.resolve()\n this.operationFlags.clear()\n this.notifyListeners()\n this.listeners.clear()\n } catch (error) {\n Logger.error(\"⚠️ Error during cleanup:\", error)\n }\n }\n\n /**\n * Reset the service instance (useful for testing).\n * サービスインスタンスをリセットします(テスト用)。\n */\n public static resetInstance(): void {\n if (DuckDBService.instance) {\n void DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\n","/**\n * @module utils/logger\n * @description Logger utility for consistent log management across the library.\n * @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。\n */\n\n/**\n * @enum LogLevel\n * @description Log levels for filtering output.\n * @description 出力をフィルタリングするためのログレベル。\n */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/**\n * @interface ILogger\n * @description Interface for a logger object compatible with Console.\n * @description Console と互換性のあるロガーオブジェクトのインターフェース。\n */\nexport interface ILogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * @class Logger\n * @description A wrapper class for handling logging with levels and prefixes.\n * @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。\n */\nexport class Logger implements ILogger {\n private level: LogLevel\n private prefix: string\n private impl: ILogger\n\n /**\n * @constructor\n * @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.\n * @param {string} [prefix=\"[duckdb-helper]\"] - The prefix to add to all log messages.\n * @param {ILogger} [impl=console] - The implementation to use for logging.\n */\n constructor(level: LogLevel = LogLevel.WARN, prefix: string = \"[duckdb-helper]\", impl: ILogger = console) {\n this.level = level\n this.prefix = prefix\n this.impl = impl\n }\n\n private static instance: Logger = new Logger(LogLevel.WARN, \"[duckdb-helper]\")\n\n /**\n * @method setLevel\n * @description Updates the current log level for the static instance.\n * @description 静的インスタンスの現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n static setLevel(level: LogLevel): void {\n Logger.instance.setLevel(level)\n }\n\n /**\n * @method setLevel\n * @description Updates the current log level.\n * @description 現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation for the static instance.\n * @description 静的インスタンスのロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n static setImplementation(impl: ILogger): void {\n Logger.instance.setImplementation(impl)\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation.\n * @description ロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n setImplementation(impl: ILogger): void {\n this.impl = impl\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix for the static instance.\n * @description 静的インスタンスのログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n static setPrefix(prefix: string): void {\n Logger.instance.setPrefix(prefix)\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix.\n * @description ログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n setPrefix(prefix: string): void {\n this.prefix = prefix\n }\n\n private formatMessage(message: unknown): unknown[] {\n if (typeof message === \"string\") {\n return [`${this.prefix} ${message}`]\n }\n return [this.prefix, message]\n }\n\n static debug(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.debug(message, ...optionalParams)\n }\n\n debug(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.DEBUG) {\n this.impl.debug(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static info(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.info(message, ...optionalParams)\n }\n\n info(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.INFO) {\n this.impl.info(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static warn(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.warn(message, ...optionalParams)\n }\n\n warn(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.WARN) {\n this.impl.warn(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static error(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.error(message, ...optionalParams)\n }\n\n error(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.ERROR) {\n this.impl.error(...this.formatMessage(message), ...optionalParams)\n }\n }\n}\n","/**\n * DuckDB Helper Functions\n *\n * Utility functions for working with DuckDB query results.\n * DuckDB クエリ結果を扱うためのユーティリティ関数。\n */\n\nimport type { DuckDBQueryResult } from \"../types/duckdb\"\n\n/**\n * Safely convert DuckDB table to typed array\n * DuckDB テーブルを型付き配列に安全に変換します\n *\n * @param result - DuckDB query result\n * @returns Typed array of results\n */\nexport const duckdbTableToArray = <T>(result: unknown): T[] => {\n if (!result || typeof result !== \"object\") {\n return []\n }\n\n if (\"toArray\" in result && typeof result.toArray === \"function\") {\n return (result as DuckDBQueryResult<T>).toArray()\n }\n\n return []\n}\n\n/**\n * Get table row count\n * テーブルの行数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of rows in the table\n */\nexport const getDuckDBRowCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numRows\" in result && typeof result.numRows === \"number\") {\n return result.numRows\n }\n\n return 0\n}\n\n/**\n * Get table column count\n * テーブルの列数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of columns in the table\n */\nexport const getDuckDBColumnCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numCols\" in result && typeof result.numCols === \"number\") {\n return result.numCols\n }\n\n return 0\n}\n\n/**\n * Check if result is a valid DuckDB table\n * 結果が有効な DuckDB テーブルかどうかをチェックします\n *\n * @param result - DuckDB query result\n * @returns True if result is a valid DuckDB table\n */\nexport const isDuckDBTable = (result: unknown): result is DuckDBQueryResult => {\n if (!result || typeof result !== \"object\") {\n return false\n }\n\n return \"toArray\" in result && typeof result.toArray === \"function\" && \"numRows\" in result && typeof result.numRows === \"number\"\n}\n\n/**\n * Returns whether the current environment can host an OPFS-backed persistent database.\n * 現在の環境が OPFS 永続データベースを利用できるか (navigator.storage.getDirectory の有無) を返します。\n */\nexport const isOpfsSupported = (): boolean => {\n return typeof navigator !== \"undefined\" && typeof navigator.storage !== \"undefined\" && typeof navigator.storage.getDirectory === \"function\"\n}\n"],"mappings":"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","names":["index_exports","__export","DuckDBService","LogLevel","Logger","duckdb_default","duckdbTableToArray","getDuckDBColumnCount","getDuckDBRowCount","isDuckDBTable","isOpfsSupported","useDuckDB","useDuckDBQuery","__toCommonJS","import_react","duckdb","LogLevel","_Logger","level","prefix","impl","message","optionalParams","Logger","INIT_TIMEOUT_MS","QUERY_TIMEOUT_MS","SPLINK_TIMEOUT_MS","BATCH_SIZE","TYPE_INFERENCE_SAMPLE_SIZE","TIMESTAMP_PATTERN","noop","_DuckDBService","listener","error","Logger","task","run","signal","options","timeoutId","timeoutPromise","_","reject","result","workerUrl","worker","baseBundles","bundle","toAbsoluteUrl","urlPath","normalizedBundle","db","connection","splinkTimeoutId","splinkTimeoutPromise","sql","timedOut","stale","queries","results","query","rows","seen","names","row","key","value","sample","columnNames","columnTypes","values","val","typeInfo","json","name","tableName","data","operationKey","verbose","inFlight","operation","quotedTable","sampleSize","columnDefs","primaryKeyClause","createTableSQL","columnList","col","i","batch","valueRows","insertSQL","txError","rollbackError","DuckDBService","duckdb_default","DuckDBService","useDependencyVersion","dependencies","ref","previous","dep","index","useDuckDB","autoInitialize","options","status","setStatus","error","setError","duckDBService","DuckDBService","updateStatus","serviceStatus","Logger","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","getTableInfo","listTables","useDuckDBQuery","isReady","setData","loading","setLoading","refetch","result","dependenciesVersion","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable","isOpfsSupported"]}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useDuckDB.ts","../src/services/duckdb.ts","../src/utils/simple-logger.ts","../src/utils/duckdb-helpers.ts"],"sourcesContent":["/**\n * DuckDB React Hook\n *\n * React hook for using DuckDB functionality in components.\n * コンポーネントで DuckDB 機能を使用するための React フック。\n */\n\nimport { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\"\nimport { DuckDBService, type DuckDBInitializeOptions } from \"../services/duckdb\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/**\n * Derive a constant-size version number that increments whenever the given\n * dependency list changes (by React's own Object.is comparison).\n * 依存配列の変化(React と同じ Object.is 比較)で増える固定サイズのバージョン番号を返す。\n *\n * これにより可変長の `dependencies` を effect の依存配列に直接スプレッドせずに済み、\n * 配列サイズがレンダー間で変わっても React の「依存配列サイズ変更」エラーを起こさない。\n */\nconst useDependencyVersion = (dependencies: DependencyList): number => {\n const ref = useRef<{ deps: DependencyList; version: number }>({ deps: dependencies, version: 0 })\n const previous = ref.current.deps\n const changed = previous.length !== dependencies.length || dependencies.some((dep, index) => !Object.is(dep, previous[index]))\n if (changed) {\n ref.current = { deps: dependencies, version: ref.current.version + 1 }\n }\n return ref.current.version\n}\n\nexport interface UseDuckDBResult {\n status: \"not-initialized\" | \"initializing\" | \"ready\" | \"error\"\n error: string | null\n executeQuery: (sql: string) => Promise<unknown>\n executeQueries: (queries: string[]) => Promise<unknown[]>\n createTableFromData: <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ) => Promise<void>\n getTableInfo: (tableName: string) => Promise<unknown>\n listTables: () => Promise<unknown>\n isReady: boolean\n initialize: () => Promise<void>\n}\n\n/**\n * DuckDB functionality hook.\n * DuckDB 機能を提供するフック。\n *\n * @param autoInitialize - 自動初期化を行うかどうか\n * @returns DuckDB の状態と操作関数\n */\nexport const useDuckDB = (autoInitialize = true, options?: DuckDBInitializeOptions): UseDuckDBResult => {\n const [status, setStatus] = useState<\"not-initialized\" | \"initializing\" | \"ready\" | \"error\">(\"not-initialized\")\n const [error, setError] = useState<string | null>(null)\n const [duckDBService] = useState(() => DuckDBService.getInstance())\n\n const updateStatus = useCallback(() => {\n const serviceStatus = duckDBService.getStatus()\n Logger.info(\"🔄 DuckDB status updated:\", serviceStatus)\n setStatus(serviceStatus)\n\n if (serviceStatus === \"error\") {\n setError(\"DuckDB initialization failed\")\n } else if (serviceStatus === \"ready\") {\n setError(null)\n }\n }, [duckDBService])\n\n useEffect(() => {\n duckDBService.addStatusListener(updateStatus)\n updateStatus()\n\n return () => {\n duckDBService.removeStatusListener(updateStatus)\n }\n }, [duckDBService, updateStatus])\n\n const initialize = useCallback(async () => {\n try {\n await duckDBService.initialize(options)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Unknown error occurred\"\n setError(errorMessage)\n Logger.error(\"❌ DuckDB initialization failed:\", err)\n }\n }, [duckDBService, options])\n\n useEffect(() => {\n if (autoInitialize && status === \"not-initialized\") {\n Logger.info(\"🚀 Auto-initializing DuckDB...\")\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(\n async (sql: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQuery(sql)\n },\n [duckDBService, status],\n )\n\n const executeQueries = useCallback(\n async (queries: string[]): Promise<unknown[]> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQueries(queries)\n },\n [duckDBService, status],\n )\n\n const createTableFromData = useCallback(\n async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ): Promise<void> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.createTableFromData(tableName, data, options)\n },\n [duckDBService, status],\n )\n\n const getTableInfo = useCallback(\n async (tableName: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.getTableInfo(tableName)\n },\n [duckDBService, status],\n )\n\n const listTables = useCallback(async (): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.listTables()\n }, [duckDBService, status])\n\n return {\n status,\n error,\n executeQuery,\n executeQueries,\n createTableFromData,\n getTableInfo,\n listTables,\n isReady: status === \"ready\",\n initialize,\n }\n}\n\n/**\n * Hook for simple DuckDB query execution.\n * シンプルな DuckDB クエリ実行のためのフック。\n *\n * @remarks\n * `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。\n * `.length` や `.map` は持たないため、行配列として扱う場合は必ず\n * {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく\n * Arrow Table 相当の型を想定)。\n *\n * @example\n * ```tsx\n * const { data } = useDuckDBQuery(\"SELECT * FROM t\")\n * const rows = duckdbTableToArray<Row>(data)\n * ```\n *\n * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果(生の Arrow Table)と状態\n */\nexport const useDuckDBQuery = <T = unknown>(\n sql: string,\n dependencies: DependencyList = [],\n): {\n data: T | null\n loading: boolean\n error: string | null\n refetch: () => Promise<void>\n} => {\n const { executeQuery, isReady } = useDuckDB()\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refetch = useCallback(async () => {\n if (!(isReady && sql.trim())) {\n return\n }\n\n try {\n setLoading(true)\n setError(null)\n const result = await executeQuery(sql)\n setData(result as T)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Query execution failed\"\n setError(errorMessage)\n setData(null)\n } finally {\n setLoading(false)\n }\n }, [executeQuery, isReady, sql])\n\n // 可変長の dependencies を直接スプレッドせず、変化を検知する固定サイズのバージョンに変換する。\n const dependenciesVersion = useDependencyVersion(dependencies)\n\n useEffect(() => {\n void refetch()\n }, [refetch, dependenciesVersion])\n\n return {\n data,\n loading,\n error,\n refetch,\n }\n}\n","/**\n * DuckDB Web Worker Service\n *\n * Provides reusable DuckDB-wasm functionality across the application.\n * アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。\n */\n\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/** 初期化タイムアウト(WASM ダウンロードを考慮した長め設定)。 */\nconst INIT_TIMEOUT_MS = 120000\n/** クエリ実行タイムアウト。 */\nconst QUERY_TIMEOUT_MS = 60000\n/** splink_udfs インストールの個別タイムアウト。 */\nconst SPLINK_TIMEOUT_MS = 10000\n/** バッチ INSERT の1回あたり行数。 */\nconst BATCH_SIZE = 1000\n/** 型推定に使用する先頭サンプル行数。 */\nconst TYPE_INFERENCE_SAMPLE_SIZE = 100\n/**\n * 文字列を TIMESTAMP とみなすための厳密パターン。\n * `YYYY-MM-DD` または `YYYY-MM-DD[ T]HH:MM(:SS(.sss)?)?(Z|±HH:MM)?` のみ許容し、\n * `ABC-123` のような「ハイフンを含むが日付ではない」文字列を誤って日付扱いしない。\n */\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n\n/** 内部初期化のアボート指示を運ぶシグナル。 */\ninterface InitSignal {\n aborted: boolean\n}\n\n/**\n * Options for opening a persistent database (e.g. OPFS-backed).\n * 永続データベース (OPFS など) を開くためのオプション。\n */\nexport interface DuckDBDatabaseOptions {\n /** Database path. Use an `opfs://` path for a persistent OPFS-backed database. `opfs://` パスで OPFS 永続 DB を開く。 */\n path: string\n /** Access mode (defaults to READ_WRITE). アクセスモード (既定 READ_WRITE)。 */\n accessMode?: duckdb.DuckDBAccessMode\n /** How `opfs://` files are handled during SQL execution (defaults to \"auto\"). SQL 実行時の `opfs://` ファイル登録の扱い (既定 \"auto\")。 */\n opfsFileHandling?: \"auto\" | \"manual\"\n}\n\n/**\n * Options accepted by {@link DuckDBService.initialize}.\n * initialize() が受け取るオプション。\n */\nexport interface DuckDBInitializeOptions {\n /** Custom bundles (defaults to jsDelivr bundles). カスタムバンドル (既定は jsDelivr)。 */\n bundles?: duckdb.DuckDBBundles\n /** Persistent database to open after instantiation. インスタンス化後に開く永続データベース。 */\n database?: DuckDBDatabaseOptions\n}\n\nconst noop = (): void => {\n // 直列化キューの継続用。前段の解決/拒否を握りつぶす。\n}\n\n/**\n * DuckDB service for browser-based SQL operations.\n * ブラウザベースの SQL 操作のための DuckDB サービス。\n */\nclass DuckDBService {\n private static instance: DuckDBService | null = null\n private operationFlags: Map<string, Promise<void>> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private connectionPromise: Promise<duckdb.AsyncDuckDBConnection> | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\n\n /** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */\n private pendingWorker: Worker | null = null\n private pendingWorkerUrl: string | null = null\n\n /**\n * 共有コネクション上での操作直列化キュー。\n * createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。\n */\n private operationQueue: Promise<unknown> = Promise.resolve()\n\n private listeners: Set<() => void> = new Set()\n\n /**\n * Get singleton instance of DuckDBService.\n * DuckDBService のシングルトンインスタンスを取得します。\n */\n public static getInstance(): DuckDBService {\n if (!DuckDBService.instance) {\n DuckDBService.instance = new DuckDBService()\n }\n return DuckDBService.instance\n }\n\n /**\n * Creates a dedicated (non-shared) service instance, e.g. for a persistent OPFS database.\n * 専用 (非共有) のサービスインスタンスを生成します (OPFS 永続 DB を共有シングルトンと分離して持つ用途)。\n */\n public static create(): DuckDBService {\n return new DuckDBService()\n }\n\n /**\n * Add status change listener.\n * 状態変更リスナーを追加します。\n */\n public addStatusListener(listener: () => void): void {\n this.listeners.add(listener)\n }\n\n /**\n * Remove status change listener.\n * 状態変更リスナーを削除します。\n */\n public removeStatusListener(listener: () => void): void {\n this.listeners.delete(listener)\n }\n\n /**\n * Notify all listeners of status change.\n * すべてのリスナーに状態変更を通知します。\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener()\n } catch (error) {\n Logger.error(\"⚠️ Error in status listener:\", error)\n }\n }\n }\n\n /**\n * Get current status.\n * 現在のステータスを取得します。\n */\n public getStatus(): \"not-initialized\" | \"initializing\" | \"ready\" | \"error\" {\n if (this.hasError) {\n return \"error\"\n }\n if (this.isInitializing) {\n return \"initializing\"\n }\n if (this.workerInstance) {\n return \"ready\"\n }\n return \"not-initialized\"\n }\n\n /** Enqueue a task on the single-connection serialization queue. */\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.operationQueue.then(task, task)\n this.operationQueue = run.then(noop, noop)\n return run\n }\n\n /** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */\n private assertNotAborted(signal: InitSignal): void {\n if (signal.aborted) {\n throw new Error(\"DuckDB initialization aborted\")\n }\n }\n\n /** 初期化中の Worker / Blob URL を確実に破棄する。 */\n private abortPendingWorker(): void {\n if (this.pendingWorker) {\n try {\n this.pendingWorker.terminate()\n } catch {\n // terminate は冪等でなくても良い。失敗は無視する。\n }\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl) {\n try {\n URL.revokeObjectURL(this.pendingWorkerUrl)\n } catch {\n // revoke 失敗は無視する。\n }\n this.pendingWorkerUrl = null\n }\n }\n\n /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB> {\n if (this.workerInstance) {\n return this.workerInstance\n }\n\n if (this.isInitializing && this.initPromise) {\n return this.initPromise\n }\n\n this.isInitializing = true\n this.hasError = false\n this.notifyListeners()\n\n // タイムアウト設定 (120秒) - WASMのダウンロードなどを考慮して長めに設定\n const signal: InitSignal = { aborted: false }\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n signal.aborted = true\n reject(new Error(`DuckDB initialization timed out after ${INIT_TIMEOUT_MS}ms`))\n }, INIT_TIMEOUT_MS)\n })\n\n this.initPromise = Promise.race([this.internalInitializeWorker(signal, options), timeoutPromise])\n\n try {\n const result = await this.initPromise\n clearTimeout(timeoutId)\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n // 失敗/タイムアウト時は状態を完全にリセットして再試行可能にする。\n signal.aborted = true\n this.isInitializing = false\n this.hasError = true\n this.workerInstance = null\n this.connectionInstance = null\n this.connectionPromise = null\n this.initPromise = null\n this.abortPendingWorker()\n this.notifyListeners()\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n *\n * workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は\n * ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。\n */\n private async internalInitializeWorker(signal: InitSignal, options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB> {\n let workerUrl: string | null = null\n let worker: Worker | null = null\n try {\n Logger.info(\"🔧 Starting DuckDB Worker initialization...\")\n Logger.info(\"📦 Getting DuckDB bundles...\")\n const baseBundles = options?.bundles || duckdb.getJsDelivrBundles()\n\n Logger.info(\"🔍 Selecting appropriate bundle for browser...\")\n const bundle = await duckdb.selectBundle(baseBundles)\n this.assertNotAborted(signal)\n\n // Blob Worker 内の importScripts/fetch でパス解決できるように、相対パス(スラッシュ始まり)は絶対 URL に変換する\n const toAbsoluteUrl = (urlPath: string): string => {\n if (urlPath.startsWith(\"/\") && typeof window !== \"undefined\" && window.location) {\n return new URL(urlPath, window.location.origin).toString()\n }\n return urlPath\n }\n\n const normalizedBundle = {\n mainWorker: bundle.mainWorker ? toAbsoluteUrl(bundle.mainWorker) : \"\",\n mainModule: bundle.mainModule ? toAbsoluteUrl(bundle.mainModule) : \"\",\n pthreadWorker: bundle.pthreadWorker ? toAbsoluteUrl(bundle.pthreadWorker) : undefined,\n }\n\n Logger.info(\"✅ Bundle selected:\", {\n mainWorker: normalizedBundle.mainWorker,\n mainModule: normalizedBundle.mainModule,\n pthreadWorker: normalizedBundle.pthreadWorker,\n })\n\n Logger.info(\"🚀 Creating Worker with Blob URL...\")\n workerUrl = URL.createObjectURL(new Blob([`importScripts(\"${normalizedBundle.mainWorker}\");`], { type: \"text/javascript\" }))\n\n Logger.info(\"👷 Creating Worker instance...\")\n worker = new Worker(workerUrl)\n\n this.assertNotAborted(signal)\n this.pendingWorker = worker\n this.pendingWorkerUrl = workerUrl\n\n Logger.info(\"🤝 Connecting to AsyncDuckDB instance...\")\n const db = new duckdb.AsyncDuckDB(new duckdb.ConsoleLogger(), worker)\n await db.instantiate(normalizedBundle.mainModule, normalizedBundle.pthreadWorker)\n this.assertNotAborted(signal)\n\n // 永続データベース指定時はインスタンス化直後に開く (opfs:// パスでリロードを跨いで保持される)\n if (options?.database) {\n Logger.info(\"🗄️ Opening persistent database:\", options.database.path)\n await db.open({\n path: options.database.path,\n accessMode: options.database.accessMode ?? duckdb.DuckDBAccessMode.READ_WRITE,\n opfs: { fileHandling: options.database.opfsFileHandling ?? \"auto\" },\n })\n this.assertNotAborted(signal)\n }\n\n Logger.info(\"📦 Installing splink_udfs from community...\")\n const connection = await db.connect()\n try {\n // splink_udfs のインストール/ロードはネットワークアクセスを伴うため、\n // 個別の短いタイムアウトを設けてハングを防止する。\n let splinkTimeoutId!: ReturnType<typeof setTimeout>\n const splinkTimeoutPromise = new Promise<never>((_, reject) => {\n splinkTimeoutId = setTimeout(() => {\n reject(new Error(`splink_udfs installation timed out after ${SPLINK_TIMEOUT_MS}ms`))\n }, SPLINK_TIMEOUT_MS)\n })\n\n await Promise.race([\n (async () => {\n await connection.query(\"INSTALL splink_udfs FROM community;\")\n Logger.info(\"✅ splink_udfs installed successfully\")\n\n await connection.query(\"LOAD splink_udfs;\")\n Logger.info(\"✅ splink_udfs loaded successfully\")\n })(),\n splinkTimeoutPromise,\n ])\n clearTimeout(splinkTimeoutId)\n } catch (error) {\n // タイムアウトやネットワークエラーの場合は警告ログを出して続行\n Logger.warn(\"⚠️ Failed to install/load splink_udfs:\", error)\n } finally {\n await connection.close()\n }\n this.assertNotAborted(signal)\n\n // すべて成功したときのみ公開する。\n this.workerInstance = db\n this.pendingWorker = null\n this.pendingWorkerUrl = null\n URL.revokeObjectURL(workerUrl)\n\n Logger.info(\"✅ DuckDB Worker initialized successfully\")\n return db\n } catch (error) {\n // 途中生成物を必ず破棄する(Worker スレッド / Blob URL のリーク防止)。\n if (worker) {\n try {\n worker.terminate()\n } catch {\n // 無視する。\n }\n }\n if (workerUrl) {\n try {\n URL.revokeObjectURL(workerUrl)\n } catch {\n // 無視する。\n }\n }\n if (this.pendingWorker === worker) {\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl === workerUrl) {\n this.pendingWorkerUrl = null\n }\n\n Logger.error(\"❌ Failed to initialize DuckDB Worker:\", error)\n if (error instanceof Error) {\n Logger.error(\"❌ Error name:\", error.name)\n Logger.error(\"❌ Error message:\", error.message)\n Logger.error(\"❌ Error stack:\", error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n *\n * 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (this.connectionInstance) {\n return this.connectionInstance\n }\n if (!this.connectionPromise) {\n this.connectionPromise = (async () => {\n const db = await this.initialize()\n const connection = await db.connect()\n this.connectionInstance = connection\n return connection\n })()\n }\n try {\n return await this.connectionPromise\n } catch (error) {\n // 失敗時はリセットして次回リトライ可能にする。\n this.connectionPromise = null\n throw error\n }\n }\n\n /**\n * Run a single query with a timeout, discarding the connection on timeout.\n * 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。\n *\n * タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して\n * 後続クエリが同じ(詰まった)コネクションで待たされないようにする。\n */\n private async runQueryWithTimeout(sql: string): Promise<unknown> {\n let timedOut = false\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n timedOut = true\n reject(new Error(`DuckDB query execution timed out after ${QUERY_TIMEOUT_MS}ms`))\n }, QUERY_TIMEOUT_MS)\n })\n\n // コネクション取得とクエリ実行の両方をタイムアウトの対象にする。\n const run = (async () => {\n const connection = await this.getConnection()\n return connection.query(sql)\n })()\n\n try {\n const result = await Promise.race([run, timeoutPromise])\n clearTimeout(timeoutId)\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n if (timedOut) {\n const stale = this.connectionInstance\n this.connectionInstance = null\n this.connectionPromise = null\n if (stale) {\n void stale.close().catch(() => {\n // 詰まったコネクションの close 失敗は無視する。\n })\n }\n }\n Logger.error(\"❌ Failed to execute SQL query:\", error)\n throw error\n }\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n return this.enqueue(() => this.runQueryWithTimeout(sql))\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します(逐次実行)。\n *\n * バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n return this.enqueue(async () => {\n const results: unknown[] = []\n for (const query of queries) {\n results.push(await this.runQueryWithTimeout(query))\n }\n return results\n })\n }\n\n /**\n * Collect the union of column names across all rows (first-seen order).\n * 全行を走査して列名の和集合を取得します(初出順を保持)。\n * 先頭行に存在しないキーを取りこぼさないため全行を対象とする。\n */\n private collectColumnNames(rows: Record<string, unknown>[]): string[] {\n const seen = new Set<string>()\n const names: string[] = []\n for (const row of rows) {\n if (!row) {\n continue\n }\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key)\n names.push(key)\n }\n }\n }\n return names\n }\n\n /** Check whether a string is a genuine (strict) date/timestamp literal. */\n private looksLikeTimestamp(value: string): boolean {\n if (!TIMESTAMP_PATTERN.test(value)) {\n return false\n }\n return !Number.isNaN(new Date(value).getTime())\n }\n\n /**\n * Infer column types from a data sample for the given columns.\n * 指定した列について、データサンプルから DuckDB の型を推定します。\n *\n * 整数は桁あふれを避けるため一律 BIGINT を採用する。\n */\n private inferColumnTypes(sample: Record<string, unknown>[], columnNames: string[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n\n for (const key of columnNames) {\n const values = sample.map((row) => row?.[key]).filter((val) => val !== null && val !== undefined)\n\n if (values.length === 0) {\n columnTypes[key] = \"VARCHAR\"\n continue\n }\n\n const typeInfo = {\n number: 0,\n integer: 0,\n boolean: 0,\n date: 0,\n string: 0,\n }\n\n for (const value of values) {\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n }\n } else {\n // NaN / Infinity は数値列にできないため文字列扱い(実挿入時は NULL 化)。\n typeInfo.string += 1\n }\n } else if (typeof value === \"bigint\") {\n typeInfo.number += 1\n typeInfo.integer += 1\n } else if (typeof value === \"boolean\") {\n typeInfo.boolean += 1\n } else if (value instanceof Date) {\n typeInfo.date += 1\n } else if (typeof value === \"string\" && this.looksLikeTimestamp(value)) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n columnTypes[key] = typeInfo.integer === values.length ? \"BIGINT\" : \"DOUBLE\"\n } else if (typeInfo.boolean === values.length) {\n columnTypes[key] = \"BOOLEAN\"\n } else if (typeInfo.date === values.length) {\n columnTypes[key] = \"TIMESTAMP\"\n } else {\n columnTypes[key] = \"VARCHAR\"\n }\n }\n\n return columnTypes\n }\n\n /**\n * Format value for SQL insertion.\n * SQL 挿入用に値をフォーマットします。\n */\n private formatValueForSQL(value: unknown): string {\n if (value === null || value === undefined) {\n return \"NULL\"\n }\n if (typeof value === \"string\") {\n return `'${value.replace(/'/g, \"''\")}'`\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`\n }\n if (typeof value === \"boolean\") {\n return value ? \"TRUE\" : \"FALSE\"\n }\n if (typeof value === \"bigint\") {\n return value.toString()\n }\n if (typeof value === \"number\") {\n // NaN / Infinity / -Infinity は有効な SQL 数値リテラルではないため NULL 化する。\n if (!Number.isFinite(value)) {\n return \"NULL\"\n }\n return String(value)\n }\n // オブジェクト / 配列などは JSON 文字列としてクォート・エスケープする。\n try {\n const json = JSON.stringify(value)\n if (json === undefined) {\n return \"NULL\"\n }\n return `'${json.replace(/'/g, \"''\")}'`\n } catch {\n return \"NULL\"\n }\n }\n\n /**\n * Quote a SQL identifier (table / column / primary key) safely.\n * SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。\n */\n private quoteIdentifier(name: string): string {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new Error(\"Invalid SQL identifier\")\n }\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\n *\n * 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。\n */\n public async createTableFromData<T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n } = {},\n ): Promise<void> {\n if (!data || data.length === 0) {\n throw new Error(\"Data array is empty\")\n }\n\n const operationKey = `create_table_${tableName}`\n const verbose = options.verbose ?? true\n\n const inFlight = this.operationFlags.get(operationKey)\n if (inFlight) {\n if (verbose) {\n Logger.info(`⏳ Table creation for '${tableName}' is already in progress, awaiting existing operation...`)\n }\n return inFlight\n }\n\n const operation = this.enqueue(() => this.buildTable(tableName, data, options, verbose))\n this.operationFlags.set(operationKey, operation)\n try {\n await operation\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /** Actual (serialized, transactional) table build. */\n private async buildTable<T extends Record<string, unknown>>(tableName: string, data: T[], options: { dropIfExists?: boolean; primaryKey?: string }, verbose: boolean): Promise<void> {\n try {\n const connection = await this.getConnection()\n const quotedTable = this.quoteIdentifier(tableName)\n\n const columnNames = this.collectColumnNames(data)\n if (columnNames.length === 0) {\n throw new Error(\"Data rows have no columns\")\n }\n\n const sampleSize = Math.min(data.length, TYPE_INFERENCE_SAMPLE_SIZE)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize), columnNames)\n\n const columnDefs = columnNames.map((key) => `${this.quoteIdentifier(key)} ${columnTypes[key]}`)\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (${this.quoteIdentifier(options.primaryKey)})` : \"\"\n const createTableSQL = `CREATE TABLE ${quotedTable} (${columnDefs.join(\", \")}${primaryKeyClause})`\n const columnList = columnNames.map((col) => this.quoteIdentifier(col)).join(\", \")\n\n if (verbose) {\n Logger.info(`📥 Building table '${tableName}' with ${data.length} rows`)\n }\n\n // DROP / CREATE / INSERT を1トランザクションにまとめ、途中失敗時はロールバックして\n // 半端に作成されたテーブルを残さない。\n await connection.query(\"BEGIN TRANSACTION\")\n try {\n if (options.dropIfExists) {\n if (verbose) {\n Logger.info(`🗑️ Dropping table '${tableName}' if exists`)\n }\n await connection.query(`DROP TABLE IF EXISTS ${quotedTable}`)\n }\n\n if (verbose) {\n Logger.info(\"🔨 Creating table with SQL:\", createTableSQL)\n }\n await connection.query(createTableSQL)\n\n for (let i = 0; i < data.length; i += BATCH_SIZE) {\n const batch = data.slice(i, i + BATCH_SIZE)\n const valueRows = batch.map((row) => {\n // null/undefined 行はすべて NULL として扱う(不揃いなデータへの耐性)。\n const values = columnNames.map((col) => this.formatValueForSQL(row?.[col]))\n return `(${values.join(\", \")})`\n })\n\n const insertSQL = `INSERT INTO ${quotedTable} (${columnList}) VALUES ${valueRows.join(\", \")}`\n await connection.query(insertSQL)\n if (verbose) {\n Logger.info(`📊 Inserted batch ${Math.floor(i / BATCH_SIZE) + 1} (${batch.length} rows)`)\n }\n }\n\n await connection.query(\"COMMIT\")\n } catch (txError) {\n try {\n await connection.query(\"ROLLBACK\")\n } catch (rollbackError) {\n Logger.warn(\"⚠️ Rollback failed:\", rollbackError)\n }\n throw txError\n }\n\n if (verbose) {\n Logger.info(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n }\n } catch (error) {\n Logger.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(`DESCRIBE ${this.quoteIdentifier(tableName)}`)\n })\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(\"SHOW TABLES\")\n })\n }\n\n /**\n * Check if DuckDB is ready.\n * DuckDB が準備完了かどうかを確認します。\n */\n public isReady(): boolean {\n return this.workerInstance !== null\n }\n\n /**\n * Cleanup DuckDB Worker resources.\n * DuckDB Worker のリソースをクリーンアップします。\n */\n public async cleanup(): Promise<void> {\n try {\n if (this.connectionInstance) {\n await this.connectionInstance.close()\n this.connectionInstance = null\n }\n if (this.workerInstance) {\n await this.workerInstance.terminate()\n this.workerInstance = null\n }\n this.abortPendingWorker()\n this.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.connectionPromise = null\n this.operationQueue = Promise.resolve()\n this.operationFlags.clear()\n this.notifyListeners()\n this.listeners.clear()\n } catch (error) {\n Logger.error(\"⚠️ Error during cleanup:\", error)\n }\n }\n\n /**\n * Reset the service instance (useful for testing).\n * サービスインスタンスをリセットします(テスト用)。\n */\n public static resetInstance(): void {\n if (DuckDBService.instance) {\n void DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\n","/**\n * @module utils/logger\n * @description Logger utility for consistent log management across the library.\n * @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。\n */\n\n/**\n * @enum LogLevel\n * @description Log levels for filtering output.\n * @description 出力をフィルタリングするためのログレベル。\n */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/**\n * @interface ILogger\n * @description Interface for a logger object compatible with Console.\n * @description Console と互換性のあるロガーオブジェクトのインターフェース。\n */\nexport interface ILogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * @class Logger\n * @description A wrapper class for handling logging with levels and prefixes.\n * @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。\n */\nexport class Logger implements ILogger {\n private level: LogLevel\n private prefix: string\n private impl: ILogger\n\n /**\n * @constructor\n * @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.\n * @param {string} [prefix=\"[duckdb-helper]\"] - The prefix to add to all log messages.\n * @param {ILogger} [impl=console] - The implementation to use for logging.\n */\n constructor(level: LogLevel = LogLevel.WARN, prefix: string = \"[duckdb-helper]\", impl: ILogger = console) {\n this.level = level\n this.prefix = prefix\n this.impl = impl\n }\n\n private static instance: Logger = new Logger(LogLevel.WARN, \"[duckdb-helper]\")\n\n /**\n * @method setLevel\n * @description Updates the current log level for the static instance.\n * @description 静的インスタンスの現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n static setLevel(level: LogLevel): void {\n Logger.instance.setLevel(level)\n }\n\n /**\n * @method setLevel\n * @description Updates the current log level.\n * @description 現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation for the static instance.\n * @description 静的インスタンスのロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n static setImplementation(impl: ILogger): void {\n Logger.instance.setImplementation(impl)\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation.\n * @description ロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n setImplementation(impl: ILogger): void {\n this.impl = impl\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix for the static instance.\n * @description 静的インスタンスのログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n static setPrefix(prefix: string): void {\n Logger.instance.setPrefix(prefix)\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix.\n * @description ログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n setPrefix(prefix: string): void {\n this.prefix = prefix\n }\n\n private formatMessage(message: unknown): unknown[] {\n if (typeof message === \"string\") {\n return [`${this.prefix} ${message}`]\n }\n return [this.prefix, message]\n }\n\n static debug(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.debug(message, ...optionalParams)\n }\n\n debug(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.DEBUG) {\n this.impl.debug(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static info(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.info(message, ...optionalParams)\n }\n\n info(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.INFO) {\n this.impl.info(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static warn(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.warn(message, ...optionalParams)\n }\n\n warn(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.WARN) {\n this.impl.warn(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static error(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.error(message, ...optionalParams)\n }\n\n error(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.ERROR) {\n this.impl.error(...this.formatMessage(message), ...optionalParams)\n }\n }\n}\n","/**\n * DuckDB Helper Functions\n *\n * Utility functions for working with DuckDB query results.\n * DuckDB クエリ結果を扱うためのユーティリティ関数。\n */\n\nimport type { DuckDBQueryResult } from \"../types/duckdb\"\n\n/**\n * Safely convert DuckDB table to typed array\n * DuckDB テーブルを型付き配列に安全に変換します\n *\n * @param result - DuckDB query result\n * @returns Typed array of results\n */\nexport const duckdbTableToArray = <T>(result: unknown): T[] => {\n if (!result || typeof result !== \"object\") {\n return []\n }\n\n if (\"toArray\" in result && typeof result.toArray === \"function\") {\n return (result as DuckDBQueryResult<T>).toArray()\n }\n\n return []\n}\n\n/**\n * Get table row count\n * テーブルの行数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of rows in the table\n */\nexport const getDuckDBRowCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numRows\" in result && typeof result.numRows === \"number\") {\n return result.numRows\n }\n\n return 0\n}\n\n/**\n * Get table column count\n * テーブルの列数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of columns in the table\n */\nexport const getDuckDBColumnCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numCols\" in result && typeof result.numCols === \"number\") {\n return result.numCols\n }\n\n return 0\n}\n\n/**\n * Check if result is a valid DuckDB table\n * 結果が有効な DuckDB テーブルかどうかをチェックします\n *\n * @param result - DuckDB query result\n * @returns True if result is a valid DuckDB table\n */\nexport const isDuckDBTable = (result: unknown): result is DuckDBQueryResult => {\n if (!result || typeof result !== \"object\") {\n return false\n }\n\n return \"toArray\" in result && typeof result.toArray === \"function\" && \"numRows\" in result && typeof result.numRows === \"number\"\n}\n\n/**\n * Returns whether the current environment can host an OPFS-backed persistent database.\n * 現在の環境が OPFS 永続データベースを利用できるか (navigator.storage.getDirectory の有無) を返します。\n */\nexport const isOpfsSupported = (): boolean => {\n return typeof navigator !== \"undefined\" && typeof navigator.storage !== \"undefined\" && typeof navigator.storage.getDirectory === \"function\"\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","duckdb","LogLevel","_Logger","level","prefix","impl","message","optionalParams","Logger","INIT_TIMEOUT_MS","QUERY_TIMEOUT_MS","SPLINK_TIMEOUT_MS","BATCH_SIZE","TYPE_INFERENCE_SAMPLE_SIZE","TIMESTAMP_PATTERN","noop","_DuckDBService","listener","error","Logger","task","run","signal","options","timeoutId","timeoutPromise","_","reject","result","workerUrl","worker","baseBundles","bundle","toAbsoluteUrl","urlPath","normalizedBundle","db","connection","splinkTimeoutId","splinkTimeoutPromise","sql","timedOut","stale","queries","results","query","rows","seen","names","row","key","value","sample","columnNames","columnTypes","values","val","typeInfo","json","name","tableName","data","operationKey","verbose","inFlight","operation","quotedTable","sampleSize","columnDefs","primaryKeyClause","createTableSQL","columnList","col","i","batch","valueRows","insertSQL","txError","rollbackError","DuckDBService","duckdb_default","DuckDBService","useDependencyVersion","dependencies","ref","useRef","previous","dep","index","useDuckDB","autoInitialize","options","status","setStatus","useState","error","setError","duckDBService","DuckDBService","updateStatus","useCallback","serviceStatus","Logger","useEffect","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","getTableInfo","listTables","useDuckDBQuery","isReady","setData","loading","setLoading","refetch","result","dependenciesVersion","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable","isOpfsSupported"]}
|