@seaverse/dataservice 1.3.0 → 1.6.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 +278 -96
- package/dist/index.d.mts +44 -34
- package/dist/index.d.ts +44 -34
- package/dist/index.js +84 -34
- package/dist/index.mjs +82 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,36 +22,46 @@ npm install @seaverse/dataservice
|
|
|
22
22
|
```typescript
|
|
23
23
|
import { createClient } from '@seaverse/dataservice';
|
|
24
24
|
|
|
25
|
-
//
|
|
26
|
-
const client = createClient({
|
|
27
|
-
|
|
25
|
+
// Auto-fetch token from parent page (iframe)
|
|
26
|
+
const client = await createClient({});
|
|
27
|
+
|
|
28
|
+
// For development/testing, use debugSetToken
|
|
29
|
+
import { debugSetToken } from '@seaverse/dataservice';
|
|
30
|
+
debugSetToken('your-test-token');
|
|
31
|
+
const client = await createClient({});
|
|
32
|
+
|
|
33
|
+
// Store user preferences (single record)
|
|
34
|
+
const userPrefs = client.userData.collection('user_preferences');
|
|
35
|
+
await userPrefs.insert({
|
|
36
|
+
theme: 'dark',
|
|
37
|
+
language: 'en',
|
|
38
|
+
notifications: true,
|
|
28
39
|
});
|
|
29
40
|
|
|
30
|
-
//
|
|
31
|
-
|
|
41
|
+
// Update preferences
|
|
42
|
+
await userPrefs.patch(userPrefs.id, { theme: 'light' });
|
|
32
43
|
|
|
33
|
-
//
|
|
34
|
-
const
|
|
44
|
+
// Store multiple items (e.g., orders) - each needs unique collection name
|
|
45
|
+
const order1 = await client.userData.collection('order_001').insert({
|
|
35
46
|
order_number: 'ORD-001',
|
|
36
47
|
status: 'pending',
|
|
37
48
|
total: 99.99,
|
|
38
49
|
});
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.
|
|
44
|
-
|
|
45
|
-
.execute();
|
|
46
|
-
|
|
47
|
-
// Update data
|
|
48
|
-
await orders.patch(order.id, { status: 'completed' });
|
|
51
|
+
const order2 = await client.userData.collection('order_002').insert({
|
|
52
|
+
order_number: 'ORD-002',
|
|
53
|
+
status: 'shipped',
|
|
54
|
+
total: 149.99,
|
|
55
|
+
});
|
|
49
56
|
|
|
50
|
-
//
|
|
51
|
-
await
|
|
57
|
+
// Or use batch insert for multiple records
|
|
58
|
+
const orders = await client.userData.batchInsert('order', [
|
|
59
|
+
{ order_number: 'ORD-003', status: 'pending', total: 79.99 },
|
|
60
|
+
{ order_number: 'ORD-004', status: 'pending', total: 199.99 },
|
|
61
|
+
]);
|
|
52
62
|
|
|
53
|
-
//
|
|
54
|
-
|
|
63
|
+
// Delete a specific record
|
|
64
|
+
await client.userData.collection('order_001').delete(order1.id);
|
|
55
65
|
```
|
|
56
66
|
|
|
57
67
|
## Core Concepts
|
|
@@ -94,22 +104,144 @@ The SDK provides access to different data tables with different permission scope
|
|
|
94
104
|
|
|
95
105
|
### Collections
|
|
96
106
|
|
|
97
|
-
|
|
107
|
+
**Critical Understanding**: A collection name identifies a **single record**, not a container for multiple records.
|
|
108
|
+
|
|
109
|
+
The `(user_id, app_id, collection_name)` combination is a **unique constraint** - meaning each collection name can only store ONE record per user and app.
|
|
98
110
|
|
|
99
|
-
**
|
|
111
|
+
**To store multiple records, you MUST use unique collection names**:
|
|
100
112
|
|
|
101
113
|
```typescript
|
|
102
|
-
// ✓
|
|
103
|
-
await client.userData.collection('
|
|
104
|
-
await client.userData.collection('
|
|
114
|
+
// ✓ CORRECT: Each record gets a unique collection name
|
|
115
|
+
await client.userData.collection('order_001').insert(order1);
|
|
116
|
+
await client.userData.collection('order_002').insert(order2);
|
|
117
|
+
await client.userData.collection('order_003').insert(order3);
|
|
105
118
|
|
|
106
|
-
// ✓
|
|
107
|
-
await client.userData.batchInsert('order', [order1, order2]);
|
|
108
|
-
// Creates:
|
|
119
|
+
// ✓ RECOMMENDED: Use batch insert (auto-generates unique names)
|
|
120
|
+
const insertedOrders = await client.userData.batchInsert('order', [order1, order2, order3]);
|
|
121
|
+
// Creates collections: order_<timestamp>_0, order_<timestamp>_1, order_<timestamp>_2
|
|
109
122
|
|
|
110
|
-
// ✗
|
|
111
|
-
|
|
112
|
-
await
|
|
123
|
+
// ✗ WRONG: Reusing the same collection name
|
|
124
|
+
const orders = client.userData.collection('orders');
|
|
125
|
+
await orders.insert(order1); // ✓ Success
|
|
126
|
+
await orders.insert(order2); // ✗ ERROR: 409 Conflict (collection 'orders' already exists)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Why this design?**
|
|
130
|
+
- Each collection name acts as a **unique key** for a single record
|
|
131
|
+
- Think of it like a key-value store: `collection_name` → single record
|
|
132
|
+
- For multiple records, use patterns like: `order_${orderId}`, `msg_${conversationId}_${msgId}`
|
|
133
|
+
|
|
134
|
+
## ⚠️ Common Pitfalls
|
|
135
|
+
|
|
136
|
+
### Pitfall #1: Treating Collections as Containers
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// ❌ WRONG: This looks like it should work, but it doesn't
|
|
140
|
+
const orders = client.userData.collection('orders');
|
|
141
|
+
await orders.insert({ order_number: 'ORD-001', total: 99.99 }); // ✓ Works
|
|
142
|
+
await orders.insert({ order_number: 'ORD-002', total: 149.99 }); // ✗ ERROR: 409 Conflict!
|
|
143
|
+
|
|
144
|
+
// ✓ CORRECT: Each record needs a unique collection name
|
|
145
|
+
await client.userData.collection('order_001').insert({ order_number: 'ORD-001', total: 99.99 });
|
|
146
|
+
await client.userData.collection('order_002').insert({ order_number: 'ORD-002', total: 149.99 });
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Why?** The `(user_id, app_id, collection_name)` combination is a unique constraint. Once you insert into `'orders'`, that collection name is "taken" for that user and app.
|
|
150
|
+
|
|
151
|
+
### Pitfall #2: Expecting Query Methods to Return Multiple Records
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// ❌ MISLEADING: This looks like it queries multiple orders
|
|
155
|
+
const orders = client.userData.collection('orders');
|
|
156
|
+
const pending = await orders.select().eq('data->>status', 'pending').execute();
|
|
157
|
+
// Returns: [] or [single order] - NOT multiple orders!
|
|
158
|
+
|
|
159
|
+
// ✓ CORRECT: To work with multiple records, track them separately
|
|
160
|
+
const orderIds = ['order_001', 'order_002', 'order_003'];
|
|
161
|
+
const allOrders = await Promise.all(
|
|
162
|
+
orderIds.map(id => client.userData.collection(id).get(recordId))
|
|
163
|
+
);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Why?** A collection name identifies a single record, so queries on that collection can only return 0 or 1 record.
|
|
167
|
+
|
|
168
|
+
### Pitfall #3: Using Plural Names for Collections
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// ❌ MISLEADING: Plural name suggests multiple items
|
|
172
|
+
const orders = client.userData.collection('orders');
|
|
173
|
+
const users = client.userData.collection('users');
|
|
174
|
+
|
|
175
|
+
// ✓ BETTER: Use singular or ID-based names
|
|
176
|
+
const order = client.userData.collection('order_12345');
|
|
177
|
+
const userProfile = client.userData.collection('user_profile');
|
|
178
|
+
const preference = client.userData.collection('user_preferences');
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Why?** Plural names create false expectations. Use singular names or include IDs to make it clear each collection is one record.
|
|
182
|
+
|
|
183
|
+
## 💡 Best Practices
|
|
184
|
+
|
|
185
|
+
### Pattern 1: Single Record per Concept
|
|
186
|
+
|
|
187
|
+
Use this for user preferences, settings, profiles - things where you only need one record:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// User preferences (one per user)
|
|
191
|
+
const prefs = await client.userData.collection('preferences').insert({
|
|
192
|
+
theme: 'dark',
|
|
193
|
+
language: 'en',
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// User profile (one per user)
|
|
197
|
+
const profile = await client.userData.collection('profile').insert({
|
|
198
|
+
name: 'John Doe',
|
|
199
|
+
avatar: 'https://...',
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Pattern 2: ID-Based Collection Names
|
|
204
|
+
|
|
205
|
+
Use this for multiple items of the same type:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Multiple orders - each with unique collection name
|
|
209
|
+
const order1 = await client.userData.collection(`order_${orderId1}`).insert(orderData1);
|
|
210
|
+
const order2 = await client.userData.collection(`order_${orderId2}`).insert(orderData2);
|
|
211
|
+
|
|
212
|
+
// Multiple conversations
|
|
213
|
+
const conv1 = await client.userData.collection(`conv_${convId1}`).insert(convData1);
|
|
214
|
+
const conv2 = await client.userData.collection(`conv_${convId2}`).insert(convData2);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Pattern 3: Batch Insert for Multiple Records
|
|
218
|
+
|
|
219
|
+
Use this when creating multiple records at once:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// Create multiple orders in one call
|
|
223
|
+
const orders = await client.userData.batchInsert('order', [
|
|
224
|
+
{ order_number: 'ORD-001', total: 99.99 },
|
|
225
|
+
{ order_number: 'ORD-002', total: 149.99 },
|
|
226
|
+
{ order_number: 'ORD-003', total: 199.99 },
|
|
227
|
+
]);
|
|
228
|
+
|
|
229
|
+
// Returns array of DataRecord with auto-generated collection names
|
|
230
|
+
// order_<timestamp>_0, order_<timestamp>_1, order_<timestamp>_2
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Pattern 4: Hierarchical Collection Names
|
|
234
|
+
|
|
235
|
+
Use this for nested data structures:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// Messages within a conversation
|
|
239
|
+
await client.userData.collection(`conv_${convId}_msg_1`).insert(message1);
|
|
240
|
+
await client.userData.collection(`conv_${convId}_msg_2`).insert(message2);
|
|
241
|
+
|
|
242
|
+
// Tasks within a project
|
|
243
|
+
await client.userData.collection(`project_${projId}_task_1`).insert(task1);
|
|
244
|
+
await client.userData.collection(`project_${projId}_task_2`).insert(task2);
|
|
113
245
|
```
|
|
114
246
|
|
|
115
247
|
### Data Records
|
|
@@ -136,33 +268,77 @@ interface DataRecord<T> {
|
|
|
136
268
|
```typescript
|
|
137
269
|
import { createClient } from '@seaverse/dataservice';
|
|
138
270
|
|
|
139
|
-
const client = createClient({
|
|
140
|
-
token: string; // JWT token (Bearer prefix optional, auto-added if missing)
|
|
271
|
+
const client = await createClient({
|
|
141
272
|
url?: string; // PostgREST API URL (default: https://dataservice-api.seaverse.ai)
|
|
142
273
|
options?: {
|
|
143
274
|
timeout?: number; // Request timeout in ms (default: 30000)
|
|
275
|
+
tokenFetchTimeout?: number; // Token fetch timeout in ms (default: 5000)
|
|
144
276
|
headers?: Record<string, string>; // Additional headers
|
|
145
277
|
};
|
|
146
278
|
});
|
|
147
279
|
```
|
|
148
280
|
|
|
149
|
-
**
|
|
281
|
+
**Token Authentication Priority:**
|
|
282
|
+
|
|
283
|
+
The SDK uses the following priority order for obtaining authentication tokens:
|
|
284
|
+
|
|
285
|
+
1. **Debug Token (Highest Priority)** - Set via `debugSetToken()`
|
|
286
|
+
2. **Parent Page Token** - Auto-fetched via PostMessage when in iframe
|
|
287
|
+
3. **No Token** - Client created without authentication (API calls will fail with 401)
|
|
288
|
+
|
|
289
|
+
**Production Usage (Auto-fetch from parent):**
|
|
290
|
+
|
|
291
|
+
The SDK automatically fetches the authentication token from the parent page via PostMessage when running in an iframe:
|
|
150
292
|
|
|
151
293
|
```typescript
|
|
152
|
-
//
|
|
153
|
-
const client = createClient({
|
|
154
|
-
token: 'your-jwt-token',
|
|
155
|
-
});
|
|
294
|
+
// No token needed - auto-fetches from parent
|
|
295
|
+
const client = await createClient({});
|
|
156
296
|
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
297
|
+
// Parent page should respond to PostMessage:
|
|
298
|
+
// Send: { type: 'seaverse:get_token' }
|
|
299
|
+
// Receive: { type: 'seaverse:token', payload: { accessToken: string, expiresIn: number } }
|
|
300
|
+
// Error: { type: 'seaverse:error', error: string }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Development/Testing (Debug Token):**
|
|
304
|
+
|
|
305
|
+
For development and testing, use `debugSetToken` to bypass the parent page token fetch:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
309
|
+
|
|
310
|
+
// Set debug token BEFORE creating client (highest priority)
|
|
311
|
+
debugSetToken('your-test-token');
|
|
312
|
+
|
|
313
|
+
// Client will use debug token directly, skipping parent page fetch
|
|
314
|
+
const client = await createClient({});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Graceful Degradation:**
|
|
318
|
+
|
|
319
|
+
If token fetching fails (e.g., not in an iframe, parent doesn't respond), the SDK will create a client without a token. API calls that require authentication will return 401 errors:
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// Client creation never throws, even if token fetch fails
|
|
323
|
+
const client = await createClient({});
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
// API calls may fail with authentication errors if no token
|
|
327
|
+
const data = await client.userData.collection('test').insert({ foo: 'bar' });
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (error instanceof DataServiceError && error.statusCode === 401) {
|
|
330
|
+
console.log('Authentication required - no token available');
|
|
331
|
+
// Handle authentication error (e.g., show login prompt)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Custom Endpoint:**
|
|
161
337
|
|
|
338
|
+
```typescript
|
|
162
339
|
// Use custom API endpoint
|
|
163
|
-
const client = createClient({
|
|
340
|
+
const client = await createClient({
|
|
164
341
|
url: 'https://your-custom-api.example.com',
|
|
165
|
-
token: 'your-jwt-token',
|
|
166
342
|
});
|
|
167
343
|
```
|
|
168
344
|
|
|
@@ -221,17 +397,8 @@ collection.patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>
|
|
|
221
397
|
#### Delete
|
|
222
398
|
|
|
223
399
|
```typescript
|
|
224
|
-
//
|
|
400
|
+
// Delete a record by ID (permanent deletion)
|
|
225
401
|
collection.delete(id: string): Promise<void>
|
|
226
|
-
|
|
227
|
-
// Hard delete all records in collection (returns count of deleted records)
|
|
228
|
-
collection.deleteAll(): Promise<number>
|
|
229
|
-
|
|
230
|
-
// Soft delete (sets deleted_at timestamp)
|
|
231
|
-
collection.softDelete(id: string): Promise<boolean>
|
|
232
|
-
|
|
233
|
-
// Restore soft-deleted record
|
|
234
|
-
collection.restore(id: string): Promise<boolean>
|
|
235
402
|
```
|
|
236
403
|
|
|
237
404
|
### Query Builder
|
|
@@ -309,15 +476,15 @@ type Order = {
|
|
|
309
476
|
notes?: string;
|
|
310
477
|
};
|
|
311
478
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const orders = client.userData.collection<Order>('orders');
|
|
479
|
+
// For development/testing with Node.js
|
|
480
|
+
import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
481
|
+
debugSetToken(process.env.JWT_TOKEN!);
|
|
482
|
+
const client = await createClient({});
|
|
317
483
|
|
|
318
|
-
// Create
|
|
319
|
-
const
|
|
320
|
-
|
|
484
|
+
// Create multiple orders - each with unique collection name
|
|
485
|
+
const orderNumber1 = `ORD-${Date.now()}`;
|
|
486
|
+
const order1 = await client.userData.collection<Order>(`order_${orderNumber1}`).insert({
|
|
487
|
+
order_number: orderNumber1,
|
|
321
488
|
customer_email: 'customer@example.com',
|
|
322
489
|
items: [
|
|
323
490
|
{ product_id: 'PROD-001', quantity: 2, price: 29.99 },
|
|
@@ -327,33 +494,36 @@ const order = await orders.insert({
|
|
|
327
494
|
total: 109.97,
|
|
328
495
|
});
|
|
329
496
|
|
|
330
|
-
console.log('Order created:',
|
|
331
|
-
|
|
332
|
-
// Query pending orders over $50
|
|
333
|
-
const pendingOrders = await orders
|
|
334
|
-
.select()
|
|
335
|
-
.eq('data->>status', 'pending')
|
|
336
|
-
.gt('data->total', '50')
|
|
337
|
-
.order('created_at', { descending: true })
|
|
338
|
-
.limit(10)
|
|
339
|
-
.execute();
|
|
340
|
-
|
|
341
|
-
console.log(`Found ${pendingOrders.length} pending orders`);
|
|
497
|
+
console.log('Order created:', order1.id, 'Collection:', `order_${orderNumber1}`);
|
|
342
498
|
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const customerOrders = await orders.search({
|
|
499
|
+
// Create another order
|
|
500
|
+
const orderNumber2 = `ORD-${Date.now() + 1}`;
|
|
501
|
+
const order2 = await client.userData.collection<Order>(`order_${orderNumber2}`).insert({
|
|
502
|
+
order_number: orderNumber2,
|
|
348
503
|
customer_email: 'customer@example.com',
|
|
504
|
+
items: [
|
|
505
|
+
{ product_id: 'PROD-003', quantity: 1, price: 199.99 },
|
|
506
|
+
],
|
|
507
|
+
status: 'pending',
|
|
508
|
+
total: 199.99,
|
|
349
509
|
});
|
|
350
510
|
|
|
511
|
+
// Update order status (access by collection name)
|
|
512
|
+
await client.userData.collection(`order_${orderNumber1}`).patch(order1.id, {
|
|
513
|
+
status: 'shipped'
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Get specific order
|
|
517
|
+
const retrieved = await client.userData.collection(`order_${orderNumber1}`).get(order1.id);
|
|
518
|
+
console.log('Order status:', retrieved?.data.status);
|
|
519
|
+
|
|
351
520
|
// Delete a specific order
|
|
352
|
-
await
|
|
521
|
+
await client.userData.collection(`order_${orderNumber1}`).delete(order1.id);
|
|
353
522
|
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
523
|
+
// Note: To query across multiple orders, you would need to:
|
|
524
|
+
// 1. Store order metadata in a separate tracking collection
|
|
525
|
+
// 2. Or use a naming convention and iterate through known order IDs
|
|
526
|
+
// 3. Or use the batchInsert pattern and track the generated collection names
|
|
357
527
|
```
|
|
358
528
|
|
|
359
529
|
### Chat Conversations
|
|
@@ -375,10 +545,9 @@ type Conversation = {
|
|
|
375
545
|
};
|
|
376
546
|
};
|
|
377
547
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const conv = await conversations.insert({
|
|
548
|
+
// Create a new conversation with unique ID
|
|
549
|
+
const conversationId = `conv_${Date.now()}`;
|
|
550
|
+
const conv = await client.userData.collection<Conversation>(conversationId).insert({
|
|
382
551
|
title: 'Project Planning Discussion',
|
|
383
552
|
model: 'claude-3-opus',
|
|
384
553
|
messages: [
|
|
@@ -390,10 +559,12 @@ const conv = await conversations.insert({
|
|
|
390
559
|
],
|
|
391
560
|
});
|
|
392
561
|
|
|
393
|
-
|
|
394
|
-
|
|
562
|
+
console.log('Conversation created:', conversationId);
|
|
563
|
+
|
|
564
|
+
// Add assistant response to existing conversation
|
|
565
|
+
const current = await client.userData.collection<Conversation>(conversationId).get(conv.id);
|
|
395
566
|
if (current) {
|
|
396
|
-
await
|
|
567
|
+
await client.userData.collection<Conversation>(conversationId).patch(conv.id, {
|
|
397
568
|
messages: [
|
|
398
569
|
...current.data.messages,
|
|
399
570
|
{
|
|
@@ -405,12 +576,23 @@ if (current) {
|
|
|
405
576
|
});
|
|
406
577
|
}
|
|
407
578
|
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
.
|
|
579
|
+
// Store individual messages separately (alternative pattern)
|
|
580
|
+
// Each message gets its own collection
|
|
581
|
+
const msgId1 = await client.userData.collection(`${conversationId}_msg_1`).insert({
|
|
582
|
+
role: 'user',
|
|
583
|
+
content: 'Help me plan a new feature',
|
|
584
|
+
timestamp: new Date().toISOString(),
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
const msgId2 = await client.userData.collection(`${conversationId}_msg_2`).insert({
|
|
588
|
+
role: 'assistant',
|
|
589
|
+
content: 'I can help with that!',
|
|
590
|
+
timestamp: new Date().toISOString(),
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
// Get a specific conversation
|
|
594
|
+
const retrieved = await client.userData.collection(conversationId).get(conv.id);
|
|
595
|
+
console.log('Conversation title:', retrieved?.data.title);
|
|
414
596
|
```
|
|
415
597
|
|
|
416
598
|
### User Preferences
|
package/dist/index.d.mts
CHANGED
|
@@ -12,15 +12,13 @@
|
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
14
14
|
* const config: ClientConfig = {
|
|
15
|
-
*
|
|
15
|
+
* url: 'https://dataservice-api.seaverse.ai',
|
|
16
16
|
* };
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
interface ClientConfig {
|
|
20
20
|
/** PostgREST API base URL (default: https://dataservice-api.seaverse.ai) */
|
|
21
21
|
url?: string;
|
|
22
|
-
/** JWT token containing user_id in payload.user_id */
|
|
23
|
-
token: string;
|
|
24
22
|
/** Optional configuration */
|
|
25
23
|
options?: {
|
|
26
24
|
/** Custom fetch implementation (useful for Node.js < 18) */
|
|
@@ -29,6 +27,8 @@ interface ClientConfig {
|
|
|
29
27
|
headers?: Record<string, string>;
|
|
30
28
|
/** Request timeout in milliseconds (default: 30000) */
|
|
31
29
|
timeout?: number;
|
|
30
|
+
/** Timeout for fetching token from parent (milliseconds, default: 5000) */
|
|
31
|
+
tokenFetchTimeout?: number;
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
@@ -193,14 +193,8 @@ interface Collection<T = any> {
|
|
|
193
193
|
update(id: string, data: T): Promise<DataRecord<T>>;
|
|
194
194
|
/** Patch a record by ID (merges with existing data) */
|
|
195
195
|
patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>;
|
|
196
|
-
/**
|
|
196
|
+
/** Delete a record by ID (permanent) */
|
|
197
197
|
delete(id: string): Promise<void>;
|
|
198
|
-
/** Hard delete all records in this collection */
|
|
199
|
-
deleteAll(): Promise<number>;
|
|
200
|
-
/** Soft delete a record by ID (sets deleted_at) */
|
|
201
|
-
softDelete(id: string): Promise<boolean>;
|
|
202
|
-
/** Restore a soft-deleted record */
|
|
203
|
-
restore(id: string): Promise<boolean>;
|
|
204
198
|
/** Search records by JSONB contains */
|
|
205
199
|
search(criteria: Partial<T>): Promise<DataRecord<T>[]>;
|
|
206
200
|
/** Count records in collection */
|
|
@@ -246,39 +240,53 @@ interface DataServiceClient {
|
|
|
246
240
|
* 4. Secure: RLS enforced, token-based auth
|
|
247
241
|
*/
|
|
248
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Set debug token for testing/debugging
|
|
245
|
+
* This allows you to bypass the token fetch logic during development
|
|
246
|
+
*
|
|
247
|
+
* @param token The token to use for debugging
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
252
|
+
*
|
|
253
|
+
* // Set debug token before creating client
|
|
254
|
+
* debugSetToken('your-test-token');
|
|
255
|
+
*
|
|
256
|
+
* // Client will use debug token instead of fetching
|
|
257
|
+
* const client = await createClient({});
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
declare function debugSetToken(token: string): void;
|
|
249
261
|
/**
|
|
250
262
|
* Create a new Data Service client
|
|
251
263
|
*
|
|
252
264
|
* AI-friendly: Single entry point with clear configuration
|
|
253
265
|
*
|
|
254
|
-
* @param config - Client configuration
|
|
266
|
+
* @param config - Client configuration
|
|
255
267
|
* @returns DataServiceClient instance
|
|
256
268
|
*
|
|
257
269
|
* @example
|
|
258
270
|
* ```typescript
|
|
259
|
-
* //
|
|
260
|
-
* const client = createClient({
|
|
261
|
-
* token: 'your-jwt-token-here',
|
|
262
|
-
* });
|
|
263
|
-
*
|
|
264
|
-
* // Or with explicit Bearer prefix (both work)
|
|
265
|
-
* const client = createClient({
|
|
266
|
-
* token: 'Bearer your-jwt-token-here',
|
|
267
|
-
* });
|
|
271
|
+
* // Auto-fetch token from parent page (iframe)
|
|
272
|
+
* const client = await createClient({});
|
|
268
273
|
*
|
|
269
|
-
* //
|
|
270
|
-
* const client = createClient({
|
|
274
|
+
* // Custom endpoint
|
|
275
|
+
* const client = await createClient({
|
|
271
276
|
* url: 'https://your-postgrest-api.example.com',
|
|
272
|
-
* token: 'your-jwt-token-here',
|
|
273
277
|
* });
|
|
274
278
|
*
|
|
279
|
+
* // For development/testing, use debugSetToken
|
|
280
|
+
* import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
281
|
+
* debugSetToken('your-test-token');
|
|
282
|
+
* const client = await createClient({});
|
|
283
|
+
*
|
|
275
284
|
* // appId is automatically extracted from current URL
|
|
276
|
-
* // Direct access to collections - clean and simple
|
|
277
285
|
* const order = await client.userData.collection('orders').insert({ ... });
|
|
278
286
|
* const orders = await client.userData.collection('orders').select().execute();
|
|
279
287
|
* ```
|
|
280
288
|
*/
|
|
281
|
-
declare function createClient(config
|
|
289
|
+
declare function createClient(config?: ClientConfig): Promise<DataServiceClient>;
|
|
282
290
|
|
|
283
291
|
/**
|
|
284
292
|
* SeaVerse Data Service SDK
|
|
@@ -287,18 +295,20 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
287
295
|
*
|
|
288
296
|
* @packageDocumentation
|
|
289
297
|
*
|
|
290
|
-
* @example Basic Usage
|
|
298
|
+
* @example Basic Usage with Auto Token Fetch
|
|
291
299
|
* ```typescript
|
|
292
|
-
* import { createClient } from '@seaverse/dataservice';
|
|
300
|
+
* import { createClient, debugSetToken } from '@seaverse/dataservice';
|
|
293
301
|
*
|
|
294
|
-
* //
|
|
295
|
-
* const client = createClient({
|
|
296
|
-
*
|
|
297
|
-
*
|
|
302
|
+
* // Auto-fetch token from parent page (iframe)
|
|
303
|
+
* const client = await createClient({});
|
|
304
|
+
*
|
|
305
|
+
* // For development/testing, use debugSetToken
|
|
306
|
+
* debugSetToken('your-test-token');
|
|
307
|
+
* const client = await createClient({});
|
|
298
308
|
*
|
|
299
309
|
* // appId is automatically extracted from current URL
|
|
300
310
|
* // Insert data
|
|
301
|
-
* const order = await client.userData.collection('
|
|
311
|
+
* const order = await client.userData.collection('order_001').insert({
|
|
302
312
|
* order_number: 'ORD-123',
|
|
303
313
|
* status: 'pending',
|
|
304
314
|
* total: 99.99,
|
|
@@ -306,7 +316,7 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
306
316
|
*
|
|
307
317
|
* // Query data
|
|
308
318
|
* const orders = await client.userData
|
|
309
|
-
* .collection('
|
|
319
|
+
* .collection('order_001')
|
|
310
320
|
* .select()
|
|
311
321
|
* .eq('data->>status', 'pending')
|
|
312
322
|
* .order('created_at', { descending: true })
|
|
@@ -317,4 +327,4 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
317
327
|
|
|
318
328
|
declare const VERSION = "1.0.0";
|
|
319
329
|
|
|
320
|
-
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient };
|
|
330
|
+
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient, debugSetToken };
|
package/dist/index.d.ts
CHANGED
|
@@ -12,15 +12,13 @@
|
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
14
14
|
* const config: ClientConfig = {
|
|
15
|
-
*
|
|
15
|
+
* url: 'https://dataservice-api.seaverse.ai',
|
|
16
16
|
* };
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
interface ClientConfig {
|
|
20
20
|
/** PostgREST API base URL (default: https://dataservice-api.seaverse.ai) */
|
|
21
21
|
url?: string;
|
|
22
|
-
/** JWT token containing user_id in payload.user_id */
|
|
23
|
-
token: string;
|
|
24
22
|
/** Optional configuration */
|
|
25
23
|
options?: {
|
|
26
24
|
/** Custom fetch implementation (useful for Node.js < 18) */
|
|
@@ -29,6 +27,8 @@ interface ClientConfig {
|
|
|
29
27
|
headers?: Record<string, string>;
|
|
30
28
|
/** Request timeout in milliseconds (default: 30000) */
|
|
31
29
|
timeout?: number;
|
|
30
|
+
/** Timeout for fetching token from parent (milliseconds, default: 5000) */
|
|
31
|
+
tokenFetchTimeout?: number;
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
@@ -193,14 +193,8 @@ interface Collection<T = any> {
|
|
|
193
193
|
update(id: string, data: T): Promise<DataRecord<T>>;
|
|
194
194
|
/** Patch a record by ID (merges with existing data) */
|
|
195
195
|
patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>;
|
|
196
|
-
/**
|
|
196
|
+
/** Delete a record by ID (permanent) */
|
|
197
197
|
delete(id: string): Promise<void>;
|
|
198
|
-
/** Hard delete all records in this collection */
|
|
199
|
-
deleteAll(): Promise<number>;
|
|
200
|
-
/** Soft delete a record by ID (sets deleted_at) */
|
|
201
|
-
softDelete(id: string): Promise<boolean>;
|
|
202
|
-
/** Restore a soft-deleted record */
|
|
203
|
-
restore(id: string): Promise<boolean>;
|
|
204
198
|
/** Search records by JSONB contains */
|
|
205
199
|
search(criteria: Partial<T>): Promise<DataRecord<T>[]>;
|
|
206
200
|
/** Count records in collection */
|
|
@@ -246,39 +240,53 @@ interface DataServiceClient {
|
|
|
246
240
|
* 4. Secure: RLS enforced, token-based auth
|
|
247
241
|
*/
|
|
248
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Set debug token for testing/debugging
|
|
245
|
+
* This allows you to bypass the token fetch logic during development
|
|
246
|
+
*
|
|
247
|
+
* @param token The token to use for debugging
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
252
|
+
*
|
|
253
|
+
* // Set debug token before creating client
|
|
254
|
+
* debugSetToken('your-test-token');
|
|
255
|
+
*
|
|
256
|
+
* // Client will use debug token instead of fetching
|
|
257
|
+
* const client = await createClient({});
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
declare function debugSetToken(token: string): void;
|
|
249
261
|
/**
|
|
250
262
|
* Create a new Data Service client
|
|
251
263
|
*
|
|
252
264
|
* AI-friendly: Single entry point with clear configuration
|
|
253
265
|
*
|
|
254
|
-
* @param config - Client configuration
|
|
266
|
+
* @param config - Client configuration
|
|
255
267
|
* @returns DataServiceClient instance
|
|
256
268
|
*
|
|
257
269
|
* @example
|
|
258
270
|
* ```typescript
|
|
259
|
-
* //
|
|
260
|
-
* const client = createClient({
|
|
261
|
-
* token: 'your-jwt-token-here',
|
|
262
|
-
* });
|
|
263
|
-
*
|
|
264
|
-
* // Or with explicit Bearer prefix (both work)
|
|
265
|
-
* const client = createClient({
|
|
266
|
-
* token: 'Bearer your-jwt-token-here',
|
|
267
|
-
* });
|
|
271
|
+
* // Auto-fetch token from parent page (iframe)
|
|
272
|
+
* const client = await createClient({});
|
|
268
273
|
*
|
|
269
|
-
* //
|
|
270
|
-
* const client = createClient({
|
|
274
|
+
* // Custom endpoint
|
|
275
|
+
* const client = await createClient({
|
|
271
276
|
* url: 'https://your-postgrest-api.example.com',
|
|
272
|
-
* token: 'your-jwt-token-here',
|
|
273
277
|
* });
|
|
274
278
|
*
|
|
279
|
+
* // For development/testing, use debugSetToken
|
|
280
|
+
* import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
281
|
+
* debugSetToken('your-test-token');
|
|
282
|
+
* const client = await createClient({});
|
|
283
|
+
*
|
|
275
284
|
* // appId is automatically extracted from current URL
|
|
276
|
-
* // Direct access to collections - clean and simple
|
|
277
285
|
* const order = await client.userData.collection('orders').insert({ ... });
|
|
278
286
|
* const orders = await client.userData.collection('orders').select().execute();
|
|
279
287
|
* ```
|
|
280
288
|
*/
|
|
281
|
-
declare function createClient(config
|
|
289
|
+
declare function createClient(config?: ClientConfig): Promise<DataServiceClient>;
|
|
282
290
|
|
|
283
291
|
/**
|
|
284
292
|
* SeaVerse Data Service SDK
|
|
@@ -287,18 +295,20 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
287
295
|
*
|
|
288
296
|
* @packageDocumentation
|
|
289
297
|
*
|
|
290
|
-
* @example Basic Usage
|
|
298
|
+
* @example Basic Usage with Auto Token Fetch
|
|
291
299
|
* ```typescript
|
|
292
|
-
* import { createClient } from '@seaverse/dataservice';
|
|
300
|
+
* import { createClient, debugSetToken } from '@seaverse/dataservice';
|
|
293
301
|
*
|
|
294
|
-
* //
|
|
295
|
-
* const client = createClient({
|
|
296
|
-
*
|
|
297
|
-
*
|
|
302
|
+
* // Auto-fetch token from parent page (iframe)
|
|
303
|
+
* const client = await createClient({});
|
|
304
|
+
*
|
|
305
|
+
* // For development/testing, use debugSetToken
|
|
306
|
+
* debugSetToken('your-test-token');
|
|
307
|
+
* const client = await createClient({});
|
|
298
308
|
*
|
|
299
309
|
* // appId is automatically extracted from current URL
|
|
300
310
|
* // Insert data
|
|
301
|
-
* const order = await client.userData.collection('
|
|
311
|
+
* const order = await client.userData.collection('order_001').insert({
|
|
302
312
|
* order_number: 'ORD-123',
|
|
303
313
|
* status: 'pending',
|
|
304
314
|
* total: 99.99,
|
|
@@ -306,7 +316,7 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
306
316
|
*
|
|
307
317
|
* // Query data
|
|
308
318
|
* const orders = await client.userData
|
|
309
|
-
* .collection('
|
|
319
|
+
* .collection('order_001')
|
|
310
320
|
* .select()
|
|
311
321
|
* .eq('data->>status', 'pending')
|
|
312
322
|
* .order('created_at', { descending: true })
|
|
@@ -317,4 +327,4 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
317
327
|
|
|
318
328
|
declare const VERSION = "1.0.0";
|
|
319
329
|
|
|
320
|
-
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient };
|
|
330
|
+
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient, debugSetToken };
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,8 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
DataServiceError: () => DataServiceError,
|
|
24
24
|
VERSION: () => VERSION,
|
|
25
|
-
createClient: () => createClient
|
|
25
|
+
createClient: () => createClient,
|
|
26
|
+
debugSetToken: () => debugSetToken
|
|
26
27
|
});
|
|
27
28
|
module.exports = __toCommonJS(src_exports);
|
|
28
29
|
|
|
@@ -39,6 +40,54 @@ var DataServiceError = class extends Error {
|
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
// src/client.ts
|
|
43
|
+
var debugToken = null;
|
|
44
|
+
function debugSetToken(token) {
|
|
45
|
+
debugToken = token;
|
|
46
|
+
}
|
|
47
|
+
function isInIframe() {
|
|
48
|
+
try {
|
|
49
|
+
return typeof globalThis !== "undefined" && "window" in globalThis && globalThis.window.self !== globalThis.window.top;
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function getTokenFromParent(timeout = 5e3) {
|
|
55
|
+
if (!isInIframe()) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
const messageHandler = (event) => {
|
|
60
|
+
if (event.data && event.data.type === "seaverse:token") {
|
|
61
|
+
cleanup();
|
|
62
|
+
const token = event.data.payload?.accessToken;
|
|
63
|
+
resolve(token || null);
|
|
64
|
+
} else if (event.data && event.data.type === "seaverse:error") {
|
|
65
|
+
cleanup();
|
|
66
|
+
console.warn("[SeaVerse DataService SDK] Error getting token from parent:", event.data.error);
|
|
67
|
+
resolve(null);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const timeoutId = setTimeout(() => {
|
|
71
|
+
cleanup();
|
|
72
|
+
resolve(null);
|
|
73
|
+
}, timeout);
|
|
74
|
+
const cleanup = () => {
|
|
75
|
+
clearTimeout(timeoutId);
|
|
76
|
+
globalThis.window.removeEventListener("message", messageHandler);
|
|
77
|
+
};
|
|
78
|
+
globalThis.window.addEventListener("message", messageHandler);
|
|
79
|
+
try {
|
|
80
|
+
globalThis.window.parent.postMessage(
|
|
81
|
+
{ type: "seaverse:get_token" },
|
|
82
|
+
"*"
|
|
83
|
+
// Allow any origin, supports cross-domain scenarios
|
|
84
|
+
);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
cleanup();
|
|
87
|
+
resolve(null);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
42
91
|
function extractAppId() {
|
|
43
92
|
if (typeof globalThis !== "undefined" && "location" in globalThis) {
|
|
44
93
|
const location = globalThis.location;
|
|
@@ -56,17 +105,27 @@ var HTTPClient = class {
|
|
|
56
105
|
headers;
|
|
57
106
|
fetchFn;
|
|
58
107
|
timeout;
|
|
59
|
-
|
|
108
|
+
tokenPromise = null;
|
|
109
|
+
constructor(config, token) {
|
|
60
110
|
this.baseUrl = (config.url || "https://dataservice-api.seaverse.ai").replace(/\/$/, "");
|
|
61
111
|
this.fetchFn = config.options?.fetch || globalThis.fetch;
|
|
62
112
|
this.timeout = config.options?.timeout || 3e4;
|
|
63
|
-
const token = config.token.startsWith("Bearer ") ? config.token : `Bearer ${config.token}`;
|
|
64
113
|
this.headers = {
|
|
65
|
-
"Authorization": token,
|
|
66
114
|
"Content-Type": "application/json",
|
|
67
115
|
"Prefer": "return=representation",
|
|
68
116
|
...config.options?.headers
|
|
69
117
|
};
|
|
118
|
+
if (token) {
|
|
119
|
+
const authToken = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
120
|
+
this.headers["Authorization"] = authToken;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Set authentication token (called after async token fetch)
|
|
125
|
+
*/
|
|
126
|
+
setToken(token) {
|
|
127
|
+
const authToken = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
128
|
+
this.headers["Authorization"] = authToken;
|
|
70
129
|
}
|
|
71
130
|
/**
|
|
72
131
|
* Make HTTP request with timeout and error handling
|
|
@@ -286,30 +345,6 @@ var CollectionImpl = class {
|
|
|
286
345
|
`/user_data?id=eq.${id}&app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
287
346
|
);
|
|
288
347
|
}
|
|
289
|
-
async deleteAll() {
|
|
290
|
-
const records = await this.client.get(
|
|
291
|
-
`/user_data?app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
292
|
-
);
|
|
293
|
-
const count = records.length;
|
|
294
|
-
if (count > 0) {
|
|
295
|
-
await this.client.delete(
|
|
296
|
-
`/user_data?app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
return count;
|
|
300
|
-
}
|
|
301
|
-
async softDelete(id) {
|
|
302
|
-
const response = await this.client.post("/rpc/soft_delete_user_data", {
|
|
303
|
-
p_id: id
|
|
304
|
-
});
|
|
305
|
-
return response;
|
|
306
|
-
}
|
|
307
|
-
async restore(id) {
|
|
308
|
-
const response = await this.client.post("/rpc/restore_user_data", {
|
|
309
|
-
p_id: id
|
|
310
|
-
});
|
|
311
|
-
return response;
|
|
312
|
-
}
|
|
313
348
|
async search(criteria) {
|
|
314
349
|
return this.client.get(this.tablePath, {
|
|
315
350
|
app_id: `eq.${this.appId}`,
|
|
@@ -357,9 +392,9 @@ var DataServiceClientImpl = class {
|
|
|
357
392
|
client;
|
|
358
393
|
userData;
|
|
359
394
|
appId;
|
|
360
|
-
constructor(
|
|
361
|
-
this.client =
|
|
362
|
-
this.appId =
|
|
395
|
+
constructor(client, appId) {
|
|
396
|
+
this.client = client;
|
|
397
|
+
this.appId = appId;
|
|
363
398
|
this.userData = new DataTableImpl(this.client, "/user_data", this.appId);
|
|
364
399
|
}
|
|
365
400
|
async getStats() {
|
|
@@ -370,8 +405,22 @@ var DataServiceClientImpl = class {
|
|
|
370
405
|
return this.client.post("/rpc/health");
|
|
371
406
|
}
|
|
372
407
|
};
|
|
373
|
-
function createClient(config) {
|
|
374
|
-
|
|
408
|
+
async function createClient(config = {}) {
|
|
409
|
+
let token = null;
|
|
410
|
+
if (debugToken) {
|
|
411
|
+
token = debugToken;
|
|
412
|
+
} else {
|
|
413
|
+
try {
|
|
414
|
+
const timeout = config.options?.tokenFetchTimeout || 5e3;
|
|
415
|
+
token = await getTokenFromParent(timeout);
|
|
416
|
+
} catch (error) {
|
|
417
|
+
console.warn("[SeaVerse DataService SDK] Failed to fetch token:", error);
|
|
418
|
+
token = null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const httpClient = new HTTPClient(config, token || void 0);
|
|
422
|
+
const appId = extractAppId();
|
|
423
|
+
return new DataServiceClientImpl(httpClient, appId);
|
|
375
424
|
}
|
|
376
425
|
|
|
377
426
|
// src/index.ts
|
|
@@ -380,5 +429,6 @@ var VERSION = "1.0.0";
|
|
|
380
429
|
0 && (module.exports = {
|
|
381
430
|
DataServiceError,
|
|
382
431
|
VERSION,
|
|
383
|
-
createClient
|
|
432
|
+
createClient,
|
|
433
|
+
debugSetToken
|
|
384
434
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,54 @@ var DataServiceError = class extends Error {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// src/client.ts
|
|
14
|
+
var debugToken = null;
|
|
15
|
+
function debugSetToken(token) {
|
|
16
|
+
debugToken = token;
|
|
17
|
+
}
|
|
18
|
+
function isInIframe() {
|
|
19
|
+
try {
|
|
20
|
+
return typeof globalThis !== "undefined" && "window" in globalThis && globalThis.window.self !== globalThis.window.top;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function getTokenFromParent(timeout = 5e3) {
|
|
26
|
+
if (!isInIframe()) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const messageHandler = (event) => {
|
|
31
|
+
if (event.data && event.data.type === "seaverse:token") {
|
|
32
|
+
cleanup();
|
|
33
|
+
const token = event.data.payload?.accessToken;
|
|
34
|
+
resolve(token || null);
|
|
35
|
+
} else if (event.data && event.data.type === "seaverse:error") {
|
|
36
|
+
cleanup();
|
|
37
|
+
console.warn("[SeaVerse DataService SDK] Error getting token from parent:", event.data.error);
|
|
38
|
+
resolve(null);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const timeoutId = setTimeout(() => {
|
|
42
|
+
cleanup();
|
|
43
|
+
resolve(null);
|
|
44
|
+
}, timeout);
|
|
45
|
+
const cleanup = () => {
|
|
46
|
+
clearTimeout(timeoutId);
|
|
47
|
+
globalThis.window.removeEventListener("message", messageHandler);
|
|
48
|
+
};
|
|
49
|
+
globalThis.window.addEventListener("message", messageHandler);
|
|
50
|
+
try {
|
|
51
|
+
globalThis.window.parent.postMessage(
|
|
52
|
+
{ type: "seaverse:get_token" },
|
|
53
|
+
"*"
|
|
54
|
+
// Allow any origin, supports cross-domain scenarios
|
|
55
|
+
);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
cleanup();
|
|
58
|
+
resolve(null);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
14
62
|
function extractAppId() {
|
|
15
63
|
if (typeof globalThis !== "undefined" && "location" in globalThis) {
|
|
16
64
|
const location = globalThis.location;
|
|
@@ -28,17 +76,27 @@ var HTTPClient = class {
|
|
|
28
76
|
headers;
|
|
29
77
|
fetchFn;
|
|
30
78
|
timeout;
|
|
31
|
-
|
|
79
|
+
tokenPromise = null;
|
|
80
|
+
constructor(config, token) {
|
|
32
81
|
this.baseUrl = (config.url || "https://dataservice-api.seaverse.ai").replace(/\/$/, "");
|
|
33
82
|
this.fetchFn = config.options?.fetch || globalThis.fetch;
|
|
34
83
|
this.timeout = config.options?.timeout || 3e4;
|
|
35
|
-
const token = config.token.startsWith("Bearer ") ? config.token : `Bearer ${config.token}`;
|
|
36
84
|
this.headers = {
|
|
37
|
-
"Authorization": token,
|
|
38
85
|
"Content-Type": "application/json",
|
|
39
86
|
"Prefer": "return=representation",
|
|
40
87
|
...config.options?.headers
|
|
41
88
|
};
|
|
89
|
+
if (token) {
|
|
90
|
+
const authToken = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
91
|
+
this.headers["Authorization"] = authToken;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Set authentication token (called after async token fetch)
|
|
96
|
+
*/
|
|
97
|
+
setToken(token) {
|
|
98
|
+
const authToken = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
99
|
+
this.headers["Authorization"] = authToken;
|
|
42
100
|
}
|
|
43
101
|
/**
|
|
44
102
|
* Make HTTP request with timeout and error handling
|
|
@@ -258,30 +316,6 @@ var CollectionImpl = class {
|
|
|
258
316
|
`/user_data?id=eq.${id}&app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
259
317
|
);
|
|
260
318
|
}
|
|
261
|
-
async deleteAll() {
|
|
262
|
-
const records = await this.client.get(
|
|
263
|
-
`/user_data?app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
264
|
-
);
|
|
265
|
-
const count = records.length;
|
|
266
|
-
if (count > 0) {
|
|
267
|
-
await this.client.delete(
|
|
268
|
-
`/user_data?app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
return count;
|
|
272
|
-
}
|
|
273
|
-
async softDelete(id) {
|
|
274
|
-
const response = await this.client.post("/rpc/soft_delete_user_data", {
|
|
275
|
-
p_id: id
|
|
276
|
-
});
|
|
277
|
-
return response;
|
|
278
|
-
}
|
|
279
|
-
async restore(id) {
|
|
280
|
-
const response = await this.client.post("/rpc/restore_user_data", {
|
|
281
|
-
p_id: id
|
|
282
|
-
});
|
|
283
|
-
return response;
|
|
284
|
-
}
|
|
285
319
|
async search(criteria) {
|
|
286
320
|
return this.client.get(this.tablePath, {
|
|
287
321
|
app_id: `eq.${this.appId}`,
|
|
@@ -329,9 +363,9 @@ var DataServiceClientImpl = class {
|
|
|
329
363
|
client;
|
|
330
364
|
userData;
|
|
331
365
|
appId;
|
|
332
|
-
constructor(
|
|
333
|
-
this.client =
|
|
334
|
-
this.appId =
|
|
366
|
+
constructor(client, appId) {
|
|
367
|
+
this.client = client;
|
|
368
|
+
this.appId = appId;
|
|
335
369
|
this.userData = new DataTableImpl(this.client, "/user_data", this.appId);
|
|
336
370
|
}
|
|
337
371
|
async getStats() {
|
|
@@ -342,8 +376,22 @@ var DataServiceClientImpl = class {
|
|
|
342
376
|
return this.client.post("/rpc/health");
|
|
343
377
|
}
|
|
344
378
|
};
|
|
345
|
-
function createClient(config) {
|
|
346
|
-
|
|
379
|
+
async function createClient(config = {}) {
|
|
380
|
+
let token = null;
|
|
381
|
+
if (debugToken) {
|
|
382
|
+
token = debugToken;
|
|
383
|
+
} else {
|
|
384
|
+
try {
|
|
385
|
+
const timeout = config.options?.tokenFetchTimeout || 5e3;
|
|
386
|
+
token = await getTokenFromParent(timeout);
|
|
387
|
+
} catch (error) {
|
|
388
|
+
console.warn("[SeaVerse DataService SDK] Failed to fetch token:", error);
|
|
389
|
+
token = null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const httpClient = new HTTPClient(config, token || void 0);
|
|
393
|
+
const appId = extractAppId();
|
|
394
|
+
return new DataServiceClientImpl(httpClient, appId);
|
|
347
395
|
}
|
|
348
396
|
|
|
349
397
|
// src/index.ts
|
|
@@ -351,5 +399,6 @@ var VERSION = "1.0.0";
|
|
|
351
399
|
export {
|
|
352
400
|
DataServiceError,
|
|
353
401
|
VERSION,
|
|
354
|
-
createClient
|
|
402
|
+
createClient,
|
|
403
|
+
debugSetToken
|
|
355
404
|
};
|