@seaverse/dataservice 1.0.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/LICENSE +21 -0
- package/README.md +389 -0
- package/dist/index.d.mts +308 -0
- package/dist/index.d.ts +308 -0
- package/dist/index.js +371 -0
- package/dist/index.mjs +342 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SeaVerse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# SeaVerse Data Service SDK
|
|
2
|
+
|
|
3
|
+
TypeScript/JavaScript SDK for universal data storage with PostgREST.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @seaverse/data-service-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createClient } from '@seaverse/data-service-sdk';
|
|
15
|
+
|
|
16
|
+
// appId is automatically extracted from current URL
|
|
17
|
+
const client = createClient({
|
|
18
|
+
url: 'https://your-postgrest-api.example.com',
|
|
19
|
+
token: 'Bearer your-jwt-token-here',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Insert
|
|
23
|
+
const order = await client.userData
|
|
24
|
+
.collection('orders')
|
|
25
|
+
.insert({
|
|
26
|
+
order_number: 'ORD-001',
|
|
27
|
+
status: 'pending',
|
|
28
|
+
total: 99.99,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Get by ID
|
|
32
|
+
const record = await client.userData
|
|
33
|
+
.collection('orders')
|
|
34
|
+
.get(order.id);
|
|
35
|
+
|
|
36
|
+
// Query
|
|
37
|
+
const results = await client.userData
|
|
38
|
+
.collection('orders')
|
|
39
|
+
.select()
|
|
40
|
+
.eq('data->>status', 'pending')
|
|
41
|
+
.order('created_at', { descending: true })
|
|
42
|
+
.limit(10)
|
|
43
|
+
.execute();
|
|
44
|
+
|
|
45
|
+
// Update
|
|
46
|
+
await client.userData
|
|
47
|
+
.collection('orders')
|
|
48
|
+
.patch(order.id, { status: 'completed' });
|
|
49
|
+
|
|
50
|
+
// Delete
|
|
51
|
+
await client.userData
|
|
52
|
+
.collection('orders')
|
|
53
|
+
.delete(order.id);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Core Concepts
|
|
57
|
+
|
|
58
|
+
### Hierarchy
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Client → DataTable (user_data) → Collection (collection_name) → Data (JSONB)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Application ID (appId)
|
|
65
|
+
|
|
66
|
+
**Automatic Extraction**: The SDK automatically extracts `app_id` from the current URL:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
URL: https://app_8e5e867e-user_f4ed2364ffcf24d6c6707d5ca5e4fe6d.app.seaverse.ai
|
|
70
|
+
→ appId: app_8e5e867e-user_f4ed2364ffcf24d6c6707d5ca5e4fe6d
|
|
71
|
+
|
|
72
|
+
URL: https://example.com
|
|
73
|
+
→ appId: example.com
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Node.js Environment**: Set via environment variable:
|
|
77
|
+
```bash
|
|
78
|
+
export SEAVERSE_APP_ID=my-app-id
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Unique Constraint
|
|
82
|
+
|
|
83
|
+
**Important**: Only ONE record per `(user_id, app_id, collection_name)`.
|
|
84
|
+
|
|
85
|
+
For multiple records, use unique collection names:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// ✓ Correct
|
|
89
|
+
await client.userData.collection('orders_1').insert(order1);
|
|
90
|
+
await client.userData.collection('orders_2').insert(order2);
|
|
91
|
+
|
|
92
|
+
// ✓ Or use batch insert
|
|
93
|
+
await client.userData.batchInsert('orders', [order1, order2]);
|
|
94
|
+
|
|
95
|
+
// ✗ Wrong - will fail
|
|
96
|
+
await client.userData.collection('orders').insert(order1);
|
|
97
|
+
await client.userData.collection('orders').insert(order2); // Error: 409 Conflict
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API Reference
|
|
101
|
+
|
|
102
|
+
### Client
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
createClient(config: ClientConfig): DataServiceClient
|
|
106
|
+
|
|
107
|
+
interface ClientConfig {
|
|
108
|
+
url: string; // PostgREST API URL
|
|
109
|
+
token: string; // JWT token with user_id in payload
|
|
110
|
+
options?: {
|
|
111
|
+
timeout?: number; // Request timeout (ms, default: 30000)
|
|
112
|
+
headers?: Record<string, string>;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Client properties
|
|
117
|
+
client.appId: string // Auto-extracted from URL
|
|
118
|
+
client.userData: DataTable
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### DataTable
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
client.userData.collection<T>(name: string): Collection<T>
|
|
125
|
+
client.userData.batchInsert<T>(baseName: string, records: T[]): Promise<DataRecord<T>[]>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Collection
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// Create
|
|
132
|
+
collection.insert(data: T, id?: string): Promise<DataRecord<T>> // Optional UUID
|
|
133
|
+
|
|
134
|
+
// Read
|
|
135
|
+
collection.get(id: string): Promise<DataRecord<T> | null>
|
|
136
|
+
collection.selectById(id: string): Promise<DataRecord<T> | null>
|
|
137
|
+
collection.select(): QueryBuilder<T>
|
|
138
|
+
collection.search(criteria: Partial<T>): Promise<DataRecord<T>[]>
|
|
139
|
+
collection.count(): Promise<number>
|
|
140
|
+
|
|
141
|
+
// Update
|
|
142
|
+
collection.update(id: string, data: T): Promise<DataRecord<T>>
|
|
143
|
+
collection.patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>
|
|
144
|
+
|
|
145
|
+
// Delete
|
|
146
|
+
collection.delete(id: string): Promise<void>
|
|
147
|
+
collection.softDelete(id: string): Promise<boolean>
|
|
148
|
+
collection.restore(id: string): Promise<boolean>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Note**: All IDs are UUIDs (strings). Database auto-generates if not provided.
|
|
152
|
+
|
|
153
|
+
### Query Builder
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
query
|
|
157
|
+
.eq(field, value) // Equal
|
|
158
|
+
.neq(field, value) // Not equal
|
|
159
|
+
.gt(field, value) // Greater than
|
|
160
|
+
.gte(field, value) // Greater than or equal
|
|
161
|
+
.lt(field, value) // Less than
|
|
162
|
+
.lte(field, value) // Less than or equal
|
|
163
|
+
.like(field, pattern) // Pattern match (case-sensitive)
|
|
164
|
+
.ilike(field, pattern) // Pattern match (case-insensitive)
|
|
165
|
+
.in(field, values) // Value in array
|
|
166
|
+
.contains(value) // JSONB contains
|
|
167
|
+
.order(field, options) // Sort
|
|
168
|
+
.limit(count) // Limit results
|
|
169
|
+
.offset(count) // Skip results
|
|
170
|
+
.execute() // Get results
|
|
171
|
+
.count() // Get count
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Client Methods
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
client.getStats(): Promise<UserDataStats>
|
|
178
|
+
client.health(): Promise<{ status: string; user_id: string }>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Examples
|
|
182
|
+
|
|
183
|
+
### E-Commerce Orders
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
type Order = {
|
|
187
|
+
order_number: string;
|
|
188
|
+
customer_email: string;
|
|
189
|
+
items: Array<{ product_id: string; quantity: number; price: number }>;
|
|
190
|
+
status: 'pending' | 'processing' | 'shipped' | 'delivered';
|
|
191
|
+
total: number;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const orders = client.userData.collection<Order>('orders');
|
|
195
|
+
|
|
196
|
+
// Create
|
|
197
|
+
const order = await orders.insert({
|
|
198
|
+
order_number: `ORD-${Date.now()}`,
|
|
199
|
+
customer_email: 'customer@example.com',
|
|
200
|
+
items: [{ product_id: 'PROD-001', quantity: 2, price: 29.99 }],
|
|
201
|
+
status: 'pending',
|
|
202
|
+
total: 59.98,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Query
|
|
206
|
+
const pending = await orders
|
|
207
|
+
.select()
|
|
208
|
+
.eq('data->>status', 'pending')
|
|
209
|
+
.gt('data->>total', '50')
|
|
210
|
+
.order('created_at', { descending: true })
|
|
211
|
+
.execute();
|
|
212
|
+
|
|
213
|
+
// Update
|
|
214
|
+
await orders.patch(order.id, { status: 'shipped' });
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### GPT Conversations
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
type Conversation = {
|
|
221
|
+
title: string;
|
|
222
|
+
model: string;
|
|
223
|
+
messages: Array<{
|
|
224
|
+
role: 'user' | 'assistant' | 'system';
|
|
225
|
+
content: string;
|
|
226
|
+
timestamp: string;
|
|
227
|
+
}>;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const conversations = client.userData.collection<Conversation>('chats');
|
|
231
|
+
|
|
232
|
+
const conv = await conversations.insert({
|
|
233
|
+
title: 'Project Planning',
|
|
234
|
+
model: 'claude-3-opus',
|
|
235
|
+
messages: [
|
|
236
|
+
{
|
|
237
|
+
role: 'user',
|
|
238
|
+
content: 'Help me plan a new feature',
|
|
239
|
+
timestamp: new Date().toISOString(),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Add message
|
|
245
|
+
const retrieved = await conversations.get(conv.id);
|
|
246
|
+
await conversations.patch(conv.id, {
|
|
247
|
+
messages: [
|
|
248
|
+
...retrieved!.data.messages,
|
|
249
|
+
{
|
|
250
|
+
role: 'assistant',
|
|
251
|
+
content: 'I can help you with that...',
|
|
252
|
+
timestamp: new Date().toISOString(),
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## UUID Primary Keys
|
|
259
|
+
|
|
260
|
+
All records use UUID strings as primary keys:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Database auto-generates UUID
|
|
264
|
+
const order = await orders.insert({
|
|
265
|
+
order_number: 'ORD-001',
|
|
266
|
+
status: 'pending',
|
|
267
|
+
});
|
|
268
|
+
console.log(order.id); // "550e8400-e29b-41d4-a716-446655440000"
|
|
269
|
+
|
|
270
|
+
// Or provide your own UUID
|
|
271
|
+
import { randomUUID } from 'crypto';
|
|
272
|
+
|
|
273
|
+
const customId = randomUUID();
|
|
274
|
+
const order2 = await orders.insert(
|
|
275
|
+
{ order_number: 'ORD-002', status: 'pending' },
|
|
276
|
+
customId
|
|
277
|
+
);
|
|
278
|
+
console.log(order2.id === customId); // true
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Client-side UUID generation** (recommended for offline-first apps):
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// Node.js
|
|
285
|
+
import { randomUUID } from 'crypto';
|
|
286
|
+
const id = randomUUID();
|
|
287
|
+
|
|
288
|
+
// Browser
|
|
289
|
+
const id = crypto.randomUUID();
|
|
290
|
+
|
|
291
|
+
// TypeScript with uuid library
|
|
292
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
293
|
+
const id = uuidv4();
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## TypeScript Support
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
type MyData = {
|
|
300
|
+
name: string;
|
|
301
|
+
count: number;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const collection = client.userData.collection<MyData>('items');
|
|
305
|
+
|
|
306
|
+
const record = await collection.insert({
|
|
307
|
+
name: 'Item 1',
|
|
308
|
+
count: 42,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// TypeScript knows the shape
|
|
312
|
+
console.log(record.data.name); // ✓ OK
|
|
313
|
+
console.log(record.data.invalid); // ✗ TypeScript error
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Error Handling
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
import { DataServiceError } from '@seaverse/data-service-sdk';
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
await collection.insert(data);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (error instanceof DataServiceError) {
|
|
325
|
+
console.error('Code:', error.code);
|
|
326
|
+
console.error('Message:', error.message);
|
|
327
|
+
console.error('Status:', error.statusCode);
|
|
328
|
+
|
|
329
|
+
if (error.code === '23505') {
|
|
330
|
+
// Unique constraint violation
|
|
331
|
+
console.log('Collection name already exists');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Development
|
|
338
|
+
|
|
339
|
+
### Setup
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
git clone https://github.com/seaverse/data-service-sdk
|
|
343
|
+
cd data-service-sdk
|
|
344
|
+
npm install
|
|
345
|
+
npm run build
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Testing
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# Copy environment template
|
|
352
|
+
cp .env.example .env
|
|
353
|
+
|
|
354
|
+
# Edit .env with your credentials
|
|
355
|
+
# POSTGREST_URL=https://your-api.example.com
|
|
356
|
+
# JWT_TOKEN=Bearer your-token-here
|
|
357
|
+
|
|
358
|
+
# Run tests
|
|
359
|
+
npm run test:examples
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Scripts
|
|
363
|
+
|
|
364
|
+
- `npm run build` - Build the SDK
|
|
365
|
+
- `npm run dev` - Build in watch mode
|
|
366
|
+
- `npm run test:examples` - Run integration tests
|
|
367
|
+
- `npm run lint` - Lint code
|
|
368
|
+
- `npm run format` - Format code
|
|
369
|
+
|
|
370
|
+
## Security
|
|
371
|
+
|
|
372
|
+
Built on PostgreSQL Row-Level Security (RLS):
|
|
373
|
+
- JWT authentication with user_id in payload
|
|
374
|
+
- Automatic data isolation per user
|
|
375
|
+
- No admin bypass
|
|
376
|
+
|
|
377
|
+
## Contributing
|
|
378
|
+
|
|
379
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
MIT - See [LICENSE](LICENSE)
|
|
384
|
+
|
|
385
|
+
## Links
|
|
386
|
+
|
|
387
|
+
- [GitHub](https://github.com/seaverse/data-service-sdk)
|
|
388
|
+
- [Issues](https://github.com/seaverse/data-service-sdk/issues)
|
|
389
|
+
- [AI Usage Guide](AI-USAGE-GUIDE.md)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for SeaVerse Data Service SDK
|
|
3
|
+
*
|
|
4
|
+
* These types are designed to be AI-friendly:
|
|
5
|
+
* - Clear, descriptive names
|
|
6
|
+
* - Self-documenting structure
|
|
7
|
+
* - Rich JSDoc comments
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Configuration options for creating a Data Service client
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const config: ClientConfig = {
|
|
15
|
+
* url: 'https://your-postgrest-api.example.com',
|
|
16
|
+
* token: 'Bearer your-jwt-token-here',
|
|
17
|
+
* };
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
interface ClientConfig {
|
|
21
|
+
/** PostgREST API base URL */
|
|
22
|
+
url: string;
|
|
23
|
+
/** JWT token containing user_id in payload.user_id */
|
|
24
|
+
token: string;
|
|
25
|
+
/** Optional configuration */
|
|
26
|
+
options?: {
|
|
27
|
+
/** Custom fetch implementation (useful for Node.js < 18) */
|
|
28
|
+
fetch?: typeof fetch;
|
|
29
|
+
/** Additional HTTP headers to include in all requests */
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
32
|
+
timeout?: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Data record structure returned from the database
|
|
37
|
+
*
|
|
38
|
+
* @template T - Type of the data field (flexible JSONB)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* type Order = DataRecord<{
|
|
43
|
+
* order_number: string;
|
|
44
|
+
* status: string;
|
|
45
|
+
* total: number;
|
|
46
|
+
* }>;
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
interface DataRecord<T = any> {
|
|
50
|
+
/** UUID primary key (database-generated or client-provided) */
|
|
51
|
+
id: string;
|
|
52
|
+
/** User ID (auto-filled from JWT token) */
|
|
53
|
+
user_id: string;
|
|
54
|
+
/** Application identifier */
|
|
55
|
+
app_id: string;
|
|
56
|
+
/** Collection name (like MongoDB collection) */
|
|
57
|
+
collection_name: string;
|
|
58
|
+
/** Flexible JSONB data field */
|
|
59
|
+
data: T;
|
|
60
|
+
/** Creation timestamp (auto-filled) */
|
|
61
|
+
created_at: string;
|
|
62
|
+
/** Last update timestamp (auto-updated) */
|
|
63
|
+
updated_at: string;
|
|
64
|
+
/** Soft delete timestamp (null if not deleted) */
|
|
65
|
+
deleted_at: string | null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Query filter operators for building complex queries
|
|
69
|
+
*
|
|
70
|
+
* AI-friendly: Each operator has a clear, predictable name
|
|
71
|
+
*/
|
|
72
|
+
interface QueryFilter {
|
|
73
|
+
/** Field path (supports JSONB paths like 'data->status') */
|
|
74
|
+
field: string;
|
|
75
|
+
/** Operator type */
|
|
76
|
+
operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in' | 'contains' | 'overlaps';
|
|
77
|
+
/** Value to compare against */
|
|
78
|
+
value: any;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Query builder options for ordering results
|
|
82
|
+
*/
|
|
83
|
+
interface OrderOptions {
|
|
84
|
+
/** Sort in descending order (default: false) */
|
|
85
|
+
descending?: boolean;
|
|
86
|
+
/** Handle null values (first or last) */
|
|
87
|
+
nullsFirst?: boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Statistics about user's data
|
|
91
|
+
*
|
|
92
|
+
* Returned by getStats() RPC function
|
|
93
|
+
*/
|
|
94
|
+
interface UserDataStats {
|
|
95
|
+
/** Total number of records (including deleted) */
|
|
96
|
+
total_records: number;
|
|
97
|
+
/** Number of active (non-deleted) records */
|
|
98
|
+
active_records: number;
|
|
99
|
+
/** Number of soft-deleted records */
|
|
100
|
+
deleted_records: number;
|
|
101
|
+
/** Number of unique app_ids */
|
|
102
|
+
total_apps: number;
|
|
103
|
+
/** Number of unique collection_names */
|
|
104
|
+
total_collections: number;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Error response from PostgREST API
|
|
108
|
+
*/
|
|
109
|
+
interface APIError {
|
|
110
|
+
/** Error code (e.g., '23505' for unique constraint violation) */
|
|
111
|
+
code?: string;
|
|
112
|
+
/** Human-readable error message */
|
|
113
|
+
message: string;
|
|
114
|
+
/** Additional error details */
|
|
115
|
+
details?: string;
|
|
116
|
+
/** Hint for resolving the error */
|
|
117
|
+
hint?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Custom error class for SDK errors
|
|
121
|
+
*
|
|
122
|
+
* AI-friendly: Clear error types and messages
|
|
123
|
+
*/
|
|
124
|
+
declare class DataServiceError extends Error {
|
|
125
|
+
code?: string | undefined;
|
|
126
|
+
details?: string | undefined;
|
|
127
|
+
hint?: string | undefined;
|
|
128
|
+
statusCode?: number | undefined;
|
|
129
|
+
constructor(message: string, code?: string | undefined, details?: string | undefined, hint?: string | undefined, statusCode?: number | undefined);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Query builder interface for fluent API
|
|
133
|
+
*
|
|
134
|
+
* AI-friendly: Method chaining with clear intent
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const results = await query
|
|
139
|
+
* .eq('status', 'active')
|
|
140
|
+
* .gt('total', 100)
|
|
141
|
+
* .order('created_at', { descending: true })
|
|
142
|
+
* .limit(20);
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
interface QueryBuilder<T = any> {
|
|
146
|
+
/** Filter: field equals value */
|
|
147
|
+
eq(field: string, value: any): QueryBuilder<T>;
|
|
148
|
+
/** Filter: field not equals value */
|
|
149
|
+
neq(field: string, value: any): QueryBuilder<T>;
|
|
150
|
+
/** Filter: field greater than value */
|
|
151
|
+
gt(field: string, value: any): QueryBuilder<T>;
|
|
152
|
+
/** Filter: field greater than or equal to value */
|
|
153
|
+
gte(field: string, value: any): QueryBuilder<T>;
|
|
154
|
+
/** Filter: field less than value */
|
|
155
|
+
lt(field: string, value: any): QueryBuilder<T>;
|
|
156
|
+
/** Filter: field less than or equal to value */
|
|
157
|
+
lte(field: string, value: any): QueryBuilder<T>;
|
|
158
|
+
/** Filter: field matches pattern (case-sensitive) */
|
|
159
|
+
like(field: string, pattern: string): QueryBuilder<T>;
|
|
160
|
+
/** Filter: field matches pattern (case-insensitive) */
|
|
161
|
+
ilike(field: string, pattern: string): QueryBuilder<T>;
|
|
162
|
+
/** Filter: field value in array */
|
|
163
|
+
in(field: string, values: any[]): QueryBuilder<T>;
|
|
164
|
+
/** Filter: JSONB contains value */
|
|
165
|
+
contains(value: Record<string, any>): QueryBuilder<T>;
|
|
166
|
+
/** Filter: JSONB overlaps with value */
|
|
167
|
+
overlaps(value: Record<string, any>): QueryBuilder<T>;
|
|
168
|
+
/** Order results by field */
|
|
169
|
+
order(field: string, options?: OrderOptions): QueryBuilder<T>;
|
|
170
|
+
/** Limit number of results */
|
|
171
|
+
limit(count: number): QueryBuilder<T>;
|
|
172
|
+
/** Skip number of results (pagination) */
|
|
173
|
+
offset(count: number): QueryBuilder<T>;
|
|
174
|
+
/** Execute query and return results */
|
|
175
|
+
execute(): Promise<DataRecord<T>[]>;
|
|
176
|
+
/** Execute query and return count */
|
|
177
|
+
count(): Promise<number>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Collection interface for CRUD operations
|
|
181
|
+
*
|
|
182
|
+
* AI-friendly: Clear method names indicating intent
|
|
183
|
+
*/
|
|
184
|
+
interface Collection<T = any> {
|
|
185
|
+
/** Insert a new record (optionally provide UUID) */
|
|
186
|
+
insert(data: T, id?: string): Promise<DataRecord<T>>;
|
|
187
|
+
/** Get a single record by ID (alias for selectById) */
|
|
188
|
+
get(id: string): Promise<DataRecord<T> | null>;
|
|
189
|
+
/** Select records with optional filters */
|
|
190
|
+
select(): QueryBuilder<T>;
|
|
191
|
+
/** Select a single record by ID */
|
|
192
|
+
selectById(id: string): Promise<DataRecord<T> | null>;
|
|
193
|
+
/** Update a record by ID (replaces entire data field) */
|
|
194
|
+
update(id: string, data: T): Promise<DataRecord<T>>;
|
|
195
|
+
/** Patch a record by ID (merges with existing data) */
|
|
196
|
+
patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>;
|
|
197
|
+
/** Hard delete a record by ID */
|
|
198
|
+
delete(id: string): Promise<void>;
|
|
199
|
+
/** Soft delete a record by ID (sets deleted_at) */
|
|
200
|
+
softDelete(id: string): Promise<boolean>;
|
|
201
|
+
/** Restore a soft-deleted record */
|
|
202
|
+
restore(id: string): Promise<boolean>;
|
|
203
|
+
/** Search records by JSONB contains */
|
|
204
|
+
search(criteria: Partial<T>): Promise<DataRecord<T>[]>;
|
|
205
|
+
/** Count records in collection */
|
|
206
|
+
count(): Promise<number>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Data table interface for accessing collections within a specific table
|
|
210
|
+
*
|
|
211
|
+
* Represents a physical database table (e.g., user_data, public_data)
|
|
212
|
+
*/
|
|
213
|
+
interface DataTable {
|
|
214
|
+
/** Get a collection from this table */
|
|
215
|
+
collection<T = any>(name: string): Collection<T>;
|
|
216
|
+
/** Batch insert multiple records into different collections */
|
|
217
|
+
batchInsert<T = any>(baseCollectionName: string, records: T[]): Promise<DataRecord<T>[]>;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Main client interface
|
|
221
|
+
*
|
|
222
|
+
* AI-friendly: Entry point with clear hierarchy
|
|
223
|
+
*/
|
|
224
|
+
interface DataServiceClient {
|
|
225
|
+
/** Automatically extracted application ID from current URL */
|
|
226
|
+
readonly appId: string;
|
|
227
|
+
/** Access user private data table (user_data) */
|
|
228
|
+
readonly userData: DataTable;
|
|
229
|
+
/** Get user data statistics (RPC call) */
|
|
230
|
+
getStats(): Promise<UserDataStats>;
|
|
231
|
+
/** Health check (RPC call) */
|
|
232
|
+
health(): Promise<{
|
|
233
|
+
status: string;
|
|
234
|
+
user_id: string;
|
|
235
|
+
}>;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Core client implementation for SeaVerse Data Service SDK
|
|
240
|
+
*
|
|
241
|
+
* Design principles:
|
|
242
|
+
* 1. AI-friendly: Clear method names, predictable behavior
|
|
243
|
+
* 2. Type-safe: Full TypeScript support
|
|
244
|
+
* 3. Chainable: Fluent API for query building
|
|
245
|
+
* 4. Secure: RLS enforced, token-based auth
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Create a new Data Service client
|
|
250
|
+
*
|
|
251
|
+
* AI-friendly: Single entry point with clear configuration
|
|
252
|
+
*
|
|
253
|
+
* @param config - Client configuration with URL and JWT token
|
|
254
|
+
* @returns DataServiceClient instance
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```typescript
|
|
258
|
+
* const client = createClient({
|
|
259
|
+
* url: 'https://your-postgrest-api.example.com',
|
|
260
|
+
* token: 'Bearer your-jwt-token-here',
|
|
261
|
+
* });
|
|
262
|
+
*
|
|
263
|
+
* // appId is automatically extracted from current URL
|
|
264
|
+
* // Direct access to collections - clean and simple
|
|
265
|
+
* const order = await client.userData.collection('orders').insert({ ... });
|
|
266
|
+
* const orders = await client.userData.collection('orders').select().execute();
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
declare function createClient(config: ClientConfig): DataServiceClient;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* SeaVerse Data Service SDK
|
|
273
|
+
*
|
|
274
|
+
* AI-Friendly Universal Data Storage for TypeScript/JavaScript
|
|
275
|
+
*
|
|
276
|
+
* @packageDocumentation
|
|
277
|
+
*
|
|
278
|
+
* @example Basic Usage
|
|
279
|
+
* ```typescript
|
|
280
|
+
* import { createClient } from '@seaverse/data-service-sdk';
|
|
281
|
+
*
|
|
282
|
+
* // appId is automatically extracted from current URL
|
|
283
|
+
* const client = createClient({
|
|
284
|
+
* url: 'https://postgrest.example.com',
|
|
285
|
+
* token: 'your-jwt-token',
|
|
286
|
+
* });
|
|
287
|
+
*
|
|
288
|
+
* // Insert data - appId is automatically included
|
|
289
|
+
* const order = await client.userData.collection('orders').insert({
|
|
290
|
+
* order_number: 'ORD-123',
|
|
291
|
+
* status: 'pending',
|
|
292
|
+
* total: 99.99,
|
|
293
|
+
* });
|
|
294
|
+
*
|
|
295
|
+
* // Query data
|
|
296
|
+
* const orders = await client.userData
|
|
297
|
+
* .collection('orders')
|
|
298
|
+
* .select()
|
|
299
|
+
* .eq('status', 'pending')
|
|
300
|
+
* .order('created_at', { descending: true })
|
|
301
|
+
* .limit(10)
|
|
302
|
+
* .execute();
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
declare const VERSION = "1.0.0";
|
|
307
|
+
|
|
308
|
+
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient };
|