@sqlrooms/duckdb 0.24.23 → 0.24.24

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.
@@ -0,0 +1,32 @@
1
+ import { DuckDbConnector } from './DuckDbConnector';
2
+ export interface WebSocketDuckDbConnectorOptions {
3
+ /**
4
+ * WebSocket endpoint of the DuckDB server.
5
+ */
6
+ wsUrl?: string;
7
+ /** SQL to run after initialization */
8
+ initializationQuery?: string;
9
+ /** Optional handler for server notifications `{ type: 'notify', payload }` */
10
+ onNotification?: (payload: any) => void;
11
+ /** Optional list of channels to subscribe to upon (re)connect */
12
+ subscribeChannels?: string[];
13
+ /** Optional bearer token to authenticate with the server */
14
+ authToken?: string;
15
+ }
16
+ export interface WebSocketDuckDbConnector extends DuckDbConnector {
17
+ readonly type: 'ws';
18
+ }
19
+ /**
20
+ * Create a DuckDB connector that talks to a WebSocket backend.
21
+ *
22
+ * Protocol expectations (as implemented by the servers):
23
+ * - Persistent connection; messages are correlated via `queryId`.
24
+ * - Client sends JSON: `{ type: 'arrow', sql: string, queryId?: string }`.
25
+ * - Server responds with a binary frame for Arrow results using framing:
26
+ * `[4-byte big-endian header length][header JSON { type, queryId }][Arrow IPC stream bytes]`.
27
+ * - Errors are sent as JSON text frames: `{ type: 'error', queryId, error }`.
28
+ * - Cancellation: client sends `{ type: 'cancel', queryId }` and keeps socket open.
29
+ * - Notifications: server may push `{ type: 'notify', payload }` as JSON text.
30
+ */
31
+ export declare function createWebSocketDuckDbConnector(options?: WebSocketDuckDbConnectorOptions): WebSocketDuckDbConnector;
32
+ //# sourceMappingURL=WebSocketDuckDbConnector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketDuckDbConnector.d.ts","sourceRoot":"","sources":["../../src/connectors/WebSocketDuckDbConnector.ts"],"names":[],"mappings":"AAKA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AASlD,MAAM,WAAW,+BAA+B;IAC9C;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,sCAAsC;IACtC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;IAExC,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAyB,SAAQ,eAAe;IAC/D,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE,+BAAoC,GAC5C,wBAAwB,CA0U1B"}
@@ -0,0 +1,321 @@
1
+ import * as arrow from 'apache-arrow';
2
+ import { createBaseDuckDbConnector, } from './BaseDuckDbConnector';
3
+ import { isSpatialLoadFileOptions, } from '@sqlrooms/room-config';
4
+ import { load, loadObjects, loadSpatial } from './load/load';
5
+ import { splitFilePath } from '@sqlrooms/utils';
6
+ /**
7
+ * Create a DuckDB connector that talks to a WebSocket backend.
8
+ *
9
+ * Protocol expectations (as implemented by the servers):
10
+ * - Persistent connection; messages are correlated via `queryId`.
11
+ * - Client sends JSON: `{ type: 'arrow', sql: string, queryId?: string }`.
12
+ * - Server responds with a binary frame for Arrow results using framing:
13
+ * `[4-byte big-endian header length][header JSON { type, queryId }][Arrow IPC stream bytes]`.
14
+ * - Errors are sent as JSON text frames: `{ type: 'error', queryId, error }`.
15
+ * - Cancellation: client sends `{ type: 'cancel', queryId }` and keeps socket open.
16
+ * - Notifications: server may push `{ type: 'notify', payload }` as JSON text.
17
+ */
18
+ export function createWebSocketDuckDbConnector(options = {}) {
19
+ const { wsUrl = 'ws://localhost:4000', initializationQuery = '', subscribeChannels, authToken, } = options;
20
+ // Persistent socket and per-query waiters
21
+ let socket = null;
22
+ let opening = null;
23
+ let lastSubscribedChannels = subscribeChannels;
24
+ const pending = new Map();
25
+ const closeAndRejectAll = (reason) => {
26
+ for (const [qid, waiter] of pending.entries()) {
27
+ try {
28
+ waiter.reject(new Error(reason));
29
+ }
30
+ catch { }
31
+ pending.delete(qid);
32
+ }
33
+ };
34
+ // Guard to avoid duplicate subscribe sends on open + initialize
35
+ const resubscribe = () => {
36
+ if (!socket || socket.readyState !== WebSocket.OPEN)
37
+ return;
38
+ if (!lastSubscribedChannels || lastSubscribedChannels.length === 0)
39
+ return;
40
+ for (const ch of lastSubscribedChannels) {
41
+ try {
42
+ socket.send(JSON.stringify({ type: 'subscribe', channel: ch }));
43
+ }
44
+ catch { }
45
+ }
46
+ };
47
+ const ensureSocket = () => {
48
+ if (socket && socket.readyState === WebSocket.OPEN)
49
+ return Promise.resolve();
50
+ if (opening)
51
+ return opening;
52
+ opening = new Promise((resolve, reject) => {
53
+ try {
54
+ const ws = new WebSocket(wsUrl);
55
+ socket = ws;
56
+ ws.binaryType = 'arraybuffer';
57
+ let authAcked = !authToken;
58
+ ws.onopen = () => {
59
+ // If auth is required, perform first-message auth and wait for ack
60
+ try {
61
+ if (authToken) {
62
+ try {
63
+ ws.send(JSON.stringify({ type: 'auth', token: authToken }));
64
+ }
65
+ catch { }
66
+ }
67
+ else {
68
+ // No auth required; resolve immediately
69
+ opening = null;
70
+ resubscribe();
71
+ resolve();
72
+ }
73
+ }
74
+ catch { }
75
+ };
76
+ ws.onmessage = (event) => {
77
+ (async () => {
78
+ if (typeof event.data === 'string') {
79
+ let parsed;
80
+ try {
81
+ parsed = JSON.parse(event.data);
82
+ }
83
+ catch {
84
+ return;
85
+ }
86
+ const t = parsed?.type;
87
+ if (!authAcked) {
88
+ if (t === 'authAck') {
89
+ authAcked = true;
90
+ opening = null;
91
+ // Subscribe once authed
92
+ resubscribe();
93
+ resolve();
94
+ }
95
+ else if (t === 'error') {
96
+ const msg = parsed?.error || 'Unauthorized';
97
+ opening = null;
98
+ reject(new Error(msg));
99
+ }
100
+ return;
101
+ }
102
+ if (t === 'cancelAck')
103
+ return;
104
+ if (t === 'notify') {
105
+ const payload = parsed?.payload;
106
+ try {
107
+ options.onNotification?.(payload);
108
+ }
109
+ catch { }
110
+ return;
111
+ }
112
+ // After initialization: if we ever receive a global unauthorized error, throw and close
113
+ if (t === 'error') {
114
+ const errMsg = String(parsed?.error || '').toLowerCase();
115
+ const hasQid = typeof parsed?.queryId === 'string';
116
+ if (!hasQid && errMsg.includes('unauthorized')) {
117
+ closeAndRejectAll('Unauthorized');
118
+ try {
119
+ ws.close();
120
+ }
121
+ catch { }
122
+ socket = null;
123
+ return;
124
+ }
125
+ }
126
+ const qid = parsed?.queryId;
127
+ if (!qid)
128
+ return;
129
+ const waiter = pending.get(qid);
130
+ if (!waiter)
131
+ return;
132
+ if (t === 'error') {
133
+ pending.delete(qid);
134
+ waiter.reject(new Error(parsed?.error || 'Unknown error'));
135
+ }
136
+ else if (t === 'ok') {
137
+ // Server acknowledged an arrow query with no result set
138
+ pending.delete(qid);
139
+ const empty = arrow.tableFromArrays({});
140
+ waiter.resolve(empty);
141
+ }
142
+ return;
143
+ }
144
+ // Binary result: [4-byte BE header length][header JSON][Arrow bytes]
145
+ let buffer;
146
+ if (event.data instanceof ArrayBuffer)
147
+ buffer = event.data;
148
+ else if (event.data instanceof Blob)
149
+ buffer = await event.data.arrayBuffer();
150
+ else
151
+ return;
152
+ const view = new DataView(buffer, 0, 4);
153
+ const headerLen = view.getUint32(0, false);
154
+ const headerStart = 4;
155
+ const headerEnd = headerStart + headerLen;
156
+ const headerBytes = new Uint8Array(buffer.slice(headerStart, headerEnd));
157
+ const headerStr = new TextDecoder().decode(headerBytes);
158
+ let header;
159
+ try {
160
+ header = JSON.parse(headerStr);
161
+ }
162
+ catch {
163
+ return;
164
+ }
165
+ const qid = header?.queryId;
166
+ if (!qid)
167
+ return;
168
+ const waiter = pending.get(qid);
169
+ if (!waiter)
170
+ return;
171
+ const arrowBytes = new Uint8Array(buffer.slice(headerEnd));
172
+ try {
173
+ const reader = await arrow.RecordBatchReader.from(arrowBytes);
174
+ const batches = [];
175
+ for await (const batch of reader)
176
+ batches.push(batch);
177
+ const table = batches.length
178
+ ? new arrow.Table(reader.schema, batches)
179
+ : arrow.tableFromArrays({});
180
+ pending.delete(qid);
181
+ waiter.resolve(table);
182
+ }
183
+ catch (e) {
184
+ pending.delete(qid);
185
+ waiter.reject(e);
186
+ }
187
+ })();
188
+ };
189
+ ws.onerror = () => {
190
+ if (opening) {
191
+ opening = null;
192
+ reject(new Error('WebSocket connection error'));
193
+ }
194
+ else {
195
+ closeAndRejectAll('WebSocket error');
196
+ }
197
+ try {
198
+ ws.close();
199
+ }
200
+ catch { }
201
+ };
202
+ ws.onclose = () => {
203
+ if (opening) {
204
+ opening = null;
205
+ reject(new Error('WebSocket closed during open'));
206
+ }
207
+ closeAndRejectAll('WebSocket closed');
208
+ socket = null;
209
+ };
210
+ }
211
+ catch (e) {
212
+ opening = null;
213
+ reject(e);
214
+ }
215
+ });
216
+ return opening;
217
+ };
218
+ const impl = {
219
+ async initializeInternal() {
220
+ await ensureSocket();
221
+ // Subscribe on initialize, too (if socket was already open)
222
+ resubscribe();
223
+ },
224
+ async destroyInternal() {
225
+ try {
226
+ socket?.close();
227
+ }
228
+ catch { }
229
+ socket = null;
230
+ },
231
+ async executeQueryInternal(query, signal, queryId) {
232
+ if (signal.aborted) {
233
+ throw new DOMException('Query was cancelled', 'AbortError');
234
+ }
235
+ await ensureSocket();
236
+ const qid = queryId || `q_${Date.now()}_${Math.random().toString(36).slice(2)}`;
237
+ return new Promise((resolve, reject) => {
238
+ const onAbort = () => {
239
+ try {
240
+ if (socket && socket.readyState === WebSocket.OPEN) {
241
+ socket.send(JSON.stringify({ type: 'cancel', queryId: qid }));
242
+ }
243
+ }
244
+ catch { }
245
+ pending.delete(qid);
246
+ reject(new DOMException('Query was cancelled', 'AbortError'));
247
+ };
248
+ signal.addEventListener('abort', onAbort, { once: true });
249
+ pending.set(qid, {
250
+ resolve: (t) => {
251
+ try {
252
+ signal.removeEventListener('abort', onAbort);
253
+ }
254
+ catch { }
255
+ resolve(t);
256
+ },
257
+ reject: (e) => {
258
+ try {
259
+ signal.removeEventListener('abort', onAbort);
260
+ }
261
+ catch { }
262
+ reject(e);
263
+ },
264
+ });
265
+ try {
266
+ socket.send(JSON.stringify({
267
+ type: 'arrow',
268
+ sql: query,
269
+ queryId: qid,
270
+ }));
271
+ }
272
+ catch (e) {
273
+ pending.delete(qid);
274
+ try {
275
+ signal.removeEventListener('abort', onAbort);
276
+ }
277
+ catch { }
278
+ reject(e);
279
+ }
280
+ });
281
+ },
282
+ async loadFileInternal(file, tableName, opts) {
283
+ // This backend executes SQL on a remote DuckDB. For local files, the path must
284
+ // be accessible to the server process.
285
+ const filePath = file instanceof File ? file.name : file;
286
+ const { ext } = splitFilePath(filePath);
287
+ if (ext === 'arrow') {
288
+ // No dedicated insert endpoint over WS; fall back to SQL semantics.
289
+ // Users should provide a server-accessible path.
290
+ const sql = `CREATE OR REPLACE TABLE ${tableName} AS SELECT * FROM read_ipc('${filePath}')`;
291
+ await impl.executeQueryInternal?.(sql, new AbortController().signal);
292
+ return;
293
+ }
294
+ let sql;
295
+ if (opts && isSpatialLoadFileOptions(opts)) {
296
+ sql = loadSpatial(tableName, filePath, opts);
297
+ }
298
+ else {
299
+ sql = load(opts?.method ?? 'auto', tableName, filePath, opts);
300
+ }
301
+ await impl.executeQueryInternal?.(sql, new AbortController().signal);
302
+ },
303
+ async loadArrowInternal(_file, _tableName) {
304
+ // Not supported over current WS protocol (no upload path). Use loadFileInternal
305
+ // with a server-accessible Arrow IPC file path instead.
306
+ throw new Error('Arrow buffer upload is not supported over WebSocket backend');
307
+ },
308
+ async loadObjectsInternal(file, tableName, opts) {
309
+ const sql = loadObjects(tableName, file, opts);
310
+ await impl.executeQueryInternal?.(sql, new AbortController().signal);
311
+ },
312
+ };
313
+ const base = createBaseDuckDbConnector({ initializationQuery: initializationQuery }, impl);
314
+ return {
315
+ ...base,
316
+ get type() {
317
+ return 'ws';
318
+ },
319
+ };
320
+ }
321
+ //# sourceMappingURL=WebSocketDuckDbConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketDuckDbConnector.js","sourceRoot":"","sources":["../../src/connectors/WebSocketDuckDbConnector.ts"],"names":[],"mappings":"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","sourcesContent":["import * as arrow from 'apache-arrow';\nimport {\n BaseDuckDbConnectorImpl,\n createBaseDuckDbConnector,\n} from './BaseDuckDbConnector';\nimport {DuckDbConnector} from './DuckDbConnector';\nimport {\n LoadFileOptions,\n StandardLoadOptions,\n isSpatialLoadFileOptions,\n} from '@sqlrooms/room-config';\nimport {load, loadObjects, loadSpatial} from './load/load';\nimport {splitFilePath} from '@sqlrooms/utils';\n\nexport interface WebSocketDuckDbConnectorOptions {\n /**\n * WebSocket endpoint of the DuckDB server.\n */\n wsUrl?: string;\n\n /** SQL to run after initialization */\n initializationQuery?: string;\n\n /** Optional handler for server notifications `{ type: 'notify', payload }` */\n onNotification?: (payload: any) => void;\n\n /** Optional list of channels to subscribe to upon (re)connect */\n subscribeChannels?: string[];\n\n /** Optional bearer token to authenticate with the server */\n authToken?: string;\n}\n\nexport interface WebSocketDuckDbConnector extends DuckDbConnector {\n readonly type: 'ws';\n}\n\n/**\n * Create a DuckDB connector that talks to a WebSocket backend.\n *\n * Protocol expectations (as implemented by the servers):\n * - Persistent connection; messages are correlated via `queryId`.\n * - Client sends JSON: `{ type: 'arrow', sql: string, queryId?: string }`.\n * - Server responds with a binary frame for Arrow results using framing:\n * `[4-byte big-endian header length][header JSON { type, queryId }][Arrow IPC stream bytes]`.\n * - Errors are sent as JSON text frames: `{ type: 'error', queryId, error }`.\n * - Cancellation: client sends `{ type: 'cancel', queryId }` and keeps socket open.\n * - Notifications: server may push `{ type: 'notify', payload }` as JSON text.\n */\nexport function createWebSocketDuckDbConnector(\n options: WebSocketDuckDbConnectorOptions = {},\n): WebSocketDuckDbConnector {\n const {\n wsUrl = 'ws://localhost:4000',\n initializationQuery = '',\n subscribeChannels,\n authToken,\n } = options;\n\n // Persistent socket and per-query waiters\n let socket: WebSocket | null = null;\n let opening: Promise<void> | null = null;\n let lastSubscribedChannels: string[] | undefined = subscribeChannels;\n const pending = new Map<\n string,\n {\n resolve: (table: arrow.Table<any>) => void;\n reject: (err: any) => void;\n }\n >();\n\n const closeAndRejectAll = (reason: string) => {\n for (const [qid, waiter] of pending.entries()) {\n try {\n waiter.reject(new Error(reason));\n } catch {}\n pending.delete(qid);\n }\n };\n\n // Guard to avoid duplicate subscribe sends on open + initialize\n const resubscribe = () => {\n if (!socket || socket.readyState !== WebSocket.OPEN) return;\n if (!lastSubscribedChannels || lastSubscribedChannels.length === 0) return;\n for (const ch of lastSubscribedChannels) {\n try {\n socket.send(JSON.stringify({type: 'subscribe', channel: ch}));\n } catch {}\n }\n };\n\n const ensureSocket = (): Promise<void> => {\n if (socket && socket.readyState === WebSocket.OPEN)\n return Promise.resolve();\n if (opening) return opening;\n opening = new Promise<void>((resolve, reject) => {\n try {\n const ws = new WebSocket(wsUrl);\n socket = ws;\n ws.binaryType = 'arraybuffer';\n\n let authAcked = !authToken;\n\n ws.onopen = () => {\n // If auth is required, perform first-message auth and wait for ack\n try {\n if (authToken) {\n try {\n ws.send(JSON.stringify({type: 'auth', token: authToken}));\n } catch {}\n } else {\n // No auth required; resolve immediately\n opening = null;\n resubscribe();\n resolve();\n }\n } catch {}\n };\n\n ws.onmessage = (event: MessageEvent) => {\n (async () => {\n if (typeof event.data === 'string') {\n let parsed: any;\n try {\n parsed = JSON.parse(event.data);\n } catch {\n return;\n }\n const t = parsed?.type;\n if (!authAcked) {\n if (t === 'authAck') {\n authAcked = true;\n opening = null;\n // Subscribe once authed\n resubscribe();\n resolve();\n } else if (t === 'error') {\n const msg = parsed?.error || 'Unauthorized';\n opening = null;\n reject(new Error(msg));\n }\n return;\n }\n if (t === 'cancelAck') return;\n if (t === 'notify') {\n const payload = parsed?.payload;\n try {\n options.onNotification?.(payload);\n } catch {}\n return;\n }\n // After initialization: if we ever receive a global unauthorized error, throw and close\n if (t === 'error') {\n const errMsg = String(parsed?.error || '').toLowerCase();\n const hasQid = typeof parsed?.queryId === 'string';\n if (!hasQid && errMsg.includes('unauthorized')) {\n closeAndRejectAll('Unauthorized');\n try {\n ws.close();\n } catch {}\n socket = null;\n return;\n }\n }\n const qid: string | undefined = parsed?.queryId;\n if (!qid) return;\n const waiter = pending.get(qid);\n if (!waiter) return;\n if (t === 'error') {\n pending.delete(qid);\n waiter.reject(new Error(parsed?.error || 'Unknown error'));\n } else if (t === 'ok') {\n // Server acknowledged an arrow query with no result set\n pending.delete(qid);\n const empty = arrow.tableFromArrays(\n {},\n ) as unknown as arrow.Table;\n waiter.resolve(empty);\n }\n return;\n }\n\n // Binary result: [4-byte BE header length][header JSON][Arrow bytes]\n let buffer: ArrayBuffer;\n if (event.data instanceof ArrayBuffer) buffer = event.data;\n else if (event.data instanceof Blob)\n buffer = await event.data.arrayBuffer();\n else return;\n\n const view = new DataView(buffer, 0, 4);\n const headerLen = view.getUint32(0, false);\n const headerStart = 4;\n const headerEnd = headerStart + headerLen;\n const headerBytes = new Uint8Array(\n buffer.slice(headerStart, headerEnd),\n );\n const headerStr = new TextDecoder().decode(headerBytes);\n let header: any;\n try {\n header = JSON.parse(headerStr);\n } catch {\n return;\n }\n const qid = header?.queryId as string | undefined;\n if (!qid) return;\n const waiter = pending.get(qid);\n if (!waiter) return;\n\n const arrowBytes = new Uint8Array(buffer.slice(headerEnd));\n try {\n const reader = await arrow.RecordBatchReader.from(arrowBytes);\n const batches: arrow.RecordBatch[] = [];\n for await (const batch of reader) batches.push(batch);\n const table = batches.length\n ? new arrow.Table(reader.schema, batches)\n : (arrow.tableFromArrays({}) as unknown as arrow.Table);\n pending.delete(qid);\n waiter.resolve(table);\n } catch (e) {\n pending.delete(qid);\n waiter.reject(e);\n }\n })();\n };\n\n ws.onerror = () => {\n if (opening) {\n opening = null;\n reject(new Error('WebSocket connection error'));\n } else {\n closeAndRejectAll('WebSocket error');\n }\n try {\n ws.close();\n } catch {}\n };\n\n ws.onclose = () => {\n if (opening) {\n opening = null;\n reject(new Error('WebSocket closed during open'));\n }\n closeAndRejectAll('WebSocket closed');\n socket = null;\n };\n } catch (e) {\n opening = null;\n reject(e);\n }\n });\n return opening;\n };\n\n const impl: BaseDuckDbConnectorImpl = {\n async initializeInternal() {\n await ensureSocket();\n // Subscribe on initialize, too (if socket was already open)\n resubscribe();\n },\n\n async destroyInternal() {\n try {\n socket?.close();\n } catch {}\n socket = null;\n },\n\n async executeQueryInternal<T extends arrow.TypeMap = any>(\n query: string,\n signal: AbortSignal,\n queryId?: string,\n ): Promise<arrow.Table<T>> {\n if (signal.aborted) {\n throw new DOMException('Query was cancelled', 'AbortError');\n }\n await ensureSocket();\n const qid =\n queryId || `q_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n return new Promise<arrow.Table<T>>((resolve, reject) => {\n const onAbort = () => {\n try {\n if (socket && socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify({type: 'cancel', queryId: qid}));\n }\n } catch {}\n pending.delete(qid);\n reject(new DOMException('Query was cancelled', 'AbortError'));\n };\n signal.addEventListener('abort', onAbort, {once: true});\n\n pending.set(qid, {\n resolve: (t) => {\n try {\n signal.removeEventListener('abort', onAbort);\n } catch {}\n resolve(t as arrow.Table<T>);\n },\n reject: (e) => {\n try {\n signal.removeEventListener('abort', onAbort);\n } catch {}\n reject(e);\n },\n });\n\n try {\n socket!.send(\n JSON.stringify({\n type: 'arrow',\n sql: query,\n queryId: qid,\n }),\n );\n } catch (e) {\n pending.delete(qid);\n try {\n signal.removeEventListener('abort', onAbort);\n } catch {}\n reject(e);\n }\n });\n },\n\n async loadFileInternal(\n file: string | File,\n tableName: string,\n opts?: LoadFileOptions,\n ) {\n // This backend executes SQL on a remote DuckDB. For local files, the path must\n // be accessible to the server process.\n const filePath = file instanceof File ? file.name : file;\n const {ext} = splitFilePath(filePath);\n if (ext === 'arrow') {\n // No dedicated insert endpoint over WS; fall back to SQL semantics.\n // Users should provide a server-accessible path.\n const sql = `CREATE OR REPLACE TABLE ${tableName} AS SELECT * FROM read_ipc('${filePath}')`;\n await impl.executeQueryInternal?.(sql, new AbortController().signal);\n return;\n }\n\n let sql: string;\n if (opts && isSpatialLoadFileOptions(opts)) {\n sql = loadSpatial(tableName, filePath, opts);\n } else {\n sql = load(opts?.method ?? 'auto', tableName, filePath, opts);\n }\n await impl.executeQueryInternal?.(sql, new AbortController().signal);\n },\n\n async loadArrowInternal(\n _file: arrow.Table | Uint8Array,\n _tableName: string,\n ) {\n // Not supported over current WS protocol (no upload path). Use loadFileInternal\n // with a server-accessible Arrow IPC file path instead.\n throw new Error(\n 'Arrow buffer upload is not supported over WebSocket backend',\n );\n },\n\n async loadObjectsInternal(\n file: Record<string, unknown>[],\n tableName: string,\n opts?: StandardLoadOptions,\n ) {\n const sql = loadObjects(tableName, file, opts);\n await impl.executeQueryInternal?.(sql, new AbortController().signal);\n },\n };\n\n const base = createBaseDuckDbConnector(\n {initializationQuery: initializationQuery},\n impl,\n );\n\n return {\n ...base,\n get type() {\n return 'ws' as const;\n },\n };\n}\n"]}
@@ -1,9 +1,14 @@
1
1
  import { DuckDbConnector } from './DuckDbConnector';
