@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/README.md CHANGED
@@ -28,264 +28,397 @@ npm install @duckdb/duckdb-wasm
28
28
 
29
29
  ## Quick Start
30
30
 
31
- ### Option 1: Client-Side Usage (Iframe with PostMessage)
31
+ ### Basic Usage
32
32
 
33
- Perfect for embedding dashboards in iframes (e.g., v0.dev, sandboxed environments):
33
+ ```typescript
34
+ import { OxySDK } from "@oxy/sdk";
35
+
36
+ // Create SDK instance
37
+ const sdk = new OxySDK({
38
+ apiKey: "your-api-key",
39
+ projectId: "your-project-id",
40
+ baseUrl: "https://api.oxy.tech"
41
+ });
42
+
43
+ // Load parquet files and query them
44
+ await sdk.loadFile('data/sales.parquet', 'sales');
45
+ await sdk.loadFile('data/customers.parquet', 'customers');
46
+
47
+ // Query with SQL - supports joins across multiple tables
48
+ const result = await sdk.query(`
49
+ SELECT s.product, s.amount, c.name as customer_name
50
+ FROM sales s
51
+ JOIN customers c ON s.customer_id = c.id
52
+ WHERE s.amount > 1000
53
+ ORDER BY s.amount DESC
54
+ `);
55
+
56
+ console.log(result.rows);
57
+ await sdk.close();
58
+ ```
59
+
60
+ ### Load App Data Automatically
34
61
 
35
62
  ```typescript
36
- import { OxyClient } from '@oxy/sdk';
63
+ import { OxySDK, createConfig } from "@oxy/sdk";
64
+
65
+ const sdk = new OxySDK(createConfig());
66
+
67
+ // Loads all data from the app and registers tables
68
+ await sdk.loadAppData('dashboard.app.yml');
69
+
70
+ // Query the loaded tables
71
+ const result = await sdk.query('SELECT * FROM my_table LIMIT 10');
72
+ ```
73
+
74
+ ### Iframe Usage (PostMessage Authentication)
75
+
76
+ For embedding in iframes (e.g., v0.dev, sandboxed environments):
77
+
78
+ ```typescript
79
+ import { OxySDK } from "@oxy/sdk";
37
80
 
38
81
  // SDK automatically requests API key from parent window
39
- const client = await OxyClient.create({
40
- parentOrigin: 'https://app.example.com', // Parent window origin
41
- projectId: 'your-project-uuid',
42
- baseUrl: 'https://api.oxy.tech'
82
+ const sdk = await OxySDK.create({
83
+ parentOrigin: "https://app.example.com",
84
+ projectId: "your-project-uuid"
43
85
  });
44
86
 
45
- // Use the client
46
- const apps = await client.listApps();
47
- console.log('Available apps:', apps);
87
+ await sdk.loadAppData('dashboard.app.yml');
88
+ const result = await sdk.query('SELECT * FROM my_table LIMIT 10');
48
89
  ```
49
90
 
50
- **Parent window setup** (one-time):
51
- ```typescript
52
- // In parent window that hosts the iframe
53
- window.addEventListener('message', (event) => {
54
- if (event.data.type !== 'OXY_AUTH_REQUEST') return;
91
+ **Parent window setup:**
55
92
 
56
- // Validate iframe origin
57
- if (event.origin !== 'https://your-iframe-app.com') return;
93
+ ```typescript
94
+ window.addEventListener("message", (event) => {
95
+ if (event.data.type !== "OXY_AUTH_REQUEST") return;
96
+ if (event.origin !== "https://your-iframe-app.com") return;
58
97
 
59
- // Send user's API key to iframe
60
98
  event.source.postMessage({
61
- type: 'OXY_AUTH_RESPONSE',
62
- version: '1.0',
99
+ type: "OXY_AUTH_RESPONSE",
100
+ version: "1.0",
63
101
  requestId: event.data.requestId,
64
- apiKey: getUserApiKey(), // Your auth logic
65
- projectId: 'your-project-uuid',
66
- baseUrl: 'https://api.oxy.tech'
102
+ apiKey: getUserApiKey(),
103
+ projectId: "your-project-uuid",
104
+ baseUrl: "https://api.oxy.tech"
67
105
  }, event.origin);
68
106
  });
69
107
  ```
70
108
 
71
- ### Option 2: Traditional Usage (Environment Variables)
72
-
73
- Set up environment variables:
109
+ ### Environment Variables
74
110
 
75
111
  ```bash
