@oxy-hq/sdk 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -26,12 +26,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  }) : target, mod));
27
27
 
28
28
  //#endregion
29
- const require_postMessage = require('./postMessage-CufWf9ji.cjs');
29
+ const require_postMessage = require('./postMessage-B1J0jDRN.cjs');
30
30
  let _duckdb_duckdb_wasm = require("@duckdb/duckdb-wasm");
31
31
  _duckdb_duckdb_wasm = __toESM(_duckdb_duckdb_wasm);
32
+ let react = require("react");
33
+ react = __toESM(react);
32
34
 
33
35
  //#region src/config.ts
34
36
  /**
37
+ * Safely get environment variable in both Node.js and browser environments
38
+ */
39
+ function getEnvVar(name) {
40
+ if (typeof process !== "undefined" && process.env) return process.env[name];
41
+ if (typeof {} !== "undefined" && {}.env) {
42
+ const viteValue = {}.env[`VITE_${name}`];
43
+ if (viteValue !== void 0) return viteValue;
44
+ return {}.env[name];
45
+ }
46
+ }
47
+ /**
35
48
  * Creates an Oxy configuration from environment variables
36
49
  *
37
50
  * Environment variables:
@@ -45,16 +58,16 @@ _duckdb_duckdb_wasm = __toESM(_duckdb_duckdb_wasm);
45
58
  * @throws Error if required environment variables are missing
46
59
  */
