@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/README.md +281 -171
- package/dist/index.cjs +523 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +416 -33
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +416 -33
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +519 -58
- package/dist/index.mjs.map +1 -1
- package/dist/postMessage-B1J0jDRN.cjs.map +1 -1
- package/dist/postMessage-BxdgtX8j.mjs.map +1 -1
- package/package.json +11 -6
package/dist/index.cjs
CHANGED
|
@@ -29,9 +29,22 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
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 ||
|
|
49
|
-
const apiKey = overrides?.apiKey ||
|
|
50
|
-
const projectId = overrides?.projectId ||
|
|
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 ||
|
|
70
|
+
branch: overrides?.branch || getEnvVar("OXY_BRANCH"),
|
|
58
71
|
timeout: overrides?.timeout || 3e4,
|
|
59
72
|
parentOrigin: overrides?.parentOrigin,
|
|
60
73
|
disableAutoAuth: overrides?.disableAutoAuth
|
|
@@ -95,9 +108,9 @@ function createConfig(overrides) {
|
|
|
95
108
|
*/
|
|
96
109
|
async function createConfigAsync(overrides) {
|
|
97
110
|
const { isInIframe: isInIframe$1 } = await Promise.resolve().then(() => require("./postMessage-BSNS3ccd.cjs"));
|
|
98
|
-
let baseUrl = overrides?.baseUrl ||
|
|
99
|
-
let apiKey = overrides?.apiKey ||
|
|
100
|
-
let projectId = overrides?.projectId ||
|
|
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
116
|
if (!disableAutoAuth && isInIframe$1() && !apiKey) if (!parentOrigin) logWarningAboutMissingParentOrigin();
|
|
@@ -134,7 +147,7 @@ function createFinalConfig(baseUrl, apiKey, projectId, overrides) {
|
|
|
134
147
|
baseUrl: baseUrl.replace(/\/$/, ""),
|
|
135
148
|
apiKey,
|
|
136
149
|
projectId,
|
|
137
|
-
branch: overrides?.branch ||
|
|
150
|
+
branch: overrides?.branch || getEnvVar("OXY_BRANCH"),
|
|
138
151
|
timeout: overrides?.timeout || 3e4,
|
|
139
152
|
parentOrigin: overrides?.parentOrigin,
|
|
140
153
|
disableAutoAuth: overrides?.disableAutoAuth
|
|
@@ -178,41 +191,76 @@ async function getConnection() {
|
|
|
178
191
|
return connection;
|
|
179
192
|
}
|
|
180
193
|
/**
|
|
181
|
-
* 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.
|
|
182
196
|
*/
|
|
183
197
|
var ParquetReader = class {
|
|
184
|
-
constructor(
|
|
185
|
-
this.
|
|
186
|
-
|
|
187
|
-
|
|
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)}`}`;
|
|
188
206
|
}
|
|
189
207
|
/**
|
|
190
|
-
* Register a Parquet file from a Blob
|
|
208
|
+
* Register a Parquet file from a Blob with a specific table name
|
|
191
209
|
*
|
|
192
210
|
* @param blob - Parquet file as Blob
|
|
211
|
+
* @param tableName - Name to use for the table in queries (required)
|
|
193
212
|
*
|
|
194
213
|
* @example
|
|
195
214
|
* ```typescript
|
|
196
215
|
* const blob = await client.getFile('data/sales.parquet');
|
|
197
|
-
* const reader = new ParquetReader(
|
|
198
|
-
* 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');
|
|
199
227
|
* ```
|
|
200
228
|
*/
|
|
201
|
-
async registerParquet(blob) {
|
|
229
|
+
async registerParquet(blob, tableName) {
|
|
230
|
+
const internalTableName = this.generateInternalTableName(tableName);
|
|
202
231
|
await enqueueOperation(async () => {
|
|
203
232
|
const conn = await getConnection();
|
|
204
233
|
const db = await initializeDuckDB();
|
|
205
234
|
const arrayBuffer = await blob.arrayBuffer();
|
|
206
235
|
const uint8Array = new Uint8Array(arrayBuffer);
|
|
207
|
-
await db.registerFileBuffer(`${
|
|
236
|
+
await db.registerFileBuffer(`${internalTableName}.parquet`, uint8Array);
|
|
208
237
|
try {
|
|
209
|
-
await conn.query(`DROP TABLE IF EXISTS ${
|
|
238
|
+
await conn.query(`DROP TABLE IF EXISTS ${internalTableName}`);
|
|
210
239
|
} catch {}
|
|
211
|
-
await conn.query(`CREATE TABLE ${
|
|
212
|
-
this.
|
|
240
|
+
await conn.query(`CREATE TABLE ${internalTableName} AS SELECT * FROM '${internalTableName}.parquet'`);
|
|
241
|
+
this.tableMap.set(tableName, internalTableName);
|
|
213
242
|
});
|
|
214
243
|
}
|
|
215
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);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
216
264
|
* Execute a SQL query against the registered Parquet data
|
|
217
265
|
*
|
|
218
266
|
* @param sql - SQL query string
|
|
@@ -224,12 +272,25 @@ var ParquetReader = class {
|
|
|
224
272
|
* console.log(result.columns);
|
|
225
273
|
* console.log(result.rows);
|
|
226
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
|
+
* ```
|
|
227
287
|
*/
|
|
228
288
|
async query(sql) {
|
|
229
|
-
if (
|
|
289
|
+
if (this.tableMap.size === 0) throw new Error("No Parquet files registered. Call registerParquet() first.");
|
|
230
290
|
return enqueueOperation(async () => {
|
|
231
291
|
const conn = await getConnection();
|
|
232
|
-
|
|
292
|
+
let rewrittenSql = sql;
|
|
293
|
+
for (const [userTableName, internalTableName] of this.tableMap.entries()) rewrittenSql = rewrittenSql.replace(new RegExp(`\\b${userTableName}\\b`, "g"), internalTableName);
|
|
233
294
|
const result = await conn.query(rewrittenSql);
|
|
234
295
|
const columns = result.schema.fields.map((field) => field.name);
|
|
235
296
|
const rows = [];
|
|
@@ -249,64 +310,69 @@ var ParquetReader = class {
|
|
|
249
310
|
});
|
|
250
311
|
}
|
|
251
312
|
/**
|
|
252
|
-
* Get all data from
|
|
313
|
+
* Get all data from a registered table
|
|
253
314
|
*
|
|
315
|
+
* @param tableName - Name of the table to query
|
|
254
316
|
* @param limit - Maximum number of rows to return (default: all)
|
|
255
317
|
* @returns Query result
|
|
256
318
|
*
|
|
257
319
|
* @example
|
|
258
320
|
* ```typescript
|
|
259
|
-
* const allData = await reader.getAll();
|
|
260
|
-
* const first100 = await reader.getAll(100);
|
|
321
|
+
* const allData = await reader.getAll('sales');
|
|
322
|
+
* const first100 = await reader.getAll('sales', 100);
|
|
261
323
|
* ```
|
|
262
324
|
*/
|
|
263
|
-
async getAll(limit) {
|
|
325
|
+
async getAll(tableName, limit) {
|
|
264
326
|
const limitClause = limit ? ` LIMIT ${limit}` : "";
|
|
265
|
-
return this.query(`SELECT * FROM ${
|
|
327
|
+
return this.query(`SELECT * FROM ${tableName}${limitClause}`);
|
|
266
328
|
}
|
|
267
329
|
/**
|
|
268
330
|
* Get table schema information
|
|
269
331
|
*
|
|
332
|
+
* @param tableName - Name of the table to describe
|
|
270
333
|
* @returns Schema information
|
|
271
334
|
*
|
|
272
335
|
* @example
|
|
273
336
|
* ```typescript
|
|
274
|
-
* const schema = await reader.getSchema();
|
|
337
|
+
* const schema = await reader.getSchema('sales');
|
|
275
338
|
* console.log(schema.columns); // ['id', 'name', 'sales']
|
|
276
339
|
* console.log(schema.rows); // [['id', 'INTEGER'], ['name', 'VARCHAR'], ...]
|
|
277
340
|
* ```
|
|
278
341
|
*/
|
|
279
|
-
async getSchema() {
|
|
280
|
-
return this.query(`DESCRIBE ${
|
|
342
|
+
async getSchema(tableName) {
|
|
343
|
+
return this.query(`DESCRIBE ${tableName}`);
|
|
281
344
|
}
|
|
282
345
|
/**
|
|
283
|
-
* Get row count
|
|
346
|
+
* Get row count for a table
|
|
284
347
|
*
|
|
348
|
+
* @param tableName - Name of the table to count
|
|
285
349
|
* @returns Number of rows in the table
|
|
286
350
|
*
|
|
287
351
|
* @example
|
|
288
352
|
* ```typescript
|
|
289
|
-
* const count = await reader.count();
|
|
353
|
+
* const count = await reader.count('sales');
|
|
290
354
|
* console.log(`Total rows: ${count}`);
|
|
291
355
|
* ```
|
|
292
356
|
*/
|
|
293
|
-
async count() {
|
|
294
|
-
return (await this.query(`SELECT COUNT(*) as count FROM ${
|
|
357
|
+
async count(tableName) {
|
|
358
|
+
return (await this.query(`SELECT COUNT(*) as count FROM ${tableName}`)).rows[0][0];
|
|
295
359
|
}
|
|
296
360
|
/**
|
|
297
|
-
* Close and cleanup resources
|
|
361
|
+
* Close and cleanup all registered resources
|
|
298
362
|
*/
|
|
299
363
|
async close() {
|
|
300
|
-
if (this.
|
|
364
|
+
if (this.tableMap.size > 0) await enqueueOperation(async () => {
|
|
301
365
|
const conn = await getConnection();
|
|
302
366
|
const db = await initializeDuckDB();
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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();
|
|
310
376
|
});
|
|
311
377
|
}
|
|
312
378
|
};
|
|
@@ -314,21 +380,21 @@ var ParquetReader = class {
|
|
|
314
380
|
* Helper function to quickly read a Parquet blob and execute a query
|
|
315
381
|
*
|
|
316
382
|
* @param blob - Parquet file as Blob
|
|
317
|
-
* @param
|
|
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)
|
|
318
385
|
* @returns Query result
|
|
319
386
|
*
|
|
320
387
|
* @example
|
|
321
388
|
* ```typescript
|
|
322
389
|
* const blob = await client.getFile('data/sales.parquet');
|
|
323
|
-
* const result = await queryParquet(blob, 'SELECT product, SUM(amount) as total FROM
|
|
390
|
+
* const result = await queryParquet(blob, 'sales', 'SELECT product, SUM(amount) as total FROM sales GROUP BY product');
|
|
324
391
|
* console.log(result);
|
|
325
392
|
* ```
|
|
326
393
|
*/
|
|
327
|
-
async function queryParquet(blob, sql) {
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
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}`;
|
|
332
398
|
const result = await reader.query(query);
|
|
333
399
|
await reader.close();
|
|
334
400
|
return result;
|
|
@@ -337,20 +403,21 @@ async function queryParquet(blob, sql) {
|
|
|
337
403
|
* Helper function to read Parquet file and get all data
|
|
338
404
|
*
|
|
339
405
|
* @param blob - Parquet file as Blob
|
|
406
|
+
* @param tableName - Name to use for the table (default: 'data')
|
|
340
407
|
* @param limit - Maximum number of rows (optional)
|
|
341
408
|
* @returns Query result
|
|
342
409
|
*
|
|
343
410
|
* @example
|
|
344
411
|
* ```typescript
|
|
345
412
|
* const blob = await client.getFile('data/sales.parquet');
|
|
346
|
-
* const data = await readParquet(blob, 1000);
|
|
413
|
+
* const data = await readParquet(blob, 'sales', 1000);
|
|
347
414
|
* console.log(`Loaded ${data.rowCount} rows`);
|
|
348
415
|
* ```
|
|
349
416
|
*/
|
|
350
|
-
async function readParquet(blob, limit) {
|
|
351
|
-
const reader = new ParquetReader(
|
|
352
|
-
await reader.registerParquet(blob);
|
|
353
|
-
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);
|
|
354
421
|
await reader.close();
|
|
355
422
|
return result;
|
|
356
423
|
}
|
|
@@ -590,7 +657,7 @@ var OxyClient = class OxyClient {
|
|
|
590
657
|
* ```
|
|
591
658
|
*/
|
|
592
659
|
async getTableData(filePath, limit = 100) {
|
|
593
|
-
const result = await readParquet(await this.getFile(filePath), limit);
|
|
660
|
+
const result = await readParquet(await this.getFile(filePath), "data", limit);
|
|
594
661
|
return {
|
|
595
662
|
columns: result.columns,
|
|
596
663
|
rows: result.rows,
|
|
@@ -599,8 +666,405 @@ var OxyClient = class OxyClient {
|
|
|
599
666
|
}
|
|
600
667
|
};
|
|
601
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
|
+
|
|
602
1064
|
//#endregion
|
|
603
1065
|
exports.OxyClient = OxyClient;
|
|
1066
|
+
exports.OxyProvider = OxyProvider;
|
|
1067
|
+
exports.OxySDK = OxySDK;
|
|
604
1068
|
exports.ParquetReader = ParquetReader;
|
|
605
1069
|
exports.PostMessageAuthInvalidOriginError = require_postMessage.PostMessageAuthInvalidOriginError;
|
|
606
1070
|
exports.PostMessageAuthInvalidResponseError = require_postMessage.PostMessageAuthInvalidResponseError;
|
|
@@ -613,4 +1077,6 @@ exports.isInIframe = require_postMessage.isInIframe;
|
|
|
613
1077
|
exports.queryParquet = queryParquet;
|
|
614
1078
|
exports.readParquet = readParquet;
|
|
615
1079
|
exports.requestAuthFromParent = require_postMessage.requestAuthFromParent;
|
|
1080
|
+
exports.useOxy = useOxy;
|
|
1081
|
+
exports.useOxySDK = useOxySDK;
|
|
616
1082
|
//# sourceMappingURL=index.cjs.map
|