2
2
  import { createWasmDuckDbConnector, WasmDuckDbConnectorOptions, WasmDuckDbConnector } from './WasmDuckDbConnector';
3
- export type DuckDbConnectorType = 'wasm';
4
- export type DuckDbConnectorOptions = {
5
- type: DuckDbConnectorType;
6
- } & WasmDuckDbConnectorOptions;
3
+ export type DuckDbConnectorType = 'wasm' | 'ws';
4
+ export type DuckDbConnectorOptions = ({
5
+ type: 'wasm';
6
+ } & WasmDuckDbConnectorOptions) | ({
7
+ type: 'ws';
8
+ } & {
9
+ wsUrl?: string;
10
+ initializationQuery?: string;
11
+ });
7
12
  export declare function createDuckDbConnector(options: DuckDbConnectorOptions): DuckDbConnector;
8
13
  export { createWasmDuckDbConnector };
9
14
  export type { WasmDuckDbConnector };
@@ -1 +1 @@
1
- {"version":3,"file":"createDuckDbConnector.d.ts","sourceRoot":"","sources":["../../src/connectors/createDuckDbConnector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EACL,yBAAyB,EACzB,0BAA0B,EAC1B,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEzC,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,mBAAmB,CAAC;CAC3B,GAAG,0BAA0B,CAAC;AAE/B,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAQjB;AAED,OAAO,EAAC,yBAAyB,EAAC,CAAC;AACnC,YAAY,EAAC,mBAAmB,EAAC,CAAC;AAElC,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,eAAe,GACzB,SAAS,IAAI,mBAAmB,CAElC"}
1
+ {"version":3,"file":"createDuckDbConnector.d.ts","sourceRoot":"","sources":["../../src/connectors/createDuckDbConnector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EACL,yBAAyB,EACzB,0BAA0B,EAC1B,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAG/B,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAAC;AAEhD,MAAM,MAAM,sBAAsB,GAC9B,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GAAG,0BAA0B,CAAC,GAC7C,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAC,GAAG;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAC,CAAC,CAAC;AAEpE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAUjB;AAED,OAAO,EAAC,yBAAyB,EAAC,CAAC;AACnC,YAAY,EAAC,mBAAmB,EAAC,CAAC;AAElC,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,eAAe,GACzB,SAAS,IAAI,mBAAmB,CAElC"}
@@ -1,9 +1,12 @@
1
1
  import { createWasmDuckDbConnector, } from './WasmDuckDbConnector';
2
+ import { createWebSocketDuckDbConnector } from './WebSocketDuckDbConnector';
2
3
  export function createDuckDbConnector(options) {
3
4
  const { type, ...rest } = options;
4
5
  switch (type) {
5
6
  case 'wasm':
6
7
  return createWasmDuckDbConnector(rest);
8
+ case 'ws':
9
+ return createWebSocketDuckDbConnector(rest);
7
10
  default:
8
11
  throw new Error(`Unsupported DuckDB connector type: ${type}`);
9
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createDuckDbConnector.js","sourceRoot":"","sources":["../../src/connectors/createDuckDbConnector.ts"],"names":[],"mappings":"AACA,OAAO,EACL,yBAAyB,GAG1B,MAAM,uBAAuB,CAAC;AAQ/B,MAAM,UAAU,qBAAqB,CACnC,OAA+B;IAE/B,MAAM,EAAC,IAAI,EAAE,GAAG,IAAI,EAAC,GAAG,OAAO,CAAC;IAChC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,yBAAyB,CAAC,IAAI,CAAC,CAAC;QACzC;YACE,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,OAAO,EAAC,yBAAyB,EAAC,CAAC;AAGnC,MAAM,UAAU,qBAAqB,CACnC,SAA0B;IAE1B,OAAQ,SAAiB,CAAC,IAAI,KAAK,MAAM,CAAC;AAC5C,CAAC","sourcesContent":["import {DuckDbConnector} from './DuckDbConnector';\nimport {\n createWasmDuckDbConnector,\n WasmDuckDbConnectorOptions,\n WasmDuckDbConnector,\n} from './WasmDuckDbConnector';\n\nexport type DuckDbConnectorType = 'wasm';\n\nexport type DuckDbConnectorOptions = {\n type: DuckDbConnectorType;\n} & WasmDuckDbConnectorOptions;\n\nexport function createDuckDbConnector(\n options: DuckDbConnectorOptions,\n): DuckDbConnector {\n const {type, ...rest} = options;\n switch (type) {\n case 'wasm':\n return createWasmDuckDbConnector(rest);\n default:\n throw new Error(`Unsupported DuckDB connector type: ${type}`);\n }\n}\n\nexport {createWasmDuckDbConnector};\nexport type {WasmDuckDbConnector};\n\nexport function isWasmDuckDbConnector(\n connector: DuckDbConnector,\n): connector is WasmDuckDbConnector {\n return (connector as any).type === 'wasm';\n}\n"]}
1
+ {"version":3,"file":"createDuckDbConnector.js","sourceRoot":"","sources":["../../src/connectors/createDuckDbConnector.ts"],"names":[],"mappings":"AACA,OAAO,EACL,yBAAyB,GAG1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAC,8BAA8B,EAAC,MAAM,4BAA4B,CAAC;AAQ1E,MAAM,UAAU,qBAAqB,CACnC,OAA+B;IAE/B,MAAM,EAAC,IAAI,EAAE,GAAG,IAAI,EAAC,GAAG,OAAO,CAAC;IAChC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,yBAAyB,CAAC,IAAI,CAAC,CAAC;QACzC,KAAK,IAAI;YACP,OAAO,8BAA8B,CAAC,IAAI,CAAC,CAAC;QAC9C;YACE,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,OAAO,EAAC,yBAAyB,EAAC,CAAC;AAGnC,MAAM,UAAU,qBAAqB,CACnC,SAA0B;IAE1B,OAAQ,SAAiB,CAAC,IAAI,KAAK,MAAM,CAAC;AAC5C,CAAC","sourcesContent":["import {DuckDbConnector} from './DuckDbConnector';\nimport {\n createWasmDuckDbConnector,\n WasmDuckDbConnectorOptions,\n WasmDuckDbConnector,\n} from './WasmDuckDbConnector';\nimport {createWebSocketDuckDbConnector} from './WebSocketDuckDbConnector';\n\nexport type DuckDbConnectorType = 'wasm' | 'ws';\n\nexport type DuckDbConnectorOptions =\n | ({type: 'wasm'} & WasmDuckDbConnectorOptions)\n | ({type: 'ws'} & {wsUrl?: string; initializationQuery?: string});\n\nexport function createDuckDbConnector(\n options: DuckDbConnectorOptions,\n): DuckDbConnector {\n const {type, ...rest} = options;\n switch (type) {\n case 'wasm':\n return createWasmDuckDbConnector(rest);\n case 'ws':\n return createWebSocketDuckDbConnector(rest);\n default:\n throw new Error(`Unsupported DuckDB connector type: ${type}`);\n }\n}\n\nexport {createWasmDuckDbConnector};\nexport type {WasmDuckDbConnector};\n\nexport function isWasmDuckDbConnector(\n connector: DuckDbConnector,\n): connector is WasmDuckDbConnector {\n return (connector as any).type === 'wasm';\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export { createDuckDbSlice, type DuckDbSliceState, useStoreWithDuckDb, } from '.
12
12
  export * from './connectors/DuckDbConnector';
13
13
  export * from './connectors/BaseDuckDbConnector';
14
14
  export { createDuckDbConnector, createWasmDuckDbConnector, isWasmDuckDbConnector, type WasmDuckDbConnector, } from './connectors/createDuckDbConnector';
15
+ export { createWebSocketDuckDbConnector } from './connectors/WebSocketDuckDbConnector';
15
16
  export * from './connectors/load/load';
16
17
  export * from './duckdb-utils';
17
18
  export { LoadFileOptions, SpatialLoadFileOptions, isSpatialLoadFileOptions, } from '@sqlrooms/room-config';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,KAAK,mBAAmB,GACzB,MAAM,oCAAoC,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,gBAAgB,EACrB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AACrD,cAAc,yBAAyB,CAAC;AACxC,YAAY,EAAC,aAAa,EAAE,YAAY,EAAC,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,KAAK,mBAAmB,GACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAC,8BAA8B,EAAC,MAAM,uCAAuC,CAAC;AACrF,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,gBAAgB,EACrB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AACrD,cAAc,yBAAyB,CAAC;AACxC,YAAY,EAAC,aAAa,EAAE,YAAY,EAAC,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ export { createDuckDbSlice, useStoreWithDuckDb, } from './DuckDbSlice';
12
12
  export * from './connectors/DuckDbConnector';
13
13
  export * from './connectors/BaseDuckDbConnector';
14
14
  export { createDuckDbConnector, createWasmDuckDbConnector, isWasmDuckDbConnector, } from './connectors/createDuckDbConnector';
15
+ export { createWebSocketDuckDbConnector } from './connectors/WebSocketDuckDbConnector';
15
16
  export * from './connectors/load/load';
16
17
  export * from './duckdb-utils';
17
18
  export { LoadFileOptions, SpatialLoadFileOptions, isSpatialLoadFileOptions, } from '@sqlrooms/room-config';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,iBAAiB,EAEjB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,GAEtB,MAAM,oCAAoC,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAEL,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AACrD,cAAc,yBAAyB,CAAC","sourcesContent":["/**\n * {@include ../README.md}\n * @packageDocumentation\n */\n\nexport * from './types';\nexport * from './useDuckDb';\nexport * from './exportToCsv';\nexport {arrowTableToJson} from './arrow-utils';\nexport {\n getDuckDbTypeCategory,\n getArrowColumnTypeCategory,\n} from './typeCategories';\nexport * from './useSql';\nexport {\n createDuckDbSlice,\n type DuckDbSliceState,\n useStoreWithDuckDb,\n} from './DuckDbSlice';\nexport * from './connectors/DuckDbConnector';\nexport * from './connectors/BaseDuckDbConnector';\nexport {\n createDuckDbConnector,\n createWasmDuckDbConnector,\n isWasmDuckDbConnector,\n type WasmDuckDbConnector,\n} from './connectors/createDuckDbConnector';\nexport * from './connectors/load/load';\nexport * from './duckdb-utils';\nexport {\n LoadFileOptions,\n SpatialLoadFileOptions,\n isSpatialLoadFileOptions,\n} from '@sqlrooms/room-config';\nexport {\n type TypedRowAccessor,\n createTypedRowAccessor,\n} from './typedRowAccessor';\nexport {DuckDBAccessMode} from '@duckdb/duckdb-wasm';\nexport * from '@sqlrooms/duckdb-config';\nexport type {DuckDBBundles, DuckDBConfig} from '@duckdb/duckdb-wasm';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,iBAAiB,EAEjB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,GAEtB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAC,8BAA8B,EAAC,MAAM,uCAAuC,CAAC;AACrF,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAEL,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AACrD,cAAc,yBAAyB,CAAC","sourcesContent":["/**\n * {@include ../README.md}\n * @packageDocumentation\n */\n\nexport * from './types';\nexport * from './useDuckDb';\nexport * from './exportToCsv';\nexport {arrowTableToJson} from './arrow-utils';\nexport {\n getDuckDbTypeCategory,\n getArrowColumnTypeCategory,\n} from './typeCategories';\nexport * from './useSql';\nexport {\n createDuckDbSlice,\n type DuckDbSliceState,\n useStoreWithDuckDb,\n} from './DuckDbSlice';\nexport * from './connectors/DuckDbConnector';\nexport * from './connectors/BaseDuckDbConnector';\nexport {\n createDuckDbConnector,\n createWasmDuckDbConnector,\n isWasmDuckDbConnector,\n type WasmDuckDbConnector,\n} from './connectors/createDuckDbConnector';\nexport {createWebSocketDuckDbConnector} from './connectors/WebSocketDuckDbConnector';\nexport * from './connectors/load/load';\nexport * from './duckdb-utils';\nexport {\n LoadFileOptions,\n SpatialLoadFileOptions,\n isSpatialLoadFileOptions,\n} from '@sqlrooms/room-config';\nexport {\n type TypedRowAccessor,\n createTypedRowAccessor,\n} from './typedRowAccessor';\nexport {DuckDBAccessMode} from '@duckdb/duckdb-wasm';\nexport * from '@sqlrooms/duckdb-config';\nexport type {DuckDBBundles, DuckDBConfig} from '@duckdb/duckdb-wasm';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlrooms/duckdb",
3
- "version": "0.24.23",
3
+ "version": "0.24.24",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/index.js",
@@ -19,10 +19,10 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@duckdb/duckdb-wasm": ">=1.29.0-0",
22
- "@sqlrooms/duckdb-config": "0.24.23",
23
- "@sqlrooms/room-config": "0.24.23",
24
- "@sqlrooms/room-store": "0.24.23",
25
- "@sqlrooms/utils": "0.24.23",
22
+ "@sqlrooms/duckdb-config": "0.24.24",
23
+ "@sqlrooms/room-config": "0.24.24",
24
+ "@sqlrooms/room-store": "0.24.24",
25
+ "@sqlrooms/utils": "0.24.24",
26
26
  "fast-deep-equal": "^3.1.3",
27
27
  "immer": "^10.1.1",
28
28
  "zod": "^3.25.73",
@@ -45,5 +45,5 @@
45
45
  "test": "jest",
46
46
  "test:watch": "jest --watch"
47
47
  },
48
- "gitHead": "d2afc5c5cce7f6d66cda39c8e2bd11d4fd6a61f3"
48
+ "gitHead": "37bb4689a2f54a2697b49b31f80ba4728fdc26ab"
49
49
  }