@aiquants/duckdb-helper 1.6.6 → 1.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/docs/specs/2026.04.11 [AI] 00-overview.md +3 -3
- package/docs/specs/2026.04.11 [AI] 02-service.md +3 -3
- package/docs/specs/2026.04.11 [AI] 06-build-config.md +1 -1
- package/package.json +3 -2
package/dist/index.d.mts
CHANGED
|
@@ -30,7 +30,9 @@ interface UseDuckDBResult {
|
|
|
30
30
|
* @param autoInitialize - 自動初期化を行うかどうか
|
|
31
31
|
* @returns DuckDB の状態と操作関数
|
|
32
32
|
*/
|
|
33
|
-
declare const useDuckDB: (autoInitialize?: boolean
|
|
33
|
+
declare const useDuckDB: (autoInitialize?: boolean, options?: {
|
|
34
|
+
bundles?: duckdb.DuckDBBundles;
|
|
35
|
+
}) => UseDuckDBResult;
|
|
34
36
|
/**
|
|
35
37
|
* Hook for simple DuckDB query execution.
|
|
36
38
|
* シンプルな DuckDB クエリ実行のためのフック。
|
|
@@ -122,7 +124,9 @@ declare class DuckDBService {
|
|
|
122
124
|
* Initialize DuckDB Worker instance.
|
|
123
125
|
* DuckDB Worker インスタンスを初期化します。
|
|
124
126
|
*/
|
|
125
|
-
initialize(
|
|
127
|
+
initialize(options?: {
|
|
128
|
+
bundles?: duckdb.DuckDBBundles;
|
|
129
|
+
}): Promise<duckdb.AsyncDuckDB>;
|
|
126
130
|
/**
|
|
127
131
|
* Internal worker initialization method.
|
|
128
132
|
* 内部的な Worker 初期化メソッド。
|
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,9 @@ interface UseDuckDBResult {
|
|
|
30
30
|
* @param autoInitialize - 自動初期化を行うかどうか
|
|
31
31
|
* @returns DuckDB の状態と操作関数
|
|
32
32
|
*/
|
|
33
|
-
declare const useDuckDB: (autoInitialize?: boolean
|
|
33
|
+
declare const useDuckDB: (autoInitialize?: boolean, options?: {
|
|
34
|
+
bundles?: duckdb.DuckDBBundles;
|
|
35
|
+
}) => UseDuckDBResult;
|
|
34
36
|
/**
|
|
35
37
|
* Hook for simple DuckDB query execution.
|
|
36
38
|
* シンプルな DuckDB クエリ実行のためのフック。
|
|
@@ -122,7 +124,9 @@ declare class DuckDBService {
|
|
|
122
124
|
* Initialize DuckDB Worker instance.
|
|
123
125
|
* DuckDB Worker インスタンスを初期化します。
|
|
124
126
|
*/
|
|
125
|
-
initialize(
|
|
127
|
+
initialize(options?: {
|
|
128
|
+
bundles?: duckdb.DuckDBBundles;
|
|
129
|
+
}): Promise<duckdb.AsyncDuckDB>;
|
|
126
130
|
/**
|
|
127
131
|
* Internal worker initialization method.
|
|
128
132
|
* 内部的な Worker 初期化メソッド。
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var O=Object.create;var I=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var V=(i,e)=>{for(var n in e)I(i,n,{get:e[n],enumerable:!0})},R=(i,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of F(e))!K.call(i,r)&&r!==n&&I(i,r,{get:()=>e[r],enumerable:!(o=q(e,r))||o.enumerable});return i};var _=(i,e,n)=>(n=i!=null?O(j(i)):{},R(e||!i||!i.__esModule?I(n,"default",{value:i,enumerable:!0}):n,i)),G=i=>R(I({},"__esModule",{value:!0}),i);var Z={};V(Z,{DuckDBService:()=>D,LogLevel:()=>v,Logger:()=>c,duckDBService:()=>A,duckdbTableToArray:()=>C,getDuckDBColumnCount:()=>z,getDuckDBRowCount:()=>U,isDuckDBTable:()=>$,useDuckDB:()=>L,useDuckDBQuery:()=>N});module.exports=G(Z);var d=require("react");var w=_(require("@duckdb/duckdb-wasm"));var v=(t=>(t[t.DEBUG=0]="DEBUG",t[t.INFO=1]="INFO",t[t.WARN=2]="WARN",t[t.ERROR=3]="ERROR",t[t.NONE=4]="NONE",t))(v||{}),m=class m{constructor(e=2,n="[duckdb-helper]",o=console){this.level=e,this.prefix=n,this.impl=o}static setLevel(e){m.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){m.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){m.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){m.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){m.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){m.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){m.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};m.instance=new m(2,"[duckdb-helper]");var c=m;var B=12e4,S=6e4,x=1e4,P=1e3,H=100,Y=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/,W=()=>{},h=class h{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.connectionPromise=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.pendingWorker=null;this.pendingWorkerUrl=null;this.operationQueue=Promise.resolve();this.listeners=new Set}static getInstance(){return h.instance||(h.instance=new h),h.instance}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){c.error("\u26A0\uFE0F Error in status listener:",n)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}enqueue(e){let n=this.operationQueue.then(e,e);return this.operationQueue=n.then(W,W),n}assertNotAborted(e){if(e.aborted)throw new Error("DuckDB initialization aborted")}abortPendingWorker(){if(this.pendingWorker){try{this.pendingWorker.terminate()}catch{}this.pendingWorker=null}if(this.pendingWorkerUrl){try{URL.revokeObjectURL(this.pendingWorkerUrl)}catch{}this.pendingWorkerUrl=null}}async initialize(){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let e={aborted:!1},n,o=new Promise((r,t)=>{n=setTimeout(()=>{e.aborted=!0,t(new Error(`DuckDB initialization timed out after ${B}ms`))},B)});this.initPromise=Promise.race([this.internalInitializeWorker(e),o]);try{let r=await this.initPromise;return clearTimeout(n),this.isInitializing=!1,this.notifyListeners(),r}catch(r){throw clearTimeout(n),e.aborted=!0,this.isInitializing=!1,this.hasError=!0,this.workerInstance=null,this.connectionInstance=null,this.connectionPromise=null,this.initPromise=null,this.abortPendingWorker(),this.notifyListeners(),r}}async internalInitializeWorker(e){let n=null,o=null;try{c.info("\u{1F527} Starting DuckDB Worker initialization..."),c.info("\u{1F4E6} Getting DuckDB bundles from CDN...");let r=w.getJsDelivrBundles();c.info("\u{1F50D} Selecting appropriate bundle for browser...");let t=await w.selectBundle(r);this.assertNotAborted(e);let s=t.mainWorker?.split("/").pop(),a=t.mainModule?.split("/").pop(),k=t.pthreadWorker?.split("/").pop(),g=l=>typeof window<"u"&&window.location?new URL(l,window.location.origin).toString():l,f={mainWorker:s?g(`/assets/duckdb/${s}`):"",mainModule:a?g(`/assets/duckdb/${a}`):"",pthreadWorker:k?g(`/assets/duckdb/${k}`):void 0};c.info("\u2705 Bundle selected:",{mainWorker:f.mainWorker,mainModule:f.mainModule,pthreadWorker:f.pthreadWorker}),c.info("\u{1F680} Creating Worker with Blob URL..."),n=URL.createObjectURL(new Blob([`importScripts("${f.mainWorker}");`],{type:"text/javascript"})),c.info("\u{1F477} Creating Worker instance..."),o=new Worker(n),this.pendingWorker=o,this.pendingWorkerUrl=n;let b=new w.ConsoleLogger;c.info("\u{1F5C4}\uFE0F Creating DuckDB instance...");let p=new w.AsyncDuckDB(b,o);c.info("\u26A1 Instantiating DuckDB with modules..."),await p.instantiate(f.mainModule,f.pthreadWorker),this.assertNotAborted(e),c.info("\u{1F4E6} Installing splink_udfs from community...");let u=await p.connect();try{let l,y=new Promise((E,T)=>{l=setTimeout(()=>{T(new Error(`splink_udfs installation timed out after ${x}ms`))},x)});await Promise.race([(async()=>{await u.query("INSTALL splink_udfs FROM community;"),c.info("\u2705 splink_udfs installed successfully"),await u.query("LOAD splink_udfs;"),c.info("\u2705 splink_udfs loaded successfully")})(),y]),clearTimeout(l)}catch(l){c.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",l)}finally{await u.close()}return this.assertNotAborted(e),this.workerInstance=p,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(n),c.info("\u2705 DuckDB Worker initialized successfully"),p}catch(r){if(o)try{o.terminate()}catch{}if(n)try{URL.revokeObjectURL(n)}catch{}throw this.pendingWorker===o&&(this.pendingWorker=null),this.pendingWorkerUrl===n&&(this.pendingWorkerUrl=null),c.error("\u274C Failed to initialize DuckDB Worker:",r),r instanceof Error&&(c.error("\u274C Error name:",r.name),c.error("\u274C Error message:",r.message),c.error("\u274C Error stack:",r.stack)),r}}async getConnection(){if(this.connectionInstance)return this.connectionInstance;this.connectionPromise||(this.connectionPromise=(async()=>{let n=await(await this.initialize()).connect();return this.connectionInstance=n,n})());try{return await this.connectionPromise}catch(e){throw this.connectionPromise=null,e}}async runQueryWithTimeout(e){let n=!1,o,r=new Promise((s,a)=>{o=setTimeout(()=>{n=!0,a(new Error(`DuckDB query execution timed out after ${S}ms`))},S)}),t=(async()=>(await this.getConnection()).query(e))();try{let s=await Promise.race([t,r]);return clearTimeout(o),s}catch(s){if(clearTimeout(o),n){let a=this.connectionInstance;this.connectionInstance=null,this.connectionPromise=null,a&&a.close().catch(()=>{})}throw c.error("\u274C Failed to execute SQL query:",s),s}}async executeQuery(e){return this.enqueue(()=>this.runQueryWithTimeout(e))}async executeQueries(e){return this.enqueue(async()=>{let n=[];for(let o of e)n.push(await this.runQueryWithTimeout(o));return n})}collectColumnNames(e){let n=new Set,o=[];for(let r of e)if(r)for(let t of Object.keys(r))n.has(t)||(n.add(t),o.push(t));return o}looksLikeTimestamp(e){return Y.test(e)?!Number.isNaN(new Date(e).getTime()):!1}inferColumnTypes(e,n){let o={};for(let r of n){let t=e.map(a=>a?.[r]).filter(a=>a!=null);if(t.length===0){o[r]="VARCHAR";continue}let s={number:0,integer:0,boolean:0,date:0,string:0};for(let a of t)typeof a=="number"?Number.isFinite(a)?(s.number+=1,Number.isInteger(a)&&(s.integer+=1)):s.string+=1:typeof a=="bigint"?(s.number+=1,s.integer+=1):typeof a=="boolean"?s.boolean+=1:a instanceof Date||typeof a=="string"&&this.looksLikeTimestamp(a)?s.date+=1:s.string+=1;s.number===t.length?o[r]=s.integer===t.length?"BIGINT":"DOUBLE":s.boolean===t.length?o[r]="BOOLEAN":s.date===t.length?o[r]="TIMESTAMP":o[r]="VARCHAR"}return o}formatValueForSQL(e){if(e==null)return"NULL";if(typeof e=="string")return`'${e.replace(/'/g,"''")}'`;if(e instanceof Date)return`'${e.toISOString()}'`;if(typeof e=="boolean")return e?"TRUE":"FALSE";if(typeof e=="bigint")return e.toString();if(typeof e=="number")return Number.isFinite(e)?String(e):"NULL";try{let n=JSON.stringify(e);return n===void 0?"NULL":`'${n.replace(/'/g,"''")}'`}catch{return"NULL"}}quoteIdentifier(e){if(typeof e!="string"||e.length===0)throw new Error("Invalid SQL identifier");return`"${e.replace(/"/g,'""')}"`}async createTableFromData(e,n,o={}){if(!n||n.length===0)throw new Error("Data array is empty");let r=`create_table_${e}`,t=o.verbose??!0,s=this.operationFlags.get(r);if(s)return t&&c.info(`\u23F3 Table creation for '${e}' is already in progress, awaiting existing operation...`),s;let a=this.enqueue(()=>this.buildTable(e,n,o,t));this.operationFlags.set(r,a);try{await a}finally{this.operationFlags.delete(r)}}async buildTable(e,n,o,r){try{let t=await this.getConnection(),s=this.quoteIdentifier(e),a=this.collectColumnNames(n);if(a.length===0)throw new Error("Data rows have no columns");let k=Math.min(n.length,H),g=this.inferColumnTypes(n.slice(0,k),a),f=a.map(l=>`${this.quoteIdentifier(l)} ${g[l]}`),b=o.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(o.primaryKey)})`:"",p=`CREATE TABLE ${s} (${f.join(", ")}${b})`,u=a.map(l=>this.quoteIdentifier(l)).join(", ");r&&c.info(`\u{1F4E5} Building table '${e}' with ${n.length} rows`),await t.query("BEGIN TRANSACTION");try{o.dropIfExists&&(r&&c.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await t.query(`DROP TABLE IF EXISTS ${s}`)),r&&c.info("\u{1F528} Creating table with SQL:",p),await t.query(p);for(let l=0;l<n.length;l+=P){let y=n.slice(l,l+P),E=y.map(M=>`(${a.map(Q=>this.formatValueForSQL(M?.[Q])).join(", ")})`),T=`INSERT INTO ${s} (${u}) VALUES ${E.join(", ")}`;await t.query(T),r&&c.info(`\u{1F4CA} Inserted batch ${Math.floor(l/P)+1} (${y.length} rows)`)}await t.query("COMMIT")}catch(l){try{await t.query("ROLLBACK")}catch(y){c.warn("\u26A0\uFE0F Rollback failed:",y)}throw l}r&&c.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(t){throw c.error(`\u274C Failed to create table '${e}':`,t),t}}async getTableInfo(e){return this.enqueue(async()=>(await this.getConnection()).query(`DESCRIBE ${this.quoteIdentifier(e)}`))}async listTables(){return this.enqueue(async()=>(await this.getConnection()).query("SHOW TABLES"))}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.abortPendingWorker(),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.connectionPromise=null,this.operationQueue=Promise.resolve(),this.operationFlags.clear(),this.notifyListeners(),this.listeners.clear()}catch(e){c.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){h.instance&&(h.instance.cleanup(),h.instance=null)}};h.instance=null;var D=h;var A=D.getInstance();var J=i=>{let e=(0,d.useRef)({deps:i,version:0}),n=e.current.deps;return(n.length!==i.length||i.some((r,t)=>!Object.is(r,n[t])))&&(e.current={deps:i,version:e.current.version+1}),e.current.version},L=(i=!0)=>{let[e,n]=(0,d.useState)("not-initialized"),[o,r]=(0,d.useState)(null),[t]=(0,d.useState)(()=>D.getInstance()),s=(0,d.useCallback)(()=>{let u=t.getStatus();c.info("\u{1F504} DuckDB status updated:",u),n(u),u==="error"?r("DuckDB initialization failed"):u==="ready"&&r(null)},[t]);(0,d.useEffect)(()=>(t.addStatusListener(s),s(),()=>{t.removeStatusListener(s)}),[t,s]);let a=(0,d.useCallback)(async()=>{try{await t.initialize()}catch(u){let l=u instanceof Error?u.message:"Unknown error occurred";r(l),c.error("\u274C DuckDB initialization failed:",u)}},[t]);(0,d.useEffect)(()=>{i&&e==="not-initialized"&&(c.info("\u{1F680} Auto-initializing DuckDB..."),a())},[i,e,a]);let k=(0,d.useCallback)(async u=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQuery(u)},[t,e]),g=(0,d.useCallback)(async u=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQueries(u)},[t,e]),f=(0,d.useCallback)(async(u,l,y)=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.createTableFromData(u,l,y)},[t,e]),b=(0,d.useCallback)(async u=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.getTableInfo(u)},[t,e]),p=(0,d.useCallback)(async()=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.listTables()},[t,e]);return{status:e,error:o,executeQuery:k,executeQueries:g,createTableFromData:f,getTableInfo:b,listTables:p,isReady:e==="ready",initialize:a}},N=(i,e=[])=>{let{executeQuery:n,isReady:o}=L(),[r,t]=(0,d.useState)(null),[s,a]=(0,d.useState)(!1),[k,g]=(0,d.useState)(null),f=(0,d.useCallback)(async()=>{if(o&&i.trim())try{a(!0),g(null);let p=await n(i);t(p)}catch(p){let u=p instanceof Error?p.message:"Query execution failed";g(u),t(null)}finally{a(!1)}},[n,o,i]),b=J(e);return(0,d.useEffect)(()=>{f()},[f,b]),{data:r,loading:s,error:k,refetch:f}};var C=i=>!i||typeof i!="object"?[]:"toArray"in i&&typeof i.toArray=="function"?i.toArray():[],U=i=>!i||typeof i!="object"?0:"numRows"in i&&typeof i.numRows=="number"?i.numRows:0,z=i=>!i||typeof i!="object"?0:"numCols"in i&&typeof i.numCols=="number"?i.numCols:0,$=i=>!i||typeof i!="object"?!1:"toArray"in i&&typeof i.toArray=="function"&&"numRows"in i&&typeof i.numRows=="number";0&&(module.exports={DuckDBService,LogLevel,Logger,duckDBService,duckdbTableToArray,getDuckDBColumnCount,getDuckDBRowCount,isDuckDBTable,useDuckDB,useDuckDBQuery});
|
|
1
|
+
"use strict";var O=Object.create;var T=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var V=(r,e)=>{for(var n in e)T(r,n,{get:e[n],enumerable:!0})},L=(r,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of F(e))!K.call(r,s)&&s!==n&&T(r,s,{get:()=>e[s],enumerable:!(o=q(e,s))||o.enumerable});return r};var _=(r,e,n)=>(n=r!=null?O(j(r)):{},L(e||!r||!r.__esModule?T(n,"default",{value:r,enumerable:!0}):n,r)),G=r=>L(T({},"__esModule",{value:!0}),r);var J={};V(J,{DuckDBService:()=>b,LogLevel:()=>I,Logger:()=>c,duckDBService:()=>W,duckdbTableToArray:()=>z,getDuckDBColumnCount:()=>U,getDuckDBRowCount:()=>C,isDuckDBTable:()=>N,useDuckDB:()=>B,useDuckDBQuery:()=>A});module.exports=G(J);var l=require("react");var w=_(require("@duckdb/duckdb-wasm"));var I=(t=>(t[t.DEBUG=0]="DEBUG",t[t.INFO=1]="INFO",t[t.WARN=2]="WARN",t[t.ERROR=3]="ERROR",t[t.NONE=4]="NONE",t))(I||{}),k=class k{constructor(e=2,n="[duckdb-helper]",o=console){this.level=e,this.prefix=n,this.impl=o}static setLevel(e){k.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){k.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){k.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){k.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){k.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){k.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){k.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};k.instance=new k(2,"[duckdb-helper]");var c=k;var E=12e4,R=6e4,S=1e4,P=1e3,H=100,Y=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/,x=()=>{},h=class h{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.connectionPromise=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.pendingWorker=null;this.pendingWorkerUrl=null;this.operationQueue=Promise.resolve();this.listeners=new Set}static getInstance(){return h.instance||(h.instance=new h),h.instance}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){c.error("\u26A0\uFE0F Error in status listener:",n)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}enqueue(e){let n=this.operationQueue.then(e,e);return this.operationQueue=n.then(x,x),n}assertNotAborted(e){if(e.aborted)throw new Error("DuckDB initialization aborted")}abortPendingWorker(){if(this.pendingWorker){try{this.pendingWorker.terminate()}catch{}this.pendingWorker=null}if(this.pendingWorkerUrl){try{URL.revokeObjectURL(this.pendingWorkerUrl)}catch{}this.pendingWorkerUrl=null}}async initialize(e){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let n={aborted:!1},o,s=new Promise((t,i)=>{o=setTimeout(()=>{n.aborted=!0,i(new Error(`DuckDB initialization timed out after ${E}ms`))},E)});this.initPromise=Promise.race([this.internalInitializeWorker(n,e),s]);try{let t=await this.initPromise;return clearTimeout(o),this.isInitializing=!1,this.notifyListeners(),t}catch(t){throw clearTimeout(o),n.aborted=!0,this.isInitializing=!1,this.hasError=!0,this.workerInstance=null,this.connectionInstance=null,this.connectionPromise=null,this.initPromise=null,this.abortPendingWorker(),this.notifyListeners(),t}}async internalInitializeWorker(e,n){let o=null,s=null;try{c.info("\u{1F527} Starting DuckDB Worker initialization..."),c.info("\u{1F4E6} Getting DuckDB bundles...");let t=n?.bundles||w.getJsDelivrBundles();c.info("\u{1F50D} Selecting appropriate bundle for browser...");let i=await w.selectBundle(t);this.assertNotAborted(e);let a=f=>f.startsWith("/")&&typeof window<"u"&&window.location?new URL(f,window.location.origin).toString():f,d={mainWorker:i.mainWorker?a(i.mainWorker):"",mainModule:i.mainModule?a(i.mainModule):"",pthreadWorker:i.pthreadWorker?a(i.pthreadWorker):void 0};c.info("\u2705 Bundle selected:",{mainWorker:d.mainWorker,mainModule:d.mainModule,pthreadWorker:d.pthreadWorker}),c.info("\u{1F680} Creating Worker with Blob URL..."),o=URL.createObjectURL(new Blob([`importScripts("${d.mainWorker}");`],{type:"text/javascript"})),c.info("\u{1F477} Creating Worker instance..."),s=new Worker(o),this.assertNotAborted(e),this.pendingWorker=s,this.pendingWorkerUrl=o,c.info("\u{1F91D} Connecting to AsyncDuckDB instance...");let m=new w.AsyncDuckDB(new w.ConsoleLogger,s);await m.instantiate(d.mainModule,d.pthreadWorker),this.assertNotAborted(e),c.info("\u{1F4E6} Installing splink_udfs from community...");let p=await m.connect();try{let f,g=new Promise((D,u)=>{f=setTimeout(()=>{u(new Error(`splink_udfs installation timed out after ${S}ms`))},S)});await Promise.race([(async()=>{await p.query("INSTALL splink_udfs FROM community;"),c.info("\u2705 splink_udfs installed successfully"),await p.query("LOAD splink_udfs;"),c.info("\u2705 splink_udfs loaded successfully")})(),g]),clearTimeout(f)}catch(f){c.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",f)}finally{await p.close()}return this.assertNotAborted(e),this.workerInstance=m,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(o),c.info("\u2705 DuckDB Worker initialized successfully"),m}catch(t){if(s)try{s.terminate()}catch{}if(o)try{URL.revokeObjectURL(o)}catch{}throw this.pendingWorker===s&&(this.pendingWorker=null),this.pendingWorkerUrl===o&&(this.pendingWorkerUrl=null),c.error("\u274C Failed to initialize DuckDB Worker:",t),t instanceof Error&&(c.error("\u274C Error name:",t.name),c.error("\u274C Error message:",t.message),c.error("\u274C Error stack:",t.stack)),t}}async getConnection(){if(this.connectionInstance)return this.connectionInstance;this.connectionPromise||(this.connectionPromise=(async()=>{let n=await(await this.initialize()).connect();return this.connectionInstance=n,n})());try{return await this.connectionPromise}catch(e){throw this.connectionPromise=null,e}}async runQueryWithTimeout(e){let n=!1,o,s=new Promise((i,a)=>{o=setTimeout(()=>{n=!0,a(new Error(`DuckDB query execution timed out after ${R}ms`))},R)}),t=(async()=>(await this.getConnection()).query(e))();try{let i=await Promise.race([t,s]);return clearTimeout(o),i}catch(i){if(clearTimeout(o),n){let a=this.connectionInstance;this.connectionInstance=null,this.connectionPromise=null,a&&a.close().catch(()=>{})}throw c.error("\u274C Failed to execute SQL query:",i),i}}async executeQuery(e){return this.enqueue(()=>this.runQueryWithTimeout(e))}async executeQueries(e){return this.enqueue(async()=>{let n=[];for(let o of e)n.push(await this.runQueryWithTimeout(o));return n})}collectColumnNames(e){let n=new Set,o=[];for(let s of e)if(s)for(let t of Object.keys(s))n.has(t)||(n.add(t),o.push(t));return o}looksLikeTimestamp(e){return Y.test(e)?!Number.isNaN(new Date(e).getTime()):!1}inferColumnTypes(e,n){let o={};for(let s of n){let t=e.map(a=>a?.[s]).filter(a=>a!=null);if(t.length===0){o[s]="VARCHAR";continue}let i={number:0,integer:0,boolean:0,date:0,string:0};for(let a of t)typeof a=="number"?Number.isFinite(a)?(i.number+=1,Number.isInteger(a)&&(i.integer+=1)):i.string+=1:typeof a=="bigint"?(i.number+=1,i.integer+=1):typeof a=="boolean"?i.boolean+=1:a instanceof Date||typeof a=="string"&&this.looksLikeTimestamp(a)?i.date+=1:i.string+=1;i.number===t.length?o[s]=i.integer===t.length?"BIGINT":"DOUBLE":i.boolean===t.length?o[s]="BOOLEAN":i.date===t.length?o[s]="TIMESTAMP":o[s]="VARCHAR"}return o}formatValueForSQL(e){if(e==null)return"NULL";if(typeof e=="string")return`'${e.replace(/'/g,"''")}'`;if(e instanceof Date)return`'${e.toISOString()}'`;if(typeof e=="boolean")return e?"TRUE":"FALSE";if(typeof e=="bigint")return e.toString();if(typeof e=="number")return Number.isFinite(e)?String(e):"NULL";try{let n=JSON.stringify(e);return n===void 0?"NULL":`'${n.replace(/'/g,"''")}'`}catch{return"NULL"}}quoteIdentifier(e){if(typeof e!="string"||e.length===0)throw new Error("Invalid SQL identifier");return`"${e.replace(/"/g,'""')}"`}async createTableFromData(e,n,o={}){if(!n||n.length===0)throw new Error("Data array is empty");let s=`create_table_${e}`,t=o.verbose??!0,i=this.operationFlags.get(s);if(i)return t&&c.info(`\u23F3 Table creation for '${e}' is already in progress, awaiting existing operation...`),i;let a=this.enqueue(()=>this.buildTable(e,n,o,t));this.operationFlags.set(s,a);try{await a}finally{this.operationFlags.delete(s)}}async buildTable(e,n,o,s){try{let t=await this.getConnection(),i=this.quoteIdentifier(e),a=this.collectColumnNames(n);if(a.length===0)throw new Error("Data rows have no columns");let d=Math.min(n.length,H),m=this.inferColumnTypes(n.slice(0,d),a),p=a.map(u=>`${this.quoteIdentifier(u)} ${m[u]}`),f=o.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(o.primaryKey)})`:"",g=`CREATE TABLE ${i} (${p.join(", ")}${f})`,D=a.map(u=>this.quoteIdentifier(u)).join(", ");s&&c.info(`\u{1F4E5} Building table '${e}' with ${n.length} rows`),await t.query("BEGIN TRANSACTION");try{o.dropIfExists&&(s&&c.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await t.query(`DROP TABLE IF EXISTS ${i}`)),s&&c.info("\u{1F528} Creating table with SQL:",g),await t.query(g);for(let u=0;u<n.length;u+=P){let y=n.slice(u,u+P),v=y.map(M=>`(${a.map(Q=>this.formatValueForSQL(M?.[Q])).join(", ")})`),$=`INSERT INTO ${i} (${D}) VALUES ${v.join(", ")}`;await t.query($),s&&c.info(`\u{1F4CA} Inserted batch ${Math.floor(u/P)+1} (${y.length} rows)`)}await t.query("COMMIT")}catch(u){try{await t.query("ROLLBACK")}catch(y){c.warn("\u26A0\uFE0F Rollback failed:",y)}throw u}s&&c.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(t){throw c.error(`\u274C Failed to create table '${e}':`,t),t}}async getTableInfo(e){return this.enqueue(async()=>(await this.getConnection()).query(`DESCRIBE ${this.quoteIdentifier(e)}`))}async listTables(){return this.enqueue(async()=>(await this.getConnection()).query("SHOW TABLES"))}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.abortPendingWorker(),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.connectionPromise=null,this.operationQueue=Promise.resolve(),this.operationFlags.clear(),this.notifyListeners(),this.listeners.clear()}catch(e){c.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){h.instance&&(h.instance.cleanup(),h.instance=null)}};h.instance=null;var b=h;var W=b.getInstance();var Z=r=>{let e=(0,l.useRef)({deps:r,version:0}),n=e.current.deps;return(n.length!==r.length||r.some((s,t)=>!Object.is(s,n[t])))&&(e.current={deps:r,version:e.current.version+1}),e.current.version},B=(r=!0,e)=>{let[n,o]=(0,l.useState)("not-initialized"),[s,t]=(0,l.useState)(null),[i]=(0,l.useState)(()=>b.getInstance()),a=(0,l.useCallback)(()=>{let u=i.getStatus();c.info("\u{1F504} DuckDB status updated:",u),o(u),u==="error"?t("DuckDB initialization failed"):u==="ready"&&t(null)},[i]);(0,l.useEffect)(()=>(i.addStatusListener(a),a(),()=>{i.removeStatusListener(a)}),[i,a]);let d=(0,l.useCallback)(async()=>{try{await i.initialize(e)}catch(u){let y=u instanceof Error?u.message:"Unknown error occurred";t(y),c.error("\u274C DuckDB initialization failed:",u)}},[i,e]);(0,l.useEffect)(()=>{r&&n==="not-initialized"&&(c.info("\u{1F680} Auto-initializing DuckDB..."),d())},[r,n,d]);let m=(0,l.useCallback)(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQuery(u)},[i,n]),p=(0,l.useCallback)(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQueries(u)},[i,n]),f=(0,l.useCallback)(async(u,y,v)=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.createTableFromData(u,y,v)},[i,n]),g=(0,l.useCallback)(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.getTableInfo(u)},[i,n]),D=(0,l.useCallback)(async()=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.listTables()},[i,n]);return{status:n,error:s,executeQuery:m,executeQueries:p,createTableFromData:f,getTableInfo:g,listTables:D,isReady:n==="ready",initialize:d}},A=(r,e=[])=>{let{executeQuery:n,isReady:o}=B(),[s,t]=(0,l.useState)(null),[i,a]=(0,l.useState)(!1),[d,m]=(0,l.useState)(null),p=(0,l.useCallback)(async()=>{if(o&&r.trim())try{a(!0),m(null);let g=await n(r);t(g)}catch(g){let D=g instanceof Error?g.message:"Query execution failed";m(D),t(null)}finally{a(!1)}},[n,o,r]),f=Z(e);return(0,l.useEffect)(()=>{p()},[p,f]),{data:s,loading:i,error:d,refetch:p}};var z=r=>!r||typeof r!="object"?[]:"toArray"in r&&typeof r.toArray=="function"?r.toArray():[],C=r=>!r||typeof r!="object"?0:"numRows"in r&&typeof r.numRows=="number"?r.numRows:0,U=r=>!r||typeof r!="object"?0:"numCols"in r&&typeof r.numCols=="number"?r.numCols:0,N=r=>!r||typeof r!="object"?!1:"toArray"in r&&typeof r.toArray=="function"&&"numRows"in r&&typeof r.numRows=="number";0&&(module.exports={DuckDBService,LogLevel,Logger,duckDBService,duckdbTableToArray,getDuckDBColumnCount,getDuckDBRowCount,isDuckDBTable,useDuckDB,useDuckDBQuery});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/hooks/useDuckDB.ts","../src/services/duckdb.ts","../src/utils/simple-logger.ts","../src/utils/duckdb-helpers.ts"],"sourcesContent":["/**\n * DuckDB Helper Library Entry Point\n *\n * Provides React hooks, services, types, and utilities for working with DuckDB in web applications.\n * Web アプリケーションで DuckDB を扱うための React フック、サービス、型、ユーティリティを提供します。\n */\n\nexport type { UseDuckDBResult } from \"./hooks/useDuckDB\"\nexport { useDuckDB, useDuckDBQuery } from \"./hooks/useDuckDB\"\nexport { DuckDBService, default as duckDBService } from \"./services/duckdb\"\nexport type {\n DuckDBQueryResult,\n DuckDBRow,\n} from \"./types/duckdb\"\nexport {\n duckdbTableToArray,\n getDuckDBColumnCount,\n getDuckDBRowCount,\n isDuckDBTable,\n} from \"./utils/duckdb-helpers\"\nexport type { ILogger } from \"./utils/simple-logger\"\nexport { Logger, LogLevel } from \"./utils/simple-logger\"\n","/**\n * DuckDB React Hook\n *\n * React hook for using DuckDB functionality in components.\n * コンポーネントで DuckDB 機能を使用するための React フック。\n */\n\nimport { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\"\nimport { DuckDBService } from \"../services/duckdb\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/**\n * Derive a constant-size version number that increments whenever the given\n * dependency list changes (by React's own Object.is comparison).\n * 依存配列の変化(React と同じ Object.is 比較)で増える固定サイズのバージョン番号を返す。\n *\n * これにより可変長の `dependencies` を effect の依存配列に直接スプレッドせずに済み、\n * 配列サイズがレンダー間で変わっても React の「依存配列サイズ変更」エラーを起こさない。\n */\nconst useDependencyVersion = (dependencies: DependencyList): number => {\n const ref = useRef<{ deps: DependencyList; version: number }>({ deps: dependencies, version: 0 })\n const previous = ref.current.deps\n const changed = previous.length !== dependencies.length || dependencies.some((dep, index) => !Object.is(dep, previous[index]))\n if (changed) {\n ref.current = { deps: dependencies, version: ref.current.version + 1 }\n }\n return ref.current.version\n}\n\nexport interface UseDuckDBResult {\n status: \"not-initialized\" | \"initializing\" | \"ready\" | \"error\"\n error: string | null\n executeQuery: (sql: string) => Promise<unknown>\n executeQueries: (queries: string[]) => Promise<unknown[]>\n createTableFromData: <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ) => Promise<void>\n getTableInfo: (tableName: string) => Promise<unknown>\n listTables: () => Promise<unknown>\n isReady: boolean\n initialize: () => Promise<void>\n}\n\n/**\n * DuckDB functionality hook.\n * DuckDB 機能を提供するフック。\n *\n * @param autoInitialize - 自動初期化を行うかどうか\n * @returns DuckDB の状態と操作関数\n */\nexport const useDuckDB = (autoInitialize = true): UseDuckDBResult => {\n const [status, setStatus] = useState<\"not-initialized\" | \"initializing\" | \"ready\" | \"error\">(\"not-initialized\")\n const [error, setError] = useState<string | null>(null)\n const [duckDBService] = useState(() => DuckDBService.getInstance())\n\n const updateStatus = useCallback(() => {\n const serviceStatus = duckDBService.getStatus()\n Logger.info(\"🔄 DuckDB status updated:\", serviceStatus)\n setStatus(serviceStatus)\n\n if (serviceStatus === \"error\") {\n setError(\"DuckDB initialization failed\")\n } else if (serviceStatus === \"ready\") {\n setError(null)\n }\n }, [duckDBService])\n\n useEffect(() => {\n duckDBService.addStatusListener(updateStatus)\n updateStatus()\n\n return () => {\n duckDBService.removeStatusListener(updateStatus)\n }\n }, [duckDBService, updateStatus])\n\n const initialize = useCallback(async () => {\n try {\n await duckDBService.initialize()\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Unknown error occurred\"\n setError(errorMessage)\n Logger.error(\"❌ DuckDB initialization failed:\", err)\n }\n }, [duckDBService])\n\n useEffect(() => {\n if (autoInitialize && status === \"not-initialized\") {\n Logger.info(\"🚀 Auto-initializing DuckDB...\")\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(\n async (sql: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQuery(sql)\n },\n [duckDBService, status],\n )\n\n const executeQueries = useCallback(\n async (queries: string[]): Promise<unknown[]> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQueries(queries)\n },\n [duckDBService, status],\n )\n\n const createTableFromData = useCallback(\n async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ): Promise<void> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.createTableFromData(tableName, data, options)\n },\n [duckDBService, status],\n )\n\n const getTableInfo = useCallback(\n async (tableName: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.getTableInfo(tableName)\n },\n [duckDBService, status],\n )\n\n const listTables = useCallback(async (): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.listTables()\n }, [duckDBService, status])\n\n return {\n status,\n error,\n executeQuery,\n executeQueries,\n createTableFromData,\n getTableInfo,\n listTables,\n isReady: status === \"ready\",\n initialize,\n }\n}\n\n/**\n * Hook for simple DuckDB query execution.\n * シンプルな DuckDB クエリ実行のためのフック。\n *\n * @remarks\n * `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。\n * `.length` や `.map` は持たないため、行配列として扱う場合は必ず\n * {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく\n * Arrow Table 相当の型を想定)。\n *\n * @example\n * ```tsx\n * const { data } = useDuckDBQuery(\"SELECT * FROM t\")\n * const rows = duckdbTableToArray<Row>(data)\n * ```\n *\n * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果(生の Arrow Table)と状態\n */\nexport const useDuckDBQuery = <T = unknown>(\n sql: string,\n dependencies: DependencyList = [],\n): {\n data: T | null\n loading: boolean\n error: string | null\n refetch: () => Promise<void>\n} => {\n const { executeQuery, isReady } = useDuckDB()\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refetch = useCallback(async () => {\n if (!(isReady && sql.trim())) {\n return\n }\n\n try {\n setLoading(true)\n setError(null)\n const result = await executeQuery(sql)\n setData(result as T)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Query execution failed\"\n setError(errorMessage)\n setData(null)\n } finally {\n setLoading(false)\n }\n }, [executeQuery, isReady, sql])\n\n // 可変長の dependencies を直接スプレッドせず、変化を検知する固定サイズのバージョンに変換する。\n const dependenciesVersion = useDependencyVersion(dependencies)\n\n useEffect(() => {\n void refetch()\n }, [refetch, dependenciesVersion])\n\n return {\n data,\n loading,\n error,\n refetch,\n }\n}\n","/**\n * DuckDB Web Worker Service\n *\n * Provides reusable DuckDB-wasm functionality across the application.\n * アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。\n */\n\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/** 初期化タイムアウト(WASM ダウンロードを考慮した長め設定)。 */\nconst INIT_TIMEOUT_MS = 120000\n/** クエリ実行タイムアウト。 */\nconst QUERY_TIMEOUT_MS = 60000\n/** splink_udfs インストールの個別タイムアウト。 */\nconst SPLINK_TIMEOUT_MS = 10000\n/** バッチ INSERT の1回あたり行数。 */\nconst BATCH_SIZE = 1000\n/** 型推定に使用する先頭サンプル行数。 */\nconst TYPE_INFERENCE_SAMPLE_SIZE = 100\n/**\n * 文字列を TIMESTAMP とみなすための厳密パターン。\n * `YYYY-MM-DD` または `YYYY-MM-DD[ T]HH:MM(:SS(.sss)?)?(Z|±HH:MM)?` のみ許容し、\n * `ABC-123` のような「ハイフンを含むが日付ではない」文字列を誤って日付扱いしない。\n */\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n\n/** 内部初期化のアボート指示を運ぶシグナル。 */\ninterface InitSignal {\n aborted: boolean\n}\n\nconst noop = (): void => {\n // 直列化キューの継続用。前段の解決/拒否を握りつぶす。\n}\n\n/**\n * DuckDB service for browser-based SQL operations.\n * ブラウザベースの SQL 操作のための DuckDB サービス。\n */\nclass DuckDBService {\n private static instance: DuckDBService | null = null\n private operationFlags: Map<string, Promise<void>> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private connectionPromise: Promise<duckdb.AsyncDuckDBConnection> | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\n\n /** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */\n private pendingWorker: Worker | null = null\n private pendingWorkerUrl: string | null = null\n\n /**\n * 共有コネクション上での操作直列化キュー。\n * createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。\n */\n private operationQueue: Promise<unknown> = Promise.resolve()\n\n private listeners: Set<() => void> = new Set()\n\n /**\n * Get singleton instance of DuckDBService.\n * DuckDBService のシングルトンインスタンスを取得します。\n */\n public static getInstance(): DuckDBService {\n if (!DuckDBService.instance) {\n DuckDBService.instance = new DuckDBService()\n }\n return DuckDBService.instance\n }\n\n /**\n * Add status change listener.\n * 状態変更リスナーを追加します。\n */\n public addStatusListener(listener: () => void): void {\n this.listeners.add(listener)\n }\n\n /**\n * Remove status change listener.\n * 状態変更リスナーを削除します。\n */\n public removeStatusListener(listener: () => void): void {\n this.listeners.delete(listener)\n }\n\n /**\n * Notify all listeners of status change.\n * すべてのリスナーに状態変更を通知します。\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener()\n } catch (error) {\n Logger.error(\"⚠️ Error in status listener:\", error)\n }\n }\n }\n\n /**\n * Get current status.\n * 現在のステータスを取得します。\n */\n public getStatus(): \"not-initialized\" | \"initializing\" | \"ready\" | \"error\" {\n if (this.hasError) {\n return \"error\"\n }\n if (this.isInitializing) {\n return \"initializing\"\n }\n if (this.workerInstance) {\n return \"ready\"\n }\n return \"not-initialized\"\n }\n\n /** Enqueue a task on the single-connection serialization queue. */\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.operationQueue.then(task, task)\n this.operationQueue = run.then(noop, noop)\n return run\n }\n\n /** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */\n private assertNotAborted(signal: InitSignal): void {\n if (signal.aborted) {\n throw new Error(\"DuckDB initialization aborted\")\n }\n }\n\n /** 初期化中の Worker / Blob URL を確実に破棄する。 */\n private abortPendingWorker(): void {\n if (this.pendingWorker) {\n try {\n this.pendingWorker.terminate()\n } catch {\n // terminate は冪等でなくても良い。失敗は無視する。\n }\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl) {\n try {\n URL.revokeObjectURL(this.pendingWorkerUrl)\n } catch {\n // revoke 失敗は無視する。\n }\n this.pendingWorkerUrl = null\n }\n }\n\n /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(): Promise<duckdb.AsyncDuckDB> {\n if (this.workerInstance) {\n return this.workerInstance\n }\n\n if (this.isInitializing && this.initPromise) {\n return this.initPromise\n }\n\n this.isInitializing = true\n this.hasError = false\n this.notifyListeners()\n\n // タイムアウト設定 (120秒) - WASMのダウンロードなどを考慮して長めに設定\n const signal: InitSignal = { aborted: false }\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n signal.aborted = true\n reject(new Error(`DuckDB initialization timed out after ${INIT_TIMEOUT_MS}ms`))\n }, INIT_TIMEOUT_MS)\n })\n\n this.initPromise = Promise.race([this.internalInitializeWorker(signal), timeoutPromise])\n\n try {\n const result = await this.initPromise\n clearTimeout(timeoutId)\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n // 失敗/タイムアウト時は状態を完全にリセットして再試行可能にする。\n signal.aborted = true\n this.isInitializing = false\n this.hasError = true\n this.workerInstance = null\n this.connectionInstance = null\n this.connectionPromise = null\n this.initPromise = null\n this.abortPendingWorker()\n this.notifyListeners()\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n *\n * workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は\n * ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。\n */\n private async internalInitializeWorker(signal: InitSignal): Promise<duckdb.AsyncDuckDB> {\n let workerUrl: string | null = null\n let worker: Worker | null = null\n try {\n Logger.info(\"🔧 Starting DuckDB Worker initialization...\")\n Logger.info(\"📦 Getting DuckDB bundles from CDN...\")\n const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles()\n\n Logger.info(\"🔍 Selecting appropriate bundle for browser...\")\n const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)\n this.assertNotAborted(signal)\n\n // CDN URL をローカルの静的配信パス (/assets/duckdb/) に差し替える\n const workerName = bundle.mainWorker?.split(\"/\").pop()\n const moduleName = bundle.mainModule?.split(\"/\").pop()\n const pthreadWorkerName = bundle.pthreadWorker?.split(\"/\").pop()\n\n // Blob Worker 内の importScripts/fetch でパス解決できるように絶対 URL に変換する\n const toAbsoluteUrl = (urlPath: string): string => {\n if (typeof window !== \"undefined\" && window.location) {\n return new URL(urlPath, window.location.origin).toString()\n }\n return urlPath\n }\n\n const localBundle = {\n mainWorker: workerName ? toAbsoluteUrl(`/assets/duckdb/${workerName}`) : \"\",\n mainModule: moduleName ? toAbsoluteUrl(`/assets/duckdb/${moduleName}`) : \"\",\n pthreadWorker: pthreadWorkerName ? toAbsoluteUrl(`/assets/duckdb/${pthreadWorkerName}`) : undefined,\n }\n\n Logger.info(\"✅ Bundle selected:\", {\n mainWorker: localBundle.mainWorker,\n mainModule: localBundle.mainModule,\n pthreadWorker: localBundle.pthreadWorker,\n })\n\n Logger.info(\"🚀 Creating Worker with Blob URL...\")\n workerUrl = URL.createObjectURL(new Blob([`importScripts(\"${localBundle.mainWorker}\");`], { type: \"text/javascript\" }))\n\n Logger.info(\"👷 Creating Worker instance...\")\n worker = new Worker(workerUrl)\n this.pendingWorker = worker\n this.pendingWorkerUrl = workerUrl\n\n const logger = new duckdb.ConsoleLogger()\n\n Logger.info(\"🗄️ Creating DuckDB instance...\")\n const db = new duckdb.AsyncDuckDB(logger, worker)\n\n Logger.info(\"⚡ Instantiating DuckDB with modules...\")\n await db.instantiate(localBundle.mainModule, localBundle.pthreadWorker)\n this.assertNotAborted(signal)\n\n Logger.info(\"📦 Installing splink_udfs from community...\")\n const connection = await db.connect()\n try {\n // splink_udfs のインストール/ロードはネットワークアクセスを伴うため、\n // 個別の短いタイムアウトを設けてハングを防止する。\n let splinkTimeoutId!: ReturnType<typeof setTimeout>\n const splinkTimeoutPromise = new Promise<never>((_, reject) => {\n splinkTimeoutId = setTimeout(() => {\n reject(new Error(`splink_udfs installation timed out after ${SPLINK_TIMEOUT_MS}ms`))\n }, SPLINK_TIMEOUT_MS)\n })\n\n await Promise.race([\n (async () => {\n await connection.query(\"INSTALL splink_udfs FROM community;\")\n Logger.info(\"✅ splink_udfs installed successfully\")\n\n await connection.query(\"LOAD splink_udfs;\")\n Logger.info(\"✅ splink_udfs loaded successfully\")\n })(),\n splinkTimeoutPromise,\n ])\n clearTimeout(splinkTimeoutId)\n } catch (error) {\n // タイムアウトやネットワークエラーの場合は警告ログを出して続行\n Logger.warn(\"⚠️ Failed to install/load splink_udfs:\", error)\n } finally {\n await connection.close()\n }\n this.assertNotAborted(signal)\n\n // すべて成功したときのみ公開する。\n this.workerInstance = db\n this.pendingWorker = null\n this.pendingWorkerUrl = null\n URL.revokeObjectURL(workerUrl)\n\n Logger.info(\"✅ DuckDB Worker initialized successfully\")\n return db\n } catch (error) {\n // 途中生成物を必ず破棄する(Worker スレッド / Blob URL のリーク防止)。\n if (worker) {\n try {\n worker.terminate()\n } catch {\n // 無視する。\n }\n }\n if (workerUrl) {\n try {\n URL.revokeObjectURL(workerUrl)\n } catch {\n // 無視する。\n }\n }\n if (this.pendingWorker === worker) {\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl === workerUrl) {\n this.pendingWorkerUrl = null\n }\n\n Logger.error(\"❌ Failed to initialize DuckDB Worker:\", error)\n if (error instanceof Error) {\n Logger.error(\"❌ Error name:\", error.name)\n Logger.error(\"❌ Error message:\", error.message)\n Logger.error(\"❌ Error stack:\", error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n *\n * 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (this.connectionInstance) {\n return this.connectionInstance\n }\n if (!this.connectionPromise) {\n this.connectionPromise = (async () => {\n const db = await this.initialize()\n const connection = await db.connect()\n this.connectionInstance = connection\n return connection\n })()\n }\n try {\n return await this.connectionPromise\n } catch (error) {\n // 失敗時はリセットして次回リトライ可能にする。\n this.connectionPromise = null\n throw error\n }\n }\n\n /**\n * Run a single query with a timeout, discarding the connection on timeout.\n * 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。\n *\n * タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して\n * 後続クエリが同じ(詰まった)コネクションで待たされないようにする。\n */\n private async runQueryWithTimeout(sql: string): Promise<unknown> {\n let timedOut = false\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n timedOut = true\n reject(new Error(`DuckDB query execution timed out after ${QUERY_TIMEOUT_MS}ms`))\n }, QUERY_TIMEOUT_MS)\n })\n\n // コネクション取得とクエリ実行の両方をタイムアウトの対象にする。\n const run = (async () => {\n const connection = await this.getConnection()\n return connection.query(sql)\n })()\n\n try {\n const result = await Promise.race([run, timeoutPromise])\n clearTimeout(timeoutId)\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n if (timedOut) {\n const stale = this.connectionInstance\n this.connectionInstance = null\n this.connectionPromise = null\n if (stale) {\n void stale.close().catch(() => {\n // 詰まったコネクションの close 失敗は無視する。\n })\n }\n }\n Logger.error(\"❌ Failed to execute SQL query:\", error)\n throw error\n }\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n return this.enqueue(() => this.runQueryWithTimeout(sql))\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します(逐次実行)。\n *\n * バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n return this.enqueue(async () => {\n const results: unknown[] = []\n for (const query of queries) {\n results.push(await this.runQueryWithTimeout(query))\n }\n return results\n })\n }\n\n /**\n * Collect the union of column names across all rows (first-seen order).\n * 全行を走査して列名の和集合を取得します(初出順を保持)。\n * 先頭行に存在しないキーを取りこぼさないため全行を対象とする。\n */\n private collectColumnNames(rows: Record<string, unknown>[]): string[] {\n const seen = new Set<string>()\n const names: string[] = []\n for (const row of rows) {\n if (!row) {\n continue\n }\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key)\n names.push(key)\n }\n }\n }\n return names\n }\n\n /** Check whether a string is a genuine (strict) date/timestamp literal. */\n private looksLikeTimestamp(value: string): boolean {\n if (!TIMESTAMP_PATTERN.test(value)) {\n return false\n }\n return !Number.isNaN(new Date(value).getTime())\n }\n\n /**\n * Infer column types from a data sample for the given columns.\n * 指定した列について、データサンプルから DuckDB の型を推定します。\n *\n * 整数は桁あふれを避けるため一律 BIGINT を採用する。\n */\n private inferColumnTypes(sample: Record<string, unknown>[], columnNames: string[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n\n for (const key of columnNames) {\n const values = sample.map((row) => row?.[key]).filter((val) => val !== null && val !== undefined)\n\n if (values.length === 0) {\n columnTypes[key] = \"VARCHAR\"\n continue\n }\n\n const typeInfo = {\n number: 0,\n integer: 0,\n boolean: 0,\n date: 0,\n string: 0,\n }\n\n for (const value of values) {\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n }\n } else {\n // NaN / Infinity は数値列にできないため文字列扱い(実挿入時は NULL 化)。\n typeInfo.string += 1\n }\n } else if (typeof value === \"bigint\") {\n typeInfo.number += 1\n typeInfo.integer += 1\n } else if (typeof value === \"boolean\") {\n typeInfo.boolean += 1\n } else if (value instanceof Date) {\n typeInfo.date += 1\n } else if (typeof value === \"string\" && this.looksLikeTimestamp(value)) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n columnTypes[key] = typeInfo.integer === values.length ? \"BIGINT\" : \"DOUBLE\"\n } else if (typeInfo.boolean === values.length) {\n columnTypes[key] = \"BOOLEAN\"\n } else if (typeInfo.date === values.length) {\n columnTypes[key] = \"TIMESTAMP\"\n } else {\n columnTypes[key] = \"VARCHAR\"\n }\n }\n\n return columnTypes\n }\n\n /**\n * Format value for SQL insertion.\n * SQL 挿入用に値をフォーマットします。\n */\n private formatValueForSQL(value: unknown): string {\n if (value === null || value === undefined) {\n return \"NULL\"\n }\n if (typeof value === \"string\") {\n return `'${value.replace(/'/g, \"''\")}'`\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`\n }\n if (typeof value === \"boolean\") {\n return value ? \"TRUE\" : \"FALSE\"\n }\n if (typeof value === \"bigint\") {\n return value.toString()\n }\n if (typeof value === \"number\") {\n // NaN / Infinity / -Infinity は有効な SQL 数値リテラルではないため NULL 化する。\n if (!Number.isFinite(value)) {\n return \"NULL\"\n }\n return String(value)\n }\n // オブジェクト / 配列などは JSON 文字列としてクォート・エスケープする。\n try {\n const json = JSON.stringify(value)\n if (json === undefined) {\n return \"NULL\"\n }\n return `'${json.replace(/'/g, \"''\")}'`\n } catch {\n return \"NULL\"\n }\n }\n\n /**\n * Quote a SQL identifier (table / column / primary key) safely.\n * SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。\n */\n private quoteIdentifier(name: string): string {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new Error(\"Invalid SQL identifier\")\n }\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\n *\n * 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。\n */\n public async createTableFromData<T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n } = {},\n ): Promise<void> {\n if (!data || data.length === 0) {\n throw new Error(\"Data array is empty\")\n }\n\n const operationKey = `create_table_${tableName}`\n const verbose = options.verbose ?? true\n\n const inFlight = this.operationFlags.get(operationKey)\n if (inFlight) {\n if (verbose) {\n Logger.info(`⏳ Table creation for '${tableName}' is already in progress, awaiting existing operation...`)\n }\n return inFlight\n }\n\n const operation = this.enqueue(() => this.buildTable(tableName, data, options, verbose))\n this.operationFlags.set(operationKey, operation)\n try {\n await operation\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /** Actual (serialized, transactional) table build. */\n private async buildTable<T extends Record<string, unknown>>(tableName: string, data: T[], options: { dropIfExists?: boolean; primaryKey?: string }, verbose: boolean): Promise<void> {\n try {\n const connection = await this.getConnection()\n const quotedTable = this.quoteIdentifier(tableName)\n\n const columnNames = this.collectColumnNames(data)\n if (columnNames.length === 0) {\n throw new Error(\"Data rows have no columns\")\n }\n\n const sampleSize = Math.min(data.length, TYPE_INFERENCE_SAMPLE_SIZE)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize), columnNames)\n\n const columnDefs = columnNames.map((key) => `${this.quoteIdentifier(key)} ${columnTypes[key]}`)\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (${this.quoteIdentifier(options.primaryKey)})` : \"\"\n const createTableSQL = `CREATE TABLE ${quotedTable} (${columnDefs.join(\", \")}${primaryKeyClause})`\n const columnList = columnNames.map((col) => this.quoteIdentifier(col)).join(\", \")\n\n if (verbose) {\n Logger.info(`📥 Building table '${tableName}' with ${data.length} rows`)\n }\n\n // DROP / CREATE / INSERT を1トランザクションにまとめ、途中失敗時はロールバックして\n // 半端に作成されたテーブルを残さない。\n await connection.query(\"BEGIN TRANSACTION\")\n try {\n if (options.dropIfExists) {\n if (verbose) {\n Logger.info(`🗑️ Dropping table '${tableName}' if exists`)\n }\n await connection.query(`DROP TABLE IF EXISTS ${quotedTable}`)\n }\n\n if (verbose) {\n Logger.info(\"🔨 Creating table with SQL:\", createTableSQL)\n }\n await connection.query(createTableSQL)\n\n for (let i = 0; i < data.length; i += BATCH_SIZE) {\n const batch = data.slice(i, i + BATCH_SIZE)\n const valueRows = batch.map((row) => {\n // null/undefined 行はすべて NULL として扱う(不揃いなデータへの耐性)。\n const values = columnNames.map((col) => this.formatValueForSQL(row?.[col]))\n return `(${values.join(\", \")})`\n })\n\n const insertSQL = `INSERT INTO ${quotedTable} (${columnList}) VALUES ${valueRows.join(\", \")}`\n await connection.query(insertSQL)\n if (verbose) {\n Logger.info(`📊 Inserted batch ${Math.floor(i / BATCH_SIZE) + 1} (${batch.length} rows)`)\n }\n }\n\n await connection.query(\"COMMIT\")\n } catch (txError) {\n try {\n await connection.query(\"ROLLBACK\")\n } catch (rollbackError) {\n Logger.warn(\"⚠️ Rollback failed:\", rollbackError)\n }\n throw txError\n }\n\n if (verbose) {\n Logger.info(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n }\n } catch (error) {\n Logger.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(`DESCRIBE ${this.quoteIdentifier(tableName)}`)\n })\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(\"SHOW TABLES\")\n })\n }\n\n /**\n * Check if DuckDB is ready.\n * DuckDB が準備完了かどうかを確認します。\n */\n public isReady(): boolean {\n return this.workerInstance !== null\n }\n\n /**\n * Cleanup DuckDB Worker resources.\n * DuckDB Worker のリソースをクリーンアップします。\n */\n public async cleanup(): Promise<void> {\n try {\n if (this.connectionInstance) {\n await this.connectionInstance.close()\n this.connectionInstance = null\n }\n if (this.workerInstance) {\n await this.workerInstance.terminate()\n this.workerInstance = null\n }\n this.abortPendingWorker()\n this.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.connectionPromise = null\n this.operationQueue = Promise.resolve()\n this.operationFlags.clear()\n this.notifyListeners()\n this.listeners.clear()\n } catch (error) {\n Logger.error(\"⚠️ Error during cleanup:\", error)\n }\n }\n\n /**\n * Reset the service instance (useful for testing).\n * サービスインスタンスをリセットします(テスト用)。\n */\n public static resetInstance(): void {\n if (DuckDBService.instance) {\n void DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\n","/**\n * @module utils/logger\n * @description Logger utility for consistent log management across the library.\n * @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。\n */\n\n/**\n * @enum LogLevel\n * @description Log levels for filtering output.\n * @description 出力をフィルタリングするためのログレベル。\n */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/**\n * @interface ILogger\n * @description Interface for a logger object compatible with Console.\n * @description Console と互換性のあるロガーオブジェクトのインターフェース。\n */\nexport interface ILogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * @class Logger\n * @description A wrapper class for handling logging with levels and prefixes.\n * @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。\n */\nexport class Logger implements ILogger {\n private level: LogLevel\n private prefix: string\n private impl: ILogger\n\n /**\n * @constructor\n * @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.\n * @param {string} [prefix=\"[duckdb-helper]\"] - The prefix to add to all log messages.\n * @param {ILogger} [impl=console] - The implementation to use for logging.\n */\n constructor(level: LogLevel = LogLevel.WARN, prefix: string = \"[duckdb-helper]\", impl: ILogger = console) {\n this.level = level\n this.prefix = prefix\n this.impl = impl\n }\n\n private static instance: Logger = new Logger(LogLevel.WARN, \"[duckdb-helper]\")\n\n /**\n * @method setLevel\n * @description Updates the current log level for the static instance.\n * @description 静的インスタンスの現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n static setLevel(level: LogLevel): void {\n Logger.instance.setLevel(level)\n }\n\n /**\n * @method setLevel\n * @description Updates the current log level.\n * @description 現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation for the static instance.\n * @description 静的インスタンスのロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n static setImplementation(impl: ILogger): void {\n Logger.instance.setImplementation(impl)\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation.\n * @description ロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n setImplementation(impl: ILogger): void {\n this.impl = impl\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix for the static instance.\n * @description 静的インスタンスのログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n static setPrefix(prefix: string): void {\n Logger.instance.setPrefix(prefix)\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix.\n * @description ログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n setPrefix(prefix: string): void {\n this.prefix = prefix\n }\n\n private formatMessage(message: unknown): unknown[] {\n if (typeof message === \"string\") {\n return [`${this.prefix} ${message}`]\n }\n return [this.prefix, message]\n }\n\n static debug(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.debug(message, ...optionalParams)\n }\n\n debug(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.DEBUG) {\n this.impl.debug(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static info(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.info(message, ...optionalParams)\n }\n\n info(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.INFO) {\n this.impl.info(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static warn(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.warn(message, ...optionalParams)\n }\n\n warn(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.WARN) {\n this.impl.warn(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static error(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.error(message, ...optionalParams)\n }\n\n error(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.ERROR) {\n this.impl.error(...this.formatMessage(message), ...optionalParams)\n }\n }\n}\n","/**\n * DuckDB Helper Functions\n *\n * Utility functions for working with DuckDB query results.\n * DuckDB クエリ結果を扱うためのユーティリティ関数。\n */\n\nimport type { DuckDBQueryResult } from \"../types/duckdb\"\n\n/**\n * Safely convert DuckDB table to typed array\n * DuckDB テーブルを型付き配列に安全に変換します\n *\n * @param result - DuckDB query result\n * @returns Typed array of results\n */\nexport const duckdbTableToArray = <T>(result: unknown): T[] => {\n if (!result || typeof result !== \"object\") {\n return []\n }\n\n if (\"toArray\" in result && typeof result.toArray === \"function\") {\n return (result as DuckDBQueryResult<T>).toArray()\n }\n\n return []\n}\n\n/**\n * Get table row count\n * テーブルの行数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of rows in the table\n */\nexport const getDuckDBRowCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numRows\" in result && typeof result.numRows === \"number\") {\n return result.numRows\n }\n\n return 0\n}\n\n/**\n * Get table column count\n * テーブルの列数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of columns in the table\n */\nexport const getDuckDBColumnCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numCols\" in result && typeof result.numCols === \"number\") {\n return result.numCols\n }\n\n return 0\n}\n\n/**\n * Check if result is a valid DuckDB table\n * 結果が有効な DuckDB テーブルかどうかをチェックします\n *\n * @param result - DuckDB query result\n * @returns True if result is a valid DuckDB table\n */\nexport const isDuckDBTable = (result: unknown): result is DuckDBQueryResult => {\n if (!result || typeof result !== \"object\") {\n return false\n }\n\n return \"toArray\" in result && typeof result.toArray === \"function\" && \"numRows\" in result && typeof result.numRows === \"number\"\n}\n"],"mappings":"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","names":["index_exports","__export","DuckDBService","LogLevel","Logger","duckdb_default","duckdbTableToArray","getDuckDBColumnCount","getDuckDBRowCount","isDuckDBTable","useDuckDB","useDuckDBQuery","__toCommonJS","import_react","duckdb","LogLevel","_Logger","level","prefix","impl","message","optionalParams","Logger","INIT_TIMEOUT_MS","QUERY_TIMEOUT_MS","SPLINK_TIMEOUT_MS","BATCH_SIZE","TYPE_INFERENCE_SAMPLE_SIZE","TIMESTAMP_PATTERN","noop","_DuckDBService","listener","error","Logger","task","run","signal","timeoutId","timeoutPromise","_","reject","result","workerUrl","worker","JSDELIVR_BUNDLES","bundle","workerName","moduleName","pthreadWorkerName","toAbsoluteUrl","urlPath","localBundle","logger","db","connection","splinkTimeoutId","splinkTimeoutPromise","sql","timedOut","stale","queries","results","query","rows","seen","names","row","key","value","sample","columnNames","columnTypes","values","val","typeInfo","json","name","tableName","data","options","operationKey","verbose","inFlight","operation","quotedTable","sampleSize","columnDefs","primaryKeyClause","createTableSQL","columnList","col","i","batch","valueRows","insertSQL","txError","rollbackError","DuckDBService","duckdb_default","DuckDBService","useDependencyVersion","dependencies","ref","previous","dep","index","useDuckDB","autoInitialize","status","setStatus","error","setError","duckDBService","DuckDBService","updateStatus","serviceStatus","Logger","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","options","getTableInfo","listTables","useDuckDBQuery","isReady","setData","loading","setLoading","refetch","result","dependenciesVersion","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/hooks/useDuckDB.ts","../src/services/duckdb.ts","../src/utils/simple-logger.ts","../src/utils/duckdb-helpers.ts"],"sourcesContent":["/**\n * DuckDB Helper Library Entry Point\n *\n * Provides React hooks, services, types, and utilities for working with DuckDB in web applications.\n * Web アプリケーションで DuckDB を扱うための React フック、サービス、型、ユーティリティを提供します。\n */\n\nexport type { UseDuckDBResult } from \"./hooks/useDuckDB\"\nexport { useDuckDB, useDuckDBQuery } from \"./hooks/useDuckDB\"\nexport { DuckDBService, default as duckDBService } from \"./services/duckdb\"\nexport type {\n DuckDBQueryResult,\n DuckDBRow,\n} from \"./types/duckdb\"\nexport {\n duckdbTableToArray,\n getDuckDBColumnCount,\n getDuckDBRowCount,\n isDuckDBTable,\n} from \"./utils/duckdb-helpers\"\nexport type { ILogger } from \"./utils/simple-logger\"\nexport { Logger, LogLevel } from \"./utils/simple-logger\"\n","/**\n * DuckDB React Hook\n *\n * React hook for using DuckDB functionality in components.\n * コンポーネントで DuckDB 機能を使用するための React フック。\n */\n\nimport { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\"\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { DuckDBService } from \"../services/duckdb\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/**\n * Derive a constant-size version number that increments whenever the given\n * dependency list changes (by React's own Object.is comparison).\n * 依存配列の変化(React と同じ Object.is 比較)で増える固定サイズのバージョン番号を返す。\n *\n * これにより可変長の `dependencies` を effect の依存配列に直接スプレッドせずに済み、\n * 配列サイズがレンダー間で変わっても React の「依存配列サイズ変更」エラーを起こさない。\n */\nconst useDependencyVersion = (dependencies: DependencyList): number => {\n const ref = useRef<{ deps: DependencyList; version: number }>({ deps: dependencies, version: 0 })\n const previous = ref.current.deps\n const changed = previous.length !== dependencies.length || dependencies.some((dep, index) => !Object.is(dep, previous[index]))\n if (changed) {\n ref.current = { deps: dependencies, version: ref.current.version + 1 }\n }\n return ref.current.version\n}\n\nexport interface UseDuckDBResult {\n status: \"not-initialized\" | \"initializing\" | \"ready\" | \"error\"\n error: string | null\n executeQuery: (sql: string) => Promise<unknown>\n executeQueries: (queries: string[]) => Promise<unknown[]>\n createTableFromData: <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ) => Promise<void>\n getTableInfo: (tableName: string) => Promise<unknown>\n listTables: () => Promise<unknown>\n isReady: boolean\n initialize: () => Promise<void>\n}\n\n/**\n * DuckDB functionality hook.\n * DuckDB 機能を提供するフック。\n *\n * @param autoInitialize - 自動初期化を行うかどうか\n * @returns DuckDB の状態と操作関数\n */\nexport const useDuckDB = (\n autoInitialize = true,\n options?: { bundles?: duckdb.DuckDBBundles },\n): UseDuckDBResult => {\n const [status, setStatus] = useState<\"not-initialized\" | \"initializing\" | \"ready\" | \"error\">(\"not-initialized\")\n const [error, setError] = useState<string | null>(null)\n const [duckDBService] = useState(() => DuckDBService.getInstance())\n\n const updateStatus = useCallback(() => {\n const serviceStatus = duckDBService.getStatus()\n Logger.info(\"🔄 DuckDB status updated:\", serviceStatus)\n setStatus(serviceStatus)\n\n if (serviceStatus === \"error\") {\n setError(\"DuckDB initialization failed\")\n } else if (serviceStatus === \"ready\") {\n setError(null)\n }\n }, [duckDBService])\n\n useEffect(() => {\n duckDBService.addStatusListener(updateStatus)\n updateStatus()\n\n return () => {\n duckDBService.removeStatusListener(updateStatus)\n }\n }, [duckDBService, updateStatus])\n\n const initialize = useCallback(async () => {\n try {\n await duckDBService.initialize(options)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Unknown error occurred\"\n setError(errorMessage)\n Logger.error(\"❌ DuckDB initialization failed:\", err)\n }\n }, [duckDBService, options])\n\n useEffect(() => {\n if (autoInitialize && status === \"not-initialized\") {\n Logger.info(\"🚀 Auto-initializing DuckDB...\")\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(\n async (sql: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQuery(sql)\n },\n [duckDBService, status],\n )\n\n const executeQueries = useCallback(\n async (queries: string[]): Promise<unknown[]> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQueries(queries)\n },\n [duckDBService, status],\n )\n\n const createTableFromData = useCallback(\n async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ): Promise<void> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.createTableFromData(tableName, data, options)\n },\n [duckDBService, status],\n )\n\n const getTableInfo = useCallback(\n async (tableName: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.getTableInfo(tableName)\n },\n [duckDBService, status],\n )\n\n const listTables = useCallback(async (): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.listTables()\n }, [duckDBService, status])\n\n return {\n status,\n error,\n executeQuery,\n executeQueries,\n createTableFromData,\n getTableInfo,\n listTables,\n isReady: status === \"ready\",\n initialize,\n }\n}\n\n/**\n * Hook for simple DuckDB query execution.\n * シンプルな DuckDB クエリ実行のためのフック。\n *\n * @remarks\n * `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。\n * `.length` や `.map` は持たないため、行配列として扱う場合は必ず\n * {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく\n * Arrow Table 相当の型を想定)。\n *\n * @example\n * ```tsx\n * const { data } = useDuckDBQuery(\"SELECT * FROM t\")\n * const rows = duckdbTableToArray<Row>(data)\n * ```\n *\n * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果(生の Arrow Table)と状態\n */\nexport const useDuckDBQuery = <T = unknown>(\n sql: string,\n dependencies: DependencyList = [],\n): {\n data: T | null\n loading: boolean\n error: string | null\n refetch: () => Promise<void>\n} => {\n const { executeQuery, isReady } = useDuckDB()\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refetch = useCallback(async () => {\n if (!(isReady && sql.trim())) {\n return\n }\n\n try {\n setLoading(true)\n setError(null)\n const result = await executeQuery(sql)\n setData(result as T)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Query execution failed\"\n setError(errorMessage)\n setData(null)\n } finally {\n setLoading(false)\n }\n }, [executeQuery, isReady, sql])\n\n // 可変長の dependencies を直接スプレッドせず、変化を検知する固定サイズのバージョンに変換する。\n const dependenciesVersion = useDependencyVersion(dependencies)\n\n useEffect(() => {\n void refetch()\n }, [refetch, dependenciesVersion])\n\n return {\n data,\n loading,\n error,\n refetch,\n }\n}\n","/**\n * DuckDB Web Worker Service\n *\n * Provides reusable DuckDB-wasm functionality across the application.\n * アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。\n */\n\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/** 初期化タイムアウト(WASM ダウンロードを考慮した長め設定)。 */\nconst INIT_TIMEOUT_MS = 120000\n/** クエリ実行タイムアウト。 */\nconst QUERY_TIMEOUT_MS = 60000\n/** splink_udfs インストールの個別タイムアウト。 */\nconst SPLINK_TIMEOUT_MS = 10000\n/** バッチ INSERT の1回あたり行数。 */\nconst BATCH_SIZE = 1000\n/** 型推定に使用する先頭サンプル行数。 */\nconst TYPE_INFERENCE_SAMPLE_SIZE = 100\n/**\n * 文字列を TIMESTAMP とみなすための厳密パターン。\n * `YYYY-MM-DD` または `YYYY-MM-DD[ T]HH:MM(:SS(.sss)?)?(Z|±HH:MM)?` のみ許容し、\n * `ABC-123` のような「ハイフンを含むが日付ではない」文字列を誤って日付扱いしない。\n */\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n\n/** 内部初期化のアボート指示を運ぶシグナル。 */\ninterface InitSignal {\n aborted: boolean\n}\n\nconst noop = (): void => {\n // 直列化キューの継続用。前段の解決/拒否を握りつぶす。\n}\n\n/**\n * DuckDB service for browser-based SQL operations.\n * ブラウザベースの SQL 操作のための DuckDB サービス。\n */\nclass DuckDBService {\n private static instance: DuckDBService | null = null\n private operationFlags: Map<string, Promise<void>> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private connectionPromise: Promise<duckdb.AsyncDuckDBConnection> | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\n\n /** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */\n private pendingWorker: Worker | null = null\n private pendingWorkerUrl: string | null = null\n\n /**\n * 共有コネクション上での操作直列化キュー。\n * createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。\n */\n private operationQueue: Promise<unknown> = Promise.resolve()\n\n private listeners: Set<() => void> = new Set()\n\n /**\n * Get singleton instance of DuckDBService.\n * DuckDBService のシングルトンインスタンスを取得します。\n */\n public static getInstance(): DuckDBService {\n if (!DuckDBService.instance) {\n DuckDBService.instance = new DuckDBService()\n }\n return DuckDBService.instance\n }\n\n /**\n * Add status change listener.\n * 状態変更リスナーを追加します。\n */\n public addStatusListener(listener: () => void): void {\n this.listeners.add(listener)\n }\n\n /**\n * Remove status change listener.\n * 状態変更リスナーを削除します。\n */\n public removeStatusListener(listener: () => void): void {\n this.listeners.delete(listener)\n }\n\n /**\n * Notify all listeners of status change.\n * すべてのリスナーに状態変更を通知します。\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener()\n } catch (error) {\n Logger.error(\"⚠️ Error in status listener:\", error)\n }\n }\n }\n\n /**\n * Get current status.\n * 現在のステータスを取得します。\n */\n public getStatus(): \"not-initialized\" | \"initializing\" | \"ready\" | \"error\" {\n if (this.hasError) {\n return \"error\"\n }\n if (this.isInitializing) {\n return \"initializing\"\n }\n if (this.workerInstance) {\n return \"ready\"\n }\n return \"not-initialized\"\n }\n\n /** Enqueue a task on the single-connection serialization queue. */\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.operationQueue.then(task, task)\n this.operationQueue = run.then(noop, noop)\n return run\n }\n\n /** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */\n private assertNotAborted(signal: InitSignal): void {\n if (signal.aborted) {\n throw new Error(\"DuckDB initialization aborted\")\n }\n }\n\n /** 初期化中の Worker / Blob URL を確実に破棄する。 */\n private abortPendingWorker(): void {\n if (this.pendingWorker) {\n try {\n this.pendingWorker.terminate()\n } catch {\n // terminate は冪等でなくても良い。失敗は無視する。\n }\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl) {\n try {\n URL.revokeObjectURL(this.pendingWorkerUrl)\n } catch {\n // revoke 失敗は無視する。\n }\n this.pendingWorkerUrl = null\n }\n }\n\n /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(options?: { bundles?: duckdb.DuckDBBundles }): Promise<duckdb.AsyncDuckDB> {\n if (this.workerInstance) {\n return this.workerInstance\n }\n\n if (this.isInitializing && this.initPromise) {\n return this.initPromise\n }\n\n this.isInitializing = true\n this.hasError = false\n this.notifyListeners()\n\n // タイムアウト設定 (120秒) - WASMのダウンロードなどを考慮して長めに設定\n const signal: InitSignal = { aborted: false }\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n signal.aborted = true\n reject(new Error(`DuckDB initialization timed out after ${INIT_TIMEOUT_MS}ms`))\n }, INIT_TIMEOUT_MS)\n })\n\n this.initPromise = Promise.race([this.internalInitializeWorker(signal, options), timeoutPromise])\n\n try {\n const result = await this.initPromise\n clearTimeout(timeoutId)\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n // 失敗/タイムアウト時は状態を完全にリセットして再試行可能にする。\n signal.aborted = true\n this.isInitializing = false\n this.hasError = true\n this.workerInstance = null\n this.connectionInstance = null\n this.connectionPromise = null\n this.initPromise = null\n this.abortPendingWorker()\n this.notifyListeners()\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n *\n * workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は\n * ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。\n */\n private async internalInitializeWorker(signal: InitSignal, options?: { bundles?: duckdb.DuckDBBundles }): Promise<duckdb.AsyncDuckDB> {\n let workerUrl: string | null = null\n let worker: Worker | null = null\n try {\n Logger.info(\"🔧 Starting DuckDB Worker initialization...\")\n Logger.info(\"📦 Getting DuckDB bundles...\")\n const baseBundles = options?.bundles || duckdb.getJsDelivrBundles()\n\n Logger.info(\"🔍 Selecting appropriate bundle for browser...\")\n const bundle = await duckdb.selectBundle(baseBundles)\n this.assertNotAborted(signal)\n\n // Blob Worker 内の importScripts/fetch でパス解決できるように、相対パス(スラッシュ始まり)は絶対 URL に変換する\n const toAbsoluteUrl = (urlPath: string): string => {\n if (urlPath.startsWith(\"/\") && typeof window !== \"undefined\" && window.location) {\n return new URL(urlPath, window.location.origin).toString()\n }\n return urlPath\n }\n\n const normalizedBundle = {\n mainWorker: bundle.mainWorker ? toAbsoluteUrl(bundle.mainWorker) : \"\",\n mainModule: bundle.mainModule ? toAbsoluteUrl(bundle.mainModule) : \"\",\n pthreadWorker: bundle.pthreadWorker ? toAbsoluteUrl(bundle.pthreadWorker) : undefined,\n }\n\n Logger.info(\"✅ Bundle selected:\", {\n mainWorker: normalizedBundle.mainWorker,\n mainModule: normalizedBundle.mainModule,\n pthreadWorker: normalizedBundle.pthreadWorker,\n })\n\n Logger.info(\"🚀 Creating Worker with Blob URL...\")\n workerUrl = URL.createObjectURL(new Blob([`importScripts(\"${normalizedBundle.mainWorker}\");`], { type: \"text/javascript\" }))\n\n Logger.info(\"👷 Creating Worker instance...\")\n worker = new Worker(workerUrl)\n\n this.assertNotAborted(signal)\n this.pendingWorker = worker\n this.pendingWorkerUrl = workerUrl\n\n Logger.info(\"🤝 Connecting to AsyncDuckDB instance...\")\n const db = new duckdb.AsyncDuckDB(new duckdb.ConsoleLogger(), worker)\n await db.instantiate(normalizedBundle.mainModule, normalizedBundle.pthreadWorker)\n this.assertNotAborted(signal)\n\n Logger.info(\"📦 Installing splink_udfs from community...\")\n const connection = await db.connect()\n try {\n // splink_udfs のインストール/ロードはネットワークアクセスを伴うため、\n // 個別の短いタイムアウトを設けてハングを防止する。\n let splinkTimeoutId!: ReturnType<typeof setTimeout>\n const splinkTimeoutPromise = new Promise<never>((_, reject) => {\n splinkTimeoutId = setTimeout(() => {\n reject(new Error(`splink_udfs installation timed out after ${SPLINK_TIMEOUT_MS}ms`))\n }, SPLINK_TIMEOUT_MS)\n })\n\n await Promise.race([\n (async () => {\n await connection.query(\"INSTALL splink_udfs FROM community;\")\n Logger.info(\"✅ splink_udfs installed successfully\")\n\n await connection.query(\"LOAD splink_udfs;\")\n Logger.info(\"✅ splink_udfs loaded successfully\")\n })(),\n splinkTimeoutPromise,\n ])\n clearTimeout(splinkTimeoutId)\n } catch (error) {\n // タイムアウトやネットワークエラーの場合は警告ログを出して続行\n Logger.warn(\"⚠️ Failed to install/load splink_udfs:\", error)\n } finally {\n await connection.close()\n }\n this.assertNotAborted(signal)\n\n // すべて成功したときのみ公開する。\n this.workerInstance = db\n this.pendingWorker = null\n this.pendingWorkerUrl = null\n URL.revokeObjectURL(workerUrl)\n\n Logger.info(\"✅ DuckDB Worker initialized successfully\")\n return db\n } catch (error) {\n // 途中生成物を必ず破棄する(Worker スレッド / Blob URL のリーク防止)。\n if (worker) {\n try {\n worker.terminate()\n } catch {\n // 無視する。\n }\n }\n if (workerUrl) {\n try {\n URL.revokeObjectURL(workerUrl)\n } catch {\n // 無視する。\n }\n }\n if (this.pendingWorker === worker) {\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl === workerUrl) {\n this.pendingWorkerUrl = null\n }\n\n Logger.error(\"❌ Failed to initialize DuckDB Worker:\", error)\n if (error instanceof Error) {\n Logger.error(\"❌ Error name:\", error.name)\n Logger.error(\"❌ Error message:\", error.message)\n Logger.error(\"❌ Error stack:\", error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n *\n * 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (this.connectionInstance) {\n return this.connectionInstance\n }\n if (!this.connectionPromise) {\n this.connectionPromise = (async () => {\n const db = await this.initialize()\n const connection = await db.connect()\n this.connectionInstance = connection\n return connection\n })()\n }\n try {\n return await this.connectionPromise\n } catch (error) {\n // 失敗時はリセットして次回リトライ可能にする。\n this.connectionPromise = null\n throw error\n }\n }\n\n /**\n * Run a single query with a timeout, discarding the connection on timeout.\n * 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。\n *\n * タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して\n * 後続クエリが同じ(詰まった)コネクションで待たされないようにする。\n */\n private async runQueryWithTimeout(sql: string): Promise<unknown> {\n let timedOut = false\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n timedOut = true\n reject(new Error(`DuckDB query execution timed out after ${QUERY_TIMEOUT_MS}ms`))\n }, QUERY_TIMEOUT_MS)\n })\n\n // コネクション取得とクエリ実行の両方をタイムアウトの対象にする。\n const run = (async () => {\n const connection = await this.getConnection()\n return connection.query(sql)\n })()\n\n try {\n const result = await Promise.race([run, timeoutPromise])\n clearTimeout(timeoutId)\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n if (timedOut) {\n const stale = this.connectionInstance\n this.connectionInstance = null\n this.connectionPromise = null\n if (stale) {\n void stale.close().catch(() => {\n // 詰まったコネクションの close 失敗は無視する。\n })\n }\n }\n Logger.error(\"❌ Failed to execute SQL query:\", error)\n throw error\n }\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n return this.enqueue(() => this.runQueryWithTimeout(sql))\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します(逐次実行)。\n *\n * バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n return this.enqueue(async () => {\n const results: unknown[] = []\n for (const query of queries) {\n results.push(await this.runQueryWithTimeout(query))\n }\n return results\n })\n }\n\n /**\n * Collect the union of column names across all rows (first-seen order).\n * 全行を走査して列名の和集合を取得します(初出順を保持)。\n * 先頭行に存在しないキーを取りこぼさないため全行を対象とする。\n */\n private collectColumnNames(rows: Record<string, unknown>[]): string[] {\n const seen = new Set<string>()\n const names: string[] = []\n for (const row of rows) {\n if (!row) {\n continue\n }\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key)\n names.push(key)\n }\n }\n }\n return names\n }\n\n /** Check whether a string is a genuine (strict) date/timestamp literal. */\n private looksLikeTimestamp(value: string): boolean {\n if (!TIMESTAMP_PATTERN.test(value)) {\n return false\n }\n return !Number.isNaN(new Date(value).getTime())\n }\n\n /**\n * Infer column types from a data sample for the given columns.\n * 指定した列について、データサンプルから DuckDB の型を推定します。\n *\n * 整数は桁あふれを避けるため一律 BIGINT を採用する。\n */\n private inferColumnTypes(sample: Record<string, unknown>[], columnNames: string[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n\n for (const key of columnNames) {\n const values = sample.map((row) => row?.[key]).filter((val) => val !== null && val !== undefined)\n\n if (values.length === 0) {\n columnTypes[key] = \"VARCHAR\"\n continue\n }\n\n const typeInfo = {\n number: 0,\n integer: 0,\n boolean: 0,\n date: 0,\n string: 0,\n }\n\n for (const value of values) {\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n }\n } else {\n // NaN / Infinity は数値列にできないため文字列扱い(実挿入時は NULL 化)。\n typeInfo.string += 1\n }\n } else if (typeof value === \"bigint\") {\n typeInfo.number += 1\n typeInfo.integer += 1\n } else if (typeof value === \"boolean\") {\n typeInfo.boolean += 1\n } else if (value instanceof Date) {\n typeInfo.date += 1\n } else if (typeof value === \"string\" && this.looksLikeTimestamp(value)) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n columnTypes[key] = typeInfo.integer === values.length ? \"BIGINT\" : \"DOUBLE\"\n } else if (typeInfo.boolean === values.length) {\n columnTypes[key] = \"BOOLEAN\"\n } else if (typeInfo.date === values.length) {\n columnTypes[key] = \"TIMESTAMP\"\n } else {\n columnTypes[key] = \"VARCHAR\"\n }\n }\n\n return columnTypes\n }\n\n /**\n * Format value for SQL insertion.\n * SQL 挿入用に値をフォーマットします。\n */\n private formatValueForSQL(value: unknown): string {\n if (value === null || value === undefined) {\n return \"NULL\"\n }\n if (typeof value === \"string\") {\n return `'${value.replace(/'/g, \"''\")}'`\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`\n }\n if (typeof value === \"boolean\") {\n return value ? \"TRUE\" : \"FALSE\"\n }\n if (typeof value === \"bigint\") {\n return value.toString()\n }\n if (typeof value === \"number\") {\n // NaN / Infinity / -Infinity は有効な SQL 数値リテラルではないため NULL 化する。\n if (!Number.isFinite(value)) {\n return \"NULL\"\n }\n return String(value)\n }\n // オブジェクト / 配列などは JSON 文字列としてクォート・エスケープする。\n try {\n const json = JSON.stringify(value)\n if (json === undefined) {\n return \"NULL\"\n }\n return `'${json.replace(/'/g, \"''\")}'`\n } catch {\n return \"NULL\"\n }\n }\n\n /**\n * Quote a SQL identifier (table / column / primary key) safely.\n * SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。\n */\n private quoteIdentifier(name: string): string {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new Error(\"Invalid SQL identifier\")\n }\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\n *\n * 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。\n */\n public async createTableFromData<T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n } = {},\n ): Promise<void> {\n if (!data || data.length === 0) {\n throw new Error(\"Data array is empty\")\n }\n\n const operationKey = `create_table_${tableName}`\n const verbose = options.verbose ?? true\n\n const inFlight = this.operationFlags.get(operationKey)\n if (inFlight) {\n if (verbose) {\n Logger.info(`⏳ Table creation for '${tableName}' is already in progress, awaiting existing operation...`)\n }\n return inFlight\n }\n\n const operation = this.enqueue(() => this.buildTable(tableName, data, options, verbose))\n this.operationFlags.set(operationKey, operation)\n try {\n await operation\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /** Actual (serialized, transactional) table build. */\n private async buildTable<T extends Record<string, unknown>>(tableName: string, data: T[], options: { dropIfExists?: boolean; primaryKey?: string }, verbose: boolean): Promise<void> {\n try {\n const connection = await this.getConnection()\n const quotedTable = this.quoteIdentifier(tableName)\n\n const columnNames = this.collectColumnNames(data)\n if (columnNames.length === 0) {\n throw new Error(\"Data rows have no columns\")\n }\n\n const sampleSize = Math.min(data.length, TYPE_INFERENCE_SAMPLE_SIZE)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize), columnNames)\n\n const columnDefs = columnNames.map((key) => `${this.quoteIdentifier(key)} ${columnTypes[key]}`)\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (${this.quoteIdentifier(options.primaryKey)})` : \"\"\n const createTableSQL = `CREATE TABLE ${quotedTable} (${columnDefs.join(\", \")}${primaryKeyClause})`\n const columnList = columnNames.map((col) => this.quoteIdentifier(col)).join(\", \")\n\n if (verbose) {\n Logger.info(`📥 Building table '${tableName}' with ${data.length} rows`)\n }\n\n // DROP / CREATE / INSERT を1トランザクションにまとめ、途中失敗時はロールバックして\n // 半端に作成されたテーブルを残さない。\n await connection.query(\"BEGIN TRANSACTION\")\n try {\n if (options.dropIfExists) {\n if (verbose) {\n Logger.info(`🗑️ Dropping table '${tableName}' if exists`)\n }\n await connection.query(`DROP TABLE IF EXISTS ${quotedTable}`)\n }\n\n if (verbose) {\n Logger.info(\"🔨 Creating table with SQL:\", createTableSQL)\n }\n await connection.query(createTableSQL)\n\n for (let i = 0; i < data.length; i += BATCH_SIZE) {\n const batch = data.slice(i, i + BATCH_SIZE)\n const valueRows = batch.map((row) => {\n // null/undefined 行はすべて NULL として扱う(不揃いなデータへの耐性)。\n const values = columnNames.map((col) => this.formatValueForSQL(row?.[col]))\n return `(${values.join(\", \")})`\n })\n\n const insertSQL = `INSERT INTO ${quotedTable} (${columnList}) VALUES ${valueRows.join(\", \")}`\n await connection.query(insertSQL)\n if (verbose) {\n Logger.info(`📊 Inserted batch ${Math.floor(i / BATCH_SIZE) + 1} (${batch.length} rows)`)\n }\n }\n\n await connection.query(\"COMMIT\")\n } catch (txError) {\n try {\n await connection.query(\"ROLLBACK\")\n } catch (rollbackError) {\n Logger.warn(\"⚠️ Rollback failed:\", rollbackError)\n }\n throw txError\n }\n\n if (verbose) {\n Logger.info(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n }\n } catch (error) {\n Logger.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(`DESCRIBE ${this.quoteIdentifier(tableName)}`)\n })\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(\"SHOW TABLES\")\n })\n }\n\n /**\n * Check if DuckDB is ready.\n * DuckDB が準備完了かどうかを確認します。\n */\n public isReady(): boolean {\n return this.workerInstance !== null\n }\n\n /**\n * Cleanup DuckDB Worker resources.\n * DuckDB Worker のリソースをクリーンアップします。\n */\n public async cleanup(): Promise<void> {\n try {\n if (this.connectionInstance) {\n await this.connectionInstance.close()\n this.connectionInstance = null\n }\n if (this.workerInstance) {\n await this.workerInstance.terminate()\n this.workerInstance = null\n }\n this.abortPendingWorker()\n this.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.connectionPromise = null\n this.operationQueue = Promise.resolve()\n this.operationFlags.clear()\n this.notifyListeners()\n this.listeners.clear()\n } catch (error) {\n Logger.error(\"⚠️ Error during cleanup:\", error)\n }\n }\n\n /**\n * Reset the service instance (useful for testing).\n * サービスインスタンスをリセットします(テスト用)。\n */\n public static resetInstance(): void {\n if (DuckDBService.instance) {\n void DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\n","/**\n * @module utils/logger\n * @description Logger utility for consistent log management across the library.\n * @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。\n */\n\n/**\n * @enum LogLevel\n * @description Log levels for filtering output.\n * @description 出力をフィルタリングするためのログレベル。\n */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/**\n * @interface ILogger\n * @description Interface for a logger object compatible with Console.\n * @description Console と互換性のあるロガーオブジェクトのインターフェース。\n */\nexport interface ILogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * @class Logger\n * @description A wrapper class for handling logging with levels and prefixes.\n * @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。\n */\nexport class Logger implements ILogger {\n private level: LogLevel\n private prefix: string\n private impl: ILogger\n\n /**\n * @constructor\n * @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.\n * @param {string} [prefix=\"[duckdb-helper]\"] - The prefix to add to all log messages.\n * @param {ILogger} [impl=console] - The implementation to use for logging.\n */\n constructor(level: LogLevel = LogLevel.WARN, prefix: string = \"[duckdb-helper]\", impl: ILogger = console) {\n this.level = level\n this.prefix = prefix\n this.impl = impl\n }\n\n private static instance: Logger = new Logger(LogLevel.WARN, \"[duckdb-helper]\")\n\n /**\n * @method setLevel\n * @description Updates the current log level for the static instance.\n * @description 静的インスタンスの現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n static setLevel(level: LogLevel): void {\n Logger.instance.setLevel(level)\n }\n\n /**\n * @method setLevel\n * @description Updates the current log level.\n * @description 現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation for the static instance.\n * @description 静的インスタンスのロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n static setImplementation(impl: ILogger): void {\n Logger.instance.setImplementation(impl)\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation.\n * @description ロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n setImplementation(impl: ILogger): void {\n this.impl = impl\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix for the static instance.\n * @description 静的インスタンスのログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n static setPrefix(prefix: string): void {\n Logger.instance.setPrefix(prefix)\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix.\n * @description ログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n setPrefix(prefix: string): void {\n this.prefix = prefix\n }\n\n private formatMessage(message: unknown): unknown[] {\n if (typeof message === \"string\") {\n return [`${this.prefix} ${message}`]\n }\n return [this.prefix, message]\n }\n\n static debug(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.debug(message, ...optionalParams)\n }\n\n debug(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.DEBUG) {\n this.impl.debug(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static info(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.info(message, ...optionalParams)\n }\n\n info(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.INFO) {\n this.impl.info(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static warn(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.warn(message, ...optionalParams)\n }\n\n warn(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.WARN) {\n this.impl.warn(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static error(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.error(message, ...optionalParams)\n }\n\n error(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.ERROR) {\n this.impl.error(...this.formatMessage(message), ...optionalParams)\n }\n }\n}\n","/**\n * DuckDB Helper Functions\n *\n * Utility functions for working with DuckDB query results.\n * DuckDB クエリ結果を扱うためのユーティリティ関数。\n */\n\nimport type { DuckDBQueryResult } from \"../types/duckdb\"\n\n/**\n * Safely convert DuckDB table to typed array\n * DuckDB テーブルを型付き配列に安全に変換します\n *\n * @param result - DuckDB query result\n * @returns Typed array of results\n */\nexport const duckdbTableToArray = <T>(result: unknown): T[] => {\n if (!result || typeof result !== \"object\") {\n return []\n }\n\n if (\"toArray\" in result && typeof result.toArray === \"function\") {\n return (result as DuckDBQueryResult<T>).toArray()\n }\n\n return []\n}\n\n/**\n * Get table row count\n * テーブルの行数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of rows in the table\n */\nexport const getDuckDBRowCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numRows\" in result && typeof result.numRows === \"number\") {\n return result.numRows\n }\n\n return 0\n}\n\n/**\n * Get table column count\n * テーブルの列数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of columns in the table\n */\nexport const getDuckDBColumnCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numCols\" in result && typeof result.numCols === \"number\") {\n return result.numCols\n }\n\n return 0\n}\n\n/**\n * Check if result is a valid DuckDB table\n * 結果が有効な DuckDB テーブルかどうかをチェックします\n *\n * @param result - DuckDB query result\n * @returns True if result is a valid DuckDB table\n */\nexport const isDuckDBTable = (result: unknown): result is DuckDBQueryResult => {\n if (!result || typeof result !== \"object\") {\n return false\n }\n\n return \"toArray\" in result && typeof result.toArray === \"function\" && \"numRows\" in result && typeof result.numRows === \"number\"\n}\n"],"mappings":"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","names":["index_exports","__export","DuckDBService","LogLevel","Logger","duckdb_default","duckdbTableToArray","getDuckDBColumnCount","getDuckDBRowCount","isDuckDBTable","useDuckDB","useDuckDBQuery","__toCommonJS","import_react","duckdb","LogLevel","_Logger","level","prefix","impl","message","optionalParams","Logger","INIT_TIMEOUT_MS","QUERY_TIMEOUT_MS","SPLINK_TIMEOUT_MS","BATCH_SIZE","TYPE_INFERENCE_SAMPLE_SIZE","TIMESTAMP_PATTERN","noop","_DuckDBService","listener","error","Logger","task","run","signal","options","timeoutId","timeoutPromise","_","reject","result","workerUrl","worker","baseBundles","bundle","toAbsoluteUrl","urlPath","normalizedBundle","db","connection","splinkTimeoutId","splinkTimeoutPromise","sql","timedOut","stale","queries","results","query","rows","seen","names","row","key","value","sample","columnNames","columnTypes","values","val","typeInfo","json","name","tableName","data","operationKey","verbose","inFlight","operation","quotedTable","sampleSize","columnDefs","primaryKeyClause","createTableSQL","columnList","col","i","batch","valueRows","insertSQL","txError","rollbackError","DuckDBService","duckdb_default","DuckDBService","useDependencyVersion","dependencies","ref","previous","dep","index","useDuckDB","autoInitialize","options","status","setStatus","error","setError","duckDBService","DuckDBService","updateStatus","serviceStatus","Logger","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","getTableInfo","listTables","useDuckDBQuery","isReady","setData","loading","setLoading","refetch","result","dependenciesVersion","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useCallback as w,useEffect as P,useRef as $,useState as I}from"react";import*as y from"@duckdb/duckdb-wasm";var E=(t=>(t[t.DEBUG=0]="DEBUG",t[t.INFO=1]="INFO",t[t.WARN=2]="WARN",t[t.ERROR=3]="ERROR",t[t.NONE=4]="NONE",t))(E||{}),p=class p{constructor(e=2,n="[duckdb-helper]",r=console){this.level=e,this.prefix=n,this.impl=r}static setLevel(e){p.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){p.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){p.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){p.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){p.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){p.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){p.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};p.instance=new p(2,"[duckdb-helper]");var c=p;var R=12e4,B=6e4,S=1e4,v=1e3,C=100,U=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/,x=()=>{},g=class g{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.connectionPromise=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.pendingWorker=null;this.pendingWorkerUrl=null;this.operationQueue=Promise.resolve();this.listeners=new Set}static getInstance(){return 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){c.error("\u26A0\uFE0F Error in status listener:",n)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}enqueue(e){let n=this.operationQueue.then(e,e);return this.operationQueue=n.then(x,x),n}assertNotAborted(e){if(e.aborted)throw new Error("DuckDB initialization aborted")}abortPendingWorker(){if(this.pendingWorker){try{this.pendingWorker.terminate()}catch{}this.pendingWorker=null}if(this.pendingWorkerUrl){try{URL.revokeObjectURL(this.pendingWorkerUrl)}catch{}this.pendingWorkerUrl=null}}async initialize(){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let e={aborted:!1},n,r=new Promise((i,t)=>{n=setTimeout(()=>{e.aborted=!0,t(new Error(`DuckDB initialization timed out after ${R}ms`))},R)});this.initPromise=Promise.race([this.internalInitializeWorker(e),r]);try{let i=await this.initPromise;return clearTimeout(n),this.isInitializing=!1,this.notifyListeners(),i}catch(i){throw clearTimeout(n),e.aborted=!0,this.isInitializing=!1,this.hasError=!0,this.workerInstance=null,this.connectionInstance=null,this.connectionPromise=null,this.initPromise=null,this.abortPendingWorker(),this.notifyListeners(),i}}async internalInitializeWorker(e){let n=null,r=null;try{c.info("\u{1F527} Starting DuckDB Worker initialization..."),c.info("\u{1F4E6} Getting DuckDB bundles from CDN...");let i=y.getJsDelivrBundles();c.info("\u{1F50D} Selecting appropriate bundle for browser...");let t=await y.selectBundle(i);this.assertNotAborted(e);let o=t.mainWorker?.split("/").pop(),s=t.mainModule?.split("/").pop(),h=t.pthreadWorker?.split("/").pop(),m=l=>typeof window<"u"&&window.location?new URL(l,window.location.origin).toString():l,d={mainWorker:o?m(`/assets/duckdb/${o}`):"",mainModule:s?m(`/assets/duckdb/${s}`):"",pthreadWorker:h?m(`/assets/duckdb/${h}`):void 0};c.info("\u2705 Bundle selected:",{mainWorker:d.mainWorker,mainModule:d.mainModule,pthreadWorker:d.pthreadWorker}),c.info("\u{1F680} Creating Worker with Blob URL..."),n=URL.createObjectURL(new Blob([`importScripts("${d.mainWorker}");`],{type:"text/javascript"})),c.info("\u{1F477} Creating Worker instance..."),r=new Worker(n),this.pendingWorker=r,this.pendingWorkerUrl=n;let b=new y.ConsoleLogger;c.info("\u{1F5C4}\uFE0F Creating DuckDB instance...");let f=new y.AsyncDuckDB(b,r);c.info("\u26A1 Instantiating DuckDB with modules..."),await f.instantiate(d.mainModule,d.pthreadWorker),this.assertNotAborted(e),c.info("\u{1F4E6} Installing splink_udfs from community...");let u=await f.connect();try{let l,k=new Promise((L,T)=>{l=setTimeout(()=>{T(new Error(`splink_udfs installation timed out after ${S}ms`))},S)});await Promise.race([(async()=>{await u.query("INSTALL splink_udfs FROM community;"),c.info("\u2705 splink_udfs installed successfully"),await u.query("LOAD splink_udfs;"),c.info("\u2705 splink_udfs loaded successfully")})(),k]),clearTimeout(l)}catch(l){c.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",l)}finally{await u.close()}return this.assertNotAborted(e),this.workerInstance=f,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(n),c.info("\u2705 DuckDB Worker initialized successfully"),f}catch(i){if(r)try{r.terminate()}catch{}if(n)try{URL.revokeObjectURL(n)}catch{}throw this.pendingWorker===r&&(this.pendingWorker=null),this.pendingWorkerUrl===n&&(this.pendingWorkerUrl=null),c.error("\u274C Failed to initialize DuckDB Worker:",i),i instanceof Error&&(c.error("\u274C Error name:",i.name),c.error("\u274C Error message:",i.message),c.error("\u274C Error stack:",i.stack)),i}}async getConnection(){if(this.connectionInstance)return this.connectionInstance;this.connectionPromise||(this.connectionPromise=(async()=>{let n=await(await this.initialize()).connect();return this.connectionInstance=n,n})());try{return await this.connectionPromise}catch(e){throw this.connectionPromise=null,e}}async runQueryWithTimeout(e){let n=!1,r,i=new Promise((o,s)=>{r=setTimeout(()=>{n=!0,s(new Error(`DuckDB query execution timed out after ${B}ms`))},B)}),t=(async()=>(await this.getConnection()).query(e))();try{let o=await Promise.race([t,i]);return clearTimeout(r),o}catch(o){if(clearTimeout(r),n){let s=this.connectionInstance;this.connectionInstance=null,this.connectionPromise=null,s&&s.close().catch(()=>{})}throw c.error("\u274C Failed to execute SQL query:",o),o}}async executeQuery(e){return this.enqueue(()=>this.runQueryWithTimeout(e))}async executeQueries(e){return this.enqueue(async()=>{let n=[];for(let r of e)n.push(await this.runQueryWithTimeout(r));return n})}collectColumnNames(e){let n=new Set,r=[];for(let i of e)if(i)for(let t of Object.keys(i))n.has(t)||(n.add(t),r.push(t));return r}looksLikeTimestamp(e){return U.test(e)?!Number.isNaN(new Date(e).getTime()):!1}inferColumnTypes(e,n){let r={};for(let i of n){let t=e.map(s=>s?.[i]).filter(s=>s!=null);if(t.length===0){r[i]="VARCHAR";continue}let o={number:0,integer:0,boolean:0,date:0,string:0};for(let s of t)typeof s=="number"?Number.isFinite(s)?(o.number+=1,Number.isInteger(s)&&(o.integer+=1)):o.string+=1:typeof s=="bigint"?(o.number+=1,o.integer+=1):typeof s=="boolean"?o.boolean+=1:s instanceof Date||typeof s=="string"&&this.looksLikeTimestamp(s)?o.date+=1:o.string+=1;o.number===t.length?r[i]=o.integer===t.length?"BIGINT":"DOUBLE":o.boolean===t.length?r[i]="BOOLEAN":o.date===t.length?r[i]="TIMESTAMP":r[i]="VARCHAR"}return r}formatValueForSQL(e){if(e==null)return"NULL";if(typeof e=="string")return`'${e.replace(/'/g,"''")}'`;if(e instanceof Date)return`'${e.toISOString()}'`;if(typeof e=="boolean")return e?"TRUE":"FALSE";if(typeof e=="bigint")return e.toString();if(typeof e=="number")return Number.isFinite(e)?String(e):"NULL";try{let n=JSON.stringify(e);return n===void 0?"NULL":`'${n.replace(/'/g,"''")}'`}catch{return"NULL"}}quoteIdentifier(e){if(typeof e!="string"||e.length===0)throw new Error("Invalid SQL identifier");return`"${e.replace(/"/g,'""')}"`}async createTableFromData(e,n,r={}){if(!n||n.length===0)throw new Error("Data array is empty");let i=`create_table_${e}`,t=r.verbose??!0,o=this.operationFlags.get(i);if(o)return t&&c.info(`\u23F3 Table creation for '${e}' is already in progress, awaiting existing operation...`),o;let s=this.enqueue(()=>this.buildTable(e,n,r,t));this.operationFlags.set(i,s);try{await s}finally{this.operationFlags.delete(i)}}async buildTable(e,n,r,i){try{let t=await this.getConnection(),o=this.quoteIdentifier(e),s=this.collectColumnNames(n);if(s.length===0)throw new Error("Data rows have no columns");let h=Math.min(n.length,C),m=this.inferColumnTypes(n.slice(0,h),s),d=s.map(l=>`${this.quoteIdentifier(l)} ${m[l]}`),b=r.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(r.primaryKey)})`:"",f=`CREATE TABLE ${o} (${d.join(", ")}${b})`,u=s.map(l=>this.quoteIdentifier(l)).join(", ");i&&c.info(`\u{1F4E5} Building table '${e}' with ${n.length} rows`),await t.query("BEGIN TRANSACTION");try{r.dropIfExists&&(i&&c.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await t.query(`DROP TABLE IF EXISTS ${o}`)),i&&c.info("\u{1F528} Creating table with SQL:",f),await t.query(f);for(let l=0;l<n.length;l+=v){let k=n.slice(l,l+v),L=k.map(A=>`(${s.map(N=>this.formatValueForSQL(A?.[N])).join(", ")})`),T=`INSERT INTO ${o} (${u}) VALUES ${L.join(", ")}`;await t.query(T),i&&c.info(`\u{1F4CA} Inserted batch ${Math.floor(l/v)+1} (${k.length} rows)`)}await t.query("COMMIT")}catch(l){try{await t.query("ROLLBACK")}catch(k){c.warn("\u26A0\uFE0F Rollback failed:",k)}throw l}i&&c.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(t){throw c.error(`\u274C Failed to create table '${e}':`,t),t}}async getTableInfo(e){return this.enqueue(async()=>(await this.getConnection()).query(`DESCRIBE ${this.quoteIdentifier(e)}`))}async listTables(){return this.enqueue(async()=>(await this.getConnection()).query("SHOW TABLES"))}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.abortPendingWorker(),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.connectionPromise=null,this.operationQueue=Promise.resolve(),this.operationFlags.clear(),this.notifyListeners(),this.listeners.clear()}catch(e){c.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){g.instance&&(g.instance.cleanup(),g.instance=null)}};g.instance=null;var D=g;var z=D.getInstance();var M=a=>{let e=$({deps:a,version:0}),n=e.current.deps;return(n.length!==a.length||a.some((i,t)=>!Object.is(i,n[t])))&&(e.current={deps:a,version:e.current.version+1}),e.current.version},W=(a=!0)=>{let[e,n]=I("not-initialized"),[r,i]=I(null),[t]=I(()=>D.getInstance()),o=w(()=>{let u=t.getStatus();c.info("\u{1F504} DuckDB status updated:",u),n(u),u==="error"?i("DuckDB initialization failed"):u==="ready"&&i(null)},[t]);P(()=>(t.addStatusListener(o),o(),()=>{t.removeStatusListener(o)}),[t,o]);let s=w(async()=>{try{await t.initialize()}catch(u){let l=u instanceof Error?u.message:"Unknown error occurred";i(l),c.error("\u274C DuckDB initialization failed:",u)}},[t]);P(()=>{a&&e==="not-initialized"&&(c.info("\u{1F680} Auto-initializing DuckDB..."),s())},[a,e,s]);let h=w(async u=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQuery(u)},[t,e]),m=w(async u=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.executeQueries(u)},[t,e]),d=w(async(u,l,k)=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.createTableFromData(u,l,k)},[t,e]),b=w(async u=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.getTableInfo(u)},[t,e]),f=w(async()=>{if(e!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return t.listTables()},[t,e]);return{status:e,error:r,executeQuery:h,executeQueries:m,createTableFromData:d,getTableInfo:b,listTables:f,isReady:e==="ready",initialize:s}},Q=(a,e=[])=>{let{executeQuery:n,isReady:r}=W(),[i,t]=I(null),[o,s]=I(!1),[h,m]=I(null),d=w(async()=>{if(r&&a.trim())try{s(!0),m(null);let f=await n(a);t(f)}catch(f){let u=f instanceof Error?f.message:"Query execution failed";m(u),t(null)}finally{s(!1)}},[n,r,a]),b=M(e);return P(()=>{d()},[d,b]),{data:i,loading:o,error:h,refetch:d}};var O=a=>!a||typeof a!="object"?[]:"toArray"in a&&typeof a.toArray=="function"?a.toArray():[],q=a=>!a||typeof a!="object"?0:"numRows"in a&&typeof a.numRows=="number"?a.numRows:0,F=a=>!a||typeof a!="object"?0:"numCols"in a&&typeof a.numCols=="number"?a.numCols:0,j=a=>!a||typeof a!="object"?!1:"toArray"in a&&typeof a.toArray=="function"&&"numRows"in a&&typeof a.numRows=="number";export{D as DuckDBService,E as LogLevel,c as Logger,z as duckDBService,O as duckdbTableToArray,F as getDuckDBColumnCount,q as getDuckDBRowCount,j as isDuckDBTable,W as useDuckDB,Q as useDuckDBQuery};
|
|
1
|
+
import{useCallback as w,useEffect as P,useRef as $,useState as T}from"react";import*as y from"@duckdb/duckdb-wasm";var B=(t=>(t[t.DEBUG=0]="DEBUG",t[t.INFO=1]="INFO",t[t.WARN=2]="WARN",t[t.ERROR=3]="ERROR",t[t.NONE=4]="NONE",t))(B||{}),g=class g{constructor(e=2,n="[duckdb-helper]",r=console){this.level=e,this.prefix=n,this.impl=r}static setLevel(e){g.instance.setLevel(e)}setLevel(e){this.level=e}static setImplementation(e){g.instance.setImplementation(e)}setImplementation(e){this.impl=e}static setPrefix(e){g.instance.setPrefix(e)}setPrefix(e){this.prefix=e}formatMessage(e){return typeof e=="string"?[`${this.prefix} ${e}`]:[this.prefix,e]}static debug(e,...n){g.instance.debug(e,...n)}debug(e,...n){this.level<=0&&this.impl.debug(...this.formatMessage(e),...n)}static info(e,...n){g.instance.info(e,...n)}info(e,...n){this.level<=1&&this.impl.info(...this.formatMessage(e),...n)}static warn(e,...n){g.instance.warn(e,...n)}warn(e,...n){this.level<=2&&this.impl.warn(...this.formatMessage(e),...n)}static error(e,...n){g.instance.error(e,...n)}error(e,...n){this.level<=3&&this.impl.error(...this.formatMessage(e),...n)}};g.instance=new g(2,"[duckdb-helper]");var c=g;var L=12e4,E=6e4,R=1e4,I=1e3,C=100,U=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/,S=()=>{},k=class k{constructor(){this.operationFlags=new Map;this.workerInstance=null;this.connectionInstance=null;this.connectionPromise=null;this.isInitializing=!1;this.initPromise=null;this.hasError=!1;this.pendingWorker=null;this.pendingWorkerUrl=null;this.operationQueue=Promise.resolve();this.listeners=new Set}static getInstance(){return k.instance||(k.instance=new k),k.instance}addStatusListener(e){this.listeners.add(e)}removeStatusListener(e){this.listeners.delete(e)}notifyListeners(){for(let e of this.listeners)try{e()}catch(n){c.error("\u26A0\uFE0F Error in status listener:",n)}}getStatus(){return this.hasError?"error":this.isInitializing?"initializing":this.workerInstance?"ready":"not-initialized"}enqueue(e){let n=this.operationQueue.then(e,e);return this.operationQueue=n.then(S,S),n}assertNotAborted(e){if(e.aborted)throw new Error("DuckDB initialization aborted")}abortPendingWorker(){if(this.pendingWorker){try{this.pendingWorker.terminate()}catch{}this.pendingWorker=null}if(this.pendingWorkerUrl){try{URL.revokeObjectURL(this.pendingWorkerUrl)}catch{}this.pendingWorkerUrl=null}}async initialize(e){if(this.workerInstance)return this.workerInstance;if(this.isInitializing&&this.initPromise)return this.initPromise;this.isInitializing=!0,this.hasError=!1,this.notifyListeners();let n={aborted:!1},r,a=new Promise((t,i)=>{r=setTimeout(()=>{n.aborted=!0,i(new Error(`DuckDB initialization timed out after ${L}ms`))},L)});this.initPromise=Promise.race([this.internalInitializeWorker(n,e),a]);try{let t=await this.initPromise;return clearTimeout(r),this.isInitializing=!1,this.notifyListeners(),t}catch(t){throw clearTimeout(r),n.aborted=!0,this.isInitializing=!1,this.hasError=!0,this.workerInstance=null,this.connectionInstance=null,this.connectionPromise=null,this.initPromise=null,this.abortPendingWorker(),this.notifyListeners(),t}}async internalInitializeWorker(e,n){let r=null,a=null;try{c.info("\u{1F527} Starting DuckDB Worker initialization..."),c.info("\u{1F4E6} Getting DuckDB bundles...");let t=n?.bundles||y.getJsDelivrBundles();c.info("\u{1F50D} Selecting appropriate bundle for browser...");let i=await y.selectBundle(t);this.assertNotAborted(e);let o=d=>d.startsWith("/")&&typeof window<"u"&&window.location?new URL(d,window.location.origin).toString():d,l={mainWorker:i.mainWorker?o(i.mainWorker):"",mainModule:i.mainModule?o(i.mainModule):"",pthreadWorker:i.pthreadWorker?o(i.pthreadWorker):void 0};c.info("\u2705 Bundle selected:",{mainWorker:l.mainWorker,mainModule:l.mainModule,pthreadWorker:l.pthreadWorker}),c.info("\u{1F680} Creating Worker with Blob URL..."),r=URL.createObjectURL(new Blob([`importScripts("${l.mainWorker}");`],{type:"text/javascript"})),c.info("\u{1F477} Creating Worker instance..."),a=new Worker(r),this.assertNotAborted(e),this.pendingWorker=a,this.pendingWorkerUrl=r,c.info("\u{1F91D} Connecting to AsyncDuckDB instance...");let f=new y.AsyncDuckDB(new y.ConsoleLogger,a);await f.instantiate(l.mainModule,l.pthreadWorker),this.assertNotAborted(e),c.info("\u{1F4E6} Installing splink_udfs from community...");let m=await f.connect();try{let d,p=new Promise((b,u)=>{d=setTimeout(()=>{u(new Error(`splink_udfs installation timed out after ${R}ms`))},R)});await Promise.race([(async()=>{await m.query("INSTALL splink_udfs FROM community;"),c.info("\u2705 splink_udfs installed successfully"),await m.query("LOAD splink_udfs;"),c.info("\u2705 splink_udfs loaded successfully")})(),p]),clearTimeout(d)}catch(d){c.warn("\u26A0\uFE0F Failed to install/load splink_udfs:",d)}finally{await m.close()}return this.assertNotAborted(e),this.workerInstance=f,this.pendingWorker=null,this.pendingWorkerUrl=null,URL.revokeObjectURL(r),c.info("\u2705 DuckDB Worker initialized successfully"),f}catch(t){if(a)try{a.terminate()}catch{}if(r)try{URL.revokeObjectURL(r)}catch{}throw this.pendingWorker===a&&(this.pendingWorker=null),this.pendingWorkerUrl===r&&(this.pendingWorkerUrl=null),c.error("\u274C Failed to initialize DuckDB Worker:",t),t instanceof Error&&(c.error("\u274C Error name:",t.name),c.error("\u274C Error message:",t.message),c.error("\u274C Error stack:",t.stack)),t}}async getConnection(){if(this.connectionInstance)return this.connectionInstance;this.connectionPromise||(this.connectionPromise=(async()=>{let n=await(await this.initialize()).connect();return this.connectionInstance=n,n})());try{return await this.connectionPromise}catch(e){throw this.connectionPromise=null,e}}async runQueryWithTimeout(e){let n=!1,r,a=new Promise((i,o)=>{r=setTimeout(()=>{n=!0,o(new Error(`DuckDB query execution timed out after ${E}ms`))},E)}),t=(async()=>(await this.getConnection()).query(e))();try{let i=await Promise.race([t,a]);return clearTimeout(r),i}catch(i){if(clearTimeout(r),n){let o=this.connectionInstance;this.connectionInstance=null,this.connectionPromise=null,o&&o.close().catch(()=>{})}throw c.error("\u274C Failed to execute SQL query:",i),i}}async executeQuery(e){return this.enqueue(()=>this.runQueryWithTimeout(e))}async executeQueries(e){return this.enqueue(async()=>{let n=[];for(let r of e)n.push(await this.runQueryWithTimeout(r));return n})}collectColumnNames(e){let n=new Set,r=[];for(let a of e)if(a)for(let t of Object.keys(a))n.has(t)||(n.add(t),r.push(t));return r}looksLikeTimestamp(e){return U.test(e)?!Number.isNaN(new Date(e).getTime()):!1}inferColumnTypes(e,n){let r={};for(let a of n){let t=e.map(o=>o?.[a]).filter(o=>o!=null);if(t.length===0){r[a]="VARCHAR";continue}let i={number:0,integer:0,boolean:0,date:0,string:0};for(let o of t)typeof o=="number"?Number.isFinite(o)?(i.number+=1,Number.isInteger(o)&&(i.integer+=1)):i.string+=1:typeof o=="bigint"?(i.number+=1,i.integer+=1):typeof o=="boolean"?i.boolean+=1:o instanceof Date||typeof o=="string"&&this.looksLikeTimestamp(o)?i.date+=1:i.string+=1;i.number===t.length?r[a]=i.integer===t.length?"BIGINT":"DOUBLE":i.boolean===t.length?r[a]="BOOLEAN":i.date===t.length?r[a]="TIMESTAMP":r[a]="VARCHAR"}return r}formatValueForSQL(e){if(e==null)return"NULL";if(typeof e=="string")return`'${e.replace(/'/g,"''")}'`;if(e instanceof Date)return`'${e.toISOString()}'`;if(typeof e=="boolean")return e?"TRUE":"FALSE";if(typeof e=="bigint")return e.toString();if(typeof e=="number")return Number.isFinite(e)?String(e):"NULL";try{let n=JSON.stringify(e);return n===void 0?"NULL":`'${n.replace(/'/g,"''")}'`}catch{return"NULL"}}quoteIdentifier(e){if(typeof e!="string"||e.length===0)throw new Error("Invalid SQL identifier");return`"${e.replace(/"/g,'""')}"`}async createTableFromData(e,n,r={}){if(!n||n.length===0)throw new Error("Data array is empty");let a=`create_table_${e}`,t=r.verbose??!0,i=this.operationFlags.get(a);if(i)return t&&c.info(`\u23F3 Table creation for '${e}' is already in progress, awaiting existing operation...`),i;let o=this.enqueue(()=>this.buildTable(e,n,r,t));this.operationFlags.set(a,o);try{await o}finally{this.operationFlags.delete(a)}}async buildTable(e,n,r,a){try{let t=await this.getConnection(),i=this.quoteIdentifier(e),o=this.collectColumnNames(n);if(o.length===0)throw new Error("Data rows have no columns");let l=Math.min(n.length,C),f=this.inferColumnTypes(n.slice(0,l),o),m=o.map(u=>`${this.quoteIdentifier(u)} ${f[u]}`),d=r.primaryKey?`, PRIMARY KEY (${this.quoteIdentifier(r.primaryKey)})`:"",p=`CREATE TABLE ${i} (${m.join(", ")}${d})`,b=o.map(u=>this.quoteIdentifier(u)).join(", ");a&&c.info(`\u{1F4E5} Building table '${e}' with ${n.length} rows`),await t.query("BEGIN TRANSACTION");try{r.dropIfExists&&(a&&c.info(`\u{1F5D1}\uFE0F Dropping table '${e}' if exists`),await t.query(`DROP TABLE IF EXISTS ${i}`)),a&&c.info("\u{1F528} Creating table with SQL:",p),await t.query(p);for(let u=0;u<n.length;u+=I){let h=n.slice(u,u+I),v=h.map(A=>`(${o.map(z=>this.formatValueForSQL(A?.[z])).join(", ")})`),W=`INSERT INTO ${i} (${b}) VALUES ${v.join(", ")}`;await t.query(W),a&&c.info(`\u{1F4CA} Inserted batch ${Math.floor(u/I)+1} (${h.length} rows)`)}await t.query("COMMIT")}catch(u){try{await t.query("ROLLBACK")}catch(h){c.warn("\u26A0\uFE0F Rollback failed:",h)}throw u}a&&c.info(`\u{1F389} Table '${e}' created successfully with ${n.length} rows`)}catch(t){throw c.error(`\u274C Failed to create table '${e}':`,t),t}}async getTableInfo(e){return this.enqueue(async()=>(await this.getConnection()).query(`DESCRIBE ${this.quoteIdentifier(e)}`))}async listTables(){return this.enqueue(async()=>(await this.getConnection()).query("SHOW TABLES"))}isReady(){return this.workerInstance!==null}async cleanup(){try{this.connectionInstance&&(await this.connectionInstance.close(),this.connectionInstance=null),this.workerInstance&&(await this.workerInstance.terminate(),this.workerInstance=null),this.abortPendingWorker(),this.isInitializing=!1,this.hasError=!1,this.initPromise=null,this.connectionPromise=null,this.operationQueue=Promise.resolve(),this.operationFlags.clear(),this.notifyListeners(),this.listeners.clear()}catch(e){c.error("\u26A0\uFE0F Error during cleanup:",e)}}static resetInstance(){k.instance&&(k.instance.cleanup(),k.instance=null)}};k.instance=null;var D=k;var N=D.getInstance();var M=s=>{let e=$({deps:s,version:0}),n=e.current.deps;return(n.length!==s.length||s.some((a,t)=>!Object.is(a,n[t])))&&(e.current={deps:s,version:e.current.version+1}),e.current.version},x=(s=!0,e)=>{let[n,r]=T("not-initialized"),[a,t]=T(null),[i]=T(()=>D.getInstance()),o=w(()=>{let u=i.getStatus();c.info("\u{1F504} DuckDB status updated:",u),r(u),u==="error"?t("DuckDB initialization failed"):u==="ready"&&t(null)},[i]);P(()=>(i.addStatusListener(o),o(),()=>{i.removeStatusListener(o)}),[i,o]);let l=w(async()=>{try{await i.initialize(e)}catch(u){let h=u instanceof Error?u.message:"Unknown error occurred";t(h),c.error("\u274C DuckDB initialization failed:",u)}},[i,e]);P(()=>{s&&n==="not-initialized"&&(c.info("\u{1F680} Auto-initializing DuckDB..."),l())},[s,n,l]);let f=w(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQuery(u)},[i,n]),m=w(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.executeQueries(u)},[i,n]),d=w(async(u,h,v)=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.createTableFromData(u,h,v)},[i,n]),p=w(async u=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.getTableInfo(u)},[i,n]),b=w(async()=>{if(n!=="ready")throw new Error("DuckDB is not ready. Please wait for initialization to complete.");return i.listTables()},[i,n]);return{status:n,error:a,executeQuery:f,executeQueries:m,createTableFromData:d,getTableInfo:p,listTables:b,isReady:n==="ready",initialize:l}},Q=(s,e=[])=>{let{executeQuery:n,isReady:r}=x(),[a,t]=T(null),[i,o]=T(!1),[l,f]=T(null),m=w(async()=>{if(r&&s.trim())try{o(!0),f(null);let p=await n(s);t(p)}catch(p){let b=p instanceof Error?p.message:"Query execution failed";f(b),t(null)}finally{o(!1)}},[n,r,s]),d=M(e);return P(()=>{m()},[m,d]),{data:a,loading:i,error:l,refetch:m}};var O=s=>!s||typeof s!="object"?[]:"toArray"in s&&typeof s.toArray=="function"?s.toArray():[],q=s=>!s||typeof s!="object"?0:"numRows"in s&&typeof s.numRows=="number"?s.numRows:0,F=s=>!s||typeof s!="object"?0:"numCols"in s&&typeof s.numCols=="number"?s.numCols:0,j=s=>!s||typeof s!="object"?!1:"toArray"in s&&typeof s.toArray=="function"&&"numRows"in s&&typeof s.numRows=="number";export{D as DuckDBService,B as LogLevel,c as Logger,N as duckDBService,O as duckdbTableToArray,F as getDuckDBColumnCount,q as getDuckDBRowCount,j as isDuckDBTable,x as useDuckDB,Q as useDuckDBQuery};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useDuckDB.ts","../src/services/duckdb.ts","../src/utils/simple-logger.ts","../src/utils/duckdb-helpers.ts"],"sourcesContent":["/**\n * DuckDB React Hook\n *\n * React hook for using DuckDB functionality in components.\n * コンポーネントで DuckDB 機能を使用するための React フック。\n */\n\nimport { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\"\nimport { DuckDBService } from \"../services/duckdb\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/**\n * Derive a constant-size version number that increments whenever the given\n * dependency list changes (by React's own Object.is comparison).\n * 依存配列の変化(React と同じ Object.is 比較)で増える固定サイズのバージョン番号を返す。\n *\n * これにより可変長の `dependencies` を effect の依存配列に直接スプレッドせずに済み、\n * 配列サイズがレンダー間で変わっても React の「依存配列サイズ変更」エラーを起こさない。\n */\nconst useDependencyVersion = (dependencies: DependencyList): number => {\n const ref = useRef<{ deps: DependencyList; version: number }>({ deps: dependencies, version: 0 })\n const previous = ref.current.deps\n const changed = previous.length !== dependencies.length || dependencies.some((dep, index) => !Object.is(dep, previous[index]))\n if (changed) {\n ref.current = { deps: dependencies, version: ref.current.version + 1 }\n }\n return ref.current.version\n}\n\nexport interface UseDuckDBResult {\n status: \"not-initialized\" | \"initializing\" | \"ready\" | \"error\"\n error: string | null\n executeQuery: (sql: string) => Promise<unknown>\n executeQueries: (queries: string[]) => Promise<unknown[]>\n createTableFromData: <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ) => Promise<void>\n getTableInfo: (tableName: string) => Promise<unknown>\n listTables: () => Promise<unknown>\n isReady: boolean\n initialize: () => Promise<void>\n}\n\n/**\n * DuckDB functionality hook.\n * DuckDB 機能を提供するフック。\n *\n * @param autoInitialize - 自動初期化を行うかどうか\n * @returns DuckDB の状態と操作関数\n */\nexport const useDuckDB = (autoInitialize = true): UseDuckDBResult => {\n const [status, setStatus] = useState<\"not-initialized\" | \"initializing\" | \"ready\" | \"error\">(\"not-initialized\")\n const [error, setError] = useState<string | null>(null)\n const [duckDBService] = useState(() => DuckDBService.getInstance())\n\n const updateStatus = useCallback(() => {\n const serviceStatus = duckDBService.getStatus()\n Logger.info(\"🔄 DuckDB status updated:\", serviceStatus)\n setStatus(serviceStatus)\n\n if (serviceStatus === \"error\") {\n setError(\"DuckDB initialization failed\")\n } else if (serviceStatus === \"ready\") {\n setError(null)\n }\n }, [duckDBService])\n\n useEffect(() => {\n duckDBService.addStatusListener(updateStatus)\n updateStatus()\n\n return () => {\n duckDBService.removeStatusListener(updateStatus)\n }\n }, [duckDBService, updateStatus])\n\n const initialize = useCallback(async () => {\n try {\n await duckDBService.initialize()\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Unknown error occurred\"\n setError(errorMessage)\n Logger.error(\"❌ DuckDB initialization failed:\", err)\n }\n }, [duckDBService])\n\n useEffect(() => {\n if (autoInitialize && status === \"not-initialized\") {\n Logger.info(\"🚀 Auto-initializing DuckDB...\")\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(\n async (sql: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQuery(sql)\n },\n [duckDBService, status],\n )\n\n const executeQueries = useCallback(\n async (queries: string[]): Promise<unknown[]> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQueries(queries)\n },\n [duckDBService, status],\n )\n\n const createTableFromData = useCallback(\n async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ): Promise<void> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.createTableFromData(tableName, data, options)\n },\n [duckDBService, status],\n )\n\n const getTableInfo = useCallback(\n async (tableName: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.getTableInfo(tableName)\n },\n [duckDBService, status],\n )\n\n const listTables = useCallback(async (): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.listTables()\n }, [duckDBService, status])\n\n return {\n status,\n error,\n executeQuery,\n executeQueries,\n createTableFromData,\n getTableInfo,\n listTables,\n isReady: status === \"ready\",\n initialize,\n }\n}\n\n/**\n * Hook for simple DuckDB query execution.\n * シンプルな DuckDB クエリ実行のためのフック。\n *\n * @remarks\n * `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。\n * `.length` や `.map` は持たないため、行配列として扱う場合は必ず\n * {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく\n * Arrow Table 相当の型を想定)。\n *\n * @example\n * ```tsx\n * const { data } = useDuckDBQuery(\"SELECT * FROM t\")\n * const rows = duckdbTableToArray<Row>(data)\n * ```\n *\n * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果(生の Arrow Table)と状態\n */\nexport const useDuckDBQuery = <T = unknown>(\n sql: string,\n dependencies: DependencyList = [],\n): {\n data: T | null\n loading: boolean\n error: string | null\n refetch: () => Promise<void>\n} => {\n const { executeQuery, isReady } = useDuckDB()\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refetch = useCallback(async () => {\n if (!(isReady && sql.trim())) {\n return\n }\n\n try {\n setLoading(true)\n setError(null)\n const result = await executeQuery(sql)\n setData(result as T)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Query execution failed\"\n setError(errorMessage)\n setData(null)\n } finally {\n setLoading(false)\n }\n }, [executeQuery, isReady, sql])\n\n // 可変長の dependencies を直接スプレッドせず、変化を検知する固定サイズのバージョンに変換する。\n const dependenciesVersion = useDependencyVersion(dependencies)\n\n useEffect(() => {\n void refetch()\n }, [refetch, dependenciesVersion])\n\n return {\n data,\n loading,\n error,\n refetch,\n }\n}\n","/**\n * DuckDB Web Worker Service\n *\n * Provides reusable DuckDB-wasm functionality across the application.\n * アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。\n */\n\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/** 初期化タイムアウト(WASM ダウンロードを考慮した長め設定)。 */\nconst INIT_TIMEOUT_MS = 120000\n/** クエリ実行タイムアウト。 */\nconst QUERY_TIMEOUT_MS = 60000\n/** splink_udfs インストールの個別タイムアウト。 */\nconst SPLINK_TIMEOUT_MS = 10000\n/** バッチ INSERT の1回あたり行数。 */\nconst BATCH_SIZE = 1000\n/** 型推定に使用する先頭サンプル行数。 */\nconst TYPE_INFERENCE_SAMPLE_SIZE = 100\n/**\n * 文字列を TIMESTAMP とみなすための厳密パターン。\n * `YYYY-MM-DD` または `YYYY-MM-DD[ T]HH:MM(:SS(.sss)?)?(Z|±HH:MM)?` のみ許容し、\n * `ABC-123` のような「ハイフンを含むが日付ではない」文字列を誤って日付扱いしない。\n */\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n\n/** 内部初期化のアボート指示を運ぶシグナル。 */\ninterface InitSignal {\n aborted: boolean\n}\n\nconst noop = (): void => {\n // 直列化キューの継続用。前段の解決/拒否を握りつぶす。\n}\n\n/**\n * DuckDB service for browser-based SQL operations.\n * ブラウザベースの SQL 操作のための DuckDB サービス。\n */\nclass DuckDBService {\n private static instance: DuckDBService | null = null\n private operationFlags: Map<string, Promise<void>> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private connectionPromise: Promise<duckdb.AsyncDuckDBConnection> | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\n\n /** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */\n private pendingWorker: Worker | null = null\n private pendingWorkerUrl: string | null = null\n\n /**\n * 共有コネクション上での操作直列化キュー。\n * createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。\n */\n private operationQueue: Promise<unknown> = Promise.resolve()\n\n private listeners: Set<() => void> = new Set()\n\n /**\n * Get singleton instance of DuckDBService.\n * DuckDBService のシングルトンインスタンスを取得します。\n */\n public static getInstance(): DuckDBService {\n if (!DuckDBService.instance) {\n DuckDBService.instance = new DuckDBService()\n }\n return DuckDBService.instance\n }\n\n /**\n * Add status change listener.\n * 状態変更リスナーを追加します。\n */\n public addStatusListener(listener: () => void): void {\n this.listeners.add(listener)\n }\n\n /**\n * Remove status change listener.\n * 状態変更リスナーを削除します。\n */\n public removeStatusListener(listener: () => void): void {\n this.listeners.delete(listener)\n }\n\n /**\n * Notify all listeners of status change.\n * すべてのリスナーに状態変更を通知します。\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener()\n } catch (error) {\n Logger.error(\"⚠️ Error in status listener:\", error)\n }\n }\n }\n\n /**\n * Get current status.\n * 現在のステータスを取得します。\n */\n public getStatus(): \"not-initialized\" | \"initializing\" | \"ready\" | \"error\" {\n if (this.hasError) {\n return \"error\"\n }\n if (this.isInitializing) {\n return \"initializing\"\n }\n if (this.workerInstance) {\n return \"ready\"\n }\n return \"not-initialized\"\n }\n\n /** Enqueue a task on the single-connection serialization queue. */\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.operationQueue.then(task, task)\n this.operationQueue = run.then(noop, noop)\n return run\n }\n\n /** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */\n private assertNotAborted(signal: InitSignal): void {\n if (signal.aborted) {\n throw new Error(\"DuckDB initialization aborted\")\n }\n }\n\n /** 初期化中の Worker / Blob URL を確実に破棄する。 */\n private abortPendingWorker(): void {\n if (this.pendingWorker) {\n try {\n this.pendingWorker.terminate()\n } catch {\n // terminate は冪等でなくても良い。失敗は無視する。\n }\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl) {\n try {\n URL.revokeObjectURL(this.pendingWorkerUrl)\n } catch {\n // revoke 失敗は無視する。\n }\n this.pendingWorkerUrl = null\n }\n }\n\n /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(): Promise<duckdb.AsyncDuckDB> {\n if (this.workerInstance) {\n return this.workerInstance\n }\n\n if (this.isInitializing && this.initPromise) {\n return this.initPromise\n }\n\n this.isInitializing = true\n this.hasError = false\n this.notifyListeners()\n\n // タイムアウト設定 (120秒) - WASMのダウンロードなどを考慮して長めに設定\n const signal: InitSignal = { aborted: false }\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n signal.aborted = true\n reject(new Error(`DuckDB initialization timed out after ${INIT_TIMEOUT_MS}ms`))\n }, INIT_TIMEOUT_MS)\n })\n\n this.initPromise = Promise.race([this.internalInitializeWorker(signal), timeoutPromise])\n\n try {\n const result = await this.initPromise\n clearTimeout(timeoutId)\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n // 失敗/タイムアウト時は状態を完全にリセットして再試行可能にする。\n signal.aborted = true\n this.isInitializing = false\n this.hasError = true\n this.workerInstance = null\n this.connectionInstance = null\n this.connectionPromise = null\n this.initPromise = null\n this.abortPendingWorker()\n this.notifyListeners()\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n *\n * workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は\n * ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。\n */\n private async internalInitializeWorker(signal: InitSignal): Promise<duckdb.AsyncDuckDB> {\n let workerUrl: string | null = null\n let worker: Worker | null = null\n try {\n Logger.info(\"🔧 Starting DuckDB Worker initialization...\")\n Logger.info(\"📦 Getting DuckDB bundles from CDN...\")\n const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles()\n\n Logger.info(\"🔍 Selecting appropriate bundle for browser...\")\n const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES)\n this.assertNotAborted(signal)\n\n // CDN URL をローカルの静的配信パス (/assets/duckdb/) に差し替える\n const workerName = bundle.mainWorker?.split(\"/\").pop()\n const moduleName = bundle.mainModule?.split(\"/\").pop()\n const pthreadWorkerName = bundle.pthreadWorker?.split(\"/\").pop()\n\n // Blob Worker 内の importScripts/fetch でパス解決できるように絶対 URL に変換する\n const toAbsoluteUrl = (urlPath: string): string => {\n if (typeof window !== \"undefined\" && window.location) {\n return new URL(urlPath, window.location.origin).toString()\n }\n return urlPath\n }\n\n const localBundle = {\n mainWorker: workerName ? toAbsoluteUrl(`/assets/duckdb/${workerName}`) : \"\",\n mainModule: moduleName ? toAbsoluteUrl(`/assets/duckdb/${moduleName}`) : \"\",\n pthreadWorker: pthreadWorkerName ? toAbsoluteUrl(`/assets/duckdb/${pthreadWorkerName}`) : undefined,\n }\n\n Logger.info(\"✅ Bundle selected:\", {\n mainWorker: localBundle.mainWorker,\n mainModule: localBundle.mainModule,\n pthreadWorker: localBundle.pthreadWorker,\n })\n\n Logger.info(\"🚀 Creating Worker with Blob URL...\")\n workerUrl = URL.createObjectURL(new Blob([`importScripts(\"${localBundle.mainWorker}\");`], { type: \"text/javascript\" }))\n\n Logger.info(\"👷 Creating Worker instance...\")\n worker = new Worker(workerUrl)\n this.pendingWorker = worker\n this.pendingWorkerUrl = workerUrl\n\n const logger = new duckdb.ConsoleLogger()\n\n Logger.info(\"🗄️ Creating DuckDB instance...\")\n const db = new duckdb.AsyncDuckDB(logger, worker)\n\n Logger.info(\"⚡ Instantiating DuckDB with modules...\")\n await db.instantiate(localBundle.mainModule, localBundle.pthreadWorker)\n this.assertNotAborted(signal)\n\n Logger.info(\"📦 Installing splink_udfs from community...\")\n const connection = await db.connect()\n try {\n // splink_udfs のインストール/ロードはネットワークアクセスを伴うため、\n // 個別の短いタイムアウトを設けてハングを防止する。\n let splinkTimeoutId!: ReturnType<typeof setTimeout>\n const splinkTimeoutPromise = new Promise<never>((_, reject) => {\n splinkTimeoutId = setTimeout(() => {\n reject(new Error(`splink_udfs installation timed out after ${SPLINK_TIMEOUT_MS}ms`))\n }, SPLINK_TIMEOUT_MS)\n })\n\n await Promise.race([\n (async () => {\n await connection.query(\"INSTALL splink_udfs FROM community;\")\n Logger.info(\"✅ splink_udfs installed successfully\")\n\n await connection.query(\"LOAD splink_udfs;\")\n Logger.info(\"✅ splink_udfs loaded successfully\")\n })(),\n splinkTimeoutPromise,\n ])\n clearTimeout(splinkTimeoutId)\n } catch (error) {\n // タイムアウトやネットワークエラーの場合は警告ログを出して続行\n Logger.warn(\"⚠️ Failed to install/load splink_udfs:\", error)\n } finally {\n await connection.close()\n }\n this.assertNotAborted(signal)\n\n // すべて成功したときのみ公開する。\n this.workerInstance = db\n this.pendingWorker = null\n this.pendingWorkerUrl = null\n URL.revokeObjectURL(workerUrl)\n\n Logger.info(\"✅ DuckDB Worker initialized successfully\")\n return db\n } catch (error) {\n // 途中生成物を必ず破棄する(Worker スレッド / Blob URL のリーク防止)。\n if (worker) {\n try {\n worker.terminate()\n } catch {\n // 無視する。\n }\n }\n if (workerUrl) {\n try {\n URL.revokeObjectURL(workerUrl)\n } catch {\n // 無視する。\n }\n }\n if (this.pendingWorker === worker) {\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl === workerUrl) {\n this.pendingWorkerUrl = null\n }\n\n Logger.error(\"❌ Failed to initialize DuckDB Worker:\", error)\n if (error instanceof Error) {\n Logger.error(\"❌ Error name:\", error.name)\n Logger.error(\"❌ Error message:\", error.message)\n Logger.error(\"❌ Error stack:\", error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n *\n * 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (this.connectionInstance) {\n return this.connectionInstance\n }\n if (!this.connectionPromise) {\n this.connectionPromise = (async () => {\n const db = await this.initialize()\n const connection = await db.connect()\n this.connectionInstance = connection\n return connection\n })()\n }\n try {\n return await this.connectionPromise\n } catch (error) {\n // 失敗時はリセットして次回リトライ可能にする。\n this.connectionPromise = null\n throw error\n }\n }\n\n /**\n * Run a single query with a timeout, discarding the connection on timeout.\n * 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。\n *\n * タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して\n * 後続クエリが同じ(詰まった)コネクションで待たされないようにする。\n */\n private async runQueryWithTimeout(sql: string): Promise<unknown> {\n let timedOut = false\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n timedOut = true\n reject(new Error(`DuckDB query execution timed out after ${QUERY_TIMEOUT_MS}ms`))\n }, QUERY_TIMEOUT_MS)\n })\n\n // コネクション取得とクエリ実行の両方をタイムアウトの対象にする。\n const run = (async () => {\n const connection = await this.getConnection()\n return connection.query(sql)\n })()\n\n try {\n const result = await Promise.race([run, timeoutPromise])\n clearTimeout(timeoutId)\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n if (timedOut) {\n const stale = this.connectionInstance\n this.connectionInstance = null\n this.connectionPromise = null\n if (stale) {\n void stale.close().catch(() => {\n // 詰まったコネクションの close 失敗は無視する。\n })\n }\n }\n Logger.error(\"❌ Failed to execute SQL query:\", error)\n throw error\n }\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n return this.enqueue(() => this.runQueryWithTimeout(sql))\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します(逐次実行)。\n *\n * バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n return this.enqueue(async () => {\n const results: unknown[] = []\n for (const query of queries) {\n results.push(await this.runQueryWithTimeout(query))\n }\n return results\n })\n }\n\n /**\n * Collect the union of column names across all rows (first-seen order).\n * 全行を走査して列名の和集合を取得します(初出順を保持)。\n * 先頭行に存在しないキーを取りこぼさないため全行を対象とする。\n */\n private collectColumnNames(rows: Record<string, unknown>[]): string[] {\n const seen = new Set<string>()\n const names: string[] = []\n for (const row of rows) {\n if (!row) {\n continue\n }\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key)\n names.push(key)\n }\n }\n }\n return names\n }\n\n /** Check whether a string is a genuine (strict) date/timestamp literal. */\n private looksLikeTimestamp(value: string): boolean {\n if (!TIMESTAMP_PATTERN.test(value)) {\n return false\n }\n return !Number.isNaN(new Date(value).getTime())\n }\n\n /**\n * Infer column types from a data sample for the given columns.\n * 指定した列について、データサンプルから DuckDB の型を推定します。\n *\n * 整数は桁あふれを避けるため一律 BIGINT を採用する。\n */\n private inferColumnTypes(sample: Record<string, unknown>[], columnNames: string[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n\n for (const key of columnNames) {\n const values = sample.map((row) => row?.[key]).filter((val) => val !== null && val !== undefined)\n\n if (values.length === 0) {\n columnTypes[key] = \"VARCHAR\"\n continue\n }\n\n const typeInfo = {\n number: 0,\n integer: 0,\n boolean: 0,\n date: 0,\n string: 0,\n }\n\n for (const value of values) {\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n }\n } else {\n // NaN / Infinity は数値列にできないため文字列扱い(実挿入時は NULL 化)。\n typeInfo.string += 1\n }\n } else if (typeof value === \"bigint\") {\n typeInfo.number += 1\n typeInfo.integer += 1\n } else if (typeof value === \"boolean\") {\n typeInfo.boolean += 1\n } else if (value instanceof Date) {\n typeInfo.date += 1\n } else if (typeof value === \"string\" && this.looksLikeTimestamp(value)) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n columnTypes[key] = typeInfo.integer === values.length ? \"BIGINT\" : \"DOUBLE\"\n } else if (typeInfo.boolean === values.length) {\n columnTypes[key] = \"BOOLEAN\"\n } else if (typeInfo.date === values.length) {\n columnTypes[key] = \"TIMESTAMP\"\n } else {\n columnTypes[key] = \"VARCHAR\"\n }\n }\n\n return columnTypes\n }\n\n /**\n * Format value for SQL insertion.\n * SQL 挿入用に値をフォーマットします。\n */\n private formatValueForSQL(value: unknown): string {\n if (value === null || value === undefined) {\n return \"NULL\"\n }\n if (typeof value === \"string\") {\n return `'${value.replace(/'/g, \"''\")}'`\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`\n }\n if (typeof value === \"boolean\") {\n return value ? \"TRUE\" : \"FALSE\"\n }\n if (typeof value === \"bigint\") {\n return value.toString()\n }\n if (typeof value === \"number\") {\n // NaN / Infinity / -Infinity は有効な SQL 数値リテラルではないため NULL 化する。\n if (!Number.isFinite(value)) {\n return \"NULL\"\n }\n return String(value)\n }\n // オブジェクト / 配列などは JSON 文字列としてクォート・エスケープする。\n try {\n const json = JSON.stringify(value)\n if (json === undefined) {\n return \"NULL\"\n }\n return `'${json.replace(/'/g, \"''\")}'`\n } catch {\n return \"NULL\"\n }\n }\n\n /**\n * Quote a SQL identifier (table / column / primary key) safely.\n * SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。\n */\n private quoteIdentifier(name: string): string {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new Error(\"Invalid SQL identifier\")\n }\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\n *\n * 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。\n */\n public async createTableFromData<T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n } = {},\n ): Promise<void> {\n if (!data || data.length === 0) {\n throw new Error(\"Data array is empty\")\n }\n\n const operationKey = `create_table_${tableName}`\n const verbose = options.verbose ?? true\n\n const inFlight = this.operationFlags.get(operationKey)\n if (inFlight) {\n if (verbose) {\n Logger.info(`⏳ Table creation for '${tableName}' is already in progress, awaiting existing operation...`)\n }\n return inFlight\n }\n\n const operation = this.enqueue(() => this.buildTable(tableName, data, options, verbose))\n this.operationFlags.set(operationKey, operation)\n try {\n await operation\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /** Actual (serialized, transactional) table build. */\n private async buildTable<T extends Record<string, unknown>>(tableName: string, data: T[], options: { dropIfExists?: boolean; primaryKey?: string }, verbose: boolean): Promise<void> {\n try {\n const connection = await this.getConnection()\n const quotedTable = this.quoteIdentifier(tableName)\n\n const columnNames = this.collectColumnNames(data)\n if (columnNames.length === 0) {\n throw new Error(\"Data rows have no columns\")\n }\n\n const sampleSize = Math.min(data.length, TYPE_INFERENCE_SAMPLE_SIZE)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize), columnNames)\n\n const columnDefs = columnNames.map((key) => `${this.quoteIdentifier(key)} ${columnTypes[key]}`)\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (${this.quoteIdentifier(options.primaryKey)})` : \"\"\n const createTableSQL = `CREATE TABLE ${quotedTable} (${columnDefs.join(\", \")}${primaryKeyClause})`\n const columnList = columnNames.map((col) => this.quoteIdentifier(col)).join(\", \")\n\n if (verbose) {\n Logger.info(`📥 Building table '${tableName}' with ${data.length} rows`)\n }\n\n // DROP / CREATE / INSERT を1トランザクションにまとめ、途中失敗時はロールバックして\n // 半端に作成されたテーブルを残さない。\n await connection.query(\"BEGIN TRANSACTION\")\n try {\n if (options.dropIfExists) {\n if (verbose) {\n Logger.info(`🗑️ Dropping table '${tableName}' if exists`)\n }\n await connection.query(`DROP TABLE IF EXISTS ${quotedTable}`)\n }\n\n if (verbose) {\n Logger.info(\"🔨 Creating table with SQL:\", createTableSQL)\n }\n await connection.query(createTableSQL)\n\n for (let i = 0; i < data.length; i += BATCH_SIZE) {\n const batch = data.slice(i, i + BATCH_SIZE)\n const valueRows = batch.map((row) => {\n // null/undefined 行はすべて NULL として扱う(不揃いなデータへの耐性)。\n const values = columnNames.map((col) => this.formatValueForSQL(row?.[col]))\n return `(${values.join(\", \")})`\n })\n\n const insertSQL = `INSERT INTO ${quotedTable} (${columnList}) VALUES ${valueRows.join(\", \")}`\n await connection.query(insertSQL)\n if (verbose) {\n Logger.info(`📊 Inserted batch ${Math.floor(i / BATCH_SIZE) + 1} (${batch.length} rows)`)\n }\n }\n\n await connection.query(\"COMMIT\")\n } catch (txError) {\n try {\n await connection.query(\"ROLLBACK\")\n } catch (rollbackError) {\n Logger.warn(\"⚠️ Rollback failed:\", rollbackError)\n }\n throw txError\n }\n\n if (verbose) {\n Logger.info(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n }\n } catch (error) {\n Logger.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(`DESCRIBE ${this.quoteIdentifier(tableName)}`)\n })\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(\"SHOW TABLES\")\n })\n }\n\n /**\n * Check if DuckDB is ready.\n * DuckDB が準備完了かどうかを確認します。\n */\n public isReady(): boolean {\n return this.workerInstance !== null\n }\n\n /**\n * Cleanup DuckDB Worker resources.\n * DuckDB Worker のリソースをクリーンアップします。\n */\n public async cleanup(): Promise<void> {\n try {\n if (this.connectionInstance) {\n await this.connectionInstance.close()\n this.connectionInstance = null\n }\n if (this.workerInstance) {\n await this.workerInstance.terminate()\n this.workerInstance = null\n }\n this.abortPendingWorker()\n this.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.connectionPromise = null\n this.operationQueue = Promise.resolve()\n this.operationFlags.clear()\n this.notifyListeners()\n this.listeners.clear()\n } catch (error) {\n Logger.error(\"⚠️ Error during cleanup:\", error)\n }\n }\n\n /**\n * Reset the service instance (useful for testing).\n * サービスインスタンスをリセットします(テスト用)。\n */\n public static resetInstance(): void {\n if (DuckDBService.instance) {\n void DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\n","/**\n * @module utils/logger\n * @description Logger utility for consistent log management across the library.\n * @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。\n */\n\n/**\n * @enum LogLevel\n * @description Log levels for filtering output.\n * @description 出力をフィルタリングするためのログレベル。\n */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/**\n * @interface ILogger\n * @description Interface for a logger object compatible with Console.\n * @description Console と互換性のあるロガーオブジェクトのインターフェース。\n */\nexport interface ILogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * @class Logger\n * @description A wrapper class for handling logging with levels and prefixes.\n * @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。\n */\nexport class Logger implements ILogger {\n private level: LogLevel\n private prefix: string\n private impl: ILogger\n\n /**\n * @constructor\n * @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.\n * @param {string} [prefix=\"[duckdb-helper]\"] - The prefix to add to all log messages.\n * @param {ILogger} [impl=console] - The implementation to use for logging.\n */\n constructor(level: LogLevel = LogLevel.WARN, prefix: string = \"[duckdb-helper]\", impl: ILogger = console) {\n this.level = level\n this.prefix = prefix\n this.impl = impl\n }\n\n private static instance: Logger = new Logger(LogLevel.WARN, \"[duckdb-helper]\")\n\n /**\n * @method setLevel\n * @description Updates the current log level for the static instance.\n * @description 静的インスタンスの現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n static setLevel(level: LogLevel): void {\n Logger.instance.setLevel(level)\n }\n\n /**\n * @method setLevel\n * @description Updates the current log level.\n * @description 現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation for the static instance.\n * @description 静的インスタンスのロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n static setImplementation(impl: ILogger): void {\n Logger.instance.setImplementation(impl)\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation.\n * @description ロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n setImplementation(impl: ILogger): void {\n this.impl = impl\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix for the static instance.\n * @description 静的インスタンスのログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n static setPrefix(prefix: string): void {\n Logger.instance.setPrefix(prefix)\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix.\n * @description ログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n setPrefix(prefix: string): void {\n this.prefix = prefix\n }\n\n private formatMessage(message: unknown): unknown[] {\n if (typeof message === \"string\") {\n return [`${this.prefix} ${message}`]\n }\n return [this.prefix, message]\n }\n\n static debug(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.debug(message, ...optionalParams)\n }\n\n debug(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.DEBUG) {\n this.impl.debug(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static info(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.info(message, ...optionalParams)\n }\n\n info(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.INFO) {\n this.impl.info(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static warn(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.warn(message, ...optionalParams)\n }\n\n warn(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.WARN) {\n this.impl.warn(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static error(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.error(message, ...optionalParams)\n }\n\n error(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.ERROR) {\n this.impl.error(...this.formatMessage(message), ...optionalParams)\n }\n }\n}\n","/**\n * DuckDB Helper Functions\n *\n * Utility functions for working with DuckDB query results.\n * DuckDB クエリ結果を扱うためのユーティリティ関数。\n */\n\nimport type { DuckDBQueryResult } from \"../types/duckdb\"\n\n/**\n * Safely convert DuckDB table to typed array\n * DuckDB テーブルを型付き配列に安全に変換します\n *\n * @param result - DuckDB query result\n * @returns Typed array of results\n */\nexport const duckdbTableToArray = <T>(result: unknown): T[] => {\n if (!result || typeof result !== \"object\") {\n return []\n }\n\n if (\"toArray\" in result && typeof result.toArray === \"function\") {\n return (result as DuckDBQueryResult<T>).toArray()\n }\n\n return []\n}\n\n/**\n * Get table row count\n * テーブルの行数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of rows in the table\n */\nexport const getDuckDBRowCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numRows\" in result && typeof result.numRows === \"number\") {\n return result.numRows\n }\n\n return 0\n}\n\n/**\n * Get table column count\n * テーブルの列数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of columns in the table\n */\nexport const getDuckDBColumnCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numCols\" in result && typeof result.numCols === \"number\") {\n return result.numCols\n }\n\n return 0\n}\n\n/**\n * Check if result is a valid DuckDB table\n * 結果が有効な DuckDB テーブルかどうかをチェックします\n *\n * @param result - DuckDB query result\n * @returns True if result is a valid DuckDB table\n */\nexport const isDuckDBTable = (result: unknown): result is DuckDBQueryResult => {\n if (!result || typeof result !== \"object\") {\n return false\n }\n\n return \"toArray\" in result && typeof result.toArray === \"function\" && \"numRows\" in result && typeof result.numRows === \"number\"\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","duckdb","LogLevel","_Logger","level","prefix","impl","message","optionalParams","Logger","INIT_TIMEOUT_MS","QUERY_TIMEOUT_MS","SPLINK_TIMEOUT_MS","BATCH_SIZE","TYPE_INFERENCE_SAMPLE_SIZE","TIMESTAMP_PATTERN","noop","_DuckDBService","listener","error","Logger","task","run","signal","timeoutId","timeoutPromise","_","reject","result","workerUrl","worker","JSDELIVR_BUNDLES","bundle","workerName","moduleName","pthreadWorkerName","toAbsoluteUrl","urlPath","localBundle","logger","db","connection","splinkTimeoutId","splinkTimeoutPromise","sql","timedOut","stale","queries","results","query","rows","seen","names","row","key","value","sample","columnNames","columnTypes","values","val","typeInfo","json","name","tableName","data","options","operationKey","verbose","inFlight","operation","quotedTable","sampleSize","columnDefs","primaryKeyClause","createTableSQL","columnList","col","i","batch","valueRows","insertSQL","txError","rollbackError","DuckDBService","duckdb_default","DuckDBService","useDependencyVersion","dependencies","ref","useRef","previous","dep","index","useDuckDB","autoInitialize","status","setStatus","useState","error","setError","duckDBService","DuckDBService","updateStatus","useCallback","serviceStatus","Logger","useEffect","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","options","getTableInfo","listTables","useDuckDBQuery","isReady","setData","loading","setLoading","refetch","result","dependenciesVersion","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useDuckDB.ts","../src/services/duckdb.ts","../src/utils/simple-logger.ts","../src/utils/duckdb-helpers.ts"],"sourcesContent":["/**\n * DuckDB React Hook\n *\n * React hook for using DuckDB functionality in components.\n * コンポーネントで DuckDB 機能を使用するための React フック。\n */\n\nimport { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\"\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { DuckDBService } from \"../services/duckdb\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/**\n * Derive a constant-size version number that increments whenever the given\n * dependency list changes (by React's own Object.is comparison).\n * 依存配列の変化(React と同じ Object.is 比較)で増える固定サイズのバージョン番号を返す。\n *\n * これにより可変長の `dependencies` を effect の依存配列に直接スプレッドせずに済み、\n * 配列サイズがレンダー間で変わっても React の「依存配列サイズ変更」エラーを起こさない。\n */\nconst useDependencyVersion = (dependencies: DependencyList): number => {\n const ref = useRef<{ deps: DependencyList; version: number }>({ deps: dependencies, version: 0 })\n const previous = ref.current.deps\n const changed = previous.length !== dependencies.length || dependencies.some((dep, index) => !Object.is(dep, previous[index]))\n if (changed) {\n ref.current = { deps: dependencies, version: ref.current.version + 1 }\n }\n return ref.current.version\n}\n\nexport interface UseDuckDBResult {\n status: \"not-initialized\" | \"initializing\" | \"ready\" | \"error\"\n error: string | null\n executeQuery: (sql: string) => Promise<unknown>\n executeQueries: (queries: string[]) => Promise<unknown[]>\n createTableFromData: <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ) => Promise<void>\n getTableInfo: (tableName: string) => Promise<unknown>\n listTables: () => Promise<unknown>\n isReady: boolean\n initialize: () => Promise<void>\n}\n\n/**\n * DuckDB functionality hook.\n * DuckDB 機能を提供するフック。\n *\n * @param autoInitialize - 自動初期化を行うかどうか\n * @returns DuckDB の状態と操作関数\n */\nexport const useDuckDB = (\n autoInitialize = true,\n options?: { bundles?: duckdb.DuckDBBundles },\n): UseDuckDBResult => {\n const [status, setStatus] = useState<\"not-initialized\" | \"initializing\" | \"ready\" | \"error\">(\"not-initialized\")\n const [error, setError] = useState<string | null>(null)\n const [duckDBService] = useState(() => DuckDBService.getInstance())\n\n const updateStatus = useCallback(() => {\n const serviceStatus = duckDBService.getStatus()\n Logger.info(\"🔄 DuckDB status updated:\", serviceStatus)\n setStatus(serviceStatus)\n\n if (serviceStatus === \"error\") {\n setError(\"DuckDB initialization failed\")\n } else if (serviceStatus === \"ready\") {\n setError(null)\n }\n }, [duckDBService])\n\n useEffect(() => {\n duckDBService.addStatusListener(updateStatus)\n updateStatus()\n\n return () => {\n duckDBService.removeStatusListener(updateStatus)\n }\n }, [duckDBService, updateStatus])\n\n const initialize = useCallback(async () => {\n try {\n await duckDBService.initialize(options)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Unknown error occurred\"\n setError(errorMessage)\n Logger.error(\"❌ DuckDB initialization failed:\", err)\n }\n }, [duckDBService, options])\n\n useEffect(() => {\n if (autoInitialize && status === \"not-initialized\") {\n Logger.info(\"🚀 Auto-initializing DuckDB...\")\n void initialize()\n }\n }, [autoInitialize, status, initialize])\n\n const executeQuery = useCallback(\n async (sql: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQuery(sql)\n },\n [duckDBService, status],\n )\n\n const executeQueries = useCallback(\n async (queries: string[]): Promise<unknown[]> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.executeQueries(queries)\n },\n [duckDBService, status],\n )\n\n const createTableFromData = useCallback(\n async <T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options?: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n },\n ): Promise<void> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.createTableFromData(tableName, data, options)\n },\n [duckDBService, status],\n )\n\n const getTableInfo = useCallback(\n async (tableName: string): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.getTableInfo(tableName)\n },\n [duckDBService, status],\n )\n\n const listTables = useCallback(async (): Promise<unknown> => {\n if (status !== \"ready\") {\n throw new Error(\"DuckDB is not ready. Please wait for initialization to complete.\")\n }\n return duckDBService.listTables()\n }, [duckDBService, status])\n\n return {\n status,\n error,\n executeQuery,\n executeQueries,\n createTableFromData,\n getTableInfo,\n listTables,\n isReady: status === \"ready\",\n initialize,\n }\n}\n\n/**\n * Hook for simple DuckDB query execution.\n * シンプルな DuckDB クエリ実行のためのフック。\n *\n * @remarks\n * `data` は DuckDB(Apache Arrow Table)の**生の結果**であり、行配列ではない。\n * `.length` や `.map` は持たないため、行配列として扱う場合は必ず\n * {@link duckdbTableToArray} で変換すること(`T` には行の型ではなく\n * Arrow Table 相当の型を想定)。\n *\n * @example\n * ```tsx\n * const { data } = useDuckDBQuery(\"SELECT * FROM t\")\n * const rows = duckdbTableToArray<Row>(data)\n * ```\n *\n * @param sql - 実行する SQL クエリ\n * @param dependencies - クエリを再実行するための依存配列\n * @returns クエリ結果(生の Arrow Table)と状態\n */\nexport const useDuckDBQuery = <T = unknown>(\n sql: string,\n dependencies: DependencyList = [],\n): {\n data: T | null\n loading: boolean\n error: string | null\n refetch: () => Promise<void>\n} => {\n const { executeQuery, isReady } = useDuckDB()\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const refetch = useCallback(async () => {\n if (!(isReady && sql.trim())) {\n return\n }\n\n try {\n setLoading(true)\n setError(null)\n const result = await executeQuery(sql)\n setData(result as T)\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Query execution failed\"\n setError(errorMessage)\n setData(null)\n } finally {\n setLoading(false)\n }\n }, [executeQuery, isReady, sql])\n\n // 可変長の dependencies を直接スプレッドせず、変化を検知する固定サイズのバージョンに変換する。\n const dependenciesVersion = useDependencyVersion(dependencies)\n\n useEffect(() => {\n void refetch()\n }, [refetch, dependenciesVersion])\n\n return {\n data,\n loading,\n error,\n refetch,\n }\n}\n","/**\n * DuckDB Web Worker Service\n *\n * Provides reusable DuckDB-wasm functionality across the application.\n * アプリケーション全体で再利用可能な DuckDB-wasm 機能を提供します。\n */\n\nimport * as duckdb from \"@duckdb/duckdb-wasm\"\nimport { Logger } from \"../utils/simple-logger\"\n\n/** 初期化タイムアウト(WASM ダウンロードを考慮した長め設定)。 */\nconst INIT_TIMEOUT_MS = 120000\n/** クエリ実行タイムアウト。 */\nconst QUERY_TIMEOUT_MS = 60000\n/** splink_udfs インストールの個別タイムアウト。 */\nconst SPLINK_TIMEOUT_MS = 10000\n/** バッチ INSERT の1回あたり行数。 */\nconst BATCH_SIZE = 1000\n/** 型推定に使用する先頭サンプル行数。 */\nconst TYPE_INFERENCE_SAMPLE_SIZE = 100\n/**\n * 文字列を TIMESTAMP とみなすための厳密パターン。\n * `YYYY-MM-DD` または `YYYY-MM-DD[ T]HH:MM(:SS(.sss)?)?(Z|±HH:MM)?` のみ許容し、\n * `ABC-123` のような「ハイフンを含むが日付ではない」文字列を誤って日付扱いしない。\n */\nconst TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n\n/** 内部初期化のアボート指示を運ぶシグナル。 */\ninterface InitSignal {\n aborted: boolean\n}\n\nconst noop = (): void => {\n // 直列化キューの継続用。前段の解決/拒否を握りつぶす。\n}\n\n/**\n * DuckDB service for browser-based SQL operations.\n * ブラウザベースの SQL 操作のための DuckDB サービス。\n */\nclass DuckDBService {\n private static instance: DuckDBService | null = null\n private operationFlags: Map<string, Promise<void>> = new Map()\n\n private workerInstance: duckdb.AsyncDuckDB | null = null\n private connectionInstance: duckdb.AsyncDuckDBConnection | null = null\n private connectionPromise: Promise<duckdb.AsyncDuckDBConnection> | null = null\n private isInitializing = false\n private initPromise: Promise<duckdb.AsyncDuckDB> | null = null\n private hasError = false\n\n /** 初期化中の Worker / Blob URL(タイムアウト時などに確実に破棄するため保持)。 */\n private pendingWorker: Worker | null = null\n private pendingWorkerUrl: string | null = null\n\n /**\n * 共有コネクション上での操作直列化キュー。\n * createTableFromData の DDL/DML と executeQuery が相互に割り込まないようにする。\n */\n private operationQueue: Promise<unknown> = Promise.resolve()\n\n private listeners: Set<() => void> = new Set()\n\n /**\n * Get singleton instance of DuckDBService.\n * DuckDBService のシングルトンインスタンスを取得します。\n */\n public static getInstance(): DuckDBService {\n if (!DuckDBService.instance) {\n DuckDBService.instance = new DuckDBService()\n }\n return DuckDBService.instance\n }\n\n /**\n * Add status change listener.\n * 状態変更リスナーを追加します。\n */\n public addStatusListener(listener: () => void): void {\n this.listeners.add(listener)\n }\n\n /**\n * Remove status change listener.\n * 状態変更リスナーを削除します。\n */\n public removeStatusListener(listener: () => void): void {\n this.listeners.delete(listener)\n }\n\n /**\n * Notify all listeners of status change.\n * すべてのリスナーに状態変更を通知します。\n */\n private notifyListeners(): void {\n for (const listener of this.listeners) {\n try {\n listener()\n } catch (error) {\n Logger.error(\"⚠️ Error in status listener:\", error)\n }\n }\n }\n\n /**\n * Get current status.\n * 現在のステータスを取得します。\n */\n public getStatus(): \"not-initialized\" | \"initializing\" | \"ready\" | \"error\" {\n if (this.hasError) {\n return \"error\"\n }\n if (this.isInitializing) {\n return \"initializing\"\n }\n if (this.workerInstance) {\n return \"ready\"\n }\n return \"not-initialized\"\n }\n\n /** Enqueue a task on the single-connection serialization queue. */\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.operationQueue.then(task, task)\n this.operationQueue = run.then(noop, noop)\n return run\n }\n\n /** アボート済みなら例外を投げる(初期化の各チェックポイントで使用)。 */\n private assertNotAborted(signal: InitSignal): void {\n if (signal.aborted) {\n throw new Error(\"DuckDB initialization aborted\")\n }\n }\n\n /** 初期化中の Worker / Blob URL を確実に破棄する。 */\n private abortPendingWorker(): void {\n if (this.pendingWorker) {\n try {\n this.pendingWorker.terminate()\n } catch {\n // terminate は冪等でなくても良い。失敗は無視する。\n }\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl) {\n try {\n URL.revokeObjectURL(this.pendingWorkerUrl)\n } catch {\n // revoke 失敗は無視する。\n }\n this.pendingWorkerUrl = null\n }\n }\n\n /**\n * Initialize DuckDB Worker instance.\n * DuckDB Worker インスタンスを初期化します。\n */\n public async initialize(options?: { bundles?: duckdb.DuckDBBundles }): Promise<duckdb.AsyncDuckDB> {\n if (this.workerInstance) {\n return this.workerInstance\n }\n\n if (this.isInitializing && this.initPromise) {\n return this.initPromise\n }\n\n this.isInitializing = true\n this.hasError = false\n this.notifyListeners()\n\n // タイムアウト設定 (120秒) - WASMのダウンロードなどを考慮して長めに設定\n const signal: InitSignal = { aborted: false }\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n signal.aborted = true\n reject(new Error(`DuckDB initialization timed out after ${INIT_TIMEOUT_MS}ms`))\n }, INIT_TIMEOUT_MS)\n })\n\n this.initPromise = Promise.race([this.internalInitializeWorker(signal, options), timeoutPromise])\n\n try {\n const result = await this.initPromise\n clearTimeout(timeoutId)\n this.isInitializing = false\n this.notifyListeners()\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n // 失敗/タイムアウト時は状態を完全にリセットして再試行可能にする。\n signal.aborted = true\n this.isInitializing = false\n this.hasError = true\n this.workerInstance = null\n this.connectionInstance = null\n this.connectionPromise = null\n this.initPromise = null\n this.abortPendingWorker()\n this.notifyListeners()\n throw error\n }\n }\n\n /**\n * Internal worker initialization method.\n * 内部的な Worker 初期化メソッド。\n *\n * workerInstance は instantiate 成功後にのみ公開する。途中で失敗/アボートした場合は\n * ローカルの Worker / Blob URL を破棄し、壊れたインスタンスを絶対に this に残さない。\n */\n private async internalInitializeWorker(signal: InitSignal, options?: { bundles?: duckdb.DuckDBBundles }): Promise<duckdb.AsyncDuckDB> {\n let workerUrl: string | null = null\n let worker: Worker | null = null\n try {\n Logger.info(\"🔧 Starting DuckDB Worker initialization...\")\n Logger.info(\"📦 Getting DuckDB bundles...\")\n const baseBundles = options?.bundles || duckdb.getJsDelivrBundles()\n\n Logger.info(\"🔍 Selecting appropriate bundle for browser...\")\n const bundle = await duckdb.selectBundle(baseBundles)\n this.assertNotAborted(signal)\n\n // Blob Worker 内の importScripts/fetch でパス解決できるように、相対パス(スラッシュ始まり)は絶対 URL に変換する\n const toAbsoluteUrl = (urlPath: string): string => {\n if (urlPath.startsWith(\"/\") && typeof window !== \"undefined\" && window.location) {\n return new URL(urlPath, window.location.origin).toString()\n }\n return urlPath\n }\n\n const normalizedBundle = {\n mainWorker: bundle.mainWorker ? toAbsoluteUrl(bundle.mainWorker) : \"\",\n mainModule: bundle.mainModule ? toAbsoluteUrl(bundle.mainModule) : \"\",\n pthreadWorker: bundle.pthreadWorker ? toAbsoluteUrl(bundle.pthreadWorker) : undefined,\n }\n\n Logger.info(\"✅ Bundle selected:\", {\n mainWorker: normalizedBundle.mainWorker,\n mainModule: normalizedBundle.mainModule,\n pthreadWorker: normalizedBundle.pthreadWorker,\n })\n\n Logger.info(\"🚀 Creating Worker with Blob URL...\")\n workerUrl = URL.createObjectURL(new Blob([`importScripts(\"${normalizedBundle.mainWorker}\");`], { type: \"text/javascript\" }))\n\n Logger.info(\"👷 Creating Worker instance...\")\n worker = new Worker(workerUrl)\n\n this.assertNotAborted(signal)\n this.pendingWorker = worker\n this.pendingWorkerUrl = workerUrl\n\n Logger.info(\"🤝 Connecting to AsyncDuckDB instance...\")\n const db = new duckdb.AsyncDuckDB(new duckdb.ConsoleLogger(), worker)\n await db.instantiate(normalizedBundle.mainModule, normalizedBundle.pthreadWorker)\n this.assertNotAborted(signal)\n\n Logger.info(\"📦 Installing splink_udfs from community...\")\n const connection = await db.connect()\n try {\n // splink_udfs のインストール/ロードはネットワークアクセスを伴うため、\n // 個別の短いタイムアウトを設けてハングを防止する。\n let splinkTimeoutId!: ReturnType<typeof setTimeout>\n const splinkTimeoutPromise = new Promise<never>((_, reject) => {\n splinkTimeoutId = setTimeout(() => {\n reject(new Error(`splink_udfs installation timed out after ${SPLINK_TIMEOUT_MS}ms`))\n }, SPLINK_TIMEOUT_MS)\n })\n\n await Promise.race([\n (async () => {\n await connection.query(\"INSTALL splink_udfs FROM community;\")\n Logger.info(\"✅ splink_udfs installed successfully\")\n\n await connection.query(\"LOAD splink_udfs;\")\n Logger.info(\"✅ splink_udfs loaded successfully\")\n })(),\n splinkTimeoutPromise,\n ])\n clearTimeout(splinkTimeoutId)\n } catch (error) {\n // タイムアウトやネットワークエラーの場合は警告ログを出して続行\n Logger.warn(\"⚠️ Failed to install/load splink_udfs:\", error)\n } finally {\n await connection.close()\n }\n this.assertNotAborted(signal)\n\n // すべて成功したときのみ公開する。\n this.workerInstance = db\n this.pendingWorker = null\n this.pendingWorkerUrl = null\n URL.revokeObjectURL(workerUrl)\n\n Logger.info(\"✅ DuckDB Worker initialized successfully\")\n return db\n } catch (error) {\n // 途中生成物を必ず破棄する(Worker スレッド / Blob URL のリーク防止)。\n if (worker) {\n try {\n worker.terminate()\n } catch {\n // 無視する。\n }\n }\n if (workerUrl) {\n try {\n URL.revokeObjectURL(workerUrl)\n } catch {\n // 無視する。\n }\n }\n if (this.pendingWorker === worker) {\n this.pendingWorker = null\n }\n if (this.pendingWorkerUrl === workerUrl) {\n this.pendingWorkerUrl = null\n }\n\n Logger.error(\"❌ Failed to initialize DuckDB Worker:\", error)\n if (error instanceof Error) {\n Logger.error(\"❌ Error name:\", error.name)\n Logger.error(\"❌ Error message:\", error.message)\n Logger.error(\"❌ Error stack:\", error.stack)\n }\n\n throw error\n }\n }\n\n /**\n * Get or create DuckDB connection.\n * DuckDB 接続を取得または作成します。\n *\n * 同時呼び出しでも connect() が二重実行されないよう、生成中の Promise を共有する。\n */\n public async getConnection(): Promise<duckdb.AsyncDuckDBConnection> {\n if (this.connectionInstance) {\n return this.connectionInstance\n }\n if (!this.connectionPromise) {\n this.connectionPromise = (async () => {\n const db = await this.initialize()\n const connection = await db.connect()\n this.connectionInstance = connection\n return connection\n })()\n }\n try {\n return await this.connectionPromise\n } catch (error) {\n // 失敗時はリセットして次回リトライ可能にする。\n this.connectionPromise = null\n throw error\n }\n }\n\n /**\n * Run a single query with a timeout, discarding the connection on timeout.\n * 単一クエリをタイムアウト付きで実行する。呼び出し側で直列化キューに載せる前提。\n *\n * タイムアウトしたクエリはワーカー側で走り続けるため、使用中コネクションを破棄して\n * 後続クエリが同じ(詰まった)コネクションで待たされないようにする。\n */\n private async runQueryWithTimeout(sql: string): Promise<unknown> {\n let timedOut = false\n // Promise executor は同期実行されるため timeoutId は必ず代入される。\n let timeoutId!: ReturnType<typeof setTimeout>\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n timedOut = true\n reject(new Error(`DuckDB query execution timed out after ${QUERY_TIMEOUT_MS}ms`))\n }, QUERY_TIMEOUT_MS)\n })\n\n // コネクション取得とクエリ実行の両方をタイムアウトの対象にする。\n const run = (async () => {\n const connection = await this.getConnection()\n return connection.query(sql)\n })()\n\n try {\n const result = await Promise.race([run, timeoutPromise])\n clearTimeout(timeoutId)\n return result\n } catch (error) {\n clearTimeout(timeoutId)\n if (timedOut) {\n const stale = this.connectionInstance\n this.connectionInstance = null\n this.connectionPromise = null\n if (stale) {\n void stale.close().catch(() => {\n // 詰まったコネクションの close 失敗は無視する。\n })\n }\n }\n Logger.error(\"❌ Failed to execute SQL query:\", error)\n throw error\n }\n }\n\n /**\n * Execute SQL query using DuckDB Worker.\n * DuckDB Worker を使用して SQL クエリを実行します。\n */\n public async executeQuery(sql: string): Promise<unknown> {\n return this.enqueue(() => this.runQueryWithTimeout(sql))\n }\n\n /**\n * Execute multiple SQL queries.\n * 複数の SQL クエリを実行します(逐次実行)。\n *\n * バッチ全体を1つのキュー単位として実行し、他操作が途中に割り込まないようにする。\n */\n public async executeQueries(queries: string[]): Promise<unknown[]> {\n return this.enqueue(async () => {\n const results: unknown[] = []\n for (const query of queries) {\n results.push(await this.runQueryWithTimeout(query))\n }\n return results\n })\n }\n\n /**\n * Collect the union of column names across all rows (first-seen order).\n * 全行を走査して列名の和集合を取得します(初出順を保持)。\n * 先頭行に存在しないキーを取りこぼさないため全行を対象とする。\n */\n private collectColumnNames(rows: Record<string, unknown>[]): string[] {\n const seen = new Set<string>()\n const names: string[] = []\n for (const row of rows) {\n if (!row) {\n continue\n }\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key)\n names.push(key)\n }\n }\n }\n return names\n }\n\n /** Check whether a string is a genuine (strict) date/timestamp literal. */\n private looksLikeTimestamp(value: string): boolean {\n if (!TIMESTAMP_PATTERN.test(value)) {\n return false\n }\n return !Number.isNaN(new Date(value).getTime())\n }\n\n /**\n * Infer column types from a data sample for the given columns.\n * 指定した列について、データサンプルから DuckDB の型を推定します。\n *\n * 整数は桁あふれを避けるため一律 BIGINT を採用する。\n */\n private inferColumnTypes(sample: Record<string, unknown>[], columnNames: string[]): Record<string, string> {\n const columnTypes: Record<string, string> = {}\n\n for (const key of columnNames) {\n const values = sample.map((row) => row?.[key]).filter((val) => val !== null && val !== undefined)\n\n if (values.length === 0) {\n columnTypes[key] = \"VARCHAR\"\n continue\n }\n\n const typeInfo = {\n number: 0,\n integer: 0,\n boolean: 0,\n date: 0,\n string: 0,\n }\n\n for (const value of values) {\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) {\n typeInfo.number += 1\n if (Number.isInteger(value)) {\n typeInfo.integer += 1\n }\n } else {\n // NaN / Infinity は数値列にできないため文字列扱い(実挿入時は NULL 化)。\n typeInfo.string += 1\n }\n } else if (typeof value === \"bigint\") {\n typeInfo.number += 1\n typeInfo.integer += 1\n } else if (typeof value === \"boolean\") {\n typeInfo.boolean += 1\n } else if (value instanceof Date) {\n typeInfo.date += 1\n } else if (typeof value === \"string\" && this.looksLikeTimestamp(value)) {\n typeInfo.date += 1\n } else {\n typeInfo.string += 1\n }\n }\n\n if (typeInfo.number === values.length) {\n columnTypes[key] = typeInfo.integer === values.length ? \"BIGINT\" : \"DOUBLE\"\n } else if (typeInfo.boolean === values.length) {\n columnTypes[key] = \"BOOLEAN\"\n } else if (typeInfo.date === values.length) {\n columnTypes[key] = \"TIMESTAMP\"\n } else {\n columnTypes[key] = \"VARCHAR\"\n }\n }\n\n return columnTypes\n }\n\n /**\n * Format value for SQL insertion.\n * SQL 挿入用に値をフォーマットします。\n */\n private formatValueForSQL(value: unknown): string {\n if (value === null || value === undefined) {\n return \"NULL\"\n }\n if (typeof value === \"string\") {\n return `'${value.replace(/'/g, \"''\")}'`\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`\n }\n if (typeof value === \"boolean\") {\n return value ? \"TRUE\" : \"FALSE\"\n }\n if (typeof value === \"bigint\") {\n return value.toString()\n }\n if (typeof value === \"number\") {\n // NaN / Infinity / -Infinity は有効な SQL 数値リテラルではないため NULL 化する。\n if (!Number.isFinite(value)) {\n return \"NULL\"\n }\n return String(value)\n }\n // オブジェクト / 配列などは JSON 文字列としてクォート・エスケープする。\n try {\n const json = JSON.stringify(value)\n if (json === undefined) {\n return \"NULL\"\n }\n return `'${json.replace(/'/g, \"''\")}'`\n } catch {\n return \"NULL\"\n }\n }\n\n /**\n * Quote a SQL identifier (table / column / primary key) safely.\n * SQL 識別子を安全にクォートします(内部のダブルクォートはエスケープ)。\n */\n private quoteIdentifier(name: string): string {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new Error(\"Invalid SQL identifier\")\n }\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n }\n\n /**\n * Create table from JavaScript array of objects.\n * JavaScript オブジェクトの配列からテーブルを作成します。\n *\n * 同名テーブルへの同時呼び出しは、進行中の同一 Promise を await する(早期 void 解決しない)。\n */\n public async createTableFromData<T extends Record<string, unknown>>(\n tableName: string,\n data: T[],\n options: {\n dropIfExists?: boolean\n primaryKey?: string\n verbose?: boolean\n } = {},\n ): Promise<void> {\n if (!data || data.length === 0) {\n throw new Error(\"Data array is empty\")\n }\n\n const operationKey = `create_table_${tableName}`\n const verbose = options.verbose ?? true\n\n const inFlight = this.operationFlags.get(operationKey)\n if (inFlight) {\n if (verbose) {\n Logger.info(`⏳ Table creation for '${tableName}' is already in progress, awaiting existing operation...`)\n }\n return inFlight\n }\n\n const operation = this.enqueue(() => this.buildTable(tableName, data, options, verbose))\n this.operationFlags.set(operationKey, operation)\n try {\n await operation\n } finally {\n this.operationFlags.delete(operationKey)\n }\n }\n\n /** Actual (serialized, transactional) table build. */\n private async buildTable<T extends Record<string, unknown>>(tableName: string, data: T[], options: { dropIfExists?: boolean; primaryKey?: string }, verbose: boolean): Promise<void> {\n try {\n const connection = await this.getConnection()\n const quotedTable = this.quoteIdentifier(tableName)\n\n const columnNames = this.collectColumnNames(data)\n if (columnNames.length === 0) {\n throw new Error(\"Data rows have no columns\")\n }\n\n const sampleSize = Math.min(data.length, TYPE_INFERENCE_SAMPLE_SIZE)\n const columnTypes = this.inferColumnTypes(data.slice(0, sampleSize), columnNames)\n\n const columnDefs = columnNames.map((key) => `${this.quoteIdentifier(key)} ${columnTypes[key]}`)\n const primaryKeyClause = options.primaryKey ? `, PRIMARY KEY (${this.quoteIdentifier(options.primaryKey)})` : \"\"\n const createTableSQL = `CREATE TABLE ${quotedTable} (${columnDefs.join(\", \")}${primaryKeyClause})`\n const columnList = columnNames.map((col) => this.quoteIdentifier(col)).join(\", \")\n\n if (verbose) {\n Logger.info(`📥 Building table '${tableName}' with ${data.length} rows`)\n }\n\n // DROP / CREATE / INSERT を1トランザクションにまとめ、途中失敗時はロールバックして\n // 半端に作成されたテーブルを残さない。\n await connection.query(\"BEGIN TRANSACTION\")\n try {\n if (options.dropIfExists) {\n if (verbose) {\n Logger.info(`🗑️ Dropping table '${tableName}' if exists`)\n }\n await connection.query(`DROP TABLE IF EXISTS ${quotedTable}`)\n }\n\n if (verbose) {\n Logger.info(\"🔨 Creating table with SQL:\", createTableSQL)\n }\n await connection.query(createTableSQL)\n\n for (let i = 0; i < data.length; i += BATCH_SIZE) {\n const batch = data.slice(i, i + BATCH_SIZE)\n const valueRows = batch.map((row) => {\n // null/undefined 行はすべて NULL として扱う(不揃いなデータへの耐性)。\n const values = columnNames.map((col) => this.formatValueForSQL(row?.[col]))\n return `(${values.join(\", \")})`\n })\n\n const insertSQL = `INSERT INTO ${quotedTable} (${columnList}) VALUES ${valueRows.join(\", \")}`\n await connection.query(insertSQL)\n if (verbose) {\n Logger.info(`📊 Inserted batch ${Math.floor(i / BATCH_SIZE) + 1} (${batch.length} rows)`)\n }\n }\n\n await connection.query(\"COMMIT\")\n } catch (txError) {\n try {\n await connection.query(\"ROLLBACK\")\n } catch (rollbackError) {\n Logger.warn(\"⚠️ Rollback failed:\", rollbackError)\n }\n throw txError\n }\n\n if (verbose) {\n Logger.info(`🎉 Table '${tableName}' created successfully with ${data.length} rows`)\n }\n } catch (error) {\n Logger.error(`❌ Failed to create table '${tableName}':`, error)\n throw error\n }\n }\n\n /**\n * Get table information.\n * テーブル情報を取得します。\n */\n public async getTableInfo(tableName: string): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(`DESCRIBE ${this.quoteIdentifier(tableName)}`)\n })\n }\n\n /**\n * List all tables.\n * すべてのテーブルを一覧表示します。\n */\n public async listTables(): Promise<unknown> {\n return this.enqueue(async () => {\n const connection = await this.getConnection()\n return connection.query(\"SHOW TABLES\")\n })\n }\n\n /**\n * Check if DuckDB is ready.\n * DuckDB が準備完了かどうかを確認します。\n */\n public isReady(): boolean {\n return this.workerInstance !== null\n }\n\n /**\n * Cleanup DuckDB Worker resources.\n * DuckDB Worker のリソースをクリーンアップします。\n */\n public async cleanup(): Promise<void> {\n try {\n if (this.connectionInstance) {\n await this.connectionInstance.close()\n this.connectionInstance = null\n }\n if (this.workerInstance) {\n await this.workerInstance.terminate()\n this.workerInstance = null\n }\n this.abortPendingWorker()\n this.isInitializing = false\n this.hasError = false\n this.initPromise = null\n this.connectionPromise = null\n this.operationQueue = Promise.resolve()\n this.operationFlags.clear()\n this.notifyListeners()\n this.listeners.clear()\n } catch (error) {\n Logger.error(\"⚠️ Error during cleanup:\", error)\n }\n }\n\n /**\n * Reset the service instance (useful for testing).\n * サービスインスタンスをリセットします(テスト用)。\n */\n public static resetInstance(): void {\n if (DuckDBService.instance) {\n void DuckDBService.instance.cleanup()\n DuckDBService.instance = null\n }\n }\n}\n\nexport { DuckDBService }\n\nexport default DuckDBService.getInstance()\n","/**\n * @module utils/logger\n * @description Logger utility for consistent log management across the library.\n * @description ライブラリ全体で一貫したログ管理を行うためのロガーユーティリティ。\n */\n\n/**\n * @enum LogLevel\n * @description Log levels for filtering output.\n * @description 出力をフィルタリングするためのログレベル。\n */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/**\n * @interface ILogger\n * @description Interface for a logger object compatible with Console.\n * @description Console と互換性のあるロガーオブジェクトのインターフェース。\n */\nexport interface ILogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * @class Logger\n * @description A wrapper class for handling logging with levels and prefixes.\n * @description レベルとプレフィックスを使用したログ記録を処理するためのラッパークラス。\n */\nexport class Logger implements ILogger {\n private level: LogLevel\n private prefix: string\n private impl: ILogger\n\n /**\n * @constructor\n * @param {LogLevel} [level=LogLevel.WARN] - The minimum log level to output.\n * @param {string} [prefix=\"[duckdb-helper]\"] - The prefix to add to all log messages.\n * @param {ILogger} [impl=console] - The implementation to use for logging.\n */\n constructor(level: LogLevel = LogLevel.WARN, prefix: string = \"[duckdb-helper]\", impl: ILogger = console) {\n this.level = level\n this.prefix = prefix\n this.impl = impl\n }\n\n private static instance: Logger = new Logger(LogLevel.WARN, \"[duckdb-helper]\")\n\n /**\n * @method setLevel\n * @description Updates the current log level for the static instance.\n * @description 静的インスタンスの現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n static setLevel(level: LogLevel): void {\n Logger.instance.setLevel(level)\n }\n\n /**\n * @method setLevel\n * @description Updates the current log level.\n * @description 現在のログレベルを更新します。\n * @param {LogLevel} level - The new log level.\n */\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation for the static instance.\n * @description 静的インスタンスのロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n static setImplementation(impl: ILogger): void {\n Logger.instance.setImplementation(impl)\n }\n\n /**\n * @method setImplementation\n * @description Updates the logger implementation.\n * @description ロガーの実装を更新します。\n * @param {ILogger} impl - The new logger implementation.\n */\n setImplementation(impl: ILogger): void {\n this.impl = impl\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix for the static instance.\n * @description 静的インスタンスのログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n static setPrefix(prefix: string): void {\n Logger.instance.setPrefix(prefix)\n }\n\n /**\n * @method setPrefix\n * @description Updates the log prefix.\n * @description ログのプレフィックスを更新します。\n * @param {string} prefix - The new prefix.\n */\n setPrefix(prefix: string): void {\n this.prefix = prefix\n }\n\n private formatMessage(message: unknown): unknown[] {\n if (typeof message === \"string\") {\n return [`${this.prefix} ${message}`]\n }\n return [this.prefix, message]\n }\n\n static debug(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.debug(message, ...optionalParams)\n }\n\n debug(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.DEBUG) {\n this.impl.debug(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static info(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.info(message, ...optionalParams)\n }\n\n info(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.INFO) {\n this.impl.info(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static warn(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.warn(message, ...optionalParams)\n }\n\n warn(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.WARN) {\n this.impl.warn(...this.formatMessage(message), ...optionalParams)\n }\n }\n\n static error(message?: unknown, ...optionalParams: unknown[]): void {\n Logger.instance.error(message, ...optionalParams)\n }\n\n error(message?: unknown, ...optionalParams: unknown[]): void {\n if (this.level <= LogLevel.ERROR) {\n this.impl.error(...this.formatMessage(message), ...optionalParams)\n }\n }\n}\n","/**\n * DuckDB Helper Functions\n *\n * Utility functions for working with DuckDB query results.\n * DuckDB クエリ結果を扱うためのユーティリティ関数。\n */\n\nimport type { DuckDBQueryResult } from \"../types/duckdb\"\n\n/**\n * Safely convert DuckDB table to typed array\n * DuckDB テーブルを型付き配列に安全に変換します\n *\n * @param result - DuckDB query result\n * @returns Typed array of results\n */\nexport const duckdbTableToArray = <T>(result: unknown): T[] => {\n if (!result || typeof result !== \"object\") {\n return []\n }\n\n if (\"toArray\" in result && typeof result.toArray === \"function\") {\n return (result as DuckDBQueryResult<T>).toArray()\n }\n\n return []\n}\n\n/**\n * Get table row count\n * テーブルの行数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of rows in the table\n */\nexport const getDuckDBRowCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numRows\" in result && typeof result.numRows === \"number\") {\n return result.numRows\n }\n\n return 0\n}\n\n/**\n * Get table column count\n * テーブルの列数を取得します\n *\n * @param result - DuckDB query result\n * @returns Number of columns in the table\n */\nexport const getDuckDBColumnCount = (result: unknown): number => {\n if (!result || typeof result !== \"object\") {\n return 0\n }\n\n if (\"numCols\" in result && typeof result.numCols === \"number\") {\n return result.numCols\n }\n\n return 0\n}\n\n/**\n * Check if result is a valid DuckDB table\n * 結果が有効な DuckDB テーブルかどうかをチェックします\n *\n * @param result - DuckDB query result\n * @returns True if result is a valid DuckDB table\n */\nexport const isDuckDBTable = (result: unknown): result is DuckDBQueryResult => {\n if (!result || typeof result !== \"object\") {\n return false\n }\n\n return \"toArray\" in result && typeof result.toArray === \"function\" && \"numRows\" in result && typeof result.numRows === \"number\"\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","duckdb","LogLevel","_Logger","level","prefix","impl","message","optionalParams","Logger","INIT_TIMEOUT_MS","QUERY_TIMEOUT_MS","SPLINK_TIMEOUT_MS","BATCH_SIZE","TYPE_INFERENCE_SAMPLE_SIZE","TIMESTAMP_PATTERN","noop","_DuckDBService","listener","error","Logger","task","run","signal","options","timeoutId","timeoutPromise","_","reject","result","workerUrl","worker","baseBundles","bundle","toAbsoluteUrl","urlPath","normalizedBundle","db","connection","splinkTimeoutId","splinkTimeoutPromise","sql","timedOut","stale","queries","results","query","rows","seen","names","row","key","value","sample","columnNames","columnTypes","values","val","typeInfo","json","name","tableName","data","operationKey","verbose","inFlight","operation","quotedTable","sampleSize","columnDefs","primaryKeyClause","createTableSQL","columnList","col","i","batch","valueRows","insertSQL","txError","rollbackError","DuckDBService","duckdb_default","DuckDBService","useDependencyVersion","dependencies","ref","useRef","previous","dep","index","useDuckDB","autoInitialize","options","status","setStatus","useState","error","setError","duckDBService","DuckDBService","updateStatus","useCallback","serviceStatus","Logger","useEffect","initialize","err","errorMessage","executeQuery","sql","executeQueries","queries","createTableFromData","tableName","data","getTableInfo","listTables","useDuckDBQuery","isReady","setData","loading","setLoading","refetch","result","dependenciesVersion","duckdbTableToArray","result","getDuckDBRowCount","getDuckDBColumnCount","isDuckDBTable"]}
|
|
@@ -135,9 +135,9 @@ flowchart TD
|
|
|
135
135
|
D -- Yes --> E["workerInstance を返す"]
|
|
136
136
|
D -- No --> F{初期化中?}
|
|
137
137
|
F -- Yes --> G["既存の initPromise を返す(重複防止)"]
|
|
138
|
-
F -- No --> H["1.
|
|
138
|
+
F -- No --> H["1. 外部から注入された bundles またはデフォルト CDN バンドルを使用"]
|
|
139
139
|
H --> I["2. ブラウザ互換バンドルを自動選択"]
|
|
140
|
-
I --> J["3.
|
|
140
|
+
I --> J["3. 相対パスである場合は絶対 URL に正規化(Blob Worker 内の importScripts 対策)"]
|
|
141
141
|
J --> K["4. Blob URL で Web Worker を作成"]
|
|
142
142
|
K --> L["5. AsyncDuckDB インスタンスを作成"]
|
|
143
143
|
L --> M["6. WASM モジュールをインスタンス化"]
|
|
@@ -169,7 +169,7 @@ flowchart TD
|
|
|
169
169
|
| 判断事項 | 採用方針 | 理由 |
|
|
170
170
|
| --- | --- | --- |
|
|
171
171
|
| シングルトンパターン | 採用 | アプリ全体で WASM インスタンスを1つだけ保持し、メモリを節約 |
|
|
172
|
-
| ローカルアセットからのバンドル取得 |
|
|
172
|
+
| ローカルアセットからのバンドル取得 | オプションによる bundles 注入 | 利用側(Vite等)の `?url` 機能などで収集したローカルアセットを bundles 引数として注入可能にすることで、ネットワーク接続飽和の回避、複数タブ起動時の Web Locks 競合・タイムアウトを防止 |
|
|
173
173
|
| Blob URL による Worker 作成 | 採用 | CORS 制約を回避し、CDN の Worker スクリプトをロード |
|
|
174
174
|
| splink_udfs 拡張の自動ロード | 初期化時に実行 | 名寄せ(レコードリンケージ)機能を標準提供( 10 秒個別タイムアウトによりネットワークハング時も続行可能) |
|
|
175
175
|
| 初期化タイムアウト | 120秒 | WASM ダウンロードを考慮した長めの設定 |
|
|
@@ -92,9 +92,9 @@ Worker の実際の初期化処理。**`workerInstance` は全ステップ成功
|
|
|
92
92
|
|
|
93
93
|
| ステップ | 処理内容 | 補足 |
|
|
94
94
|
| --- | --- | --- |
|
|
95
|
-
| 1 | `duckdb.getJsDelivrBundles()`
|
|
96
|
-
| 2 | `duckdb.selectBundle()` → **アボート確認** |
|
|
97
|
-
| 3 |
|
|
95
|
+
| 1 | bundles の決定 | 外部から渡されたカスタムの `bundles` 定義を使用(未指定時は `duckdb.getJsDelivrBundles()` で CDN バンドル一覧をフォールバックとして使用) |
|
|
96
|
+
| 2 | `duckdb.selectBundle()` → **アボート確認** | ブラウザの WasmFeatureDetect に基づき互換バンドルを選択 |
|
|
97
|
+
| 3 | パスの正規化 | スラッシュで始まる相対パス(Vite などのローカルアセット解決パス等)である場合は、 `window.location.origin` を付与して絶対 URL に正規化 |
|
|
98
98
|
| 4 | Blob URL を作成し Web Worker を生成、`pendingWorker`/`pendingWorkerUrl` に退避 | 失敗時の破棄用 |
|
|
99
99
|
| 5 | `new duckdb.AsyncDuckDB(logger, worker)`(ローカル変数 `db`) | まだ `this` に代入しない |
|
|
100
100
|
| 6 | `db.instantiate()` → **アボート確認** | WASM モジュール読み込み |
|
|
@@ -191,4 +191,4 @@ dist/
|
|
|
191
191
|
4. **`external` に指定されたパッケージはバンドルに含まれない**。利用側でインストールが必要。
|
|
192
192
|
5. **ビルドは tsup(esbuild ベース)が行い、tsc は型チェック専用**。この分業により高速ビルドと厳密な型チェックを両立。
|
|
193
193
|
6. **`src/` は npm パッケージに含まれない**。利用者が見るのは `dist/` のビルド済みコードのみ。
|
|
194
|
-
7. **WASM/Worker
|
|
194
|
+
7. **WASM/Worker アセットのパッケージ同梱**: パッケージ内に DuckDB-Wasm アセットが同梱されており、利用側のアプリケーション(Vite 等)から `?url` クエリなどを用いてインポートすることで、アセットの自動収集およびローカル配信(CDN 不使用)が行える設計になっている。これによりネットワーク競合やデッドロックが防止される。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiquants/duckdb-helper",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"description": "DuckDB helper utilities with React hooks, worker initialization, and typed query helpers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"import": "./dist/index.mjs",
|
|
12
12
|
"require": "./dist/index.js",
|
|
13
13
|
"default": "./dist/index.mjs"
|
|
14
|
-
}
|
|
14
|
+
},
|
|
15
|
+
"./assets/*": "./dist/assets/*"
|
|
15
16
|
},
|
|
16
17
|
"files": [
|
|
17
18
|
"dist",
|