76
- export OXY_URL="https://api.oxy.tech" # or your self-hosted URL
112
+ export OXY_URL="https://api.oxy.tech"
77
113
  export OXY_API_KEY="your-api-key"
78
114
  export OXY_PROJECT_ID="your-project-uuid"
79
115
  export OXY_BRANCH="main" # optional
80
116
  ```
81
117
 
82
- Basic usage:
118
+ Use `createConfig()` to load from environment:
83
119
 
84
120
  ```typescript
85
- import { OxyClient, createConfig } from '@oxy/sdk';
86
-
87
- // Create configuration from environment variables
88
- const config = createConfig();
89
-
90
- // Initialize the client
91
- const client = new OxyClient(config);
121
+ import { OxySDK, createConfig } from "@oxy/sdk";
92
122
 
93
- // List all apps
94
- const apps = await client.listApps();
95
- console.log('Available apps:', apps);
96
-
97
- // Get app data
98
- const data = await client.getAppData('dashboard.app.yml');
99
- if (!data.error) {
100
- console.log('App data:', data.data);
101
- }
102
-
103
- // Fetch a file (e.g., chart image)
104
- const blob = await client.getFile('charts/sales-chart.png');
105
- const url = URL.createObjectURL(blob);
106
- // Use the URL in an <img> tag
107
- ```
108
-
109
- ### Reading Parquet Files
110
-
111
- ```typescript
112
- import { OxyClient, createConfig, readParquet, ParquetReader } from '@oxy/sdk';
113
-
114
- const config = createConfig();
115
- const client = new OxyClient(config);
116
-
117
- // Quick read - get all data
118
- const blob = await client.getFile('data/sales.parquet');
119
- const data = await readParquet(blob, 1000); // Read first 1000 rows
120
- console.log(data.columns);
121
- console.log(data.rows);
122
-
123
- // Advanced - query with SQL
124
- const reader = new ParquetReader('sales');
125
- await reader.registerParquet(blob);
126
-
127
- const result = await reader.query(`
128
- SELECT product, SUM(amount) as total_sales
129
- FROM sales
130
- GROUP BY product
131
- ORDER BY total_sales DESC
132
- LIMIT 10
133
- `);
134
-
135
- console.log('Top 10 products:', result.rows);
136
- await reader.close();
123
+ const sdk = new OxySDK(createConfig());
137
124
  ```
138
125
 
139
126
  ## React Integration
140
127
 
141
- ### Displaying App Data
128
+ ### Using React Context (Recommended)
142
129
 
143
- ```typescript
144
- import { OxyClient, createConfig } from '@oxy/sdk';
130
+ The SDK provides `OxyProvider` and `useOxy` hooks for easy integration:
131
+
132
+ ```tsx
133
+ import { OxyProvider, useOxy, createConfig } from '@oxy/sdk';
145
134
  import { useEffect, useState } from 'react';
146
135
 
147
- const client = new OxyClient(createConfig());
136
+ // Wrap your app with OxyProvider
137
+ function App() {
138
+ return (
139
+ <OxyProvider config={createConfig()}>
140
+ <Dashboard />
141
+ </OxyProvider>
142
+ );
143
+ }
148
144
 
145
+ // Access SDK in child components
149
146
  function Dashboard() {
147
+ const { sdk, isLoading, error } = useOxy();
150
148
  const [data, setData] = useState(null);
151
149
 
152
150
  useEffect(() => {
153
- client.getAppData('dashboard.app.yml')
154
- .then(result => setData(result.data))
155
- .catch(console.error);
156
- }, []);
151
+ if (sdk) {
152
+ sdk.loadAppData('dashboard.app.yml')
153
+ .then(() => sdk.query('SELECT * FROM my_table LIMIT 100'))
154
+ .then(setData);
155
+ }
156
+ }, [sdk]);
157
157
 
158
- if (!data) return <div>Loading...</div>;
158
+ if (isLoading) return <div>Initializing SDK...</div>;
159
+ if (error) return <div>Error: {error.message}</div>;
160
+ if (!data) return <div>Loading data...</div>;
159
161
 
160
162
  return (
161
163
  <div>
162
164
  <h1>Dashboard</h1>
163
- {/* Render your data */}
165
+ <table>
166
+ <thead>
167
+ <tr>
168
+ {data.columns.map(col => <th key={col}>{col}</th>)}
169
+ </tr>
170
+ </thead>
171
+ <tbody>
172
+ {data.rows.map((row, i) => (
173
+ <tr key={i}>
174
+ {row.map((cell, j) => <td key={j}>{String(cell)}</td>)}
175
+ </tr>
176
+ ))}
177
+ </tbody>
178
+ </table>
164
179
  </div>
165
180
  );
166
181
  }
167
182
  ```
