@aiquants/duckdb-helper 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/dist/index.d.mts +226 -0
- package/dist/index.d.ts +226 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/docs/useDuckDB.md +163 -0
- package/package.json +91 -0
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @aiquants/duckdb-helper
|
|
2
|
+
|
|
3
|
+
DuckDB helper utilities with React hooks, worker initialization, and typed query helpers for browser applications.
|
|
4
|
+
|
|
5
|
+
DuckDB をブラウザアプリケーションで扱うための React フック、Worker 初期化、型付きクエリヘルパーを提供するユーティリティ集。
|
|
6
|
+
|
|
7
|
+
## Features / 機能
|
|
8
|
+
|
|
9
|
+
- **Worker-backed initialization**: Set up DuckDB-wasm workers with the recommended CDN bundles / 推奨 CDN バンドルを用いた DuckDB-wasm Worker 初期化
|
|
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 / インストール
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Using pnpm (recommended) / pnpm を使用(推奨)
|
|
19
|
+
pnpm add @aiquants/duckdb-helper
|
|
20
|
+
|
|
21
|
+
# Using npm
|
|
22
|
+
npm install @aiquants/duckdb-helper
|
|
23
|
+
|
|
24
|
+
# Using yarn
|
|
25
|
+
yarn add @aiquants/duckdb-helper
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start / クイックスタート
|
|
29
|
+
|
|
30
|
+
### Initialize the service / サービスの初期化
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import duckDBService from '@aiquants/duckdb-helper'
|
|
34
|
+
|
|
35
|
+
await duckDBService.initialize()
|
|
36
|
+
|
|
37
|
+
const result = await duckDBService.executeQuery('SELECT 1 AS value')
|
|
38
|
+
console.log(result)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Use the React hook / React フックを利用
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { useDuckDB } from '@aiquants/duckdb-helper'
|
|
45
|
+
|
|
46
|
+
export const Dashboard = () => {
|
|
47
|
+
const { status, executeQuery, isReady } = useDuckDB()
|
|
48
|
+
|
|
49
|
+
const handleClick = async () => {
|
|
50
|
+
if (!isReady) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const table = await executeQuery('SELECT * FROM users LIMIT 10')
|
|
54
|
+
console.log(table)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div>
|
|
59
|
+
<p>Status: {status}</p>
|
|
60
|
+
<button onClick={handleClick} disabled={!isReady}>
|
|
61
|
+
Run Query
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Convert results to arrays / 結果を配列に変換
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { duckdbTableToArray } from '@aiquants/duckdb-helper'
|
|
72
|
+
|
|
73
|
+
const table = await duckDBService.executeQuery('SELECT * FROM items')
|
|
74
|
+
const rows = duckdbTableToArray<{ id: number; name: string }>(table)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Documentation / ドキュメント
|
|
78
|
+
|
|
79
|
+
詳しい使用方法は `docs/useDuckDB.md` を参照してください。
|
|
80
|
+
|
|
81
|
+
For detailed guidance, see `docs/useDuckDB.md`.
|
|
82
|
+
|
|
83
|
+
## Requirements / 必要要件
|
|
84
|
+
|
|
85
|
+
- React 16.8+ with Hooks support / React 16.8 以上(Hooks 対応)
|
|
86
|
+
- Modern browsers with Web Worker support / Web Worker をサポートする最新ブラウザ
|
|
87
|
+
|
|
88
|
+
## License / ライセンス
|
|
89
|
+
|
|
90
|
+
MIT License
|
|
91
|
+
|
|
92
|
+
## Contributing / 貢献
|
|
93
|
+
|
|
94
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
95
|
+
|
|
96
|
+
貢献を歓迎します。Issue や Pull Request をぜひお寄せください。
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { DependencyList } from 'react';
|
|
2
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DuckDB React Hook
|
|
6
|
+
*
|
|
7
|
+
* React hook for using DuckDB functionality in components.
|
|
8
|
+
* コンポーネントで DuckDB 機能を使用するための React フック。
|
|
9
|
+
*/
|
|
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
|
+
}) => Promise<void>;
|
|
20
|
+
getTableInfo: (tableName: string) => Promise<unknown>;
|
|
21
|
+
listTables: () => Promise<unknown>;
|
|
22
|
+
isReady: boolean;
|
|
23
|
+
initialize: () => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* DuckDB functionality hook.
|
|
27
|
+
* DuckDB 機能を提供するフック。
|
|
28
|
+
*
|
|
29
|
+
* @param autoInitialize - 自動初期化を行うかどうか
|
|
30
|
+
* @returns DuckDB の状態と操作関数
|
|
31
|
+
*/
|
|
32
|
+
declare const useDuckDB: (autoInitialize?: boolean) => UseDuckDBResult;
|
|
33
|
+
/**
|
|
34
|
+
* Hook for simple DuckDB query execution.
|
|
35
|
+
* シンプルな DuckDB クエリ実行のためのフック。
|
|
36
|
+
*
|
|
37
|
+
* @param sql - 実行する SQL クエリ
|
|
38
|
+
* @param dependencies - クエリを再実行するための依存配列
|
|
39
|
+
* @returns クエリ結果と状態
|
|
40
|
+
*/
|
|
41
|
+
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
42
|
+
data: T | null;
|
|
43
|
+
loading: boolean;
|
|
44
|
+
error: string | null;
|
|
45
|
+
refetch: () => Promise<void>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* DuckDB Web Worker Service
|
|
50
|
+
*
|
|
51
|
+
* Provides reusable DuckDB-wasm functionality across the application.
|
|
52
|
+
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* DuckDB service for browser-based SQL operations.
|
|
57
|
+
* ブラウザベースの SQL 操作のための DuckDB サービス。
|
|
58
|
+
*/
|
|
59
|
+
declare class DuckDBService {
|
|
60
|
+
private static instance;
|
|
61
|
+
private operationFlags;
|
|
62
|
+
private workerInstance;
|
|
63
|
+
private connectionInstance;
|
|
64
|
+
private isInitializing;
|
|
65
|
+
private initPromise;
|
|
66
|
+
private hasError;
|
|
67
|
+
private listeners;
|
|
68
|
+
/**
|
|
69
|
+
* Get singleton instance of DuckDBService.
|
|
70
|
+
* DuckDBService のシングルトンインスタンスを取得します。
|
|
71
|
+
*/
|
|
72
|
+
static getInstance(): DuckDBService;
|
|
73
|
+
/**
|
|
74
|
+
* Add status change listener.
|
|
75
|
+
* 状態変更リスナーを追加します。
|
|
76
|
+
*/
|
|
77
|
+
addStatusListener(listener: () => void): void;
|
|
78
|
+
/**
|
|
79
|
+
* Remove status change listener.
|
|
80
|
+
* 状態変更リスナーを削除します。
|
|
81
|
+
*/
|
|
82
|
+
removeStatusListener(listener: () => void): void;
|
|
83
|
+
/**
|
|
84
|
+
* Notify all listeners of status change.
|
|
85
|
+
* すべてのリスナーに状態変更を通知します。
|
|
86
|
+
*/
|
|
87
|
+
private notifyListeners;
|
|
88
|
+
/**
|
|
89
|
+
* Get current status.
|
|
90
|
+
* 現在のステータスを取得します。
|
|
91
|
+
*/
|
|
92
|
+
getStatus(): 'not-initialized' | 'initializing' | 'ready' | 'error';
|
|
93
|
+
/**
|
|
94
|
+
* Initialize DuckDB Worker instance.
|
|
95
|
+
* DuckDB Worker インスタンスを初期化します。
|
|
96
|
+
*/
|
|
97
|
+
initialize(): Promise<duckdb.AsyncDuckDB>;
|
|
98
|
+
/**
|
|
99
|
+
* Internal worker initialization method.
|
|
100
|
+
* 内部的な Worker 初期化メソッド。
|
|
101
|
+
*/
|
|
102
|
+
private internalInitializeWorker;
|
|
103
|
+
/**
|
|
104
|
+
* Get or create DuckDB connection.
|
|
105
|
+
* DuckDB 接続を取得または作成します。
|
|
106
|
+
*/
|
|
107
|
+
getConnection(): Promise<duckdb.AsyncDuckDBConnection>;
|
|
108
|
+
/**
|
|
109
|
+
* Execute SQL query using DuckDB Worker.
|
|
110
|
+
* DuckDB Worker を使用して SQL クエリを実行します。
|
|
111
|
+
*/
|
|
112
|
+
executeQuery(sql: string): Promise<unknown>;
|
|
113
|
+
/**
|
|
114
|
+
* Execute multiple SQL queries.
|
|
115
|
+
* 複数の SQL クエリを実行します。
|
|
116
|
+
*/
|
|
117
|
+
executeQueries(queries: string[]): Promise<unknown[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Infer column types from data sample.
|
|
120
|
+
* データサンプルから列の型を推定します。
|
|
121
|
+
*/
|
|
122
|
+
private inferColumnTypes;
|
|
123
|
+
/**
|
|
124
|
+
* Format value for SQL insertion.
|
|
125
|
+
* SQL 挿入用に値をフォーマットします。
|
|
126
|
+
*/
|
|
127
|
+
private formatValueForSQL;
|
|
128
|
+
/**
|
|
129
|
+
* Create table from JavaScript array of objects.
|
|
130
|
+
* JavaScript オブジェクトの配列からテーブルを作成します。
|
|
131
|
+
*/
|
|
132
|
+
createTableFromData<T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
133
|
+
dropIfExists?: boolean;
|
|
134
|
+
primaryKey?: string;
|
|
135
|
+
}): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* Get table information.
|
|
138
|
+
* テーブル情報を取得します。
|
|
139
|
+
*/
|
|
140
|
+
getTableInfo(tableName: string): Promise<unknown>;
|
|
141
|
+
/**
|
|
142
|
+
* List all tables.
|
|
143
|
+
* すべてのテーブルを一覧表示します。
|
|
144
|
+
*/
|
|
145
|
+
listTables(): Promise<unknown>;
|
|
146
|
+
/**
|
|
147
|
+
* Check if DuckDB is ready.
|
|
148
|
+
* DuckDB が準備完了かどうかを確認します。
|
|
149
|
+
*/
|
|
150
|
+
isReady(): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Cleanup DuckDB Worker resources.
|
|
153
|
+
* DuckDB Worker のリソースをクリーンアップします。
|
|
154
|
+
*/
|
|
155
|
+
cleanup(): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Reset the service instance (useful for testing).
|
|
158
|
+
* サービスインスタンスをリセットします(テスト用)。
|
|
159
|
+
*/
|
|
160
|
+
static resetInstance(): void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
declare const _default: DuckDBService;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* DuckDB Type Definitions
|
|
167
|
+
*
|
|
168
|
+
* Type definitions for DuckDB query results and data structures.
|
|
169
|
+
* DuckDB クエリ結果とデータ構造の型定義。
|
|
170
|
+
*/
|
|
171
|
+
/**
|
|
172
|
+
* DuckDB query result type (Apache Arrow Table)
|
|
173
|
+
* DuckDB クエリ結果の型(Apache Arrow テーブル)
|
|
174
|
+
*/
|
|
175
|
+
interface DuckDBQueryResult<T = unknown> {
|
|
176
|
+
toArray(): T[];
|
|
177
|
+
numRows: number;
|
|
178
|
+
numCols: number;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Helper type to extract row data from DuckDB table
|
|
182
|
+
* DuckDB テーブルから行データを抽出するヘルパー型
|
|
183
|
+
*/
|
|
184
|
+
type DuckDBRow<T> = T;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* DuckDB Helper Functions
|
|
188
|
+
*
|
|
189
|
+
* Utility functions for working with DuckDB query results.
|
|
190
|
+
* DuckDB クエリ結果を扱うためのユーティリティ関数。
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Safely convert DuckDB table to typed array
|
|
195
|
+
* DuckDB テーブルを型付き配列に安全に変換します
|
|
196
|
+
*
|
|
197
|
+
* @param result - DuckDB query result
|
|
198
|
+
* @returns Typed array of results
|
|
199
|
+
*/
|
|
200
|
+
declare const duckdbTableToArray: <T>(result: unknown) => T[];
|
|
201
|
+
/**
|
|
202
|
+
* Get table row count
|
|
203
|
+
* テーブルの行数を取得します
|
|
204
|
+
*
|
|
205
|
+
* @param result - DuckDB query result
|
|
206
|
+
* @returns Number of rows in the table
|
|
207
|
+
*/
|
|
208
|
+
declare const getDuckDBRowCount: (result: unknown) => number;
|
|
209
|
+
/**
|
|
210
|
+
* Get table column count
|
|
211
|
+
* テーブルの列数を取得します
|
|
212
|
+
*
|
|
213
|
+
* @param result - DuckDB query result
|
|
214
|
+
* @returns Number of columns in the table
|
|
215
|
+
*/
|
|
216
|
+
declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
217
|
+
/**
|
|
218
|
+
* Check if result is a valid DuckDB table
|
|
219
|
+
* 結果が有効な DuckDB テーブルかどうかをチェックします
|
|
220
|
+
*
|
|
221
|
+
* @param result - DuckDB query result
|
|
222
|
+
* @returns True if result is a valid DuckDB table
|
|
223
|
+
*/
|
|
224
|
+
declare const isDuckDBTable: (result: unknown) => result is DuckDBQueryResult;
|
|
225
|
+
|
|
226
|
+
export { type DuckDBQueryResult, type DuckDBRow, DuckDBService, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, useDuckDB, useDuckDBQuery };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { DependencyList } from 'react';
|
|
2
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DuckDB React Hook
|
|
6
|
+
*
|
|
7
|
+
* React hook for using DuckDB functionality in components.
|
|
8
|
+
* コンポーネントで DuckDB 機能を使用するための React フック。
|
|
9
|
+
*/
|
|
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
|
+
}) => Promise<void>;
|
|
20
|
+
getTableInfo: (tableName: string) => Promise<unknown>;
|
|
21
|
+
listTables: () => Promise<unknown>;
|
|
22
|
+
isReady: boolean;
|
|
23
|
+
initialize: () => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* DuckDB functionality hook.
|
|
27
|
+
* DuckDB 機能を提供するフック。
|
|
28
|
+
*
|
|
29
|
+
* @param autoInitialize - 自動初期化を行うかどうか
|
|
30
|
+
* @returns DuckDB の状態と操作関数
|
|
31
|
+
*/
|
|
32
|
+
declare const useDuckDB: (autoInitialize?: boolean) => UseDuckDBResult;
|
|
33
|
+
/**
|
|
34
|
+
* Hook for simple DuckDB query execution.
|
|
35
|
+
* シンプルな DuckDB クエリ実行のためのフック。
|
|
36
|
+
*
|
|
37
|
+
* @param sql - 実行する SQL クエリ
|
|
38
|
+
* @param dependencies - クエリを再実行するための依存配列
|
|
39
|
+
* @returns クエリ結果と状態
|
|
40
|
+
*/
|
|
41
|
+
declare const useDuckDBQuery: <T = unknown>(sql: string, dependencies?: DependencyList) => {
|
|
42
|
+
data: T | null;
|
|
43
|
+
loading: boolean;
|
|
44
|
+
error: string | null;
|
|
45
|
+
refetch: () => Promise<void>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* DuckDB Web Worker Service
|
|
50
|
+
*
|
|
51
|
+
* Provides reusable DuckDB-wasm functionality across the application.
|
|
52
|
+
* アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* DuckDB service for browser-based SQL operations.
|
|
57
|
+
* ブラウザベースの SQL 操作のための DuckDB サービス。
|
|
58
|
+
*/
|
|
59
|
+
declare class DuckDBService {
|
|
60
|
+
private static instance;
|
|
61
|
+
private operationFlags;
|
|
62
|
+
private workerInstance;
|
|
63
|
+
private connectionInstance;
|
|
64
|
+
private isInitializing;
|
|
65
|
+
private initPromise;
|
|
66
|
+
private hasError;
|
|
67
|
+
private listeners;
|
|
68
|
+
/**
|
|
69
|
+
* Get singleton instance of DuckDBService.
|
|
70
|
+
* DuckDBService のシングルトンインスタンスを取得します。
|
|
71
|
+
*/
|
|
72
|
+
static getInstance(): DuckDBService;
|
|
73
|
+
/**
|
|
74
|
+
* Add status change listener.
|
|
75
|
+
* 状態変更リスナーを追加します。
|
|
76
|
+
*/
|
|
77
|
+
addStatusListener(listener: () => void): void;
|
|
78
|
+
/**
|
|
79
|
+
* Remove status change listener.
|
|
80
|
+
* 状態変更リスナーを削除します。
|
|
81
|
+
*/
|
|
82
|
+
removeStatusListener(listener: () => void): void;
|
|
83
|
+
/**
|
|
84
|
+
* Notify all listeners of status change.
|
|
85
|
+
* すべてのリスナーに状態変更を通知します。
|
|
86
|
+
*/
|
|
87
|
+
private notifyListeners;
|
|
88
|
+
/**
|
|
89
|
+
* Get current status.
|
|
90
|
+
* 現在のステータスを取得します。
|
|
91
|
+
*/
|
|
92
|
+
getStatus(): 'not-initialized' | 'initializing' | 'ready' | 'error';
|
|
93
|
+
/**
|
|
94
|
+
* Initialize DuckDB Worker instance.
|
|
95
|
+
* DuckDB Worker インスタンスを初期化します。
|
|
96
|
+
*/
|
|
97
|
+
initialize(): Promise<duckdb.AsyncDuckDB>;
|
|
98
|
+
/**
|
|
99
|
+
* Internal worker initialization method.
|
|
100
|
+
* 内部的な Worker 初期化メソッド。
|
|
101
|
+
*/
|
|
102
|
+
private internalInitializeWorker;
|
|
103
|
+
/**
|
|
104
|
+
* Get or create DuckDB connection.
|
|
105
|
+
* DuckDB 接続を取得または作成します。
|
|
106
|
+
*/
|
|
107
|
+
getConnection(): Promise<duckdb.AsyncDuckDBConnection>;
|
|
108
|
+
/**
|
|
109
|
+
* Execute SQL query using DuckDB Worker.
|
|
110
|
+
* DuckDB Worker を使用して SQL クエリを実行します。
|
|
111
|
+
*/
|
|
112
|
+
executeQuery(sql: string): Promise<unknown>;
|
|
113
|
+
/**
|
|
114
|
+
* Execute multiple SQL queries.
|
|
115
|
+
* 複数の SQL クエリを実行します。
|
|
116
|
+
*/
|
|
117
|
+
executeQueries(queries: string[]): Promise<unknown[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Infer column types from data sample.
|
|
120
|
+
* データサンプルから列の型を推定します。
|
|
121
|
+
*/
|
|
122
|
+
private inferColumnTypes;
|
|
123
|
+
/**
|
|
124
|
+
* Format value for SQL insertion.
|
|
125
|
+
* SQL 挿入用に値をフォーマットします。
|
|
126
|
+
*/
|
|
127
|
+
private formatValueForSQL;
|
|
128
|
+
/**
|
|
129
|
+
* Create table from JavaScript array of objects.
|
|
130
|
+
* JavaScript オブジェクトの配列からテーブルを作成します。
|
|
131
|
+
*/
|
|
132
|
+
createTableFromData<T extends Record<string, unknown>>(tableName: string, data: T[], options?: {
|
|
133
|
+
dropIfExists?: boolean;
|
|
134
|
+
primaryKey?: string;
|
|
135
|
+
}): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* Get table information.
|
|
138
|
+
* テーブル情報を取得します。
|
|
139
|
+
*/
|
|
140
|
+
getTableInfo(tableName: string): Promise<unknown>;
|
|
141
|
+
/**
|
|
142
|
+
* List all tables.
|
|
143
|
+
* すべてのテーブルを一覧表示します。
|
|
144
|
+
*/
|
|
145
|
+
listTables(): Promise<unknown>;
|
|
146
|
+
/**
|
|
147
|
+
* Check if DuckDB is ready.
|
|
148
|
+
* DuckDB が準備完了かどうかを確認します。
|
|
149
|
+
*/
|
|
150
|
+
isReady(): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Cleanup DuckDB Worker resources.
|
|
153
|
+
* DuckDB Worker のリソースをクリーンアップします。
|
|
154
|
+
*/
|
|
155
|
+
cleanup(): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Reset the service instance (useful for testing).
|
|
158
|
+
* サービスインスタンスをリセットします(テスト用)。
|
|
159
|
+
*/
|
|
160
|
+
static resetInstance(): void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
declare const _default: DuckDBService;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* DuckDB Type Definitions
|
|
167
|
+
*
|
|
168
|
+
* Type definitions for DuckDB query results and data structures.
|
|
169
|
+
* DuckDB クエリ結果とデータ構造の型定義。
|
|
170
|
+
*/
|
|
171
|
+
/**
|
|
172
|
+
* DuckDB query result type (Apache Arrow Table)
|
|
173
|
+
* DuckDB クエリ結果の型(Apache Arrow テーブル)
|
|
174
|
+
*/
|
|
175
|
+
interface DuckDBQueryResult<T = unknown> {
|
|
176
|
+
toArray(): T[];
|
|
177
|
+
numRows: number;
|
|
178
|
+
numCols: number;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Helper type to extract row data from DuckDB table
|
|
182
|
+
* DuckDB テーブルから行データを抽出するヘルパー型
|
|
183
|
+
*/
|
|
184
|
+
type DuckDBRow<T> = T;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* DuckDB Helper Functions
|
|
188
|
+
*
|
|
189
|
+
* Utility functions for working with DuckDB query results.
|
|
190
|
+
* DuckDB クエリ結果を扱うためのユーティリティ関数。
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Safely convert DuckDB table to typed array
|
|
195
|
+
* DuckDB テーブルを型付き配列に安全に変換します
|
|
196
|
+
*
|
|
197
|
+
* @param result - DuckDB query result
|
|
198
|
+
* @returns Typed array of results
|
|
199
|
+
*/
|
|
200
|
+
declare const duckdbTableToArray: <T>(result: unknown) => T[];
|
|
201
|
+
/**
|
|
202
|
+
* Get table row count
|
|
203
|
+
* テーブルの行数を取得します
|
|
204
|
+
*
|
|
205
|
+
* @param result - DuckDB query result
|
|
206
|
+
* @returns Number of rows in the table
|
|
207
|
+
*/
|
|
208
|
+
declare const getDuckDBRowCount: (result: unknown) => number;
|
|
209
|
+
/**
|
|
210
|
+
* Get table column count
|
|
211
|
+
* テーブルの列数を取得します
|
|
212
|
+
*
|
|
213
|
+
* @param result - DuckDB query result
|
|
214
|
+
* @returns Number of columns in the table
|
|
215
|
+
*/
|
|
216
|
+
declare const getDuckDBColumnCount: (result: unknown) => number;
|
|
217
|
+
/**
|
|
218
|
+
* Check if result is a valid DuckDB table
|
|
219
|
+
* 結果が有効な DuckDB テーブルかどうかをチェックします
|
|
220
|
+
*
|
|
221
|
+
* @param result - DuckDB query result
|
|
222
|
+
* @returns True if result is a valid DuckDB table
|
|
223
|
+
*/
|
|
224
|
+
declare const isDuckDBTable: (result: unknown) => result is DuckDBQueryResult;
|
|
225
|
+
|
|
226
|
+
export { type DuckDBQueryResult, type DuckDBRow, DuckDBService, _default as duckDBService, duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable, useDuckDB, useDuckDBQuery };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var A=Object.create;var b=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var Q=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var O=(t,e)=>{for(var n in e)b(t,n,{get:e[n],enumerable:!0})},T=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of $(e))!F.call(t,a)&&a!==n&&b(t,a,{get:()=>e[a],enumerable:!(s=v(e,a))||s.enumerable});return t};var W=(t,e,n)=>(n=t!=null?A(Q(t)):{},T(e||!t||!t.__esModule?b(n,"default",{value:t,enumerable:!0}):n,t)),M=t=>T(b({},"__esModule",{value:!0}),t);var U={};O(U,{DuckDBService:()=>h,duckDBService:()=>B,duckdbTableToArray:()=>S,getDuckDBColumnCount:()=>L,getDuckDBRowCount:()=>R,isDuckDBTable:()=>x,useDuckDB:()=>I,useDuckDBQuery:()=>E});module.exports=M(U);var c=require("react");var m=W(require("@duckdb/duckdb-wasm")),g=class g{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.listeners=new Set}static getInstance(){return g.instance||(g.instance=new g),g.instance}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){console.error("Error in status listener:",n)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}async initialize(){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners(),this.initPromise=this.internalInitializeWorker();try{let e=await this.initPromise;return this.isInitializing=!1,this.notifyListeners(),e}catch(e){throw this.isInitializing=!1,this.hasError=!0,this.notifyListeners(),this.initPromise=null,e}}async internalInitializeWorker(){try{console.log("\u{1F527} Starting DuckDB Worker initialization..."),console.log("\u{1F4E6} Getting DuckDB bundles from CDN...");let e=m.getJsDelivrBundles();console.log("\u{1F50D} Selecting appropriate bundle for browser...");let n=await m.selectBundle(e);console.log("\u2705 Bundle selected:",{mainWorker:n.mainWorker,mainModule:n.mainModule,pthreadWorker:n.pthreadWorker}),console.log("\u{1F680} Creating Worker with Blob URL...");let s=URL.createObjectURL(new Blob([`importScripts("${n.mainWorker}");`],{type:"text/javascript"}));console.log("\u{1F477} Creating Worker instance...");let a=new Worker(s),r=new m.ConsoleLogger;console.log("\u{1F5C4}\uFE0F Creating DuckDB instance..."),this.workerInstance=new m.AsyncDuckDB(r,a),console.log("\u26A1 Instantiating DuckDB with modules..."),await this.workerInstance.instantiate(n.mainModule,n.pthreadWorker),console.log("\u{1F4E6} Installing splink_udfs from community...");let u=await this.workerInstance.connect();try{await u.query("INSTALL splink_udfs FROM community;"),console.log("\u2705 splink_udfs installed successfully"),await u.query("LOAD splink_udfs;"),console.log("\u2705 splink_udfs loaded successfully")}catch(o){console.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",o)}finally{await u.close()}return URL.revokeObjectURL(s),console.log("\u2705 DuckDB Worker initialized successfully"),this.workerInstance}catch(e){throw console.error("\u274C Failed to initialize DuckDB Worker:",e),e instanceof Error&&(console.error("Error name:",e.name),console.error("Error message:",e.message),console.error("Error stack:",e.stack)),e}}async getConnection(){if(!this.connectionInstance){let e=await this.initialize();this.connectionInstance=await e.connect()}return this.connectionInstance}async executeQuery(e){try{return await(await this.getConnection()).query(e)}catch(n){throw console.error("Failed to execute SQL query:",n),n}}async executeQueries(e){let n=[];for(let s of e){let a=await this.executeQuery(s);n.push(a)}return n}inferColumnTypes(e){let n={},s=e.slice(0,Math.min(e.length,10)),a=Object.keys(s[0]||{});for(let r of a){let u=s.map(l=>l[r]).filter(l=>l!=null);if(u.length===0){n[r]="VARCHAR";continue}let o={number:0,integer:0,bigint:0,boolean:0,date:0,string:0};for(let l of u)if(typeof l=="number")o.number+=1,Number.isInteger(l)&&(o.integer+=1,(l<-2147483648||l>2147483647)&&(o.bigint+=1));else if(typeof l=="boolean")o.boolean+=1;else if(l instanceof Date)o.date+=1;else if(typeof l=="string"){let p=new Date(l);!Number.isNaN(p.getTime())&&l.includes("-")?o.date+=1:o.string+=1}else o.string+=1;o.number===u.length?o.integer===u.length?o.bigint>0?n[r]="BIGINT":n[r]="INTEGER":n[r]="DOUBLE":o.boolean===u.length?n[r]="BOOLEAN":o.date===u.length?n[r]="TIMESTAMP":n[r]="VARCHAR"}return n}formatValueForSQL(e){return e==null?"NULL":typeof e=="string"?`'${e.replace(/'/g,"''")}'`:e instanceof Date?`'${e.toISOString()}'`:typeof e=="boolean"?e?"TRUE":"FALSE":(typeof e=="number"&&Number.isInteger(e)&&(e<-2147483648||e>2147483647),String(e))}async createTableFromData(e,n,s={}){if(!n||n.length===0)throw new Error("Data array is empty");let a=`create_table_${e}`;if(this.operationFlags.has(a)){console.log(`\u23F3 Table creation for '${e}' is already in progress, skipping...`);return}this.operationFlags.set(a,!0);try{let r=await this.getConnection();s.dropIfExists&&(console.log(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await r.query(`DROP TABLE IF EXISTS ${e}`));try{let i=await r.query("SHOW TABLES");console.log("\u{1F4CB} Existing tables before creation:",i)}catch(i){console.log("\u26A0\uFE0F Could not list existing tables:",i)}let u=Math.min(n.length,100),o=this.inferColumnTypes(n.slice(0,u)),l=Object.keys(o).map(i=>`"${i}" ${o[i]}`),p=s.primaryKey?`, PRIMARY KEY ("${s.primaryKey}")`:"",k=`CREATE TABLE ${e} (${l.join(", ")}${p})`;console.log("\u{1F528} Creating table with SQL:",k),await r.query(k);try{let i=await r.query("SHOW TABLES");console.log("\u{1F4CB} Tables after creation:",i);let f=await r.query(`SELECT COUNT(*) as count FROM information_schema.tables WHERE table_name = '${e}'`);console.log(`\u2705 Table '${e}' exists check:`,f)}catch(i){console.log("\u26A0\uFE0F Could not verify table creation:",i)}console.log(`\u{1F4E5} Inserting ${n.length} rows into table '${e}'`);let y=Object.keys(n[0]),w=1e3;for(let i=0;i<n.length;i+=w){let f=n.slice(i,i+w);try{let d=f.map(D=>`(${y.map(P=>{let C=D[P];return this.formatValueForSQL(C)}).join(", ")})`),z=`INSERT INTO ${e} (${y.map(D=>`"${D}"`).join(", ")}) VALUES ${d.join(", ")}`;await r.query(z),console.log(`\u{1F4CA} Inserted batch ${Math.floor(i/w)+1} (${f.length} rows)`)}catch(d){throw console.error(`\u274C Failed to insert batch ${Math.floor(i/w)+1}:`,d),d}}console.log(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(r){throw console.error(`\u274C Failed to create table '${e}':`,r),r}finally{this.operationFlags.delete(a)}}async getTableInfo(e){return await(await this.getConnection()).query(`DESCRIBE ${e}`)}async listTables(){return await(await this.getConnection()).query("SHOW TABLES")}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.listeners.clear(),this.operationFlags.clear()}catch(e){console.error("Error during cleanup:",e)}}static resetInstance(){g.instance&&(g.instance.cleanup(),g.instance=null)}};g.instance=null;var h=g;var B=h.getInstance();var I=(t=!0)=>{let[e,n]=(0,c.useState)("not-initialized"),[s,a]=(0,c.useState)(null),[r]=(0,c.useState)(()=>h.getInstance()),u=(0,c.useCallback)(()=>{let i=r.getStatus();console.info("\u{1F504} DuckDB status updated:",i),n(i),i==="error"?a("DuckDB initialization failed"):i==="ready"&&a(null)},[r]);(0,c.useEffect)(()=>(r.addStatusListener(u),u(),()=>{r.removeStatusListener(u)}),[r,u]);let o=(0,c.useCallback)(async()=>{try{await r.initialize()}catch(i){let f=i instanceof Error?i.message:"Unknown error occurred";a(f),console.error("DuckDB initialization failed:",i)}},[r]);(0,c.useEffect)(()=>{t&&e==="not-initialized"&&(console.info("\u{1F680} Auto-initializing DuckDB..."),o())},[t,e,o]);let l=(0,c.useCallback)(async i=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return r.executeQuery(i)},[r,e]),p=(0,c.useCallback)(async i=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return r.executeQueries(i)},[r,e]),k=(0,c.useCallback)(async(i,f,d)=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return r.createTableFromData(i,f,d)},[r,e]),y=(0,c.useCallback)(async i=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return r.getTableInfo(i)},[r,e]),w=(0,c.useCallback)(async()=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return r.listTables()},[r,e]);return(0,c.useEffect)(()=>{let i=()=>{let d=r.getStatus();d!==e&&d!=="initializing"&&n(d)};if(typeof window>"u"){i();return}let f=window.setInterval(i,1e3);return i(),()=>window.clearInterval(f)},[r,e]),{status:e,error:s,executeQuery:l,executeQueries:p,createTableFromData:k,getTableInfo:y,listTables:w,isReady:e==="ready",initialize:o}},E=(t,e=[])=>{let{executeQuery:n,isReady:s}=I(),[a,r]=(0,c.useState)(null),[u,o]=(0,c.useState)(!1),[l,p]=(0,c.useState)(null),k=(0,c.useCallback)(async()=>{if(s&&t.trim())try{o(!0),p(null);let y=await n(t);r(y)}catch(y){let w=y instanceof Error?y.message:"Query execution failed";p(w),r(null)}finally{o(!1)}},[n,s,t]);return(0,c.useEffect)(()=>{k()},[k,...e]),{data:a,loading:u,error:l,refetch:k}};var S=t=>!t||typeof t!="object"?[]:"toArray"in t&&typeof t.toArray=="function"?t.toArray():[],R=t=>!t||typeof t!="object"?0:"numRows"in t&&typeof t.numRows=="number"?t.numRows:0,L=t=>!t||typeof t!="object"?0:"numCols"in t&&typeof t.numCols=="number"?t.numCols:0,x=t=>!t||typeof t!="object"?!1:"toArray"in t&&typeof t.toArray=="function"&&"numRows"in t&&typeof t.numRows=="number";0&&(module.exports={DuckDBService,duckDBService,duckdbTableToArray,getDuckDBColumnCount,getDuckDBRowCount,isDuckDBTable,useDuckDB,useDuckDBQuery});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/hooks/useDuckDB.ts","../src/services/duckdb.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 { useDuckDB, useDuckDBQuery } from './hooks/useDuckDB'\nexport { DuckDBService, default as duckDBService } from './services/duckdb'\nexport type {\n DuckDBQueryResult,\n DuckDBRow\n} from './types/duckdb'\nexport {\n duckdbTableToArray, getDuckDBColumnCount, getDuckDBRowCount, isDuckDBTable\n} from './utils/duckdb-helpers'\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, useState } from 'react'\nimport { DuckDBService } from '../services/duckdb'\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 }\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): 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 console.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()\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'\n setError(errorMessage)\n console.error('DuckDB initialization failed:', err)\n }\n }, [duckDBService])\n\n useEffect(() => {\n if (autoInitialize && status === 'not-initialized') {\n console.info('🚀 Auto-initializing DuckDB...')\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(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 }, [duckDBService, status])\n\n const executeQueries = useCallback(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 }, [duckDBService, status])\n\n const createTableFromData = useCallback(async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\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 }, [duckDBService, status])\n\n const getTableInfo = useCallback(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 }, [duckDBService, status])\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 useEffect(() => {\n const pollStatus = () => {\n const currentStatus = duckDBService.getStatus()\n if (currentStatus !== status && currentStatus !== 'initializing') {\n setStatus(currentStatus)\n }\n }\n\n if (typeof window === 'undefined') {\n pollStatus()\n return\n }\n\n const interval = window.setInterval(pollStatus, 1000)\n pollStatus()\n\n return () => window.clearInterval(interval)\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 * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果と状態\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 useEffect(() => {\n void refetch()\n }, [refetch, ...dependencies])\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'\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, boolean> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\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 * 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 console.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 /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(): 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 this.initPromise = this.internalInitializeWorker()\n\n try {\n const result = await this.initPromise\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n this.isInitializing = false\n this.hasError = true\n this.notifyListeners()\n this.initPromise = null\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n */\n private async internalInitializeWorker(): Promise<duckdb.AsyncDuckDB> {\n try {\n console.log('🔧 Starting DuckDB Worker initialization...')\n console.log('📦 Getting DuckDB bundles from CDN...')\n const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles()\n\n console.log('🔍 Selecting appropriate bundle for browser...')\n const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)\n console.log('✅ Bundle selected:', {\n mainWorker: bundle.mainWorker,\n mainModule: bundle.mainModule,\n pthreadWorker: bundle.pthreadWorker\n })\n\n console.log('🚀 Creating Worker with Blob URL...')\n const workerUrl = URL.createObjectURL(\n new Blob([`importScripts(\"${bundle.mainWorker}\");`], { type: 'text/javascript' })\n )\n\n console.log('👷 Creating Worker instance...')\n const worker = new Worker(workerUrl)\n const logger = new duckdb.ConsoleLogger()\n\n console.log('🗄️ Creating DuckDB instance...')\n this.workerInstance = new duckdb.AsyncDuckDB(logger, worker)\n\n console.log('⚡ Instantiating DuckDB with modules...')\n await this.workerInstance.instantiate(bundle.mainModule, bundle.pthreadWorker)\n\n console.log('📦 Installing splink_udfs from community...')\n const connection = await this.workerInstance.connect()\n try {\n await connection.query('INSTALL splink_udfs FROM community;')\n console.log('✅ splink_udfs installed successfully')\n\n await connection.query('LOAD splink_udfs;')\n console.log('✅ splink_udfs loaded successfully')\n } catch (error) {\n console.warn('⚠️ Failed to install/load splink_udfs:', error)\n } finally {\n await connection.close()\n }\n\n URL.revokeObjectURL(workerUrl)\n\n console.log('✅ DuckDB Worker initialized successfully')\n return this.workerInstance\n } catch (error) {\n console.error('❌ Failed to initialize DuckDB Worker:', error)\n\n if (error instanceof Error) {\n console.error('Error name:', error.name)\n console.error('Error message:', error.message)\n console.error('Error stack:', error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (!this.connectionInstance) {\n const db = await this.initialize()\n this.connectionInstance = await db.connect()\n }\n return this.connectionInstance\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n try {\n const connection = await this.getConnection()\n const result = await connection.query(sql)\n return result\n } catch (error) {\n console.error('Failed to execute SQL query:', error)\n throw error\n }\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n const results: unknown[] = []\n for (const query of queries) {\n const result = await this.executeQuery(query)\n results.push(result)\n }\n return results\n }\n\n /**\n * Infer column types from data sample.\n * データサンプルから列の型を推定します。\n */\n private inferColumnTypes(data: Record<string, unknown>[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n const sampleData = data.slice(0, Math.min(data.length, 10))\n\n const keys = Object.keys(sampleData[0] || {})\n for (const key of keys) {\n const values = sampleData.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 bigint: 0,\n boolean: 0,\n date: 0,\n string: 0\n }\n\n for (const value of values) {\n if (typeof value === 'number') {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n if (value < -2147483648 || value > 2147483647) {\n typeInfo.bigint += 1\n }\n }\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') {\n const dateValue = new Date(value)\n if (!Number.isNaN(dateValue.getTime()) && value.includes('-')) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n if (typeInfo.integer === values.length) {\n if (typeInfo.bigint > 0) {\n columnTypes[key] = 'BIGINT'\n } else {\n columnTypes[key] = 'INTEGER'\n }\n } else {\n columnTypes[key] = 'DOUBLE'\n }\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 === 'number') {\n if (Number.isInteger(value) && (value < -2147483648 || value > 2147483647)) {\n return String(value)\n }\n return String(value)\n }\n return String(value)\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\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 } = {}\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 if (this.operationFlags.has(operationKey)) {\n console.log(`⏳ Table creation for '${tableName}' is already in progress, skipping...`)\n return\n }\n this.operationFlags.set(operationKey, true)\n\n try {\n const connection = await this.getConnection()\n\n if (options.dropIfExists) {\n console.log(`🗑️ Dropping table '${tableName}' if exists`)\n await connection.query(`DROP TABLE IF EXISTS ${tableName}`)\n }\n\n try {\n const existingTables = await connection.query('SHOW TABLES')\n console.log('📋 Existing tables before creation:', existingTables)\n } catch (error) {\n console.log('⚠️ Could not list existing tables:', error)\n }\n\n const sampleSize = Math.min(data.length, 100)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize))\n\n const columns = Object.keys(columnTypes).map(key => {\n return `\"${key}\" ${columnTypes[key]}`\n })\n\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (\"${options.primaryKey}\")` : ''\n\n const createTableSQL = `CREATE TABLE ${tableName} (${columns.join(', ')}${primaryKeyClause})`\n console.log('🔨 Creating table with SQL:', createTableSQL)\n await connection.query(createTableSQL)\n\n try {\n const tablesAfterCreation = await connection.query('SHOW TABLES')\n console.log('📋 Tables after creation:', tablesAfterCreation)\n\n const tableExists = await connection.query(`SELECT COUNT(*) as count FROM information_schema.tables WHERE table_name = '${tableName}'`)\n console.log(`✅ Table '${tableName}' exists check:`, tableExists)\n } catch (error) {\n console.log('⚠️ Could not verify table creation:', error)\n }\n\n console.log(`📥 Inserting ${data.length} rows into table '${tableName}'`)\n const dataColumns = Object.keys(data[0])\n const batchSize = 1000\n\n for (let i = 0; i < data.length; i += batchSize) {\n const batch = data.slice(i, i + batchSize)\n\n try {\n const valueRows = batch.map(row => {\n const values = dataColumns.map(col => {\n const value = row[col]\n return this.formatValueForSQL(value)\n })\n return `(${values.join(', ')})`\n })\n\n const insertSQL = `INSERT INTO ${tableName} (${dataColumns.map(col => `\"${col}\"`).join(', ')}) VALUES ${valueRows.join(', ')}`\n await connection.query(insertSQL)\n console.log(`📊 Inserted batch ${Math.floor(i / batchSize) + 1} (${batch.length} rows)`)\n } catch (insertError) {\n console.error(`❌ Failed to insert batch ${Math.floor(i / batchSize) + 1}:`, insertError)\n throw insertError\n }\n }\n\n console.log(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n } catch (error) {\n console.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n const connection = await this.getConnection()\n const result = await connection.query(`DESCRIBE ${tableName}`)\n return result\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n const connection = await this.getConnection()\n const result = await connection.query('SHOW TABLES')\n return result\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.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.listeners.clear()\n this.operationFlags.clear()\n } catch (error) {\n console.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 DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\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 (\n 'toArray' in result &&\n typeof result.toArray === 'function' &&\n 'numRows' in result &&\n typeof result.numRows === 'number'\n )\n}\n"],"mappings":"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","names":["index_exports","__export","DuckDBService","duckdb_default","duckdbTableToArray","getDuckDBColumnCount","getDuckDBRowCount","isDuckDBTable","useDuckDB","useDuckDBQuery","__toCommonJS","import_react","duckdb","_DuckDBService","listener","error","result","JSDELIVR_BUNDLES","bundle","workerUrl","worker","logger","connection","db","sql","queries","results","query","data","columnTypes","sampleData","keys","key","values","row","val","typeInfo","value","dateValue","tableName","options","operationKey","existingTables","sampleSize","columns","primaryKeyClause","createTableSQL","tablesAfterCreation","tableExists","dataColumns","batchSize","batch","valueRows","col","insertSQL","insertError","DuckDBService","duckdb_default","DuckDBService","useDuckDB","autoInitialize","status","setStatus","error","setError","duckDBService","DuckDBService","updateStatus","serviceStatus","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","options","getTableInfo","listTables","pollStatus","currentStatus","interval","useDuckDBQuery","dependencies","isReady","setData","loading","setLoading","refetch","result","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useCallback as m,useEffect as D,useState as b}from"react";import*as w from"@duckdb/duckdb-wasm";var d=class d{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.listeners=new Set}static getInstance(){return d.instance||(d.instance=new d),d.instance}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(t){console.error("Error in status listener:",t)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}async initialize(){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners(),this.initPromise=this.internalInitializeWorker();try{let e=await this.initPromise;return this.isInitializing=!1,this.notifyListeners(),e}catch(e){throw this.isInitializing=!1,this.hasError=!0,this.notifyListeners(),this.initPromise=null,e}}async internalInitializeWorker(){try{console.log("\u{1F527} Starting DuckDB Worker initialization..."),console.log("\u{1F4E6} Getting DuckDB bundles from CDN...");let e=w.getJsDelivrBundles();console.log("\u{1F50D} Selecting appropriate bundle for browser...");let t=await w.selectBundle(e);console.log("\u2705 Bundle selected:",{mainWorker:t.mainWorker,mainModule:t.mainModule,pthreadWorker:t.pthreadWorker}),console.log("\u{1F680} Creating Worker with Blob URL...");let c=URL.createObjectURL(new Blob([`importScripts("${t.mainWorker}");`],{type:"text/javascript"}));console.log("\u{1F477} Creating Worker instance...");let l=new Worker(c),n=new w.ConsoleLogger;console.log("\u{1F5C4}\uFE0F Creating DuckDB instance..."),this.workerInstance=new w.AsyncDuckDB(n,l),console.log("\u26A1 Instantiating DuckDB with modules..."),await this.workerInstance.instantiate(t.mainModule,t.pthreadWorker),console.log("\u{1F4E6} Installing splink_udfs from community...");let a=await this.workerInstance.connect();try{await a.query("INSTALL splink_udfs FROM community;"),console.log("\u2705 splink_udfs installed successfully"),await a.query("LOAD splink_udfs;"),console.log("\u2705 splink_udfs loaded successfully")}catch(o){console.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",o)}finally{await a.close()}return URL.revokeObjectURL(c),console.log("\u2705 DuckDB Worker initialized successfully"),this.workerInstance}catch(e){throw console.error("\u274C Failed to initialize DuckDB Worker:",e),e instanceof Error&&(console.error("Error name:",e.name),console.error("Error message:",e.message),console.error("Error stack:",e.stack)),e}}async getConnection(){if(!this.connectionInstance){let e=await this.initialize();this.connectionInstance=await e.connect()}return this.connectionInstance}async executeQuery(e){try{return await(await this.getConnection()).query(e)}catch(t){throw console.error("Failed to execute SQL query:",t),t}}async executeQueries(e){let t=[];for(let c of e){let l=await this.executeQuery(c);t.push(l)}return t}inferColumnTypes(e){let t={},c=e.slice(0,Math.min(e.length,10)),l=Object.keys(c[0]||{});for(let n of l){let a=c.map(s=>s[n]).filter(s=>s!=null);if(a.length===0){t[n]="VARCHAR";continue}let o={number:0,integer:0,bigint:0,boolean:0,date:0,string:0};for(let s of a)if(typeof s=="number")o.number+=1,Number.isInteger(s)&&(o.integer+=1,(s<-2147483648||s>2147483647)&&(o.bigint+=1));else if(typeof s=="boolean")o.boolean+=1;else if(s instanceof Date)o.date+=1;else if(typeof s=="string"){let y=new Date(s);!Number.isNaN(y.getTime())&&s.includes("-")?o.date+=1:o.string+=1}else o.string+=1;o.number===a.length?o.integer===a.length?o.bigint>0?t[n]="BIGINT":t[n]="INTEGER":t[n]="DOUBLE":o.boolean===a.length?t[n]="BOOLEAN":o.date===a.length?t[n]="TIMESTAMP":t[n]="VARCHAR"}return t}formatValueForSQL(e){return e==null?"NULL":typeof e=="string"?`'${e.replace(/'/g,"''")}'`:e instanceof Date?`'${e.toISOString()}'`:typeof e=="boolean"?e?"TRUE":"FALSE":(typeof e=="number"&&Number.isInteger(e)&&(e<-2147483648||e>2147483647),String(e))}async createTableFromData(e,t,c={}){if(!t||t.length===0)throw new Error("Data array is empty");let l=`create_table_${e}`;if(this.operationFlags.has(l)){console.log(`\u23F3 Table creation for '${e}' is already in progress, skipping...`);return}this.operationFlags.set(l,!0);try{let n=await this.getConnection();c.dropIfExists&&(console.log(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await n.query(`DROP TABLE IF EXISTS ${e}`));try{let r=await n.query("SHOW TABLES");console.log("\u{1F4CB} Existing tables before creation:",r)}catch(r){console.log("\u26A0\uFE0F Could not list existing tables:",r)}let a=Math.min(t.length,100),o=this.inferColumnTypes(t.slice(0,a)),s=Object.keys(o).map(r=>`"${r}" ${o[r]}`),y=c.primaryKey?`, PRIMARY KEY ("${c.primaryKey}")`:"",p=`CREATE TABLE ${e} (${s.join(", ")}${y})`;console.log("\u{1F528} Creating table with SQL:",p),await n.query(p);try{let r=await n.query("SHOW TABLES");console.log("\u{1F4CB} Tables after creation:",r);let u=await n.query(`SELECT COUNT(*) as count FROM information_schema.tables WHERE table_name = '${e}'`);console.log(`\u2705 Table '${e}' exists check:`,u)}catch(r){console.log("\u26A0\uFE0F Could not verify table creation:",r)}console.log(`\u{1F4E5} Inserting ${t.length} rows into table '${e}'`);let g=Object.keys(t[0]),k=1e3;for(let r=0;r<t.length;r+=k){let u=t.slice(r,r+k);try{let f=u.map(I=>`(${g.map(E=>{let S=I[E];return this.formatValueForSQL(S)}).join(", ")})`),B=`INSERT INTO ${e} (${g.map(I=>`"${I}"`).join(", ")}) VALUES ${f.join(", ")}`;await n.query(B),console.log(`\u{1F4CA} Inserted batch ${Math.floor(r/k)+1} (${u.length} rows)`)}catch(f){throw console.error(`\u274C Failed to insert batch ${Math.floor(r/k)+1}:`,f),f}}console.log(`\u{1F389} Table '${e}' created successfully with ${t.length} rows`)}catch(n){throw console.error(`\u274C Failed to create table '${e}':`,n),n}finally{this.operationFlags.delete(l)}}async getTableInfo(e){return await(await this.getConnection()).query(`DESCRIBE ${e}`)}async listTables(){return await(await this.getConnection()).query("SHOW TABLES")}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.listeners.clear(),this.operationFlags.clear()}catch(e){console.error("Error during cleanup:",e)}}static resetInstance(){d.instance&&(d.instance.cleanup(),d.instance=null)}};d.instance=null;var h=d;var R=h.getInstance();var T=(i=!0)=>{let[e,t]=b("not-initialized"),[c,l]=b(null),[n]=b(()=>h.getInstance()),a=m(()=>{let r=n.getStatus();console.info("\u{1F504} DuckDB status updated:",r),t(r),r==="error"?l("DuckDB initialization failed"):r==="ready"&&l(null)},[n]);D(()=>(n.addStatusListener(a),a(),()=>{n.removeStatusListener(a)}),[n,a]);let o=m(async()=>{try{await n.initialize()}catch(r){let u=r instanceof Error?r.message:"Unknown error occurred";l(u),console.error("DuckDB initialization failed:",r)}},[n]);D(()=>{i&&e==="not-initialized"&&(console.info("\u{1F680} Auto-initializing DuckDB..."),o())},[i,e,o]);let s=m(async r=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return n.executeQuery(r)},[n,e]),y=m(async r=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return n.executeQueries(r)},[n,e]),p=m(async(r,u,f)=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return n.createTableFromData(r,u,f)},[n,e]),g=m(async r=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return n.getTableInfo(r)},[n,e]),k=m(async()=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return n.listTables()},[n,e]);return D(()=>{let r=()=>{let f=n.getStatus();f!==e&&f!=="initializing"&&t(f)};if(typeof window>"u"){r();return}let u=window.setInterval(r,1e3);return r(),()=>window.clearInterval(u)},[n,e]),{status:e,error:c,executeQuery:s,executeQueries:y,createTableFromData:p,getTableInfo:g,listTables:k,isReady:e==="ready",initialize:o}},L=(i,e=[])=>{let{executeQuery:t,isReady:c}=T(),[l,n]=b(null),[a,o]=b(!1),[s,y]=b(null),p=m(async()=>{if(c&&i.trim())try{o(!0),y(null);let g=await t(i);n(g)}catch(g){let k=g instanceof Error?g.message:"Query execution failed";y(k),n(null)}finally{o(!1)}},[t,c,i]);return D(()=>{p()},[p,...e]),{data:l,loading:a,error:s,refetch:p}};var x=i=>!i||typeof i!="object"?[]:"toArray"in i&&typeof i.toArray=="function"?i.toArray():[],z=i=>!i||typeof i!="object"?0:"numRows"in i&&typeof i.numRows=="number"?i.numRows:0,P=i=>!i||typeof i!="object"?0:"numCols"in i&&typeof i.numCols=="number"?i.numCols:0,C=i=>!i||typeof i!="object"?!1:"toArray"in i&&typeof i.toArray=="function"&&"numRows"in i&&typeof i.numRows=="number";export{h as DuckDBService,R as duckDBService,x as duckdbTableToArray,P as getDuckDBColumnCount,z as getDuckDBRowCount,C as isDuckDBTable,T as useDuckDB,L as useDuckDBQuery};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useDuckDB.ts","../src/services/duckdb.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, useState } from 'react'\nimport { DuckDBService } from '../services/duckdb'\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 }\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): 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 console.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()\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'\n setError(errorMessage)\n console.error('DuckDB initialization failed:', err)\n }\n }, [duckDBService])\n\n useEffect(() => {\n if (autoInitialize && status === 'not-initialized') {\n console.info('🚀 Auto-initializing DuckDB...')\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(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 }, [duckDBService, status])\n\n const executeQueries = useCallback(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 }, [duckDBService, status])\n\n const createTableFromData = useCallback(async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\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 }, [duckDBService, status])\n\n const getTableInfo = useCallback(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 }, [duckDBService, status])\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 useEffect(() => {\n const pollStatus = () => {\n const currentStatus = duckDBService.getStatus()\n if (currentStatus !== status && currentStatus !== 'initializing') {\n setStatus(currentStatus)\n }\n }\n\n if (typeof window === 'undefined') {\n pollStatus()\n return\n }\n\n const interval = window.setInterval(pollStatus, 1000)\n pollStatus()\n\n return () => window.clearInterval(interval)\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 * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果と状態\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 useEffect(() => {\n void refetch()\n }, [refetch, ...dependencies])\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'\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, boolean> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\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 * 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 console.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 /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(): 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 this.initPromise = this.internalInitializeWorker()\n\n try {\n const result = await this.initPromise\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n this.isInitializing = false\n this.hasError = true\n this.notifyListeners()\n this.initPromise = null\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n */\n private async internalInitializeWorker(): Promise<duckdb.AsyncDuckDB> {\n try {\n console.log('🔧 Starting DuckDB Worker initialization...')\n console.log('📦 Getting DuckDB bundles from CDN...')\n const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles()\n\n console.log('🔍 Selecting appropriate bundle for browser...')\n const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)\n console.log('✅ Bundle selected:', {\n mainWorker: bundle.mainWorker,\n mainModule: bundle.mainModule,\n pthreadWorker: bundle.pthreadWorker\n })\n\n console.log('🚀 Creating Worker with Blob URL...')\n const workerUrl = URL.createObjectURL(\n new Blob([`importScripts(\"${bundle.mainWorker}\");`], { type: 'text/javascript' })\n )\n\n console.log('👷 Creating Worker instance...')\n const worker = new Worker(workerUrl)\n const logger = new duckdb.ConsoleLogger()\n\n console.log('🗄️ Creating DuckDB instance...')\n this.workerInstance = new duckdb.AsyncDuckDB(logger, worker)\n\n console.log('⚡ Instantiating DuckDB with modules...')\n await this.workerInstance.instantiate(bundle.mainModule, bundle.pthreadWorker)\n\n console.log('📦 Installing splink_udfs from community...')\n const connection = await this.workerInstance.connect()\n try {\n await connection.query('INSTALL splink_udfs FROM community;')\n console.log('✅ splink_udfs installed successfully')\n\n await connection.query('LOAD splink_udfs;')\n console.log('✅ splink_udfs loaded successfully')\n } catch (error) {\n console.warn('⚠️ Failed to install/load splink_udfs:', error)\n } finally {\n await connection.close()\n }\n\n URL.revokeObjectURL(workerUrl)\n\n console.log('✅ DuckDB Worker initialized successfully')\n return this.workerInstance\n } catch (error) {\n console.error('❌ Failed to initialize DuckDB Worker:', error)\n\n if (error instanceof Error) {\n console.error('Error name:', error.name)\n console.error('Error message:', error.message)\n console.error('Error stack:', error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (!this.connectionInstance) {\n const db = await this.initialize()\n this.connectionInstance = await db.connect()\n }\n return this.connectionInstance\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n try {\n const connection = await this.getConnection()\n const result = await connection.query(sql)\n return result\n } catch (error) {\n console.error('Failed to execute SQL query:', error)\n throw error\n }\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n const results: unknown[] = []\n for (const query of queries) {\n const result = await this.executeQuery(query)\n results.push(result)\n }\n return results\n }\n\n /**\n * Infer column types from data sample.\n * データサンプルから列の型を推定します。\n */\n private inferColumnTypes(data: Record<string, unknown>[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n const sampleData = data.slice(0, Math.min(data.length, 10))\n\n const keys = Object.keys(sampleData[0] || {})\n for (const key of keys) {\n const values = sampleData.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 bigint: 0,\n boolean: 0,\n date: 0,\n string: 0\n }\n\n for (const value of values) {\n if (typeof value === 'number') {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n if (value < -2147483648 || value > 2147483647) {\n typeInfo.bigint += 1\n }\n }\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') {\n const dateValue = new Date(value)\n if (!Number.isNaN(dateValue.getTime()) && value.includes('-')) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n if (typeInfo.integer === values.length) {\n if (typeInfo.bigint > 0) {\n columnTypes[key] = 'BIGINT'\n } else {\n columnTypes[key] = 'INTEGER'\n }\n } else {\n columnTypes[key] = 'DOUBLE'\n }\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 === 'number') {\n if (Number.isInteger(value) && (value < -2147483648 || value > 2147483647)) {\n return String(value)\n }\n return String(value)\n }\n return String(value)\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\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 } = {}\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 if (this.operationFlags.has(operationKey)) {\n console.log(`⏳ Table creation for '${tableName}' is already in progress, skipping...`)\n return\n }\n this.operationFlags.set(operationKey, true)\n\n try {\n const connection = await this.getConnection()\n\n if (options.dropIfExists) {\n console.log(`🗑️ Dropping table '${tableName}' if exists`)\n await connection.query(`DROP TABLE IF EXISTS ${tableName}`)\n }\n\n try {\n const existingTables = await connection.query('SHOW TABLES')\n console.log('📋 Existing tables before creation:', existingTables)\n } catch (error) {\n console.log('⚠️ Could not list existing tables:', error)\n }\n\n const sampleSize = Math.min(data.length, 100)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize))\n\n const columns = Object.keys(columnTypes).map(key => {\n return `\"${key}\" ${columnTypes[key]}`\n })\n\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (\"${options.primaryKey}\")` : ''\n\n const createTableSQL = `CREATE TABLE ${tableName} (${columns.join(', ')}${primaryKeyClause})`\n console.log('🔨 Creating table with SQL:', createTableSQL)\n await connection.query(createTableSQL)\n\n try {\n const tablesAfterCreation = await connection.query('SHOW TABLES')\n console.log('📋 Tables after creation:', tablesAfterCreation)\n\n const tableExists = await connection.query(`SELECT COUNT(*) as count FROM information_schema.tables WHERE table_name = '${tableName}'`)\n console.log(`✅ Table '${tableName}' exists check:`, tableExists)\n } catch (error) {\n console.log('⚠️ Could not verify table creation:', error)\n }\n\n console.log(`📥 Inserting ${data.length} rows into table '${tableName}'`)\n const dataColumns = Object.keys(data[0])\n const batchSize = 1000\n\n for (let i = 0; i < data.length; i += batchSize) {\n const batch = data.slice(i, i + batchSize)\n\n try {\n const valueRows = batch.map(row => {\n const values = dataColumns.map(col => {\n const value = row[col]\n return this.formatValueForSQL(value)\n })\n return `(${values.join(', ')})`\n })\n\n const insertSQL = `INSERT INTO ${tableName} (${dataColumns.map(col => `\"${col}\"`).join(', ')}) VALUES ${valueRows.join(', ')}`\n await connection.query(insertSQL)\n console.log(`📊 Inserted batch ${Math.floor(i / batchSize) + 1} (${batch.length} rows)`)\n } catch (insertError) {\n console.error(`❌ Failed to insert batch ${Math.floor(i / batchSize) + 1}:`, insertError)\n throw insertError\n }\n }\n\n console.log(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n } catch (error) {\n console.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n const connection = await this.getConnection()\n const result = await connection.query(`DESCRIBE ${tableName}`)\n return result\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n const connection = await this.getConnection()\n const result = await connection.query('SHOW TABLES')\n return result\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.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.listeners.clear()\n this.operationFlags.clear()\n } catch (error) {\n console.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 DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\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 (\n 'toArray' in result &&\n typeof result.toArray === 'function' &&\n 'numRows' in result &&\n typeof result.numRows === 'number'\n )\n}\n"],"mappings":"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","names":["useCallback","useEffect","useState","duckdb","_DuckDBService","listener","error","result","JSDELIVR_BUNDLES","bundle","workerUrl","worker","logger","connection","db","sql","queries","results","query","data","columnTypes","sampleData","keys","key","values","row","val","typeInfo","value","dateValue","tableName","options","operationKey","existingTables","sampleSize","columns","primaryKeyClause","createTableSQL","tablesAfterCreation","tableExists","dataColumns","batchSize","i","batch","valueRows","col","insertSQL","insertError","DuckDBService","duckdb_default","DuckDBService","useDuckDB","autoInitialize","status","setStatus","useState","error","setError","duckDBService","DuckDBService","updateStatus","useCallback","serviceStatus","useEffect","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","options","getTableInfo","listTables","pollStatus","currentStatus","interval","useDuckDBQuery","dependencies","isReady","setData","loading","setLoading","refetch","result","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable"]}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# DuckDB Helper Usage Guide
|
|
2
|
+
|
|
3
|
+
このガイドでは、`@aiquants/duckdb-helper` が提供するサービスとフックを使ってブラウザで DuckDB を活用する方法を説明します。
|
|
4
|
+
|
|
5
|
+
## 概要
|
|
6
|
+
|
|
7
|
+
DuckDB ヘルパーは次の主要要素から構成されています。
|
|
8
|
+
|
|
9
|
+
- `DuckDBService` (`@aiquants/duckdb-helper` からデフォルトインポート) — DuckDB Worker の管理とクエリ実行を行うシングルトンサービス
|
|
10
|
+
- `useDuckDB` — React コンポーネントから DuckDB 機能を安全に利用するためのフック
|
|
11
|
+
- `useDuckDBQuery` — クエリの実行と結果ステート管理を簡略化するフック
|
|
12
|
+
- `duckdbTableToArray` などのユーティリティ群 — DuckDB の結果を扱いやすいデータ構造に変換
|
|
13
|
+
|
|
14
|
+
## 基本的な使用方法
|
|
15
|
+
|
|
16
|
+
### 1. サービスを直接利用する
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import duckDBService from '@aiquants/duckdb-helper'
|
|
20
|
+
|
|
21
|
+
async function performDatabaseOperations() {
|
|
22
|
+
try {
|
|
23
|
+
await duckDBService.initialize()
|
|
24
|
+
|
|
25
|
+
const result = await duckDBService.executeQuery('SELECT 1 AS test')
|
|
26
|
+
console.log('Query result:', result)
|
|
27
|
+
|
|
28
|
+
const results = await duckDBService.executeQueries([
|
|
29
|
+
'CREATE TABLE sample (id INTEGER, name VARCHAR)',
|
|
30
|
+
`INSERT INTO sample VALUES (1, 'Alice'), (2, 'Bob')`,
|
|
31
|
+
'SELECT * FROM sample'
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
console.log('Multiple query results:', results)
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('Database operation failed:', error)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. React フックを利用する
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { useDuckDB } from '@aiquants/duckdb-helper'
|
|
45
|
+
|
|
46
|
+
const MyComponent = () => {
|
|
47
|
+
const { status, error, executeQuery, isReady } = useDuckDB()
|
|
48
|
+
|
|
49
|
+
const handleQuery = async () => {
|
|
50
|
+
if (!isReady) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const result = await executeQuery('SELECT * FROM my_table')
|
|
55
|
+
console.log(result)
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error('Query failed:', err)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div>
|
|
63
|
+
<p>Status: {status}</p>
|
|
64
|
+
{error && <p>Error: {error}</p>}
|
|
65
|
+
<button onClick={handleQuery} disabled={!isReady}>
|
|
66
|
+
Run Query
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. クエリ結果を配列に変換する
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { duckdbTableToArray } from '@aiquants/duckdb-helper'
|
|
77
|
+
|
|
78
|
+
interface User {
|
|
79
|
+
id: number
|
|
80
|
+
name: string
|
|
81
|
+
email: string | null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const table = await duckDBService.executeQuery('SELECT * FROM users LIMIT 10')
|
|
85
|
+
const rows = duckdbTableToArray<User>(table)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## フックオプション
|
|
89
|
+
|
|
90
|
+
### useDuckDB
|
|
91
|
+
|
|
92
|
+
- `autoInitialize` (boolean, default: `true`) — コンポーネントマウント時に自動初期化を行うかどうか
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
const { initialize, status } = useDuckDB(false)
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (status === 'not-initialized') {
|
|
99
|
+
void initialize()
|
|
100
|
+
}
|
|
101
|
+
}, [status, initialize])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### useDuckDBQuery
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
const { data, loading, error } = useDuckDBQuery(
|
|
108
|
+
`SELECT * FROM users WHERE id = ${userId}`,
|
|
109
|
+
[userId]
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`data` はクエリ結果、`loading` は実行状態、`error` はエラーメッセージを表します。`refetch` を呼び出すとクエリを再実行できます。
|
|
114
|
+
|
|
115
|
+
## エラーハンドリング
|
|
116
|
+
|
|
117
|
+
`executeQuery` などのメソッドは Promise を返し、失敗時には例外を送出します。また、`useDuckDB` フックは初期化エラーを `error` ステートで提供します。
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
const { error, executeQuery } = useDuckDB()
|
|
121
|
+
|
|
122
|
+
if (error) {
|
|
123
|
+
console.error('DuckDB initialization error:', error)
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## パフォーマンス最適化
|
|
128
|
+
|
|
129
|
+
- `DuckDBService` はシングルトンであり、アプリケーション全体で 1 つのインスタンスを共有します。
|
|
130
|
+
- Worker は `@duckdb/duckdb-wasm` の推奨バンドルを自動選択して初期化します。
|
|
131
|
+
- 利用が終わった際は `cleanup` を呼ぶことでリソースを解放できます。
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { DuckDBService } from '@aiquants/duckdb-helper'
|
|
135
|
+
|
|
136
|
+
await DuckDBService.getInstance().cleanup()
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## トラブルシューティング
|
|
140
|
+
|
|
141
|
+
1. **Worker が初期化されない** — ブラウザが Web Worker をサポートしているか確認し、コンソールのエラーログを確認してください。
|
|
142
|
+
2. **クエリが失敗する** — SQL 文を検証し、`status` が `ready` であることを確認してください。
|
|
143
|
+
3. **パフォーマンス問題** — 大きなデータを扱う際はクエリを分割し、必要に応じてテーブルを再利用してください。
|
|
144
|
+
|
|
145
|
+
## 型安全性
|
|
146
|
+
|
|
147
|
+
TypeScript でクエリ結果に型注釈を付けることで IntelliSense と型チェックを最大限に活用できます。
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
interface TDEightRow {
|
|
151
|
+
person_id: number
|
|
152
|
+
full_name: string | null
|
|
153
|
+
company_name: string | null
|
|
154
|
+
department: string | null
|
|
155
|
+
title: string | null
|
|
156
|
+
email: string | null
|
|
157
|
+
company_phone_number: string | null
|
|
158
|
+
mobile_phone_number: string | null
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const result = await duckDBService.executeQuery('SELECT * FROM tdeights LIMIT 100')
|
|
162
|
+
const rows = duckdbTableToArray<TDEightRow>(result)
|
|
163
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aiquants/duckdb-helper",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "DuckDB helper utilities with React hooks, worker initialization, and typed query helpers",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"docs"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"prepare": "pnpm run build",
|
|
25
|
+
"build:watch": "tsup --watch",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"type-check": "tsc --noEmit",
|
|
28
|
+
"typecheck": "npm run type-check",
|
|
29
|
+
"clean": "rimraf dist",
|
|
30
|
+
"prepublishOnly": "npm run clean && npm run typecheck && npm run test --if-present && npm run build",
|
|
31
|
+
"publish:patch": "npm version patch && npm publish",
|
|
32
|
+
"publish:minor": "npm version minor && npm publish",
|
|
33
|
+
"publish:major": "npm version major && npm publish",
|
|
34
|
+
"lint": "biome lint src/",
|
|
35
|
+
"lint:fix": "biome lint --write src/",
|
|
36
|
+
"format": "biome format src/",
|
|
37
|
+
"format:fix": "biome format --write src/",
|
|
38
|
+
"check": "biome check src/",
|
|
39
|
+
"check:fix": "biome check --write src/",
|
|
40
|
+
"license-check": "pnpm dlx license-checker --production --onlyAllow \"MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;Unlicense\"",
|
|
41
|
+
"license-check:json": "pnpm dlx license-checker --production --onlyAllow \"MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;Unlicense\" --json",
|
|
42
|
+
"test": "vitest run --passWithNoTests",
|
|
43
|
+
"test:coverage": "vitest run --coverage --passWithNoTests"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"duckdb",
|
|
47
|
+
"database",
|
|
48
|
+
"wasm",
|
|
49
|
+
"typescript",
|
|
50
|
+
"react",
|
|
51
|
+
"web-worker"
|
|
52
|
+
],
|
|
53
|
+
"author": {
|
|
54
|
+
"name": "fehde-k",
|
|
55
|
+
"url": "https://x.com/fehdek"
|
|
56
|
+
},
|
|
57
|
+
"license": "MIT",
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@biomejs/biome": "^2.3.1",
|
|
60
|
+
"@types/react": "^18.3.26",
|
|
61
|
+
"@types/react-dom": "^18.3.7",
|
|
62
|
+
"react": "^18.3.1",
|
|
63
|
+
"react-dom": "^18.3.1",
|
|
64
|
+
"rimraf": "^6.0.1",
|
|
65
|
+
"tsup": "^8.5.0",
|
|
66
|
+
"typescript": "^5.9.3",
|
|
67
|
+
"vitest": "^3.2.4"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"react": ">=16.8.0",
|
|
71
|
+
"react-dom": ">=16.8.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"react": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"react-dom": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"@duckdb/duckdb-wasm": "^1.30.0"
|
|
83
|
+
},
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">=16.0.0",
|
|
86
|
+
"pnpm": ">=8.0.0"
|
|
87
|
+
},
|
|
88
|
+
"publishConfig": {
|
|
89
|
+
"access": "public"
|
|
90
|
+
}
|
|
91
|
+
}
|