@aiquants/duckdb-helper 1.6.7 → 1.6.8
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/README.md +18 -24
- package/dist/index.d.mts +90 -62
- package/dist/index.d.ts +90 -62
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,20 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
DuckDB helper utilities with React hooks, worker initialization, and typed query helpers for browser applications.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **Worker-backed initialization**: Set up DuckDB-wasm workers with recommended CDN bundles.
|
|
8
|
+
- **React hooks**: Provide `useDuckDB` and `useDuckDBQuery` for component integrations.
|
|
9
|
+
- **Singleton service**: Share a single DuckDB instance across application components.
|
|
10
|
+
- **Typed helpers**: Safely convert query results with type guards.
|
|
11
|
+
- **TypeScript first**: Full typings for queries, tables, and helper functions.
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
- **React hooks**: Provide `useDuckDB` と `useDuckDBQuery` for component integrations / コンポーネント統合向けの `useDuckDB` と `useDuckDBQuery`
|
|
11
|
-
- **Singleton service**: Share a single DuckDB インスタンス全体で共有できるシングルトンサービス
|
|
12
|
-
- **Typed helpers**: Safely convert query results with type guards / 型ガード付きでクエリ結果を安全に変換
|
|
13
|
-
- **TypeScript first**: Full typings for queries, tables, and helper functions / クエリやテーブル、ヘルパー関数の完全な型定義
|
|
14
|
-
|
|
15
|
-
## Installation / インストール
|
|
13
|
+
## Installation
|
|
16
14
|
|
|
17
15
|
```bash
|
|
18
|
-
# Using pnpm (recommended)
|
|
16
|
+
# Using pnpm (recommended)
|
|
19
17
|
pnpm add @aiquants/duckdb-helper
|
|
20
18
|
|
|
21
19
|
# Using npm
|
|
@@ -25,9 +23,9 @@ npm install @aiquants/duckdb-helper
|
|
|
25
23
|
yarn add @aiquants/duckdb-helper
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
## Quick Start
|
|
26
|
+
## Quick Start
|
|
29
27
|
|
|
30
|
-
### Initialize the
|
|
28
|
+
### Initialize the Service
|
|
31
29
|
|
|
32
30
|
```typescript
|
|
33
31
|
import duckDBService from '@aiquants/duckdb-helper'
|
|
@@ -38,7 +36,7 @@ const result = await duckDBService.executeQuery('SELECT 1 AS value')
|
|
|
38
36
|
console.log(result)
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
### Use the React
|
|
39
|
+
### Use the React Hook
|
|
42
40
|
|
|
43
41
|
```tsx
|
|
44
42
|
import { useDuckDB } from '@aiquants/duckdb-helper'
|
|
@@ -65,7 +63,7 @@ export const Dashboard = () => {
|
|
|
65
63
|
}
|
|
66
64
|
```
|
|
67
65
|
|
|
68
|
-
### Convert
|
|
66
|
+
### Convert Results to Arrays
|
|
69
67
|
|
|
70
68
|
```typescript
|
|
71
69
|
import { duckdbTableToArray } from '@aiquants/duckdb-helper'
|
|
@@ -74,23 +72,19 @@ const table = await duckDBService.executeQuery('SELECT * FROM items')
|
|
|
74
72
|
const rows = duckdbTableToArray<{ id: number; name: string }>(table)
|
|
75
73
|
```
|
|
76
74
|
|
|
77
|
-
## Documentation
|
|
78
|
-
|
|
79
|
-
詳しい使用方法は `docs/useDuckDB.md` を参照してください。
|
|
75
|
+
## Documentation
|
|
80
76
|
|
|
81
77
|
For detailed guidance, see `docs/useDuckDB.md`.
|
|
82
78
|
|
|
83
|
-
## Requirements
|
|
79
|
+
## Requirements
|
|
84
80
|
|
|
85
|
-
- React 16.8+ with Hooks support
|
|
86
|
-
- Modern browsers with Web Worker support
|
|
81
|
+
- React 16.8+ with Hooks support
|
|
82
|
+
- Modern browsers with Web Worker support
|
|
87
83
|
|
|
88
|
-
## License
|
|
84
|
+
## License
|
|
89
85
|
|
|
90
86
|
MIT License
|
|
91
87
|
|
|
92
|
-
## Contributing
|
|
88
|
+
## Contributing
|
|
93
89
|
|
|
94
90
|
Contributions are welcome! Please open an issue or submit a pull request.
|
|
95
|
-
|
|
96
|
-
貢献を歓迎します。Issue や Pull Request をぜひお寄せください。
|
package/dist/index.d.mts
CHANGED
|
@@ -2,71 +2,34 @@ import { DependencyList } from 'react';
|
|
|
2
2
|
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* DuckDB
|
|
5
|
+
* DuckDB Web Worker Service
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Provides reusable DuckDB-wasm functionality across the application.
|
|
8
|
+
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
interface UseDuckDBResult {
|
|
12
|
-
status: "not-initialized" | "initializing" | "ready" | "error";
|
|
13
|
-
error: string | null;
|
|
14
|
-
executeQuery: (sql: string) => Promise<unknown>;
|
|
15
|
-
executeQueries: (queries: string[]) => Promise<unknown[]>;
|
|
16
|
-
createTableFromData: <T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
17
|
-
dropIfExists?: boolean;
|
|
18
|
-
primaryKey?: string;
|
|
19
|
-
verbose?: boolean;
|
|
20
|
-
}) => Promise<void>;
|
|
21
|
-
getTableInfo: (tableName: string) => Promise<unknown>;
|
|
22
|
-
listTables: () => Promise<unknown>;
|
|
23
|
-
isReady: boolean;
|
|
24
|
-
initialize: () => Promise<void>;
|
|
25
|
-
}
|
|
26
11
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* @param autoInitialize - 自動初期化を行うかどうか
|
|
31
|
-
* @returns DuckDB の状態と操作関数
|
|
12
|
+
* Options for opening a persistent database (e.g. OPFS-backed).
|
|
13
|
+
* 永続データベース (OPFS など) を開くためのオプション。
|
|
32
14
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。
|
|
42
|
-
* `.length` や `.map` は持たないため、行配列として扱う場合は必ず
|
|
43
|
-
* {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく
|
|
44
|
-
* Arrow Table 相当の型を想定)。
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```tsx
|
|
48
|
-
* const { data } = useDuckDBQuery("SELECT * FROM t")
|
|
49
|
-
* const rows = duckdbTableToArray<Row>(data)
|
|
50
|
-
* ```
|
|
51
|
-
*
|
|
52
|
-
* @param sql - 実行する SQL クエリ
|
|
53
|
-
* @param dependencies - クエリを再実行するための依存配列
|
|
54
|
-
* @returns クエリ結果(生の Arrow Table)と状態
|
|
55
|
-
*/
|
|
56
|
-
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
57
|
-
data: T | null;
|
|
58
|
-
loading: boolean;
|
|
59
|
-
error: string | null;
|
|
60
|
-
refetch: () => Promise<void>;
|
|
61
|
-
};
|
|
62
|
-
|
|
15
|
+
interface DuckDBDatabaseOptions {
|
|
16
|
+
/** Database path. Use an `opfs://` path for a persistent OPFS-backed database. `opfs://` パスで OPFS 永続 DB を開く。 */
|
|
17
|
+
path: string;
|
|
18
|
+
/** Access mode (defaults to READ_WRITE). アクセスモード (既定 READ_WRITE)。 */
|
|
19
|
+
accessMode?: duckdb.DuckDBAccessMode;
|
|
20
|
+
/** How `opfs://` files are handled during SQL execution (defaults to "auto"). SQL 実行時の `opfs://` ファイル登録の扱い (既定 "auto")。 */
|
|
21
|
+
opfsFileHandling?: "auto" | "manual";
|
|
22
|
+
}
|
|
63
23
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* Provides reusable DuckDB-wasm functionality across the application.
|
|
67
|
-
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
24
|
+
* Options accepted by {@link DuckDBService.initialize}.
|
|
25
|
+
* initialize() が受け取るオプション。
|
|
68
26
|
*/
|
|
69
|
-
|
|
27
|
+
interface DuckDBInitializeOptions {
|
|
28
|
+
/** Custom bundles (defaults to jsDelivr bundles). カスタムバンドル (既定は jsDelivr)。 */
|
|
29
|
+
bundles?: duckdb.DuckDBBundles;
|
|
30
|
+
/** Persistent database to open after instantiation. インスタンス化後に開く永続データベース。 */
|
|
31
|
+
database?: DuckDBDatabaseOptions;
|
|
32
|
+
}
|
|
70
33
|
/**
|
|
71
34
|
* DuckDB service for browser-based SQL operations.
|
|
72
35
|
* ブラウザベースの SQL 操作のための DuckDB サービス。
|
|
@@ -94,6 +57,11 @@ declare class DuckDBService {
|
|
|
94
57
|
* DuckDBService のシングルトンインスタンスを取得します。
|
|
95
58
|
*/
|
|
96
59
|
static getInstance(): DuckDBService;
|
|
60
|
+
/**
|
|
61
|
+
* Creates a dedicated (non-shared) service instance, e.g. for a persistent OPFS database.
|
|
62
|
+
* 専用 (非共有) のサービスインスタンスを生成します (OPFS 永続 DB を共有シングルトンと分離して持つ用途)。
|
|
63
|
+
*/
|
|
64
|
+
static create(): DuckDBService;
|
|
97
65
|
/**
|
|
98
66
|
* Add status change listener.
|
|
99
67
|
* 状態変更リスナーを追加します。
|
|
@@ -124,9 +92,7 @@ declare class DuckDBService {
|
|
|
124
92
|
* Initialize DuckDB Worker instance.
|
|
125
93
|
* DuckDB Worker インスタンスを初期化します。
|
|
126
94
|
*/
|
|
127
|
-
initialize(options?:
|
|
128
|
-
bundles?: duckdb.DuckDBBundles;
|
|
129
|
-
}): Promise<duckdb.AsyncDuckDB>;
|
|
95
|
+
initialize(options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB>;
|
|
130
96
|
/**
|
|
131
97
|
* Internal worker initialization method.
|
|
132
98
|
* 内部的な Worker 初期化メソッド。
|
|
@@ -229,6 +195,63 @@ declare class DuckDBService {
|
|
|
229
195
|
|
|
230
196
|
declare const _default: DuckDBService;
|
|
231
197
|
|
|
198
|
+
/**
|
|
199
|
+
* DuckDB React Hook
|
|
200
|
+
*
|
|
201
|
+
* React hook for using DuckDB functionality in components.
|
|
202
|
+
* コンポーネントで DuckDB 機能を使用するための React フック。
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
interface UseDuckDBResult {
|
|
206
|
+
status: "not-initialized" | "initializing" | "ready" | "error";
|
|
207
|
+
error: string | null;
|
|
208
|
+
executeQuery: (sql: string) => Promise<unknown>;
|
|
209
|
+
executeQueries: (queries: string[]) => Promise<unknown[]>;
|
|
210
|
+
createTableFromData: <T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
211
|
+
dropIfExists?: boolean;
|
|
212
|
+
primaryKey?: string;
|
|
213
|
+
verbose?: boolean;
|
|
214
|
+
}) => Promise<void>;
|
|
215
|
+
getTableInfo: (tableName: string) => Promise<unknown>;
|
|
216
|
+
listTables: () => Promise<unknown>;
|
|
217
|
+
isReady: boolean;
|
|
218
|
+
initialize: () => Promise<void>;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* DuckDB functionality hook.
|
|
222
|
+
* DuckDB 機能を提供するフック。
|
|
223
|
+
*
|
|
224
|
+
* @param autoInitialize - 自動初期化を行うかどうか
|
|
225
|
+
* @returns DuckDB の状態と操作関数
|
|
226
|
+
*/
|
|
227
|
+
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
|
+
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
249
|
+
data: T | null;
|
|
250
|
+
loading: boolean;
|
|
251
|
+
error: string | null;
|
|
252
|
+
refetch: () => Promise<void>;
|
|
253
|
+
};
|
|
254
|
+
|
|
232
255
|
/**
|
|
233
256
|
* DuckDB Type Definitions
|
|
234
257
|
*
|
|
@@ -289,6 +312,11 @@ declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
|
289
312
|
* @returns True if result is a valid DuckDB table
|
|
290
313
|
*/
|
|
291
314
|
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
|
+
declare const isOpfsSupported: () => boolean;
|
|
292
320
|
|
|
293
321
|
/**
|
|
294
322
|
* @module utils/logger
|
|
@@ -388,4 +416,4 @@ declare class Logger implements ILogger {
|
|
|
388
416
|
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
389
417
|
}
|
|
390
418
|
|
|
391
|
-
export { type DuckDBQueryResult, type DuckDBRow, DuckDBService, type ILogger, LogLevel, Logger, type UseDuckDBResult, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, useDuckDB, useDuckDBQuery };
|
|
419
|
+
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
|
@@ -2,71 +2,34 @@ import { DependencyList } from 'react';
|
|
|
2
2
|
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* DuckDB
|
|
5
|
+
* DuckDB Web Worker Service
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Provides reusable DuckDB-wasm functionality across the application.
|
|
8
|
+
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
interface UseDuckDBResult {
|
|
12
|
-
status: "not-initialized" | "initializing" | "ready" | "error";
|
|
13
|
-
error: string | null;
|
|
14
|
-
executeQuery: (sql: string) => Promise<unknown>;
|
|
15
|
-
executeQueries: (queries: string[]) => Promise<unknown[]>;
|
|
16
|
-
createTableFromData: <T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
17
|
-
dropIfExists?: boolean;
|
|
18
|
-
primaryKey?: string;
|
|
19
|
-
verbose?: boolean;
|
|
20
|
-
}) => Promise<void>;
|
|
21
|
-
getTableInfo: (tableName: string) => Promise<unknown>;
|
|
22
|
-
listTables: () => Promise<unknown>;
|
|
23
|
-
isReady: boolean;
|
|
24
|
-
initialize: () => Promise<void>;
|
|
25
|
-
}
|
|
26
11
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* @param autoInitialize - 自動初期化を行うかどうか
|
|
31
|
-
* @returns DuckDB の状態と操作関数
|
|
12
|
+
* Options for opening a persistent database (e.g. OPFS-backed).
|
|
13
|
+
* 永続データベース (OPFS など) を開くためのオプション。
|
|
32
14
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。
|
|
42
|
-
* `.length` や `.map` は持たないため、行配列として扱う場合は必ず
|
|
43
|
-
* {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく
|
|
44
|
-
* Arrow Table 相当の型を想定)。
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```tsx
|
|
48
|
-
* const { data } = useDuckDBQuery("SELECT * FROM t")
|
|
49
|
-
* const rows = duckdbTableToArray<Row>(data)
|
|
50
|
-
* ```
|
|
51
|
-
*
|
|
52
|
-
* @param sql - 実行する SQL クエリ
|
|
53
|
-
* @param dependencies - クエリを再実行するための依存配列
|
|
54
|
-
* @returns クエリ結果(生の Arrow Table)と状態
|
|
55
|
-
*/
|
|
56
|
-
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
57
|
-
data: T | null;
|
|
58
|
-
loading: boolean;
|
|
59
|
-
error: string | null;
|
|
60
|
-
refetch: () => Promise<void>;
|
|
61
|
-
};
|
|
62
|
-
|
|
15
|
+
interface DuckDBDatabaseOptions {
|
|
16
|
+
/** Database path. Use an `opfs://` path for a persistent OPFS-backed database. `opfs://` パスで OPFS 永続 DB を開く。 */
|
|
17
|
+
path: string;
|
|
18
|
+
/** Access mode (defaults to READ_WRITE). アクセスモード (既定 READ_WRITE)。 */
|
|
19
|
+
accessMode?: duckdb.DuckDBAccessMode;
|
|
20
|
+
/** How `opfs://` files are handled during SQL execution (defaults to "auto"). SQL 実行時の `opfs://` ファイル登録の扱い (既定 "auto")。 */
|
|
21
|
+
opfsFileHandling?: "auto" | "manual";
|
|
22
|
+
}
|
|
63
23
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* Provides reusable DuckDB-wasm functionality across the application.
|
|
67
|
-
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
24
|
+
* Options accepted by {@link DuckDBService.initialize}.
|
|
25
|
+
* initialize() が受け取るオプション。
|
|
68
26
|
*/
|
|
69
|
-
|
|
27
|
+
interface DuckDBInitializeOptions {
|
|
28
|
+
/** Custom bundles (defaults to jsDelivr bundles). カスタムバンドル (既定は jsDelivr)。 */
|
|
29
|
+
bundles?: duckdb.DuckDBBundles;
|
|
30
|
+
/** Persistent database to open after instantiation. インスタンス化後に開く永続データベース。 */
|
|
31
|
+
database?: DuckDBDatabaseOptions;
|
|
32
|
+
}
|
|
70
33
|
/**
|
|
71
34
|
* DuckDB service for browser-based SQL operations.
|
|
72
35
|
* ブラウザベースの SQL 操作のための DuckDB サービス。
|
|
@@ -94,6 +57,11 @@ declare class DuckDBService {
|
|
|
94
57
|
* DuckDBService のシングルトンインスタンスを取得します。
|
|
95
58
|
*/
|
|
96
59
|
static getInstance(): DuckDBService;
|
|
60
|
+
/**
|
|
61
|
+
* Creates a dedicated (non-shared) service instance, e.g. for a persistent OPFS database.
|
|
62
|
+
* 専用 (非共有) のサービスインスタンスを生成します (OPFS 永続 DB を共有シングルトンと分離して持つ用途)。
|
|
63
|
+
*/
|
|
64
|
+
static create(): DuckDBService;
|
|
97
65
|
/**
|
|
98
66
|
* Add status change listener.
|
|
99
67
|
* 状態変更リスナーを追加します。
|
|
@@ -124,9 +92,7 @@ declare class DuckDBService {
|
|
|
124
92
|
* Initialize DuckDB Worker instance.
|
|
125
93
|
* DuckDB Worker インスタンスを初期化します。
|
|
126
94
|
*/
|
|
127
|
-
initialize(options?:
|
|
128
|
-
bundles?: duckdb.DuckDBBundles;
|
|
129
|
-
}): Promise<duckdb.AsyncDuckDB>;
|
|
95
|
+
initialize(options?: DuckDBInitializeOptions): Promise<duckdb.AsyncDuckDB>;
|
|
130
96
|
/**
|
|
131
97
|
* Internal worker initialization method.
|
|
132
98
|
* 内部的な Worker 初期化メソッド。
|
|
@@ -229,6 +195,63 @@ declare class DuckDBService {
|
|
|
229
195
|
|
|
230
196
|
declare const _default: DuckDBService;
|
|
231
197
|
|
|
198
|
+
/**
|
|
199
|
+
* DuckDB React Hook
|
|
200
|
+
*
|
|
201
|
+
* React hook for using DuckDB functionality in components.
|
|
202
|
+
* コンポーネントで DuckDB 機能を使用するための React フック。
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
interface UseDuckDBResult {
|
|
206
|
+
status: "not-initialized" | "initializing" | "ready" | "error";
|
|
207
|
+
error: string | null;
|
|
208
|
+
executeQuery: (sql: string) => Promise<unknown>;
|
|
209
|
+
executeQueries: (queries: string[]) => Promise<unknown[]>;
|
|
210
|
+
createTableFromData: <T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
211
|
+
dropIfExists?: boolean;
|
|
212
|
+
primaryKey?: string;
|
|
213
|
+
verbose?: boolean;
|
|
214
|
+
}) => Promise<void>;
|
|
215
|
+
getTableInfo: (tableName: string) => Promise<unknown>;
|
|
216
|
+
listTables: () => Promise<unknown>;
|
|
217
|
+
isReady: boolean;
|
|
218
|
+
initialize: () => Promise<void>;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* DuckDB functionality hook.
|
|
222
|
+
* DuckDB 機能を提供するフック。
|
|
223
|
+
*
|
|
224
|
+
* @param autoInitialize - 自動初期化を行うかどうか
|
|
225
|
+
* @returns DuckDB の状態と操作関数
|
|
226
|
+
*/
|
|
227
|
+
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
|
+
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
249
|
+
data: T | null;
|
|
250
|
+
loading: boolean;
|
|
251
|
+
error: string | null;
|
|
252
|
+
refetch: () => Promise<void>;
|
|
253
|
+
};
|
|
254
|
+
|
|
232
255
|
/**
|
|
233
256
|
* DuckDB Type Definitions
|
|
234
257
|
*
|
|
@@ -289,6 +312,11 @@ declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
|
289
312
|
* @returns True if result is a valid DuckDB table
|
|
290
313
|
*/
|
|
291
314
|
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
|
+
declare const isOpfsSupported: () => boolean;
|
|
292
320
|
|
|
293
321
|
/**
|
|
294
322
|
* @module utils/logger
|
|
@@ -388,4 +416,4 @@ declare class Logger implements ILogger {
|
|
|
388
416
|
error(message?: unknown, ...optionalParams: unknown[]): void;
|
|
389
417
|
}
|
|
390
418
|
|
|
391
|
-
export { type DuckDBQueryResult, type DuckDBRow, DuckDBService, type ILogger, LogLevel, Logger, type UseDuckDBResult, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, useDuckDB, useDuckDBQuery };
|
|
419
|
+
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,2 @@
|
|
|
1
|
-
"use strict";var O=Object.create;var T=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var V=(r,e)=>{for(var n in e)T(r,n,{get:e[n],enumerable:!0})},L=(r,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of F(e))!K.call(r,s)&&s!==n&&T(r,s,{get:()=>e[s],enumerable:!(o=q(e,s))||o.enumerable});return r};var _=(r,e,n)=>(n=r!=null?O(j(r)):{},L(e||!r||!r.__esModule?T(n,"default",{value:r,enumerable:!0}):n,r)),G=r=>L(T({},"__esModule",{value:!0}),r);var J={};V(J,{DuckDBService:()=>b,LogLevel:()=>I,Logger:()=>c,duckDBService:()=>W,duckdbTableToArray:()=>z,getDuckDBColumnCount:()=>U,getDuckDBRowCount:()=>C,isDuckDBTable:()=>N,useDuckDB:()=>B,useDuckDBQuery:()=>A});module.exports=G(J);var l=require("react");var w=_(require("@duckdb/duckdb-wasm"));var I=(t=>(t[t.DEBUG=0]="DEBUG",t[t.INFO=1]="INFO",t[t.WARN=2]="WARN",t[t.ERROR=3]="ERROR",t[t.NONE=4]="NONE",t))(I||{}),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 E=12e4,R=6e4,S=1e4,P=1e3,H=100,Y=/^\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}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 ${E}ms`))},E)});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||w.getJsDelivrBundles();c.info("\u{1F50D} Selecting appropriate bundle for browser...");let i=await w.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 m=new w.AsyncDuckDB(new w.ConsoleLogger,s);await m.instantiate(d.mainModule,d.pthreadWorker),this.assertNotAborted(e),c.info("\u{1F4E6} Installing splink_udfs from community...");let p=await m.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 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")})(),g]),clearTimeout(f)}catch(f){c.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",f)}finally{await p.close()}return this.assertNotAborted(e),this.workerInstance=m,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(o),c.info("\u2705 DuckDB Worker initialized successfully"),m}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 ${R}ms`))},R)}),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 Y.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,H),m=this.inferColumnTypes(n.slice(0,d),a),p=a.map(u=>`${this.quoteIdentifier(u)} ${m[u]}`),f=o.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(o.primaryKey)})`:"",g=`CREATE TABLE ${i} (${p.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+=P){let y=n.slice(u,u+P),v=y.map(M=>`(${a.map(Q=>this.formatValueForSQL(M?.[Q])).join(", ")})`),$=`INSERT INTO ${i} (${D}) VALUES ${v.join(", ")}`;await t.query($),s&&c.info(`\u{1F4CA} Inserted batch ${Math.floor(u/P)+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}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 W=b.getInstance();var Z=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},B=(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 y=u instanceof Error?u.message:"Unknown error occurred";t(y),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 m=(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]),p=(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,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]),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:m,executeQueries:p,createTableFromData:f,getTableInfo:g,listTables:D,isReady:n==="ready",initialize:d}},A=(r,e=[])=>{let{executeQuery:n,isReady:o}=B(),[s,t]=(0,l.useState)(null),[i,a]=(0,l.useState)(!1),[d,m]=(0,l.useState)(null),p=(0,l.useCallback)(async()=>{if(o&&r.trim())try{a(!0),m(null);let g=await n(r);t(g)}catch(g){let D=g instanceof Error?g.message:"Query execution failed";m(D),t(null)}finally{a(!1)}},[n,o,r]),f=Z(e);return(0,l.useEffect)(()=>{p()},[p,f]),{data:s,loading:i,error:d,refetch:p}};var z=r=>!r||typeof r!="object"?[]:"toArray"in r&&typeof r.toArray=="function"?r.toArray():[],C=r=>!r||typeof r!="object"?0:"numRows"in r&&typeof r.numRows=="number"?r.numRows:0,U=r=>!r||typeof r!="object"?0:"numCols"in r&&typeof r.numCols=="number"?r.numCols:0,N=r=>!r||typeof r!="object"?!1:"toArray"in r&&typeof r.toArray=="function"&&"numRows"in r&&typeof r.numRows=="number";0&&(module.exports={DuckDBService,LogLevel,Logger,duckDBService,duckdbTableToArray,getDuckDBColumnCount,getDuckDBRowCount,isDuckDBTable,useDuckDB,useDuckDBQuery});
|
|
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
2
|
//# sourceMappingURL=index.js.map
|