@seaverse/dataservice 1.2.0 → 1.5.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 +252 -76
- package/dist/index.d.mts +51 -22
- package/dist/index.d.ts +51 -22
- package/dist/index.js +88 -21
- package/dist/index.mjs +86 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,33 +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
|
+
// Option 1: Auto-fetch token from parent page (iframe only)
|
|
26
|
+
const client = await createClient({});
|
|
27
|
+
|
|
28
|
+
// Option 2: Provide token explicitly
|
|
29
|
+
const client = await createClient({
|
|
30
|
+
token: 'your-jwt-token',
|
|
28
31
|
});
|
|
29
32
|
|
|
30
|
-
//
|
|
31
|
-
const
|
|
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,
|
|
39
|
+
});
|
|
32
40
|
|
|
33
|
-
//
|
|
34
|
-
|
|
41
|
+
// Update preferences
|
|
42
|
+
await userPrefs.patch(userPrefs.id, { theme: 'light' });
|
|
43
|
+
|
|
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();
|
|
51
|
+
const order2 = await client.userData.collection('order_002').insert({
|
|
52
|
+
order_number: 'ORD-002',
|
|
53
|
+
status: 'shipped',
|
|
54
|
+
total: 149.99,
|
|
55
|
+
});
|
|
46
56
|
|
|
47
|
-
//
|
|
48
|
-
|
|
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
|
+
]);
|
|
49
62
|
|
|
50
|
-
// Delete
|
|
51
|
-
await
|
|
63
|
+
// Delete a specific record
|
|
64
|
+
await client.userData.collection('order_001').delete(order1.id);
|
|
52
65
|
```
|
|
53
66
|
|
|
54
67
|
## Core Concepts
|
|
@@ -91,22 +104,144 @@ The SDK provides access to different data tables with different permission scope
|
|
|
91
104
|
|
|
92
105
|
### Collections
|
|
93
106
|
|
|
94
|
-
|
|
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.
|
|
95
110
|
|
|
96
|
-
**
|
|
111
|
+
**To store multiple records, you MUST use unique collection names**:
|
|
97
112
|
|
|
98
113
|
```typescript
|
|
99
|
-
// ✓
|
|
100
|
-
await client.userData.collection('
|
|
101
|
-
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);
|
|
102
118
|
|
|
103
|
-
// ✓
|
|
104
|
-
await client.userData.batchInsert('order', [order1, order2]);
|
|
105
|
-
// 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
|
|
106
122
|
|
|
107
|
-
// ✗
|
|
108
|
-
|
|
109
|
-
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);
|
|
110
245
|
```
|
|
111
246
|
|
|
112
247
|
### Data Records
|
|
@@ -133,28 +268,54 @@ interface DataRecord<T> {
|
|
|
133
268
|
```typescript
|
|
134
269
|
import { createClient } from '@seaverse/dataservice';
|
|
135
270
|
|
|
136
|
-
const client = createClient({
|
|
137
|
-
token
|
|
271
|
+
const client = await createClient({
|
|
272
|
+
token?: string; // JWT token (optional, auto-fetched from parent if in iframe)
|
|
138
273
|
url?: string; // PostgREST API URL (default: https://dataservice-api.seaverse.ai)
|
|
139
274
|
options?: {
|
|
140
275
|
timeout?: number; // Request timeout in ms (default: 30000)
|
|
276
|
+
tokenFetchTimeout?: number; // Token fetch timeout in ms (default: 5000)
|
|
141
277
|
headers?: Record<string, string>; // Additional headers
|
|
142
278
|
};
|
|
143
279
|
});
|
|
144
280
|
```
|
|
145
281
|
|
|
146
|
-
**
|
|
282
|
+
**Token Options:**
|
|
283
|
+
|
|
284
|
+
The SDK supports two ways to provide authentication:
|
|
285
|
+
|
|
286
|
+
1. **Auto-fetch from parent page (iframe only)**: When running in an iframe, the SDK can automatically request the token from the parent page via PostMessage:
|
|
147
287
|
|
|
148
288
|
```typescript
|
|
149
|
-
//
|
|
150
|
-
const client = createClient({
|
|
289
|
+
// No token needed - auto-fetches from parent
|
|
290
|
+
const client = await createClient({});
|
|
291
|
+
|
|
292
|
+
// Parent page should respond to PostMessage:
|
|
293
|
+
// Send: { type: 'seaverse:get_token' }
|
|
294
|
+
// Receive: { type: 'seaverse:token', payload: { accessToken: string, expiresIn: number } }
|
|
295
|
+
// Error: { type: 'seaverse:error', error: string }
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
2. **Explicit token**: Provide the token directly (Bearer prefix is auto-added):
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// Token without Bearer prefix
|
|
302
|
+
const client = await createClient({
|
|
303
|
+
token: 'your-jwt-token',
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Or with explicit Bearer prefix (both work)
|
|
307
|
+
const client = await createClient({
|
|
151
308
|
token: 'Bearer your-jwt-token',
|
|
152
309
|
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Custom Endpoint:**
|
|
153
313
|
|
|
314
|
+
```typescript
|
|
154
315
|
// Use custom API endpoint
|
|
155
|
-
const client = createClient({
|
|
316
|
+
const client = await createClient({
|
|
156
317
|
url: 'https://your-custom-api.example.com',
|
|
157
|
-
token: '
|
|
318
|
+
token: 'your-jwt-token',
|
|
158
319
|
});
|
|
159
320
|
```
|
|
160
321
|
|
|
@@ -213,14 +374,8 @@ collection.patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>
|
|
|
213
374
|
#### Delete
|
|
214
375
|
|
|
215
376
|
```typescript
|
|
216
|
-
//
|
|
377
|
+
// Delete a record by ID (permanent deletion)
|
|
217
378
|
collection.delete(id: string): Promise<void>
|
|
218
|
-
|
|
219
|
-
// Soft delete (sets deleted_at timestamp)
|
|
220
|
-
collection.softDelete(id: string): Promise<boolean>
|
|
221
|
-
|
|
222
|
-
// Restore soft-deleted record
|
|
223
|
-
collection.restore(id: string): Promise<boolean>
|
|
224
379
|
```
|
|
225
380
|
|
|
226
381
|
### Query Builder
|
|
@@ -302,11 +457,10 @@ const client = createClient({
|
|
|
302
457
|
token: process.env.JWT_TOKEN!,
|
|
303
458
|
});
|
|
304
459
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
order_number: `ORD-${Date.now()}`,
|
|
460
|
+
// Create multiple orders - each with unique collection name
|
|
461
|
+
const orderNumber1 = `ORD-${Date.now()}`;
|
|
462
|
+
const order1 = await client.userData.collection<Order>(`order_${orderNumber1}`).insert({
|
|
463
|
+
order_number: orderNumber1,
|
|
310
464
|
customer_email: 'customer@example.com',
|
|
311
465
|
items: [
|
|
312
466
|
{ product_id: 'PROD-001', quantity: 2, price: 29.99 },
|
|
@@ -316,26 +470,36 @@ const order = await orders.insert({
|
|
|
316
470
|
total: 109.97,
|
|
317
471
|
});
|
|
318
472
|
|
|
319
|
-
console.log('Order created:',
|
|
473
|
+
console.log('Order created:', order1.id, 'Collection:', `order_${orderNumber1}`);
|
|
474
|
+
|
|
475
|
+
// Create another order
|
|
476
|
+
const orderNumber2 = `ORD-${Date.now() + 1}`;
|
|
477
|
+
const order2 = await client.userData.collection<Order>(`order_${orderNumber2}`).insert({
|
|
478
|
+
order_number: orderNumber2,
|
|
479
|
+
customer_email: 'customer@example.com',
|
|
480
|
+
items: [
|
|
481
|
+
{ product_id: 'PROD-003', quantity: 1, price: 199.99 },
|
|
482
|
+
],
|
|
483
|
+
status: 'pending',
|
|
484
|
+
total: 199.99,
|
|
485
|
+
});
|
|
320
486
|
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
.gt('data->total', '50')
|
|
326
|
-
.order('created_at', { descending: true })
|
|
327
|
-
.limit(10)
|
|
328
|
-
.execute();
|
|
487
|
+
// Update order status (access by collection name)
|
|
488
|
+
await client.userData.collection(`order_${orderNumber1}`).patch(order1.id, {
|
|
489
|
+
status: 'shipped'
|
|
490
|
+
});
|
|
329
491
|
|
|
330
|
-
|
|
492
|
+
// Get specific order
|
|
493
|
+
const retrieved = await client.userData.collection(`order_${orderNumber1}`).get(order1.id);
|
|
494
|
+
console.log('Order status:', retrieved?.data.status);
|
|
331
495
|
|
|
332
|
-
//
|
|
333
|
-
await
|
|
496
|
+
// Delete a specific order
|
|
497
|
+
await client.userData.collection(`order_${orderNumber1}`).delete(order1.id);
|
|
334
498
|
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
499
|
+
// Note: To query across multiple orders, you would need to:
|
|
500
|
+
// 1. Store order metadata in a separate tracking collection
|
|
501
|
+
// 2. Or use a naming convention and iterate through known order IDs
|
|
502
|
+
// 3. Or use the batchInsert pattern and track the generated collection names
|
|
339
503
|
```
|
|
340
504
|
|
|
341
505
|
### Chat Conversations
|
|
@@ -357,10 +521,9 @@ type Conversation = {
|
|
|
357
521
|
};
|
|
358
522
|
};
|
|
359
523
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const conv = await conversations.insert({
|
|
524
|
+
// Create a new conversation with unique ID
|
|
525
|
+
const conversationId = `conv_${Date.now()}`;
|
|
526
|
+
const conv = await client.userData.collection<Conversation>(conversationId).insert({
|
|
364
527
|
title: 'Project Planning Discussion',
|
|
365
528
|
model: 'claude-3-opus',
|
|
366
529
|
messages: [
|
|
@@ -372,10 +535,12 @@ const conv = await conversations.insert({
|
|
|
372
535
|
],
|
|
373
536
|
});
|
|
374
537
|
|
|
375
|
-
|
|
376
|
-
|
|
538
|
+
console.log('Conversation created:', conversationId);
|
|
539
|
+
|
|
540
|
+
// Add assistant response to existing conversation
|
|
541
|
+
const current = await client.userData.collection<Conversation>(conversationId).get(conv.id);
|
|
377
542
|
if (current) {
|
|
378
|
-
await
|
|
543
|
+
await client.userData.collection<Conversation>(conversationId).patch(conv.id, {
|
|
379
544
|
messages: [
|
|
380
545
|
...current.data.messages,
|
|
381
546
|
{
|
|
@@ -387,12 +552,23 @@ if (current) {
|
|
|
387
552
|
});
|
|
388
553
|
}
|
|
389
554
|
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
.
|
|
555
|
+
// Store individual messages separately (alternative pattern)
|
|
556
|
+
// Each message gets its own collection
|
|
557
|
+
const msgId1 = await client.userData.collection(`${conversationId}_msg_1`).insert({
|
|
558
|
+
role: 'user',
|
|
559
|
+
content: 'Help me plan a new feature',
|
|
560
|
+
timestamp: new Date().toISOString(),
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
const msgId2 = await client.userData.collection(`${conversationId}_msg_2`).insert({
|
|
564
|
+
role: 'assistant',
|
|
565
|
+
content: 'I can help with that!',
|
|
566
|
+
timestamp: new Date().toISOString(),
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// Get a specific conversation
|
|
570
|
+
const retrieved = await client.userData.collection(conversationId).get(conv.id);
|
|
571
|
+
console.log('Conversation title:', retrieved?.data.title);
|
|
396
572
|
```
|
|
397
573
|
|
|
398
574
|
### User Preferences
|
package/dist/index.d.mts
CHANGED
|
@@ -19,8 +19,11 @@
|
|
|
19
19
|
interface ClientConfig {
|
|
20
20
|
/** PostgREST API base URL (default: https://dataservice-api.seaverse.ai) */
|
|
21
21
|
url?: string;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* JWT token containing user_id in payload.user_id
|
|
24
|
+
* If not provided, will attempt to fetch from parent page via PostMessage (iframe only)
|
|
25
|
+
*/
|
|
26
|
+
token?: string;
|
|
24
27
|
/** Optional configuration */
|
|
25
28
|
options?: {
|
|
26
29
|
/** Custom fetch implementation (useful for Node.js < 18) */
|
|
@@ -29,6 +32,8 @@ interface ClientConfig {
|
|
|
29
32
|
headers?: Record<string, string>;
|
|
30
33
|
/** Request timeout in milliseconds (default: 30000) */
|
|
31
34
|
timeout?: number;
|
|
35
|
+
/** Timeout for fetching token from parent (milliseconds, default: 5000) */
|
|
36
|
+
tokenFetchTimeout?: number;
|
|
32
37
|
};
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
@@ -193,12 +198,8 @@ interface Collection<T = any> {
|
|
|
193
198
|
update(id: string, data: T): Promise<DataRecord<T>>;
|
|
194
199
|
/** Patch a record by ID (merges with existing data) */
|
|
195
200
|
patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>;
|
|
196
|
-
/**
|
|
201
|
+
/** Delete a record by ID (permanent) */
|
|
197
202
|
delete(id: string): Promise<void>;
|
|
198
|
-
/** Soft delete a record by ID (sets deleted_at) */
|
|
199
|
-
softDelete(id: string): Promise<boolean>;
|
|
200
|
-
/** Restore a soft-deleted record */
|
|
201
|
-
restore(id: string): Promise<boolean>;
|
|
202
203
|
/** Search records by JSONB contains */
|
|
203
204
|
search(criteria: Partial<T>): Promise<DataRecord<T>[]>;
|
|
204
205
|
/** Count records in collection */
|
|
@@ -244,34 +245,59 @@ interface DataServiceClient {
|
|
|
244
245
|
* 4. Secure: RLS enforced, token-based auth
|
|
245
246
|
*/
|
|
246
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Set debug token for testing/debugging
|
|
250
|
+
* This allows you to bypass the token fetch logic during development
|
|
251
|
+
*
|
|
252
|
+
* @param token The token to use for debugging
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
257
|
+
*
|
|
258
|
+
* // Set debug token before creating client
|
|
259
|
+
* debugSetToken('your-test-token');
|
|
260
|
+
*
|
|
261
|
+
* // Client will use debug token instead of fetching
|
|
262
|
+
* const client = await createClient({});
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
declare function debugSetToken(token: string): void;
|
|
247
266
|
/**
|
|
248
267
|
* Create a new Data Service client
|
|
249
268
|
*
|
|
250
269
|
* AI-friendly: Single entry point with clear configuration
|
|
251
270
|
*
|
|
252
|
-
* @param config - Client configuration
|
|
271
|
+
* @param config - Client configuration (token optional, will auto-fetch from parent if in iframe)
|
|
253
272
|
* @returns DataServiceClient instance
|
|
254
273
|
*
|
|
255
274
|
* @example
|
|
256
275
|
* ```typescript
|
|
257
|
-
* //
|
|
258
|
-
* const client = createClient({
|
|
276
|
+
* // Auto-fetch token from parent page (iframe only)
|
|
277
|
+
* const client = await createClient({});
|
|
278
|
+
*
|
|
279
|
+
* // Or provide token explicitly
|
|
280
|
+
* const client = await createClient({
|
|
281
|
+
* token: 'your-jwt-token-here',
|
|
282
|
+
* });
|
|
283
|
+
*
|
|
284
|
+
* // Bearer prefix is auto-added
|
|
285
|
+
* const client = await createClient({
|
|
259
286
|
* token: 'Bearer your-jwt-token-here',
|
|
260
287
|
* });
|
|
261
288
|
*
|
|
262
|
-
* //
|
|
263
|
-
* const client = createClient({
|
|
289
|
+
* // Custom endpoint
|
|
290
|
+
* const client = await createClient({
|
|
264
291
|
* url: 'https://your-postgrest-api.example.com',
|
|
265
|
-
* token: '
|
|
292
|
+
* token: 'your-jwt-token-here',
|
|
266
293
|
* });
|
|
267
294
|
*
|
|
268
295
|
* // appId is automatically extracted from current URL
|
|
269
|
-
* // Direct access to collections - clean and simple
|
|
270
296
|
* const order = await client.userData.collection('orders').insert({ ... });
|
|
271
297
|
* const orders = await client.userData.collection('orders').select().execute();
|
|
272
298
|
* ```
|
|
273
299
|
*/
|
|
274
|
-
declare function createClient(config
|
|
300
|
+
declare function createClient(config?: ClientConfig): Promise<DataServiceClient>;
|
|
275
301
|
|
|
276
302
|
/**
|
|
277
303
|
* SeaVerse Data Service SDK
|
|
@@ -280,18 +306,21 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
280
306
|
*
|
|
281
307
|
* @packageDocumentation
|
|
282
308
|
*
|
|
283
|
-
* @example Basic Usage
|
|
309
|
+
* @example Basic Usage with Auto Token Fetch
|
|
284
310
|
* ```typescript
|
|
285
311
|
* import { createClient } from '@seaverse/dataservice';
|
|
286
312
|
*
|
|
287
|
-
* //
|
|
288
|
-
* const client = createClient({
|
|
289
|
-
*
|
|
313
|
+
* // Auto-fetch token from parent page (iframe only)
|
|
314
|
+
* const client = await createClient({});
|
|
315
|
+
*
|
|
316
|
+
* // Or provide token explicitly
|
|
317
|
+
* const client = await createClient({
|
|
318
|
+
* token: 'your-jwt-token',
|
|
290
319
|
* });
|
|
291
320
|
*
|
|
292
321
|
* // appId is automatically extracted from current URL
|
|
293
322
|
* // Insert data
|
|
294
|
-
* const order = await client.userData.collection('
|
|
323
|
+
* const order = await client.userData.collection('order_001').insert({
|
|
295
324
|
* order_number: 'ORD-123',
|
|
296
325
|
* status: 'pending',
|
|
297
326
|
* total: 99.99,
|
|
@@ -299,7 +328,7 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
299
328
|
*
|
|
300
329
|
* // Query data
|
|
301
330
|
* const orders = await client.userData
|
|
302
|
-
* .collection('
|
|
331
|
+
* .collection('order_001')
|
|
303
332
|
* .select()
|
|
304
333
|
* .eq('data->>status', 'pending')
|
|
305
334
|
* .order('created_at', { descending: true })
|
|
@@ -310,4 +339,4 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
310
339
|
|
|
311
340
|
declare const VERSION = "1.0.0";
|
|
312
341
|
|
|
313
|
-
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient };
|
|
342
|
+
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
|
@@ -19,8 +19,11 @@
|
|
|
19
19
|
interface ClientConfig {
|
|
20
20
|
/** PostgREST API base URL (default: https://dataservice-api.seaverse.ai) */
|
|
21
21
|
url?: string;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* JWT token containing user_id in payload.user_id
|
|
24
|
+
* If not provided, will attempt to fetch from parent page via PostMessage (iframe only)
|
|
25
|
+
*/
|
|
26
|
+
token?: string;
|
|
24
27
|
/** Optional configuration */
|
|
25
28
|
options?: {
|
|
26
29
|
/** Custom fetch implementation (useful for Node.js < 18) */
|
|
@@ -29,6 +32,8 @@ interface ClientConfig {
|
|
|
29
32
|
headers?: Record<string, string>;
|
|
30
33
|
/** Request timeout in milliseconds (default: 30000) */
|
|
31
34
|
timeout?: number;
|
|
35
|
+
/** Timeout for fetching token from parent (milliseconds, default: 5000) */
|
|
36
|
+
tokenFetchTimeout?: number;
|
|
32
37
|
};
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
@@ -193,12 +198,8 @@ interface Collection<T = any> {
|
|
|
193
198
|
update(id: string, data: T): Promise<DataRecord<T>>;
|
|
194
199
|
/** Patch a record by ID (merges with existing data) */
|
|
195
200
|
patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>;
|
|
196
|
-
/**
|
|
201
|
+
/** Delete a record by ID (permanent) */
|
|
197
202
|
delete(id: string): Promise<void>;
|
|
198
|
-
/** Soft delete a record by ID (sets deleted_at) */
|
|
199
|
-
softDelete(id: string): Promise<boolean>;
|
|
200
|
-
/** Restore a soft-deleted record */
|
|
201
|
-
restore(id: string): Promise<boolean>;
|
|
202
203
|
/** Search records by JSONB contains */
|
|
203
204
|
search(criteria: Partial<T>): Promise<DataRecord<T>[]>;
|
|
204
205
|
/** Count records in collection */
|
|
@@ -244,34 +245,59 @@ interface DataServiceClient {
|
|
|
244
245
|
* 4. Secure: RLS enforced, token-based auth
|
|
245
246
|
*/
|
|
246
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Set debug token for testing/debugging
|
|
250
|
+
* This allows you to bypass the token fetch logic during development
|
|
251
|
+
*
|
|
252
|
+
* @param token The token to use for debugging
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* import { debugSetToken, createClient } from '@seaverse/dataservice';
|
|
257
|
+
*
|
|
258
|
+
* // Set debug token before creating client
|
|
259
|
+
* debugSetToken('your-test-token');
|
|
260
|
+
*
|
|
261
|
+
* // Client will use debug token instead of fetching
|
|
262
|
+
* const client = await createClient({});
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
declare function debugSetToken(token: string): void;
|
|
247
266
|
/**
|
|
248
267
|
* Create a new Data Service client
|
|
249
268
|
*
|
|
250
269
|
* AI-friendly: Single entry point with clear configuration
|
|
251
270
|
*
|
|
252
|
-
* @param config - Client configuration
|
|
271
|
+
* @param config - Client configuration (token optional, will auto-fetch from parent if in iframe)
|
|
253
272
|
* @returns DataServiceClient instance
|
|
254
273
|
*
|
|
255
274
|
* @example
|
|
256
275
|
* ```typescript
|
|
257
|
-
* //
|
|
258
|
-
* const client = createClient({
|
|
276
|
+
* // Auto-fetch token from parent page (iframe only)
|
|
277
|
+
* const client = await createClient({});
|
|
278
|
+
*
|
|
279
|
+
* // Or provide token explicitly
|
|
280
|
+
* const client = await createClient({
|
|
281
|
+
* token: 'your-jwt-token-here',
|
|
282
|
+
* });
|
|
283
|
+
*
|
|
284
|
+
* // Bearer prefix is auto-added
|
|
285
|
+
* const client = await createClient({
|
|
259
286
|
* token: 'Bearer your-jwt-token-here',
|
|
260
287
|
* });
|
|
261
288
|
*
|
|
262
|
-
* //
|
|
263
|
-
* const client = createClient({
|
|
289
|
+
* // Custom endpoint
|
|
290
|
+
* const client = await createClient({
|
|
264
291
|
* url: 'https://your-postgrest-api.example.com',
|
|
265
|
-
* token: '
|
|
292
|
+
* token: 'your-jwt-token-here',
|
|
266
293
|
* });
|
|
267
294
|
*
|
|
268
295
|
* // appId is automatically extracted from current URL
|
|
269
|
-
* // Direct access to collections - clean and simple
|
|
270
296
|
* const order = await client.userData.collection('orders').insert({ ... });
|
|
271
297
|
* const orders = await client.userData.collection('orders').select().execute();
|
|
272
298
|
* ```
|
|
273
299
|
*/
|
|
274
|
-
declare function createClient(config
|
|
300
|
+
declare function createClient(config?: ClientConfig): Promise<DataServiceClient>;
|
|
275
301
|
|
|
276
302
|
/**
|
|
277
303
|
* SeaVerse Data Service SDK
|
|
@@ -280,18 +306,21 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
280
306
|
*
|
|
281
307
|
* @packageDocumentation
|
|
282
308
|
*
|
|
283
|
-
* @example Basic Usage
|
|
309
|
+
* @example Basic Usage with Auto Token Fetch
|
|
284
310
|
* ```typescript
|
|
285
311
|
* import { createClient } from '@seaverse/dataservice';
|
|
286
312
|
*
|
|
287
|
-
* //
|
|
288
|
-
* const client = createClient({
|
|
289
|
-
*
|
|
313
|
+
* // Auto-fetch token from parent page (iframe only)
|
|
314
|
+
* const client = await createClient({});
|
|
315
|
+
*
|
|
316
|
+
* // Or provide token explicitly
|
|
317
|
+
* const client = await createClient({
|
|
318
|
+
* token: 'your-jwt-token',
|
|
290
319
|
* });
|
|
291
320
|
*
|
|
292
321
|
* // appId is automatically extracted from current URL
|
|
293
322
|
* // Insert data
|
|
294
|
-
* const order = await client.userData.collection('
|
|
323
|
+
* const order = await client.userData.collection('order_001').insert({
|
|
295
324
|
* order_number: 'ORD-123',
|
|
296
325
|
* status: 'pending',
|
|
297
326
|
* total: 99.99,
|
|
@@ -299,7 +328,7 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
299
328
|
*
|
|
300
329
|
* // Query data
|
|
301
330
|
* const orders = await client.userData
|
|
302
|
-
* .collection('
|
|
331
|
+
* .collection('order_001')
|
|
303
332
|
* .select()
|
|
304
333
|
* .eq('data->>status', 'pending')
|
|
305
334
|
* .order('created_at', { descending: true })
|
|
@@ -310,4 +339,4 @@ declare function createClient(config: ClientConfig): DataServiceClient;
|
|
|
310
339
|
|
|
311
340
|
declare const VERSION = "1.0.0";
|
|
312
341
|
|
|
313
|
-
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient };
|
|
342
|
+
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,16 +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
113
|
this.headers = {
|
|
64
|
-
"Authorization": config.token,
|
|
65
114
|
"Content-Type": "application/json",
|
|
66
115
|
"Prefer": "return=representation",
|
|
67
116
|
...config.options?.headers
|
|
68
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;
|
|
69
129
|
}
|
|
70
130
|
/**
|
|
71
131
|
* Make HTTP request with timeout and error handling
|
|
@@ -285,18 +345,6 @@ var CollectionImpl = class {
|
|
|
285
345
|
`/user_data?id=eq.${id}&app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
286
346
|
);
|
|
287
347
|
}
|
|
288
|
-
async softDelete(id) {
|
|
289
|
-
const response = await this.client.post("/rpc/soft_delete_user_data", {
|
|
290
|
-
p_id: id
|
|
291
|
-
});
|
|
292
|
-
return response;
|
|
293
|
-
}
|
|
294
|
-
async restore(id) {
|
|
295
|
-
const response = await this.client.post("/rpc/restore_user_data", {
|
|
296
|
-
p_id: id
|
|
297
|
-
});
|
|
298
|
-
return response;
|
|
299
|
-
}
|
|
300
348
|
async search(criteria) {
|
|
301
349
|
return this.client.get(this.tablePath, {
|
|
302
350
|
app_id: `eq.${this.appId}`,
|
|
@@ -344,9 +392,9 @@ var DataServiceClientImpl = class {
|
|
|
344
392
|
client;
|
|
345
393
|
userData;
|
|
346
394
|
appId;
|
|
347
|
-
constructor(
|
|
348
|
-
this.client =
|
|
349
|
-
this.appId =
|
|
395
|
+
constructor(client, appId) {
|
|
396
|
+
this.client = client;
|
|
397
|
+
this.appId = appId;
|
|
350
398
|
this.userData = new DataTableImpl(this.client, "/user_data", this.appId);
|
|
351
399
|
}
|
|
352
400
|
async getStats() {
|
|
@@ -357,8 +405,26 @@ var DataServiceClientImpl = class {
|
|
|
357
405
|
return this.client.post("/rpc/health");
|
|
358
406
|
}
|
|
359
407
|
};
|
|
360
|
-
function createClient(config) {
|
|
361
|
-
|
|
408
|
+
async function createClient(config = {}) {
|
|
409
|
+
let token = config.token;
|
|
410
|
+
if (!token) {
|
|
411
|
+
if (debugToken) {
|
|
412
|
+
token = debugToken;
|
|
413
|
+
} else {
|
|
414
|
+
const timeout = config.options?.tokenFetchTimeout || 5e3;
|
|
415
|
+
const fetchedToken = await getTokenFromParent(timeout);
|
|
416
|
+
if (!fetchedToken) {
|
|
417
|
+
throw new DataServiceError(
|
|
418
|
+
"No token provided and failed to fetch from parent page. Please provide a token or ensure the app is running in an iframe with a parent that responds to seaverse:get_token messages.",
|
|
419
|
+
"NO_TOKEN"
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
token = fetchedToken;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const httpClient = new HTTPClient(config, token);
|
|
426
|
+
const appId = extractAppId();
|
|
427
|
+
return new DataServiceClientImpl(httpClient, appId);
|
|
362
428
|
}
|
|
363
429
|
|
|
364
430
|
// src/index.ts
|
|
@@ -367,5 +433,6 @@ var VERSION = "1.0.0";
|
|
|
367
433
|
0 && (module.exports = {
|
|
368
434
|
DataServiceError,
|
|
369
435
|
VERSION,
|
|
370
|
-
createClient
|
|
436
|
+
createClient,
|
|
437
|
+
debugSetToken
|
|
371
438
|
});
|
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,16 +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
84
|
this.headers = {
|
|
36
|
-
"Authorization": config.token,
|
|
37
85
|
"Content-Type": "application/json",
|
|
38
86
|
"Prefer": "return=representation",
|
|
39
87
|
...config.options?.headers
|
|
40
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;
|
|
41
100
|
}
|
|
42
101
|
/**
|
|
43
102
|
* Make HTTP request with timeout and error handling
|
|
@@ -257,18 +316,6 @@ var CollectionImpl = class {
|
|
|
257
316
|
`/user_data?id=eq.${id}&app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
258
317
|
);
|
|
259
318
|
}
|
|
260
|
-
async softDelete(id) {
|
|
261
|
-
const response = await this.client.post("/rpc/soft_delete_user_data", {
|
|
262
|
-
p_id: id
|
|
263
|
-
});
|
|
264
|
-
return response;
|
|
265
|
-
}
|
|
266
|
-
async restore(id) {
|
|
267
|
-
const response = await this.client.post("/rpc/restore_user_data", {
|
|
268
|
-
p_id: id
|
|
269
|
-
});
|
|
270
|
-
return response;
|
|
271
|
-
}
|
|
272
319
|
async search(criteria) {
|
|
273
320
|
return this.client.get(this.tablePath, {
|
|
274
321
|
app_id: `eq.${this.appId}`,
|
|
@@ -316,9 +363,9 @@ var DataServiceClientImpl = class {
|
|
|
316
363
|
client;
|
|
317
364
|
userData;
|
|
318
365
|
appId;
|
|
319
|
-
constructor(
|
|
320
|
-
this.client =
|
|
321
|
-
this.appId =
|
|
366
|
+
constructor(client, appId) {
|
|
367
|
+
this.client = client;
|
|
368
|
+
this.appId = appId;
|
|
322
369
|
this.userData = new DataTableImpl(this.client, "/user_data", this.appId);
|
|
323
370
|
}
|
|
324
371
|
async getStats() {
|
|
@@ -329,8 +376,26 @@ var DataServiceClientImpl = class {
|
|
|
329
376
|
return this.client.post("/rpc/health");
|
|
330
377
|
}
|
|
331
378
|
};
|
|
332
|
-
function createClient(config) {
|
|
333
|
-
|
|
379
|
+
async function createClient(config = {}) {
|
|
380
|
+
let token = config.token;
|
|
381
|
+
if (!token) {
|
|
382
|
+
if (debugToken) {
|
|
383
|
+
token = debugToken;
|
|
384
|
+
} else {
|
|
385
|
+
const timeout = config.options?.tokenFetchTimeout || 5e3;
|
|
386
|
+
const fetchedToken = await getTokenFromParent(timeout);
|
|
387
|
+
if (!fetchedToken) {
|
|
388
|
+
throw new DataServiceError(
|
|
389
|
+
"No token provided and failed to fetch from parent page. Please provide a token or ensure the app is running in an iframe with a parent that responds to seaverse:get_token messages.",
|
|
390
|
+
"NO_TOKEN"
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
token = fetchedToken;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const httpClient = new HTTPClient(config, token);
|
|
397
|
+
const appId = extractAppId();
|
|
398
|
+
return new DataServiceClientImpl(httpClient, appId);
|
|
334
399
|
}
|
|
335
400
|
|
|
336
401
|
// src/index.ts
|
|
@@ -338,5 +403,6 @@ var VERSION = "1.0.0";
|
|
|
338
403
|
export {
|
|
339
404
|
DataServiceError,
|
|
340
405
|
VERSION,
|
|
341
|
-
createClient
|
|
406
|
+
createClient,
|
|
407
|
+
debugSetToken
|
|
342
408
|
};
|