168
183
 
169
- ### Displaying Parquet Data
184
+ ### Iframe with PostMessage Auth
185
+
186
+ ```tsx
187
+ import { OxyProvider, useOxySDK } from '@oxy/sdk';
188
+
189
+ function App() {
190
+ return (
191
+ <OxyProvider
192
+ useAsync
193
+ config={{ parentOrigin: 'https://app.example.com' }}
194
+ >
195
+ <Dashboard />
196
+ </OxyProvider>
197
+ );
198
+ }
199
+
200
+ function Dashboard() {
201
+ const sdk = useOxySDK(); // Throws if not ready
202
+ const [data, setData] = useState(null);
203
+
204
+ useEffect(() => {
205
+ sdk.loadFile('data/sales.parquet', 'sales')
206
+ .then(() => sdk.query('SELECT * FROM sales LIMIT 100'))
207
+ .then(setData);
208
+ }, [sdk]);
209
+
210
+ return <div>{/* render data */}</div>;
211
+ }
212
+ ```
213
+
214
+ ### Without Context (Alternative)
170
215
 
171
216
  ```typescript
172
- import { ParquetReader } from '@oxy/sdk';
217
+ import { OxySDK, createConfig } from '@oxy/sdk';
218
+ import { useEffect, useState } from 'react';
173
219
 
174
- function DataTable({ filePath }: { filePath: string }) {
220
+ const sdk = new OxySDK(createConfig());
221
+
222
+ function Dashboard() {
175
223
  const [data, setData] = useState(null);
176
224
 
177
225
  useEffect(() => {
178
- async function loadData() {
179
- const blob = await client.getFile(filePath);
180
- const reader = new ParquetReader();
181
- await reader.registerParquet(blob);
182
- const result = await reader.getAll(100);
183
- setData(result);
184
- await reader.close();
185
- }
186
- loadData();
187
- }, [filePath]);
226
+ sdk.loadAppData('dashboard.app.yml')
227
+ .then(() => sdk.query('SELECT * FROM my_table LIMIT 100'))
228
+ .then(setData);
229
+ }, []);
188
230
 
189
- // Render table...
231
+ return <div>{/* render data */}</div>;
190
232
  }
191
233
  ```
192
234
 
193
- ## Use with v0 and Other Sandbox Services
235
+ ## Use with v0 and Sandbox Services
194
236
 
195
- Perfect for integrating Oxy data into v0-generated apps or other sandbox environments:
237
+ **For AI Assistants (v0.dev, Cursor, etc.):** See [.v0/rules.md](.v0/rules.md) and [.cursorrules](.cursorrules) for integration guidelines.
196
238
 
197
239
  ```typescript
198
- // In your v0 app
199
- import { OxyClient, createConfig, readParquet } from '@oxy/sdk';
240
+ import { OxySDK, createConfig } from "@oxy/sdk";
200
241
 
201
- // Configure with environment variables set in v0 project settings
202
- const client = new OxyClient(createConfig());
242
+ const sdk = new OxySDK(createConfig());
203
243
 
204
- export async function fetchDashboardData() {
205
- const [sales, customers] = await Promise.all([
206
- client.getAppData('apps/sales.app.yml'),
207
- client.getAppData('apps/customers.app.yml'),
208
- ]);
244
+ export async function getDashboardData() {
245
+ await sdk.loadAppData("dashboard.app.yml");
209
246
 
210
- return { sales: sales.data, customers: customers.data };
247
+ return await sdk.query(`
248
+ SELECT s.*, c.name as customer_name
249
+ FROM sales s
250
+ LEFT JOIN customers c ON s.customer_id = c.id
251
+ ORDER BY s.date DESC
252
+ LIMIT 100
253
+ `);
211
254
  }
212
255
 
213
- export function getSalesChartUrl() {
214
- return client.getFileUrl('charts/sales-overview.png');
256
+ export function getChartUrl() {
257
+ return sdk.getClient().getFileUrl("charts/sales-overview.png");
215
258
  }
216
259
  ```
217
260
 
218
261
  ## API Reference
219
262
 
