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