@oxy-hq/sdk 0.1.6 → 0.2.1

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.mjs CHANGED
@@ -1,9 +1,21 @@
1
1
  // @oxy/sdk - TypeScript SDK for Oxy data platform
2
2
  import { a as PostMessageAuthInvalidOriginError, c as PostMessageAuthTimeoutError, n as isInIframe, o as PostMessageAuthInvalidResponseError, r as requestAuthFromParent, s as PostMessageAuthNotInIframeError } from "./postMessage-BxdgtX8j.mjs";
3
3
  import * as duckdb from "@duckdb/duckdb-wasm";
4
+ import React, { createContext, useContext, useEffect, useState } from "react";
4
5
 
5
6
  //#region src/config.ts
6
7
  /**
8
+ * Safely get environment variable in both Node.js and browser environments
9
+ */
10
+ function getEnvVar(name) {
11
+ if (typeof process !== "undefined" && process.env) return process.env[name];
12
+ if (typeof import.meta !== "undefined" && import.meta.env) {
13
+ const viteValue = import.meta.env[`VITE_${name}`];
14
+ if (viteValue !== void 0) return viteValue;
15
+ return import.meta.env[name];
16
+ }
17
+ }
18
+ /**
7
19
  * Creates an Oxy configuration from environment variables
8
20
  *
9
21
  * Environment variables:
@@ -17,16 +29,16 @@ import * as duckdb from "@duckdb/duckdb-wasm";
17
29
  * @throws Error if required environment variables are missing
18
30
  */