47
60
  function createConfig(overrides) {
48
- const baseUrl = overrides?.baseUrl || process.env.OXY_URL;
49
- const apiKey = overrides?.apiKey || process.env.OXY_API_KEY;
50
- const projectId = overrides?.projectId || process.env.OXY_PROJECT_ID;
61
+ const baseUrl = overrides?.baseUrl || getEnvVar("OXY_URL");
62
+ const apiKey = overrides?.apiKey || getEnvVar("OXY_API_KEY");
63
+ const projectId = overrides?.projectId || getEnvVar("OXY_PROJECT_ID");
51
64
  if (!baseUrl) throw new Error("OXY_URL environment variable or baseUrl config is required");
52
65
  if (!projectId) throw new Error("OXY_PROJECT_ID environment variable or projectId config is required");
53
66
  return {
54
67
  baseUrl: baseUrl.replace(/\/$/, ""),
55
68
  apiKey,
56
69
  projectId,
57
- branch: overrides?.branch || process.env.OXY_BRANCH,
70
+ branch: overrides?.branch || getEnvVar("OXY_BRANCH"),
58
71
  timeout: overrides?.timeout || 3e4,
59
72
  parentOrigin: overrides?.parentOrigin,
60
73
  disableAutoAuth: overrides?.disableAutoAuth
@@ -94,35 +107,50 @@ function createConfig(overrides) {
94
107
  * ```
95
108
  */
96
109
  async function createConfigAsync(overrides) {
97
- const { isInIframe: isInIframe$1, requestAuthFromParent: requestAuthFromParent$1 } = await Promise.resolve().then(() => require("./postMessage-CVS3MsL5.cjs"));
98
- let baseUrl = overrides?.baseUrl || process.env.OXY_URL;
99
- let apiKey = overrides?.apiKey || process.env.OXY_API_KEY;
100
- let projectId = overrides?.projectId || process.env.OXY_PROJECT_ID;
110
+ const { isInIframe: isInIframe$1 } = await Promise.resolve().then(() => require("./postMessage-BSNS3ccd.cjs"));
111
+ let baseUrl = overrides?.baseUrl || getEnvVar("OXY_URL");
112
+ let apiKey = overrides?.apiKey || getEnvVar("OXY_API_KEY");
113
+ let projectId = overrides?.projectId || getEnvVar("OXY_PROJECT_ID");
101
114
  const disableAutoAuth = overrides?.disableAutoAuth ?? false;
102
115
  const parentOrigin = overrides?.parentOrigin;
103
- if (!disableAutoAuth && isInIframe$1() && !apiKey) if (!parentOrigin) console.warn("[Oxy SDK] Running in iframe without API key and no parentOrigin specified. PostMessage authentication will be skipped. Provide parentOrigin config to enable automatic authentication.");
104
- else try {
105
- const authResult = await requestAuthFromParent$1({
106
- parentOrigin,
107
- timeout: overrides?.timeout || 5e3
108
- });
109
- apiKey = authResult.apiKey;
110
- if (authResult.projectId) projectId = authResult.projectId;
111
- if (authResult.baseUrl) baseUrl = authResult.baseUrl;
112
- console.log("[Oxy SDK] Successfully authenticated via postMessage");
113
- } catch (error) {
116
+ if (!disableAutoAuth && isInIframe$1() && !apiKey) if (!parentOrigin) logWarningAboutMissingParentOrigin();
117
+ else apiKey = await attemptPostMessageAuth(parentOrigin, overrides?.timeout || 5e3, apiKey, projectId, baseUrl).then((result) => {
118
+ if (result.projectId) projectId = result.projectId;
119
+ if (result.baseUrl) baseUrl = result.baseUrl;
120
+ return result.apiKey;
121
+ }).catch((error) => {
114
122
  console.error("[Oxy SDK] Failed to authenticate via postMessage:", error.message);
115
- }
123
+ return apiKey;
124
+ });
125
+ return createFinalConfig(baseUrl, apiKey, projectId, overrides);
126
+ }
127
+ function logWarningAboutMissingParentOrigin() {
128
+ console.warn("[Oxy SDK] Running in iframe without API key and no parentOrigin specified. PostMessage authentication will be skipped. Provide parentOrigin config to enable automatic authentication.");
129
+ }
130
+ async function attemptPostMessageAuth(parentOrigin, timeout, currentApiKey, currentProjectId, currentBaseUrl) {
131
+ const { requestAuthFromParent: requestAuthFromParent$1 } = await Promise.resolve().then(() => require("./postMessage-BSNS3ccd.cjs"));
132
+ const authResult = await requestAuthFromParent$1({
133
+ parentOrigin,
134
+ timeout
135
+ });
136
+ console.log("[Oxy SDK] Successfully authenticated via postMessage");
137
+ return {
138
+ apiKey: authResult.apiKey || currentApiKey,
139
+ projectId: authResult.projectId || currentProjectId,
140
+ baseUrl: authResult.baseUrl || currentBaseUrl
141
+ };
142
+ }
143
+ function createFinalConfig(baseUrl, apiKey, projectId, overrides) {
116
144
  if (!baseUrl) throw new Error("OXY_URL environment variable or baseUrl config is required");
117
145
  if (!projectId) throw new Error("OXY_PROJECT_ID environment variable or projectId config is required");
118
146
  return {
119
147
  baseUrl: baseUrl.replace(/\/$/, ""),
120
148
  apiKey,
121
149
  projectId,
122
- branch: overrides?.branch || process.env.OXY_BRANCH,
150
+ branch: overrides?.branch || getEnvVar("OXY_BRANCH"),
123
151
  timeout: overrides?.timeout || 3e4,
124
- parentOrigin,
125
- disableAutoAuth
152
+ parentOrigin: overrides?.parentOrigin,
153
+ disableAutoAuth: overrides?.disableAutoAuth
126
154
  };
127
155
  }
128
156
 
@@ -130,6 +158,15 @@ async function createConfigAsync(overrides) {
130
158
  //#region src/parquet.ts
131
159
  let dbInstance = null;
132
160
  let connection = null;
161
+ let operationQueue = Promise.resolve();
162
+ /**
163
+ * Enqueue an operation to prevent race conditions on shared DuckDB instance
164
+ */
165
+ function enqueueOperation(operation) {
166
+ const currentOperation = operationQueue.then(operation, operation);
167
+ operationQueue = currentOperation.then(() => {}, () => {});
168
+ return currentOperation;
169
+ }
133
170
  /**
134
171
  * Initialize DuckDB-WASM instance
135
172
  */
@@ -154,36 +191,74 @@ async function getConnection() {
154
191
  return connection;
155
192
  }
156
193
  /**
157
- * ParquetReader provides methods to read and query Parquet files
194
+ * ParquetReader provides methods to read and query Parquet files.
195
+ * Supports registering multiple Parquet files with different table names.
158
196
  */
159
197
  var ParquetReader = class {
160
- constructor(tableName = "data") {
161
- this.registered = false;
162
- this.tableName = tableName;
198
+ constructor() {
199
+ this.tableMap = /* @__PURE__ */ new Map();
200
+ }
201
+ /**
202
+ * Generate a unique internal table name to prevent conflicts
203
+ */
204
+ generateInternalTableName(tableName) {
205
+ return `${tableName}_${`${Date.now()}_${Math.random().toString(36).substring(2, 9)}`}`;
163
206
  }
164
207
  /**
165
- * Register a Parquet file from a Blob
208
+ * Register a Parquet file from a Blob with a specific table name
166
209
  *
167
210
  * @param blob - Parquet file as Blob
211
+ * @param tableName - Name to use for the table in queries (required)
168
212
  *
169
213
  * @example
170
214
  * ```typescript
171
215
  * const blob = await client.getFile('data/sales.parquet');
172
- * const reader = new ParquetReader('sales');
173
- * await reader.registerParquet(blob);
216
+ * const reader = new ParquetReader();
217
+ * await reader.registerParquet(blob, 'sales');
218
+ * ```
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * // Register multiple files
223
+ * const reader = new ParquetReader();
224
+ * await reader.registerParquet(salesBlob, 'sales');
225
+ * await reader.registerParquet(customersBlob, 'customers');
226
+ * const result = await reader.query('SELECT * FROM sales JOIN customers ON sales.customer_id = customers.id');
174
227
  * ```
175
228
  */
176
- async registerParquet(blob) {
177
- const conn = await getConnection();
178
- const db = await initializeDuckDB();
179
- const arrayBuffer = await blob.arrayBuffer();
180
- const uint8Array = new Uint8Array(arrayBuffer);
181
- await db.registerFileBuffer(`${this.tableName}.parquet`, uint8Array);
182
- try {
183
- await conn.query(`DROP TABLE IF EXISTS ${this.tableName}`);
184
- } catch (e) {}
185
- await conn.query(`CREATE TABLE ${this.tableName} AS SELECT * FROM '${this.tableName}.parquet'`);
186
- this.registered = true;
229
+ async registerParquet(blob, tableName) {
230
+ const internalTableName = this.generateInternalTableName(tableName);
231
+ await enqueueOperation(async () => {
232
+ const conn = await getConnection();
233
+ const db = await initializeDuckDB();
234
+ const arrayBuffer = await blob.arrayBuffer();
235
+ const uint8Array = new Uint8Array(arrayBuffer);
236
+ await db.registerFileBuffer(`${internalTableName}.parquet`, uint8Array);
237
+ try {
238
+ await conn.query(`DROP TABLE IF EXISTS ${internalTableName}`);
239
+ } catch {}
240
+ await conn.query(`CREATE TABLE ${internalTableName} AS SELECT * FROM '${internalTableName}.parquet'`);
241
+ this.tableMap.set(tableName, internalTableName);
242
+ });
243
+ }
244
+ /**
245
+ * Register multiple Parquet files at once
246
+ *
247
+ * @param files - Array of objects containing blob and tableName
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * const reader = new ParquetReader();
252
+ * await reader.registerMultipleParquet([
253
+ * { blob: salesBlob, tableName: 'sales' },
254
+ * { blob: customersBlob, tableName: 'customers' },
255
+ * { blob: productsBlob, tableName: 'products' }
256
+ * ]);
257
+ * const result = await reader.query('SELECT * FROM sales JOIN customers ON sales.customer_id = customers.id');
258
+ * ```
259
+ */
260
+ async registerMultipleParquet(files) {
261
+ for (const file of files) await this.registerParquet(file.blob, file.tableName);
187
262
  }
188
263
  /**
189
264
  * Execute a SQL query against the registered Parquet data
@@ -197,107 +272,129 @@ var ParquetReader = class {
197
272
  * console.log(result.columns);
198
273
  * console.log(result.rows);
199
274
  * ```
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * // Query multiple tables
279
+ * await reader.registerParquet(salesBlob, 'sales');
280
+ * await reader.registerParquet(customersBlob, 'customers');
281
+ * const result = await reader.query(`
282
+ * SELECT s.*, c.name
283
+ * FROM sales s
284
+ * JOIN customers c ON s.customer_id = c.id
285
+ * `);
286
+ * ```
200
287
  */
201
288
  async query(sql) {
202
- if (!this.registered) throw new Error("Parquet file not registered. Call registerParquet() first.");
203
- const result = await (await getConnection()).query(sql);
204
- const columns = result.schema.fields.map((field) => field.name);
205
- const rows = [];
206
- for (let i = 0; i < result.numRows; i++) {
207
- const row = [];
208
- for (let j = 0; j < result.numCols; j++) {
209
- const col = result.getChildAt(j);
210
- row.push(col?.get(i));
289
+ if (this.tableMap.size === 0) throw new Error("No Parquet files registered. Call registerParquet() first.");
290
+ return enqueueOperation(async () => {
291
+ const conn = await getConnection();
292
+ let rewrittenSql = sql;
293
+ for (const [userTableName, internalTableName] of this.tableMap.entries()) rewrittenSql = rewrittenSql.replace(new RegExp(`\\b${userTableName}\\b`, "g"), internalTableName);
294
+ const result = await conn.query(rewrittenSql);
295
+ const columns = result.schema.fields.map((field) => field.name);
296
+ const rows = [];
297
+ for (let i = 0; i < result.numRows; i++) {
298
+ const row = [];
299
+ for (let j = 0; j < result.numCols; j++) {
300
+ const col = result.getChildAt(j);
301
+ row.push(col?.get(i));
302
+ }
303
+ rows.push(row);
211
304
  }
212
- rows.push(row);
213
- }
214
- return {
215
- columns,
216
- rows,
217
- rowCount: result.numRows
218
- };
305
+ return {
306
+ columns,
307
+ rows,
308
+ rowCount: result.numRows
309
+ };
310
+ });
219
311
  }
220
312
  /**
221
- * Get all data from the registered table
313
+ * Get all data from a registered table
222
314
  *
315
+ * @param tableName - Name of the table to query
223
316
  * @param limit - Maximum number of rows to return (default: all)
224
317
  * @returns Query result
225
318
  *
226
319
  * @example
227
320
  * ```typescript
228
- * const allData = await reader.getAll();
229
- * const first100 = await reader.getAll(100);
321
+ * const allData = await reader.getAll('sales');
322
+ * const first100 = await reader.getAll('sales', 100);
230
323
  * ```
231
324
  */
232
- async getAll(limit) {
325
+ async getAll(tableName, limit) {
233
326
  const limitClause = limit ? ` LIMIT ${limit}` : "";
234
- return this.query(`SELECT * FROM ${this.tableName}${limitClause}`);
327
+ return this.query(`SELECT * FROM ${tableName}${limitClause}`);
235
328
  }
236
329
  /**
237
330
  * Get table schema information
238
331
  *
332
+ * @param tableName - Name of the table to describe
239
333
  * @returns Schema information
240
334
  *
241
335
  * @example
242
336
  * ```typescript
243
- * const schema = await reader.getSchema();
337
+ * const schema = await reader.getSchema('sales');
244
338
  * console.log(schema.columns); // ['id', 'name', 'sales']
245
339
  * console.log(schema.rows); // [['id', 'INTEGER'], ['name', 'VARCHAR'], ...]
246
340
  * ```
247
341
  */
248
- async getSchema() {
249
- return this.query(`DESCRIBE ${this.tableName}`);
342
+ async getSchema(tableName) {
343
+ return this.query(`DESCRIBE ${tableName}`);
250
344
  }
251
345
  /**
252
- * Get row count
346
+ * Get row count for a table
253
347
  *
348
+ * @param tableName - Name of the table to count
254
349
  * @returns Number of rows in the table
255
350
  *
256
351
  * @example
257
352
  * ```typescript
258
- * const count = await reader.count();
353
+ * const count = await reader.count('sales');
259
354
  * console.log(`Total rows: ${count}`);
260
355
  * ```
261
356
  */
262
- async count() {
263
- return (await this.query(`SELECT COUNT(*) as count FROM ${this.tableName}`)).rows[0][0];
357
+ async count(tableName) {
358
+ return (await this.query(`SELECT COUNT(*) as count FROM ${tableName}`)).rows[0][0];
264
359
  }
265
360
  /**
266
- * Close and cleanup resources
361
+ * Close and cleanup all registered resources
267
362
  */
268
363
  async close() {
269
- if (this.registered) {
364
+ if (this.tableMap.size > 0) await enqueueOperation(async () => {
270
365
  const conn = await getConnection();
271
366
  const db = await initializeDuckDB();
272
- try {
273
- await conn.query(`DROP TABLE IF EXISTS ${this.tableName}`);
274
- } catch (e) {}
275
- try {
276
- await db.dropFile(`${this.tableName}.parquet`);
277
- } catch (e) {}
278
- this.registered = false;
279
- }
367
+ for (const [, internalTableName] of this.tableMap.entries()) {
368
+ try {
369
+ await conn.query(`DROP TABLE IF EXISTS ${internalTableName}`);
370
+ } catch {}
371
+ try {
372
+ await db.dropFile(`${internalTableName}.parquet`);
373
+ } catch {}
374
+ }
375
+ this.tableMap.clear();
376
+ });
280
377
  }
281
378
  };
282
379
  /**
283
380
  * Helper function to quickly read a Parquet blob and execute a query
284
381
  *
285
382
  * @param blob - Parquet file as Blob
286
- * @param sql - SQL query to execute (optional, defaults to SELECT *)
383
+ * @param tableName - Name to use for the table in queries (default: 'data')
384
+ * @param sql - SQL query to execute (optional, defaults to SELECT * FROM tableName)
287
385
  * @returns Query result
288
386
  *
289
387
  * @example
290
388
  * ```typescript
291
389
  * const blob = await client.getFile('data/sales.parquet');
292
- * const result = await queryParquet(blob, 'SELECT product, SUM(amount) as total FROM data GROUP BY product');
390
+ * const result = await queryParquet(blob, 'sales', 'SELECT product, SUM(amount) as total FROM sales GROUP BY product');
293
391
  * console.log(result);
294
392
  * ```
295
393
  */
296
- async function queryParquet(blob, sql) {
297
- const uniqueId = `temp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
298
- const reader = new ParquetReader(uniqueId);
299
- await reader.registerParquet(blob);
300
- const query = sql || `SELECT * FROM ${uniqueId}`;
394
+ async function queryParquet(blob, tableName = "data", sql) {
395
+ const reader = new ParquetReader();
396
+ await reader.registerParquet(blob, tableName);
397
+ const query = sql || `SELECT * FROM ${tableName}`;
301
398
  const result = await reader.query(query);
302
399
  await reader.close();
303
400
  return result;
@@ -306,20 +403,21 @@ async function queryParquet(blob, sql) {
306
403
  * Helper function to read Parquet file and get all data
307
404
  *
308
405
  * @param blob - Parquet file as Blob
406
+ * @param tableName - Name to use for the table (default: 'data')
309
407
  * @param limit - Maximum number of rows (optional)
310
408
  * @returns Query result
311
409
  *
312
410
  * @example
313
411
  * ```typescript
314
412
  * const blob = await client.getFile('data/sales.parquet');
315
- * const data = await readParquet(blob, 1000);
413
+ * const data = await readParquet(blob, 'sales', 1000);
316
414
  * console.log(`Loaded ${data.rowCount} rows`);
317
415
  * ```
318
416
  */
319
- async function readParquet(blob, limit) {
320
- const reader = new ParquetReader(`temp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`);
321
- await reader.registerParquet(blob);
322
- const result = await reader.getAll(limit);
417
+ async function readParquet(blob, tableName = "data", limit) {
418
+ const reader = new ParquetReader();
419
+ await reader.registerParquet(blob, tableName);
420
+ const result = await reader.getAll(tableName, limit);
323
421
  await reader.close();
324
422
  return result;
325
423
  }
@@ -398,7 +496,7 @@ var OxyClient = class OxyClient {
398
496
  return response.json();
399
497
  } catch (error) {
400
498
  clearTimeout(timeoutId);
401
- if (error.name === "AbortError") throw new Error(`Request timeout after ${this.config.timeout || 3e4}ms`);
499
+ if (error instanceof Error && error.name === "AbortError") throw new Error(`Request timeout after ${this.config.timeout || 3e4}ms`);
402
500
  throw error;
403
501
  }
404
502
  }
@@ -519,7 +617,7 @@ var OxyClient = class OxyClient {
519
617
  async getFile(filePath) {
520
618
  const pathb64 = this.encodePathBase64(filePath);
521
619
  const query = this.buildQueryParams();
522
- return this.request(`/${this.config.projectId}/app/file/${pathb64}${query}`, { headers: { "Accept": "application/octet-stream" } });
620
+ return this.request(`/${this.config.projectId}/app/file/${pathb64}${query}`, { headers: { Accept: "application/octet-stream" } });
523
621
  }
524
622
  /**
525
623
  * Gets a file URL for direct browser access
@@ -559,7 +657,7 @@ var OxyClient = class OxyClient {
559
657
  * ```
560
658
  */
561
659
  async getTableData(filePath, limit = 100) {
562
- const result = await readParquet(await this.getFile(filePath), limit);
660
+ const result = await readParquet(await this.getFile(filePath), "data", limit);
563
661
  return {
564
662
  columns: result.columns,
565
663
  rows: result.rows,
@@ -568,8 +666,405 @@ var OxyClient = class OxyClient {
568
666
  }
569
667
  };
570
668
 
669
+ //#endregion
670
+ //#region src/sdk.ts
671
+ /**
672
+ * OxySDK provides a unified interface for fetching data from Oxy and querying it with SQL.
673
+ * It combines OxyClient (for API calls) and ParquetReader (for SQL queries) into a single,
674
+ * easy-to-use interface.
675
+ *
676
+ * @example
677
+ * ```typescript
678
+ * // Create SDK instance
679
+ * const sdk = new OxySDK({ apiKey: 'your-key', projectId: 'your-project' });
680
+ *
681
+ * // Load a parquet file and query it
682
+ * await sdk.loadFile('data/sales.parquet', 'sales');
683
+ * const result = await sdk.query('SELECT * FROM sales WHERE amount > 1000');
684
+ * console.log(result.rows);
685
+ *
686
+ * // Clean up when done
687
+ * await sdk.close();
688
+ * ```
689
+ */
690
+ var OxySDK = class OxySDK {
691
+ constructor(config) {
692
+ this.client = new OxyClient(config);
693
+ this.reader = new ParquetReader();
694
+ }
695
+ /**
696
+ * Creates an OxySDK instance asynchronously with support for postMessage authentication
697
+ *
698
+ * @param config - Optional configuration overrides
699
+ * @returns Promise resolving to OxySDK instance
700
+ *
701
+ * @example
702
+ * ```typescript
703
+ * // In an iframe - automatic postMessage auth
704
+ * const sdk = await OxySDK.create({
705
+ * parentOrigin: 'https://app.example.com',
706
+ * projectId: 'my-project-id'
707
+ * });
708
+ * ```
709
+ */
710
+ static async create(config) {
711
+ return new OxySDK(await createConfigAsync(config));
712
+ }
713
+ /**
714
+ * Load a Parquet file from Oxy and register it for SQL queries
715
+ *
716
+ * @param filePath - Path to the parquet file in the app state directory
717
+ * @param tableName - Name to use for the table in SQL queries
718
+ *
719
+ * @example
720
+ * ```typescript
721
+ * await sdk.loadFile('data/sales.parquet', 'sales');
722
+ * await sdk.loadFile('data/customers.parquet', 'customers');
723
+ *
724
+ * const result = await sdk.query(`
725
+ * SELECT s.*, c.name
726
+ * FROM sales s
727
+ * JOIN customers c ON s.customer_id = c.id
728
+ * `);
729
+ * ```
730
+ */
731
+ async loadFile(filePath, tableName) {
732
+ const blob = await this.client.getFile(filePath);
733
+ await this.reader.registerParquet(blob, tableName);
734
+ }
735
+ /**
736
+ * Load multiple Parquet files at once
737
+ *
738
+ * @param files - Array of file paths and table names
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * await sdk.loadFiles([
743
+ * { filePath: 'data/sales.parquet', tableName: 'sales' },
744
+ * { filePath: 'data/customers.parquet', tableName: 'customers' },
745
+ * { filePath: 'data/products.parquet', tableName: 'products' }
746
+ * ]);
747
+ *
748
+ * const result = await sdk.query('SELECT * FROM sales');
749
+ * ```
750
+ */
751
+ async loadFiles(files) {
752
+ for (const file of files) await this.loadFile(file.filePath, file.tableName);
753
+ }
754
+ /**
755
+ * Load all data from an app's data container
756
+ *
757
+ * This fetches the app's data and registers all parquet files using their container keys as table names.
758
+ *
759
+ * @param appPath - Path to the app file
760
+ * @returns DataContainer with file references
761
+ *
762
+ * @example
763
+ * ```typescript
764
+ * // If app has data: { sales: { file_path: 'data/sales.parquet' } }
765
+ * const data = await sdk.loadAppData('dashboard.app.yml');
766
+ * // Now you can query the 'sales' table
767
+ * const result = await sdk.query('SELECT * FROM sales LIMIT 10');
768
+ * ```
769
+ */
770
+ async loadAppData(appPath) {
771
+ const appDataResponse = await this.client.getAppData(appPath);
772
+ if (appDataResponse.error) throw new Error(`Failed to load app data: ${appDataResponse.error}`);
773
+ if (!appDataResponse.data) return null;
774
+ const loadPromises = Object.entries(appDataResponse.data).map(async ([tableName, fileRef]) => {
775
+ await this.loadFile(fileRef.file_path, tableName);
776
+ });
777
+ await Promise.all(loadPromises);
778
+ return appDataResponse.data;
779
+ }
780
+ /**
781
+ * Execute a SQL query against loaded data
782
+ *
783
+ * @param sql - SQL query to execute
784
+ * @returns Query result with columns and rows
785
+ *
786
+ * @example
787
+ * ```typescript
788
+ * await sdk.loadFile('data/sales.parquet', 'sales');
789
+ *
790
+ * const result = await sdk.query('SELECT product, SUM(amount) as total FROM sales GROUP BY product');
791
+ * console.log(result.columns); // ['product', 'total']
792
+ * console.log(result.rows); // [['Product A', 1000], ['Product B', 2000]]
793
+ * console.log(result.rowCount); // 2
794
+ * ```
795
+ */
796
+ async query(sql) {
797
+ return this.reader.query(sql);
798
+ }
799
+ /**
800
+ * Get all data from a loaded table
801
+ *
802
+ * @param tableName - Name of the table
803
+ * @param limit - Maximum number of rows (optional)
804
+ * @returns Query result
805
+ *
806
+ * @example
807
+ * ```typescript
808
+ * await sdk.loadFile('data/sales.parquet', 'sales');
809
+ * const allData = await sdk.getAll('sales');
810
+ * const first100 = await sdk.getAll('sales', 100);
811
+ * ```
812
+ */
813
+ async getAll(tableName, limit) {
814
+ return this.reader.getAll(tableName, limit);
815
+ }
816
+ /**
817
+ * Get schema information for a loaded table
818
+ *
819
+ * @param tableName - Name of the table
820
+ * @returns Schema information
821
+ *
822
+ * @example
823
+ * ```typescript
824
+ * await sdk.loadFile('data/sales.parquet', 'sales');
825
+ * const schema = await sdk.getSchema('sales');
826
+ * console.log(schema.columns); // ['column_name', 'column_type', ...]
827
+ * console.log(schema.rows); // [['id', 'INTEGER'], ['name', 'VARCHAR'], ...]
828
+ * ```
829
+ */
830
+ async getSchema(tableName) {
831
+ return this.reader.getSchema(tableName);
832
+ }
833
+ /**
834
+ * Get row count for a loaded table
835
+ *
836
+ * @param tableName - Name of the table
837
+ * @returns Number of rows
838
+ *
839
+ * @example
840
+ * ```typescript
841
+ * await sdk.loadFile('data/sales.parquet', 'sales');
842
+ * const count = await sdk.count('sales');
843
+ * console.log(`Total rows: ${count}`);
844
+ * ```
845
+ */
846
+ async count(tableName) {
847
+ return this.reader.count(tableName);
848
+ }
849
+ /**
850
+ * Get direct access to the underlying OxyClient
851
+ *
852
+ * Useful for advanced operations like listing apps, getting displays, etc.
853
+ *
854
+ * @returns The OxyClient instance
855
+ *
856
+ * @example
857
+ * ```typescript
858
+ * const apps = await sdk.getClient().listApps();
859
+ * const displays = await sdk.getClient().getDisplays('my-app.app.yml');
860
+ * ```
861
+ */
862
+ getClient() {
863
+ return this.client;
864
+ }
865
+ /**
866
+ * Get direct access to the underlying ParquetReader
867
+ *
868
+ * Useful for advanced operations like registering blobs directly.
869
+ *
870
+ * @returns The ParquetReader instance
871
+ *
872
+ * @example
873
+ * ```typescript
874
+ * const myBlob = new Blob([parquetData]);
875
+ * await sdk.getReader().registerParquet(myBlob, 'mydata');
876
+ * ```
877
+ */
878
+ getReader() {
879
+ return this.reader;
880
+ }
881
+ /**
882
+ * Close and cleanup all resources
883
+ *
884
+ * This clears all loaded data and releases resources. Call this when you're done with the SDK.
885
+ *
886
+ * @example
887
+ * ```typescript
888
+ * const sdk = new OxySDK({ apiKey: 'key', projectId: 'project' });
889
+ * await sdk.loadFile('data/sales.parquet', 'sales');
890
+ * const result = await sdk.query('SELECT * FROM sales');
891
+ * await sdk.close(); // Clean up
892
+ * ```
893
+ */
894
+ async close() {
895
+ await this.reader.close();
896
+ }
897
+ };
898
+
899
+ //#endregion
900
+ //#region src/react.tsx
901
+ /**
902
+ * React context for OxySDK
903
+ */
904
+ const OxyContext = (0, react.createContext)(void 0);
905
+ /**
906
+ * Provider component that initializes and provides OxySDK to child components
907
+ *
908
+ * @example
909
+ * ```tsx
910
+ * // Synchronous initialization with config
911
+ * function App() {
912
+ * return (
913
+ * <OxyProvider config={{
914
+ * apiKey: 'your-key',
915
+ * projectId: 'your-project',
916
+ * baseUrl: 'https://api.oxy.tech'
917
+ * }}>
918
+ * <Dashboard />
919
+ * </OxyProvider>
920
+ * );
921
+ * }
922
+ * ```
923
+ *
924
+ * @example
925
+ * ```tsx
926
+ * // Async initialization (for iframe/postMessage auth)
927
+ * function App() {
928
+ * return (
929
+ * <OxyProvider
930
+ * useAsync
931
+ * config={{ parentOrigin: 'https://app.example.com' }}
932
+ * >
933
+ * <Dashboard />
934
+ * </OxyProvider>
935
+ * );
936
+ * }
937
+ * ```
938
+ *
939
+ * @example
940
+ * ```tsx
941
+ * // With environment variables
942
+ * import { createConfig } from '@oxy/sdk';
943
+ *
944
+ * function App() {
945
+ * return (
946
+ * <OxyProvider config={createConfig()}>
947
+ * <Dashboard />
948
+ * </OxyProvider>
949
+ * );
950
+ * }
951
+ * ```
952
+ */
953
+ function OxyProvider({ children, config, useAsync = false, onReady, onError }) {
954
+ const [sdk, setSdk] = (0, react.useState)(null);
955
+ const [isLoading, setIsLoading] = (0, react.useState)(true);
956
+ const [error, setError] = (0, react.useState)(null);
957
+ (0, react.useEffect)(() => {
958
+ let mounted = true;
959
+ let sdkInstance = null;
960
+ async function initializeSDK() {
961
+ try {
962
+ setIsLoading(true);
963
+ setError(null);
964
+ if (useAsync) sdkInstance = await OxySDK.create(config);
965
+ else {
966
+ if (!config) throw new Error("Config is required when useAsync is false. Either provide config or set useAsync=true.");
967
+ sdkInstance = new OxySDK(config);
968
+ }
969
+ if (mounted) {
970
+ setSdk(sdkInstance);
971
+ setIsLoading(false);
972
+ onReady?.(sdkInstance);
973
+ }
974
+ } catch (err) {
975
+ const error$1 = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to initialize SDK");
976
+ if (mounted) {
977
+ setError(error$1);
978
+ setIsLoading(false);
979
+ onError?.(error$1);
980
+ }
981
+ }
982
+ }
983
+ initializeSDK();
984
+ return () => {
985
+ mounted = false;
986
+ if (sdkInstance) sdkInstance.close().catch(console.error);
987
+ };
988
+ }, [
989
+ config,
990
+ useAsync,
991
+ onReady,
992
+ onError
993
+ ]);
994
+ return /* @__PURE__ */ react.default.createElement(OxyContext.Provider, { value: {
995
+ sdk,
996
+ isLoading,
997
+ error
998
+ } }, children);
999
+ }
1000
+ /**
1001
+ * Hook to access OxySDK from child components
1002
+ *
1003
+ * @throws {Error} If used outside of OxyProvider
1004
+ * @returns {OxyContextValue} The SDK instance, loading state, and error
1005
+ *
1006
+ * @example
1007
+ * ```tsx
1008
+ * function Dashboard() {
1009
+ * const { sdk, isLoading, error } = useOxy();
1010
+ *
1011
+ * useEffect(() => {
1012
+ * if (sdk) {
1013
+ * sdk.loadAppData('dashboard.app.yml')
1014
+ * .then(() => sdk.query('SELECT * FROM my_table'))
1015
+ * .then(result => console.log(result));
1016
+ * }
1017
+ * }, [sdk]);
1018
+ *
1019
+ * if (isLoading) return <div>Loading SDK...</div>;
1020
+ * if (error) return <div>Error: {error.message}</div>;
1021
+ * if (!sdk) return null;
1022
+ *
1023
+ * return <div>Dashboard</div>;
1024
+ * }
1025
+ * ```
1026
+ */
1027
+ function useOxy() {
1028
+ const context = (0, react.useContext)(OxyContext);
1029
+ if (context === void 0) throw new Error("useOxy must be used within an OxyProvider");
1030
+ return context;
1031
+ }
1032
+ /**
1033
+ * Hook to access OxySDK that throws if not ready
1034
+ *
1035
+ * This is a convenience hook that returns the SDK directly or throws an error if not initialized.
1036
+ * Use this when you know the SDK should be ready.
1037
+ *
1038
+ * @throws {Error} If used outside of OxyProvider or if SDK is not initialized
1039
+ * @returns {OxySDK} The SDK instance
1040
+ *
1041
+ * @example
1042
+ * ```tsx
1043
+ * function DataTable() {
1044
+ * const sdk = useOxySDK();
1045
+ * const [data, setData] = useState(null);
1046
+ *
1047
+ * useEffect(() => {
1048
+ * sdk.loadFile('data.parquet', 'data')
1049
+ * .then(() => sdk.query('SELECT * FROM data LIMIT 100'))
1050
+ * .then(setData);
1051
+ * }, [sdk]);
1052
+ *
1053
+ * return <table>...</table>;
1054
+ * }
1055
+ * ```
1056
+ */
1057
+ function useOxySDK() {
1058
+ const { sdk, isLoading, error } = useOxy();
1059
+ if (error) throw error;
1060
+ if (isLoading || !sdk) throw new Error("OxySDK is not yet initialized");
1061
+ return sdk;
1062
+ }
1063
+
571
1064
  //#endregion
572
1065
  exports.OxyClient = OxyClient;
1066
+ exports.OxyProvider = OxyProvider;
1067
+ exports.OxySDK = OxySDK;
573
1068
  exports.ParquetReader = ParquetReader;
574
1069
  exports.PostMessageAuthInvalidOriginError = require_postMessage.PostMessageAuthInvalidOriginError;
575
1070
  exports.PostMessageAuthInvalidResponseError = require_postMessage.PostMessageAuthInvalidResponseError;
@@ -582,4 +1077,6 @@ exports.isInIframe = require_postMessage.isInIframe;
582
1077
  exports.queryParquet = queryParquet;
583
1078
  exports.readParquet = readParquet;
584
1079
  exports.requestAuthFromParent = require_postMessage.requestAuthFromParent;
1080
+ exports.useOxy = useOxy;
1081
+ exports.useOxySDK = useOxySDK;
585
1082
  //# sourceMappingURL=index.cjs.map