220
- ### OxyClient
263
+ ### OxySDK (Unified Interface)
264
+
265
+ The `OxySDK` class combines `OxyClient` and `ParquetReader` into a single, easy-to-use interface.
221
266
 
222
267
  #### `constructor(config: OxyConfig)`
223
268
 
224
- Creates a new Oxy client instance.
269
+ Creates a new SDK instance.
270
+
271
+ #### `static async create(config?: Partial<OxyConfig>): Promise<OxySDK>`
272
+
273
+ Creates an SDK instance with async configuration (supports postMessage auth).
274
+
275
+ ```typescript
276
+ const sdk = await OxySDK.create({
277
+ parentOrigin: 'https://app.example.com',
278
+ projectId: 'your-project-id'
279
+ });
280
+ ```
281
+
282
+ #### `async loadFile(filePath: string, tableName: string): Promise<void>`
283
+
284
+ Loads a Parquet file from Oxy and registers it for SQL queries.
285
+
286
+ ```typescript
287
+ await sdk.loadFile('data/sales.parquet', 'sales');
288
+ ```
289
+
290
+ #### `async loadFiles(files: Array<{filePath: string, tableName: string}>): Promise<void>`
291
+
292
+ Loads multiple Parquet files at once.
293
+
294
+ ```typescript
295
+ await sdk.loadFiles([
296
+ { filePath: 'data/sales.parquet', tableName: 'sales' },
297
+ { filePath: 'data/customers.parquet', tableName: 'customers' }
298
+ ]);
299
+ ```
300
+
301
+ #### `async loadAppData(appPath: string): Promise<DataContainer | null>`
302
+
303
+ Loads all data from an app's data container. Uses container keys as table names.
304
+
305
+ ```typescript
306
+ const data = await sdk.loadAppData('dashboard.app.yml');
307
+ // Now query the tables using their container keys
308
+ const result = await sdk.query('SELECT * FROM my_table');
309
+ ```
310
+
311
+ #### `async query(sql: string): Promise<QueryResult>`
312
+
313
+ Executes a SQL query against loaded data.
225
314
 
226
- #### `listApps(): Promise<AppItem[]>`
315
+ ```typescript
316
+ const result = await sdk.query('SELECT * FROM sales WHERE amount > 1000');
317
+ ```
227
318
 
228
- Lists all apps in the project.
319
+ #### `async getAll(tableName: string, limit?: number): Promise<QueryResult>`
229
320
 
230
- #### `getAppData(appPath: string): Promise<AppDataResponse>`
321
+ Gets all data from a loaded table.
322
+
323
+ ```typescript
324
+ const data = await sdk.getAll('sales', 100);
325
+ ```
231
326
 
232
- Gets data for a specific app (with caching).
327
+ #### `async getSchema(tableName: string): Promise<QueryResult>`
233
328
 
234
- #### `runApp(appPath: string): Promise<AppDataResponse>`
329
+ Gets schema information for a loaded table.
235
330
 
236
- Runs an app and returns fresh data (bypasses cache).
331
+ #### `async count(tableName: string): Promise<number>`
332
+
333
+ Gets row count for a loaded table.
334
+
335
+ #### `getClient(): OxyClient`
336
+
337
+ Returns the underlying `OxyClient` for advanced operations.
338
+
339
+ ```typescript
340
+ const apps = await sdk.getClient().listApps();
341
+ ```
237
342
 
238
- #### `getDisplays(appPath: string): Promise<GetDisplaysResponse>`
343
+ #### `getReader(): ParquetReader`
239
344
 
240
- Gets display configurations for an app.
345
+ Returns the underlying `ParquetReader` for advanced operations.
241
346
 
242
- #### `getFile(filePath: string): Promise<Blob>`
347
+ #### `async close(): Promise<void>`
243
348
 
244
- Fetches a file from the app state directory.
349
+ Closes and cleans up all resources.
245
350
 
246
- #### `getFileUrl(filePath: string): string`
351
+ ### React Hooks
247
352
 
248
- Returns a URL for direct file access.
353
+ #### `OxyProvider`
249
354
 
250
- ### ParquetReader
355
+ Provider component that initializes and provides OxySDK to child components.
251
356
 
252
- #### `registerParquet(blob: Blob): Promise<void>`
357
+ **Props:**
358
+ - `config?: Partial<OxyConfig>` - SDK configuration
359
+ - `useAsync?: boolean` - If true, uses async initialization (supports postMessage auth)
360
+ - `onReady?: (sdk: OxySDK) => void` - Called when SDK is initialized
361
+ - `onError?: (error: Error) => void` - Called on initialization error
253
362
 