19
31
  function createConfig(overrides) {
20
- const baseUrl = overrides?.baseUrl || process.env.OXY_URL;
21
- const apiKey = overrides?.apiKey || process.env.OXY_API_KEY;
22
- const projectId = overrides?.projectId || process.env.OXY_PROJECT_ID;
32
+ const baseUrl = overrides?.baseUrl || getEnvVar("OXY_URL");
33
+ const apiKey = overrides?.apiKey || getEnvVar("OXY_API_KEY");
34
+ const projectId = overrides?.projectId || getEnvVar("OXY_PROJECT_ID");
23
35
  if (!baseUrl) throw new Error("OXY_URL environment variable or baseUrl config is required");
24
36
  if (!projectId) throw new Error("OXY_PROJECT_ID environment variable or projectId config is required");
25
37
  return {
26
38
  baseUrl: baseUrl.replace(/\/$/, ""),
27
39
  apiKey,
28
40
  projectId,
29
- branch: overrides?.branch || process.env.OXY_BRANCH,
41
+ branch: overrides?.branch || getEnvVar("OXY_BRANCH"),
30
42
  timeout: overrides?.timeout || 3e4,
31
43
  parentOrigin: overrides?.parentOrigin,
32
44
  disableAutoAuth: overrides?.disableAutoAuth
@@ -67,9 +79,9 @@ function createConfig(overrides) {
67
79
  */
68
80
  async function createConfigAsync(overrides) {
69
81
  const { isInIframe: isInIframe$1 } = await import("./postMessage-D5wWgwcO.mjs");
70
- let baseUrl = overrides?.baseUrl || process.env.OXY_URL;
71
- let apiKey = overrides?.apiKey || process.env.OXY_API_KEY;
72
- let projectId = overrides?.projectId || process.env.OXY_PROJECT_ID;
82
+ let baseUrl = overrides?.baseUrl || getEnvVar("OXY_URL");
83
+ let apiKey = overrides?.apiKey || getEnvVar("OXY_API_KEY");
84
+ let projectId = overrides?.projectId || getEnvVar("OXY_PROJECT_ID");
73
85
  const disableAutoAuth = overrides?.disableAutoAuth ?? false;
74
86
  const parentOrigin = overrides?.parentOrigin;
75
87
  if (!disableAutoAuth && isInIframe$1() && !apiKey) if (!parentOrigin) logWarningAboutMissingParentOrigin();
@@ -106,7 +118,7 @@ function createFinalConfig(baseUrl, apiKey, projectId, overrides) {
106
118
  baseUrl: baseUrl.replace(/\/$/, ""),
107
119
  apiKey,
108
120
  projectId,
109
- branch: overrides?.branch || process.env.OXY_BRANCH,
121
+ branch: overrides?.branch || getEnvVar("OXY_BRANCH"),
110
122
  timeout: overrides?.timeout || 3e4,
111
123
  parentOrigin: overrides?.parentOrigin,
112
124
  disableAutoAuth: overrides?.disableAutoAuth
@@ -150,41 +162,76 @@ async function getConnection() {
150
162
  return connection;
151
163
  }
152
164
  /**
153
- * ParquetReader provides methods to read and query Parquet files
165
+ * ParquetReader provides methods to read and query Parquet files.
166
+ * Supports registering multiple Parquet files with different table names.
154
167
  */
155
168
  var ParquetReader = class {
156
- constructor(tableName = "data") {
157
- this.registered = false;
158
- this.tableName = tableName;
159
- this.internalTableName = `${tableName}_${`${Date.now()}_${Math.random().toString(36).substring(2, 9)}`}`;
169
+ constructor() {
170
+ this.tableMap = /* @__PURE__ */ new Map();
171
+ }
172
+ /**
173
+ * Generate a unique internal table name to prevent conflicts
174
+ */
175
+ generateInternalTableName(tableName) {
176
+ return `${tableName}_${`${Date.now()}_${Math.random().toString(36).substring(2, 9)}`}`;
160
177
  }
161
178
  /**
162
- * Register a Parquet file from a Blob
179
+ * Register a Parquet file from a Blob with a specific table name
163
180
  *
164
181
  * @param blob - Parquet file as Blob
182
+ * @param tableName - Name to use for the table in queries (required)
165
183
  *
166
184
  * @example
167
185
  * ```typescript
168
186
  * const blob = await client.getFile('data/sales.parquet');
169
- * const reader = new ParquetReader('sales');
170
- * await reader.registerParquet(blob);
187
+ * const reader = new ParquetReader();
188
+ * await reader.registerParquet(blob, 'sales');
189
+ * ```
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * // Register multiple files
194
+ * const reader = new ParquetReader();
195
+ * await reader.registerParquet(salesBlob, 'sales');
196
+ * await reader.registerParquet(customersBlob, 'customers');
197
+ * const result = await reader.query('SELECT * FROM sales JOIN customers ON sales.customer_id = customers.id');
171
198
  * ```
172
199
  */
173
- async registerParquet(blob) {
200
+ async registerParquet(blob, tableName) {
201
+ const internalTableName = this.generateInternalTableName(tableName);
174
202
  await enqueueOperation(async () => {
175
203
  const conn = await getConnection();
176
204
  const db = await initializeDuckDB();
177
205
  const arrayBuffer = await blob.arrayBuffer();
178
206
  const uint8Array = new Uint8Array(arrayBuffer);
179
- await db.registerFileBuffer(`${this.internalTableName}.parquet`, uint8Array);
207
+ await db.registerFileBuffer(`${internalTableName}.parquet`, uint8Array);
180
208
  try {
181
- await conn.query(`DROP TABLE IF EXISTS ${this.internalTableName}`);
209
+ await conn.query(`DROP TABLE IF EXISTS ${internalTableName}`);
182
210
  } catch {}
183
- await conn.query(`CREATE TABLE ${this.internalTableName} AS SELECT * FROM '${this.internalTableName}.parquet'`);
184
- this.registered = true;
211
+ await conn.query(`CREATE TABLE ${internalTableName} AS SELECT * FROM '${internalTableName}.parquet'`);
212
+ this.tableMap.set(tableName, internalTableName);
185
213
  });
186
214
  }
187
215
  /**
216
+ * Register multiple Parquet files at once
217
+ *
218
+ * @param files - Array of objects containing blob and tableName
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * const reader = new ParquetReader();
223
+ * await reader.registerMultipleParquet([
224
+ * { blob: salesBlob, tableName: 'sales' },
225
+ * { blob: customersBlob, tableName: 'customers' },
226
+ * { blob: productsBlob, tableName: 'products' }
227
+ * ]);
228
+ * const result = await reader.query('SELECT * FROM sales JOIN customers ON sales.customer_id = customers.id');
229
+ * ```
230
+ */
231
+ async registerMultipleParquet(files) {
232
+ for (const file of files) await this.registerParquet(file.blob, file.tableName);
233
+ }
234
+ /**
188
235
  * Execute a SQL query against the registered Parquet data
189
236
  *
190
237
  * @param sql - SQL query string
@@ -196,12 +243,25 @@ var ParquetReader = class {
196
243
  * console.log(result.columns);
197
244
  * console.log(result.rows);
198
245
  * ```
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * // Query multiple tables
250
+ * await reader.registerParquet(salesBlob, 'sales');
251
+ * await reader.registerParquet(customersBlob, 'customers');
252
+ * const result = await reader.query(`
253
+ * SELECT s.*, c.name
254
+ * FROM sales s
255
+ * JOIN customers c ON s.customer_id = c.id
256
+ * `);
257
+ * ```
199
258
  */
200
259
  async query(sql) {
201
- if (!this.registered) throw new Error("Parquet file not registered. Call registerParquet() first.");
260
+ if (this.tableMap.size === 0) throw new Error("No Parquet files registered. Call registerParquet() first.");
202
261
  return enqueueOperation(async () => {
203
262
  const conn = await getConnection();
204
- const rewrittenSql = sql.replace(new RegExp(`\\b${this.tableName}\\b`, "g"), this.internalTableName);
263
+ let rewrittenSql = sql;
264
+ for (const [userTableName, internalTableName] of this.tableMap.entries()) rewrittenSql = rewrittenSql.replace(new RegExp(`\\b${userTableName}\\b`, "g"), internalTableName);
205
265
  const result = await conn.query(rewrittenSql);
206
266
  const columns = result.schema.fields.map((field) => field.name);
207
267
  const rows = [];
@@ -221,64 +281,69 @@ var ParquetReader = class {
221
281
  });
222
282
  }
223
283
  /**
224
- * Get all data from the registered table
284
+ * Get all data from a registered table
225
285
  *
286
+ * @param tableName - Name of the table to query
226
287
  * @param limit - Maximum number of rows to return (default: all)
227
288
  * @returns Query result
228
289
  *
229
290
  * @example
230
291
  * ```typescript
231
- * const allData = await reader.getAll();
232
- * const first100 = await reader.getAll(100);
292
+ * const allData = await reader.getAll('sales');
293
+ * const first100 = await reader.getAll('sales', 100);
233
294
  * ```
234
295
  */
235
- async getAll(limit) {
296
+ async getAll(tableName, limit) {
236
297
  const limitClause = limit ? ` LIMIT ${limit}` : "";
237
- return this.query(`SELECT * FROM ${this.tableName}${limitClause}`);
298
+ return this.query(`SELECT * FROM ${tableName}${limitClause}`);
238
299
  }
239
300
  /**
240
301
  * Get table schema information
241
302
  *
303
+ * @param tableName - Name of the table to describe
242
304
  * @returns Schema information
243
305
  *
244
306
  * @example
245
307
  * ```typescript
246
- * const schema = await reader.getSchema();
308
+ * const schema = await reader.getSchema('sales');
247
309
  * console.log(schema.columns); // ['id', 'name', 'sales']
248
310
  * console.log(schema.rows); // [['id', 'INTEGER'], ['name', 'VARCHAR'], ...]
249
311
  * ```
250
312
  */
251
- async getSchema() {
252
- return this.query(`DESCRIBE ${this.tableName}`);
313
+ async getSchema(tableName) {
314
+ return this.query(`DESCRIBE ${tableName}`);
253
315
  }
254
316
  /**
255
- * Get row count
317
+ * Get row count for a table
256
318
  *
319
+ * @param tableName - Name of the table to count
257
320
  * @returns Number of rows in the table
258
321
  *
259
322
  * @example
260
323
  * ```typescript
261
- * const count = await reader.count();
324
+ * const count = await reader.count('sales');
262
325
  * console.log(`Total rows: ${count}`);
263
326
  * ```
264
327
  */
265
- async count() {
266
- return (await this.query(`SELECT COUNT(*) as count FROM ${this.tableName}`)).rows[0][0];
328
+ async count(tableName) {
329
+ return (await this.query(`SELECT COUNT(*) as count FROM ${tableName}`)).rows[0][0];
267
330
  }
268
331
  /**
269
- * Close and cleanup resources
332
+ * Close and cleanup all registered resources
270
333
  */
271
334
  async close() {
272
- if (this.registered) await enqueueOperation(async () => {
335
+ if (this.tableMap.size > 0) await enqueueOperation(async () => {
273
336
  const conn = await getConnection();
274
337
  const db = await initializeDuckDB();
275
- try {
276
- await conn.query(`DROP TABLE IF EXISTS ${this.internalTableName}`);
277
- } catch {}
278
- try {
279
- await db.dropFile(`${this.internalTableName}.parquet`);
280
- } catch {}
281
- this.registered = false;
338
+ for (const [, internalTableName] of this.tableMap.entries()) {
339
+ try {
340
+ await conn.query(`DROP TABLE IF EXISTS ${internalTableName}`);
341
+ } catch {}
342
+ try {
343
+ await db.dropFile(`${internalTableName}.parquet`);
344
+ } catch {}
345
+ }
346
+ this.tableMap.clear();
282
347
  });
283
348
  }
284
349
  };
@@ -286,21 +351,21 @@ var ParquetReader = class {
286
351
  * Helper function to quickly read a Parquet blob and execute a query
287
352
  *
288
353
  * @param blob - Parquet file as Blob
289
- * @param sql - SQL query to execute (optional, defaults to SELECT *)
354
+ * @param tableName - Name to use for the table in queries (default: 'data')
355
+ * @param sql - SQL query to execute (optional, defaults to SELECT * FROM tableName)
290
356
  * @returns Query result
291
357
  *
292
358
  * @example
293
359
  * ```typescript
294
360
  * const blob = await client.getFile('data/sales.parquet');
295
- * const result = await queryParquet(blob, 'SELECT product, SUM(amount) as total FROM data GROUP BY product');
361
+ * const result = await queryParquet(blob, 'sales', 'SELECT product, SUM(amount) as total FROM sales GROUP BY product');
296
362
  * console.log(result);
297
363
  * ```
298
364
  */
299
- async function queryParquet(blob, sql) {
300
- const uniqueId = `temp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
301
- const reader = new ParquetReader(uniqueId);
302
- await reader.registerParquet(blob);
303
- const query = sql || `SELECT * FROM ${uniqueId}`;
365
+ async function queryParquet(blob, tableName = "data", sql) {
366
+ const reader = new ParquetReader();
367
+ await reader.registerParquet(blob, tableName);
368
+ const query = sql || `SELECT * FROM ${tableName}`;
304
369
  const result = await reader.query(query);
305
370
  await reader.close();
306
371
  return result;
@@ -309,20 +374,21 @@ async function queryParquet(blob, sql) {
309
374
  * Helper function to read Parquet file and get all data
310
375
  *
311
376
  * @param blob - Parquet file as Blob
377
+ * @param tableName - Name to use for the table (default: 'data')
312
378
  * @param limit - Maximum number of rows (optional)
313
379
  * @returns Query result
314
380
  *
315
381
  * @example
316
382
  * ```typescript
317
383
  * const blob = await client.getFile('data/sales.parquet');
318
- * const data = await readParquet(blob, 1000);
384
+ * const data = await readParquet(blob, 'sales', 1000);
319
385
  * console.log(`Loaded ${data.rowCount} rows`);
320
386
  * ```
321
387
  */
322
- async function readParquet(blob, limit) {
323
- const reader = new ParquetReader(`temp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`);
324
- await reader.registerParquet(blob);
325
- const result = await reader.getAll(limit);
388
+ async function readParquet(blob, tableName = "data", limit) {
389
+ const reader = new ParquetReader();
390
+ await reader.registerParquet(blob, tableName);
391
+ const result = await reader.getAll(tableName, limit);
326
392
  await reader.close();
327
393
  return result;
328
394
  }
@@ -562,7 +628,7 @@ var OxyClient = class OxyClient {
562
628
  * ```
563
629
  */
564
630
  async getTableData(filePath, limit = 100) {
565
- const result = await readParquet(await this.getFile(filePath), limit);
631
+ const result = await readParquet(await this.getFile(filePath), "data", limit);
566
632
  return {
567
633
  columns: result.columns,
568
634
  rows: result.rows,
@@ -572,5 +638,400 @@ var OxyClient = class OxyClient {
572
638
  };
573
639
 
574
640
  //#endregion
575
- export { OxyClient, ParquetReader, PostMessageAuthInvalidOriginError, PostMessageAuthInvalidResponseError, PostMessageAuthNotInIframeError, PostMessageAuthTimeoutError, createConfig, createConfigAsync, initializeDuckDB, isInIframe, queryParquet, readParquet, requestAuthFromParent };
641
+ //#region src/sdk.ts
642
+ /**
643
+ * OxySDK provides a unified interface for fetching data from Oxy and querying it with SQL.
644
+ * It combines OxyClient (for API calls) and ParquetReader (for SQL queries) into a single,
645
+ * easy-to-use interface.
646
+ *
647
+ * @example
648
+ * ```typescript
649
+ * // Create SDK instance
650
+ * const sdk = new OxySDK({ apiKey: 'your-key', projectId: 'your-project' });
651
+ *
652
+ * // Load a parquet file and query it
653
+ * await sdk.loadFile('data/sales.parquet', 'sales');
654
+ * const result = await sdk.query('SELECT * FROM sales WHERE amount > 1000');
655
+ * console.log(result.rows);
656
+ *
657
+ * // Clean up when done
658
+ * await sdk.close();
659
+ * ```
660
+ */
661
+ var OxySDK = class OxySDK {
662
+ constructor(config) {
663
+ this.client = new OxyClient(config);
664
+ this.reader = new ParquetReader();
665
+ }
666
+ /**
667
+ * Creates an OxySDK instance asynchronously with support for postMessage authentication
668
+ *
669
+ * @param config - Optional configuration overrides
670
+ * @returns Promise resolving to OxySDK instance
671
+ *
672
+ * @example
673
+ * ```typescript
674
+ * // In an iframe - automatic postMessage auth
675
+ * const sdk = await OxySDK.create({
676
+ * parentOrigin: 'https://app.example.com',
677
+ * projectId: 'my-project-id'
678
+ * });
679
+ * ```
680
+ */
681
+ static async create(config) {
682
+ return new OxySDK(await createConfigAsync(config));
683
+ }
684
+ /**
685
+ * Load a Parquet file from Oxy and register it for SQL queries
686
+ *
687
+ * @param filePath - Path to the parquet file in the app state directory
688
+ * @param tableName - Name to use for the table in SQL queries
689
+ *
690
+ * @example
691
+ * ```typescript
692
+ * await sdk.loadFile('data/sales.parquet', 'sales');
693
+ * await sdk.loadFile('data/customers.parquet', 'customers');
694
+ *
695
+ * const result = await sdk.query(`
696
+ * SELECT s.*, c.name
697
+ * FROM sales s
698
+ * JOIN customers c ON s.customer_id = c.id
699
+ * `);
700
+ * ```
701
+ */
702
+ async loadFile(filePath, tableName) {
703
+ const blob = await this.client.getFile(filePath);
704
+ await this.reader.registerParquet(blob, tableName);
705
+ }
706
+ /**
707
+ * Load multiple Parquet files at once
708
+ *
709
+ * @param files - Array of file paths and table names
710
+ *
711
+ * @example
712
+ * ```typescript
713
+ * await sdk.loadFiles([
714
+ * { filePath: 'data/sales.parquet', tableName: 'sales' },
715
+ * { filePath: 'data/customers.parquet', tableName: 'customers' },
716
+ * { filePath: 'data/products.parquet', tableName: 'products' }
717
+ * ]);
718
+ *
719
+ * const result = await sdk.query('SELECT * FROM sales');
720
+ * ```
721
+ */
722
+ async loadFiles(files) {
723
+ for (const file of files) await this.loadFile(file.filePath, file.tableName);
724
+ }
725
+ /**
726
+ * Load all data from an app's data container
727
+ *
728
+ * This fetches the app's data and registers all parquet files using their container keys as table names.
729
+ *
730
+ * @param appPath - Path to the app file
731
+ * @returns DataContainer with file references
732
+ *
733
+ * @example
734
+ * ```typescript
735
+ * // If app has data: { sales: { file_path: 'data/sales.parquet' } }
736
+ * const data = await sdk.loadAppData('dashboard.app.yml');
737
+ * // Now you can query the 'sales' table
738
+ * const result = await sdk.query('SELECT * FROM sales LIMIT 10');
739
+ * ```
740
+ */
741
+ async loadAppData(appPath) {
742
+ const appDataResponse = await this.client.getAppData(appPath);
743
+ if (appDataResponse.error) throw new Error(`Failed to load app data: ${appDataResponse.error}`);
744
+ if (!appDataResponse.data) return null;
745
+ const loadPromises = Object.entries(appDataResponse.data).map(async ([tableName, fileRef]) => {
746
+ await this.loadFile(fileRef.file_path, tableName);
747
+ });
748
+ await Promise.all(loadPromises);
749
+ return appDataResponse.data;
750
+ }
751
+ /**
752
+ * Execute a SQL query against loaded data
753
+ *
754
+ * @param sql - SQL query to execute
755
+ * @returns Query result with columns and rows
756
+ *
757
+ * @example
758
+ * ```typescript
759
+ * await sdk.loadFile('data/sales.parquet', 'sales');
760
+ *
761
+ * const result = await sdk.query('SELECT product, SUM(amount) as total FROM sales GROUP BY product');
762
+ * console.log(result.columns); // ['product', 'total']
763
+ * console.log(result.rows); // [['Product A', 1000], ['Product B', 2000]]
764
+ * console.log(result.rowCount); // 2
765
+ * ```
766
+ */
767
+ async query(sql) {
768
+ return this.reader.query(sql);
769
+ }
770
+ /**
771
+ * Get all data from a loaded table
772
+ *
773
+ * @param tableName - Name of the table
774
+ * @param limit - Maximum number of rows (optional)
775
+ * @returns Query result
776
+ *
777
+ * @example
778
+ * ```typescript
779
+ * await sdk.loadFile('data/sales.parquet', 'sales');
780
+ * const allData = await sdk.getAll('sales');
781
+ * const first100 = await sdk.getAll('sales', 100);
782
+ * ```
783
+ */
784
+ async getAll(tableName, limit) {
785
+ return this.reader.getAll(tableName, limit);
786
+ }
787
+ /**
788
+ * Get schema information for a loaded table
789
+ *
790
+ * @param tableName - Name of the table
791
+ * @returns Schema information
792
+ *
793
+ * @example
794
+ * ```typescript
795
+ * await sdk.loadFile('data/sales.parquet', 'sales');
796
+ * const schema = await sdk.getSchema('sales');
797
+ * console.log(schema.columns); // ['column_name', 'column_type', ...]
798
+ * console.log(schema.rows); // [['id', 'INTEGER'], ['name', 'VARCHAR'], ...]
799
+ * ```
800
+ */
801
+ async getSchema(tableName) {
802
+ return this.reader.getSchema(tableName);
803
+ }
804
+ /**
805
+ * Get row count for a loaded table
806
+ *
807
+ * @param tableName - Name of the table
808
+ * @returns Number of rows
809
+ *
810
+ * @example
811
+ * ```typescript
812
+ * await sdk.loadFile('data/sales.parquet', 'sales');
813
+ * const count = await sdk.count('sales');
814
+ * console.log(`Total rows: ${count}`);
815
+ * ```
816
+ */
817
+ async count(tableName) {
818
+ return this.reader.count(tableName);
819
+ }
820
+ /**
821
+ * Get direct access to the underlying OxyClient
822
+ *
823
+ * Useful for advanced operations like listing apps, getting displays, etc.
824
+ *
825
+ * @returns The OxyClient instance
826
+ *
827
+ * @example
828
+ * ```typescript
829
+ * const apps = await sdk.getClient().listApps();
830
+ * const displays = await sdk.getClient().getDisplays('my-app.app.yml');
831
+ * ```
832
+ */
833
+ getClient() {
834
+ return this.client;
835
+ }
836
+ /**
837
+ * Get direct access to the underlying ParquetReader
838
+ *
839
+ * Useful for advanced operations like registering blobs directly.
840
+ *
841
+ * @returns The ParquetReader instance
842
+ *
843
+ * @example
844
+ * ```typescript
845
+ * const myBlob = new Blob([parquetData]);
846
+ * await sdk.getReader().registerParquet(myBlob, 'mydata');
847
+ * ```
848
+ */
849
+ getReader() {
850
+ return this.reader;
851
+ }
852
+ /**
853
+ * Close and cleanup all resources
854
+ *
855
+ * This clears all loaded data and releases resources. Call this when you're done with the SDK.
856
+ *
857
+ * @example
858
+ * ```typescript
859
+ * const sdk = new OxySDK({ apiKey: 'key', projectId: 'project' });
860
+ * await sdk.loadFile('data/sales.parquet', 'sales');
861
+ * const result = await sdk.query('SELECT * FROM sales');
862
+ * await sdk.close(); // Clean up
863
+ * ```
864
+ */
865
+ async close() {
866
+ await this.reader.close();
867
+ }
868
+ };
869
+
870
+ //#endregion
871
+ //#region src/react.tsx
872
+ /**
873
+ * React context for OxySDK
874
+ */
875
+ const OxyContext = createContext(void 0);
876
+ /**
877
+ * Provider component that initializes and provides OxySDK to child components
878
+ *
879
+ * @example
880
+ * ```tsx
881
+ * // Synchronous initialization with config
882
+ * function App() {
883
+ * return (
884
+ * <OxyProvider config={{
885
+ * apiKey: 'your-key',
886
+ * projectId: 'your-project',
887
+ * baseUrl: 'https://api.oxy.tech'
888
+ * }}>
889
+ * <Dashboard />
890
+ * </OxyProvider>
891
+ * );
892
+ * }
893
+ * ```
894
+ *
895
+ * @example
896
+ * ```tsx
897
+ * // Async initialization (for iframe/postMessage auth)
898
+ * function App() {
899
+ * return (
900
+ * <OxyProvider
901
+ * useAsync
902
+ * config={{ parentOrigin: 'https://app.example.com' }}
903
+ * >
904
+ * <Dashboard />
905
+ * </OxyProvider>
906
+ * );
907
+ * }
908
+ * ```
909
+ *
910
+ * @example
911
+ * ```tsx
912
+ * // With environment variables
913
+ * import { createConfig } from '@oxy/sdk';
914
+ *
915
+ * function App() {
916
+ * return (
917
+ * <OxyProvider config={createConfig()}>
918
+ * <Dashboard />
919
+ * </OxyProvider>
920
+ * );
921
+ * }
922
+ * ```
923
+ */
924
+ function OxyProvider({ children, config, useAsync = false, onReady, onError }) {
925
+ const [sdk, setSdk] = useState(null);
926
+ const [isLoading, setIsLoading] = useState(true);
927
+ const [error, setError] = useState(null);
928
+ useEffect(() => {
929
+ let mounted = true;
930
+ let sdkInstance = null;
931
+ async function initializeSDK() {
932
+ try {
933
+ setIsLoading(true);
934
+ setError(null);
935
+ if (useAsync) sdkInstance = await OxySDK.create(config);
936
+ else {
937
+ if (!config) throw new Error("Config is required when useAsync is false. Either provide config or set useAsync=true.");
938
+ sdkInstance = new OxySDK(config);
939
+ }
940
+ if (mounted) {
941
+ setSdk(sdkInstance);
942
+ setIsLoading(false);
943
+ onReady?.(sdkInstance);
944
+ }
945
+ } catch (err) {
946
+ const error$1 = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to initialize SDK");
947
+ if (mounted) {
948
+ setError(error$1);
949
+ setIsLoading(false);
950
+ onError?.(error$1);
951
+ }
952
+ }
953
+ }
954
+ initializeSDK();
955
+ return () => {
956
+ mounted = false;
957
+ if (sdkInstance) sdkInstance.close().catch(console.error);
958
+ };
959
+ }, [
960
+ config,
961
+ useAsync,
962
+ onReady,
963
+ onError
964
+ ]);
965
+ return /* @__PURE__ */ React.createElement(OxyContext.Provider, { value: {
966
+ sdk,
967
+ isLoading,
968
+ error
969
+ } }, children);
970
+ }
971
+ /**
972
+ * Hook to access OxySDK from child components
973
+ *
974
+ * @throws {Error} If used outside of OxyProvider
975
+ * @returns {OxyContextValue} The SDK instance, loading state, and error
976
+ *
977
+ * @example
978
+ * ```tsx
979
+ * function Dashboard() {
980
+ * const { sdk, isLoading, error } = useOxy();
981
+ *
982
+ * useEffect(() => {
983
+ * if (sdk) {
984
+ * sdk.loadAppData('dashboard.app.yml')
985
+ * .then(() => sdk.query('SELECT * FROM my_table'))
986
+ * .then(result => console.log(result));
987
+ * }
988
+ * }, [sdk]);
989
+ *
990
+ * if (isLoading) return <div>Loading SDK...</div>;
991
+ * if (error) return <div>Error: {error.message}</div>;
992
+ * if (!sdk) return null;
993
+ *
994
+ * return <div>Dashboard</div>;
995
+ * }
996
+ * ```
997
+ */
998
+ function useOxy() {
999
+ const context = useContext(OxyContext);
1000
+ if (context === void 0) throw new Error("useOxy must be used within an OxyProvider");
1001
+ return context;
1002
+ }
1003
+ /**
1004
+ * Hook to access OxySDK that throws if not ready
1005
+ *
1006
+ * This is a convenience hook that returns the SDK directly or throws an error if not initialized.
1007
+ * Use this when you know the SDK should be ready.
1008
+ *
1009
+ * @throws {Error} If used outside of OxyProvider or if SDK is not initialized
1010
+ * @returns {OxySDK} The SDK instance
1011
+ *
1012
+ * @example
1013
+ * ```tsx
1014
+ * function DataTable() {
1015
+ * const sdk = useOxySDK();
1016
+ * const [data, setData] = useState(null);
1017
+ *
1018
+ * useEffect(() => {
1019
+ * sdk.loadFile('data.parquet', 'data')
1020
+ * .then(() => sdk.query('SELECT * FROM data LIMIT 100'))
1021
+ * .then(setData);
1022
+ * }, [sdk]);
1023
+ *
1024
+ * return <table>...</table>;
1025
+ * }
1026
+ * ```
1027
+ */
1028
+ function useOxySDK() {
1029
+ const { sdk, isLoading, error } = useOxy();
1030
+ if (error) throw error;
1031
+ if (isLoading || !sdk) throw new Error("OxySDK is not yet initialized");
1032
+ return sdk;
1033
+ }
1034
+
1035
+ //#endregion
1036
+ export { OxyClient, OxyProvider, OxySDK, ParquetReader, PostMessageAuthInvalidOriginError, PostMessageAuthInvalidResponseError, PostMessageAuthNotInIframeError, PostMessageAuthTimeoutError, createConfig, createConfigAsync, initializeDuckDB, isInIframe, queryParquet, readParquet, requestAuthFromParent, useOxy, useOxySDK };
576
1037
  //# sourceMappingURL=index.mjs.map