254
- Registers a Parquet file for querying.
363
+ ```tsx
364
+ <OxyProvider config={createConfig()}>
365
+ <YourApp />
366
+ </OxyProvider>
367
+ ```
255
368
 
256
- #### `query(sql: string): Promise<QueryResult>`
369
+ #### `useOxy()`
257
370
 
258
- Executes a SQL query against the Parquet data.
371
+ Hook to access SDK, loading state, and errors.
259
372
 
260
- #### `getAll(limit?: number): Promise<QueryResult>`
373
+ ```tsx
374
+ const { sdk, isLoading, error } = useOxy();
375
+ ```
261
376
 
262
- Gets all data from the Parquet file.
377
+ Returns:
378
+ - `sdk: OxySDK | null` - The SDK instance (null if not ready)
379
+ - `isLoading: boolean` - True while initializing
380
+ - `error: Error | null` - Initialization error if any
263
381
 
264
- #### `getSchema(): Promise<QueryResult>`
382
+ #### `useOxySDK()`
265
383
 
266
- Gets schema information for the Parquet file.
384
+ Hook that returns SDK directly or throws if not ready. Useful when you know SDK should be initialized.
267
385
 
268
- #### `count(): Promise<number>`
386
+ ```tsx
387
+ const sdk = useOxySDK(); // Throws if not ready
388
+ ```
269
389
 
270
- Returns the row count.
390
+ ### Advanced: OxyClient
271
391
 
272
- #### `close(): Promise<void>`
392
+ For advanced use cases, access the underlying client via `sdk.getClient()`:
273
393
 
274
- Closes the reader and cleans up resources.
394
+ ```typescript
395
+ const client = sdk.getClient();
396
+ await client.listApps();
397
+ await client.getDisplays('my-app.app.yml');
398
+ const blob = await client.getFile('path/to/file.parquet');
399
+ ```
275
400
 
276
- ### Helper Functions
401
+ ### Advanced: ParquetReader
277
402
 
278
- #### `createConfig(overrides?: Partial<OxyConfig>): OxyConfig`
403
+ For advanced use cases, access the underlying reader via `sdk.getReader()`:
279
404
 
280
- Creates configuration from environment variables with optional overrides.
405
+ ```typescript
406
+ const reader = sdk.getReader();
407
+ await reader.registerParquet(customBlob, 'custom_table');
408
+ ```
281
409
 
282
- #### `queryParquet(blob: Blob, sql?: string): Promise<QueryResult>`
410
+ Or use standalone:
283
411
 
284
- Quick helper to query a Parquet blob.
412
+ ```typescript
413
+ import { ParquetReader } from "@oxy/sdk";
285
414
 
286
- #### `readParquet(blob: Blob, limit?: number): Promise<QueryResult>`
415
+ const reader = new ParquetReader();
416
+ await reader.registerParquet(blob1, 'table1');
417
+ await reader.registerParquet(blob2, 'table2');
287
418
 
288
- Quick helper to read all data from a Parquet blob.
419
+ const result = await reader.query('SELECT * FROM table1 JOIN table2 ON ...');
420
+ await reader.close();
421
+ ```
289
422
 
290
423
  ## Environment Variables
291
424
 
@@ -298,12 +431,6 @@ Quick helper to read all data from a Parquet blob.
298
431
 
299
432
  See the [examples](./examples) directory for more detailed examples:
300
433
 
301
- - [basic-usage.ts](./examples/basic-usage.ts) - Basic SDK usage
302
- - [parquet-usage.ts](./examples/parquet-usage.ts) - Parquet file reading
303
- - [react-example.tsx](./examples/react-example.tsx) - React integration
304
- - [react-parquet.tsx](./examples/react-parquet.tsx) - React with Parquet
305
- - [v0-integration.ts](./examples/v0-integration.ts) - v0 sandbox integration
306
-
307
434
  ## Building and Publishing
308
435
 
309
436
  ```bash
@@ -333,3 +460,7 @@ MIT
333
460
  ## Support
334
461
 
335
462
  For issues and questions, please visit [GitHub Issues](https://github.com/dataframehq/oxy-internal/issues).
463
+
464
+ ### Local development with v0 or cloud service
465
+
466
+ - Disable the local network access check [flag](chrome ://flags/#local-network-access-check)