@seaverse/data-sdk 0.3.0 → 0.3.1
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 +133 -249
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,22 @@
|
|
|
5
5
|
|
|
6
6
|
高性能数据服务 SDK,基于 PostgreSQL/AlloyDB + PostgREST + RLS 架构。
|
|
7
7
|
|
|
8
|
+
## 🚀 核心概念
|
|
9
|
+
|
|
10
|
+
### App ID 是什么?
|
|
11
|
+
|
|
12
|
+
**App ID** 是您在 SeaVerse 平台上创建的应用的唯一标识符。它用于:
|
|
13
|
+
- ✅ 多租户数据隔离(每个应用的数据互不干扰)
|
|
14
|
+
- ✅ 权限控制(RLS 策略基于 app_id)
|
|
15
|
+
- ✅ 资源配额管理
|
|
16
|
+
|
|
17
|
+
**如何获取 App ID?**
|
|
18
|
+
1. 登录 [SeaVerse 控制台](https://console.seaverse.ai)
|
|
19
|
+
2. 进入"应用管理"
|
|
20
|
+
3. 复制您的 App ID
|
|
21
|
+
|
|
22
|
+
> ⚠️ **重要**:所有 SDK 操作都需要提供有效的 `appId`,否则无法访问数据。
|
|
23
|
+
|
|
8
24
|
## 特性
|
|
9
25
|
|
|
10
26
|
- ✅ TypeScript 类型支持
|
|
@@ -16,6 +32,7 @@
|
|
|
16
32
|
- ✅ 支持过滤、排序、分页
|
|
17
33
|
- ✅ 支持标签和 JSONB 字段查询
|
|
18
34
|
- ✅ **零配置使用** - 自动从 PostMessage 获取配置
|
|
35
|
+
- ✅ **自动配额限制** - 数据库层强制执行配额,防止资源滥用
|
|
19
36
|
|
|
20
37
|
## 安装
|
|
21
38
|
|
|
@@ -25,58 +42,64 @@ npm install @seaverse/data-sdk
|
|
|
25
42
|
|
|
26
43
|
## 快速开始
|
|
27
44
|
|
|
28
|
-
### 方式
|
|
45
|
+
### 方式1: 函数式 API(推荐 ⭐)
|
|
29
46
|
|
|
30
|
-
|
|
47
|
+
最简单的使用方式 - 零配置,直接调用函数:
|
|
31
48
|
|
|
32
49
|
```typescript
|
|
33
50
|
import { query, create, update, deleteData } from '@seaverse/data-sdk';
|
|
34
51
|
|
|
35
|
-
//
|
|
52
|
+
// 第一次调用自动初始化 SDK(从 PostMessage 获取 appId 和 token)
|
|
36
53
|
// 无需任何配置代码!
|
|
37
54
|
|
|
38
55
|
// 查询数据
|
|
39
56
|
const notes = await query({
|
|
40
57
|
table_name: 'notes',
|
|
41
58
|
filters: { category: 'work' },
|
|
42
|
-
order: { field: 'created_at', direction: 'desc' }
|
|
59
|
+
order: { field: 'created_at', direction: 'desc' }
|
|
43
60
|
});
|
|
44
61
|
|
|
45
62
|
// 创建数据
|
|
46
63
|
const newNote = await create({
|
|
47
64
|
table_name: 'notes',
|
|
48
65
|
data_value: { title: 'My Note', content: '...' },
|
|
49
|
-
visibility: 'private'
|
|
66
|
+
visibility: 'private'
|
|
50
67
|
});
|
|
51
68
|
|
|
52
69
|
// 更新数据
|
|
53
|
-
await update(
|
|
54
|
-
data_value: { title: 'Updated' }
|
|
70
|
+
await update(newNote.id, {
|
|
71
|
+
data_value: { title: 'Updated Title' }
|
|
55
72
|
});
|
|
56
73
|
|
|
57
74
|
// 删除数据
|
|
58
|
-
await deleteData(
|
|
75
|
+
await deleteData(newNote.id);
|
|
59
76
|
```
|
|
60
77
|
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
**为什么推荐函数式 API:**
|
|
63
79
|
- ✅ **零配置** - 无需创建客户端实例,直接调用函数
|
|
64
|
-
- ✅ **自动初始化** -
|
|
65
|
-
- ✅
|
|
80
|
+
- ✅ **自动初始化** - 第一次调用自动获取配置(PostMessage)
|
|
81
|
+
- ✅ **代码简洁** - 代码更简洁,更易理解
|
|
66
82
|
- ✅ **单例管理** - SDK 自动管理全局实例,避免重复初始化
|
|
67
83
|
|
|
68
|
-
|
|
84
|
+
---
|
|
69
85
|
|
|
70
|
-
|
|
86
|
+
### 方式2: DataClient 类 API(高级场景)
|
|
87
|
+
|
|
88
|
+
适用于需要多个实例或自定义配置的场景:
|
|
71
89
|
|
|
72
90
|
```typescript
|
|
73
91
|
import { DataClient } from '@seaverse/data-sdk';
|
|
74
92
|
|
|
75
|
-
//
|
|
76
|
-
const client = await DataClient.
|
|
93
|
+
// 方式2a: 零配置(推荐)- 自动从 PostMessage 获取 appId 和 token
|
|
94
|
+
const client = await DataClient.create();
|
|
95
|
+
|
|
96
|
+
// 方式2b: 只提供 appId,token 自动获取
|
|
97
|
+
const client = await DataClient.create({
|
|
98
|
+
appId: 'my-app-123',
|
|
99
|
+
});
|
|
77
100
|
|
|
78
|
-
//
|
|
79
|
-
const client =
|
|
101
|
+
// 方式2c: 显式提供所有配置
|
|
102
|
+
const client = new DataClient({
|
|
80
103
|
appId: 'my-app-123',
|
|
81
104
|
token: 'user-jwt-token',
|
|
82
105
|
});
|
|
@@ -88,12 +111,6 @@ const notes = await client.query({
|
|
|
88
111
|
});
|
|
89
112
|
```
|
|
90
113
|
|
|
91
|
-
**🎯 何时使用 DataClient 类?**
|
|
92
|
-
|
|
93
|
-
- 需要管理多个应用的实例
|
|
94
|
-
- 需要动态切换 token 或 appId
|
|
95
|
-
- 需要自定义配置(如 baseURL、timeout)
|
|
96
|
-
|
|
97
114
|
**配置选项说明:**
|
|
98
115
|
|
|
99
116
|
| 参数 | 类型 | 必需 | 说明 |
|
|
@@ -129,61 +146,9 @@ environment?: 'production' | 'staging' | 'development' | 'local'
|
|
|
129
146
|
|
|
130
147
|
## API 文档
|
|
131
148
|
|
|
132
|
-
###
|
|
133
|
-
|
|
134
|
-
零配置,直接导入使用:
|
|
149
|
+
### DataClient 方法
|
|
135
150
|
|
|
136
|
-
|
|
137
|
-
import {
|
|
138
|
-
query, // 查询数据
|
|
139
|
-
get, // 获取单条数据
|
|
140
|
-
create, // 创建数据
|
|
141
|
-
update, // 更新数据
|
|
142
|
-
deleteData, // 删除单条数据
|
|
143
|
-
batchDelete, // 批量删除
|
|
144
|
-
queryWithPagination,// 分页查询
|
|
145
|
-
isAdmin, // 检查管理员权限
|
|
146
|
-
reinit, // 重新初始化(切换用户/应用)
|
|
147
|
-
getClientStatus, // 获取客户端状态
|
|
148
|
-
} from '@seaverse/data-sdk';
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
| 函数 | 说明 | 参数 | 返回值 |
|
|
152
|
-
|------|------|------|--------|
|
|
153
|
-
| `query(options)` | 查询数据列表 | `QueryOptions` | `Promise<DataRecord[]>` |
|
|
154
|
-
| `get(id)` | 获取单条数据 | `id: string` | `Promise<DataRecord \| null>` |
|
|
155
|
-
| `create(options)` | 创建数据 | `CreateDataOptions` | `Promise<DataRecord>` |
|
|
156
|
-
| `update(id, options)` | 更新数据 | `id: string`, `UpdateDataOptions` | `Promise<DataRecord>` |
|
|
157
|
-
| `deleteData(id)` | 删除数据 | `id: string` | `Promise<void>` |
|
|
158
|
-
| `batchDelete(ids)` | 批量删除 | `ids: string[]` | `Promise<void>` |
|
|
159
|
-
| `queryWithPagination(options)` | 查询带分页信息 | `QueryOptions` | `Promise<PaginatedResponse<DataRecord>>` |
|
|
160
|
-
| `isAdmin()` | 检查管理员权限 | - | `Promise<boolean>` |
|
|
161
|
-
| `reinit(options?)` | 重新初始化 | `DataClientOptions?` | `Promise<void>` |
|
|
162
|
-
| `getClientStatus()` | 获取客户端状态 | - | `ClientStatus` |
|
|
163
|
-
|
|
164
|
-
**使用示例:**
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
// 查询数据
|
|
168
|
-
const notes = await query({ table_name: 'notes' });
|
|
169
|
-
|
|
170
|
-
// 创建数据
|
|
171
|
-
const note = await create({
|
|
172
|
-
table_name: 'notes',
|
|
173
|
-
data_value: { title: 'Hello' },
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// 重新初始化(用户登录后切换 token)
|
|
177
|
-
await reinit({ token: newToken });
|
|
178
|
-
|
|
179
|
-
// 检查初始化状态
|
|
180
|
-
const status = getClientStatus();
|
|
181
|
-
console.log('已初始化:', status.initialized);
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### DataClient 类 API(高级场景)
|
|
185
|
-
|
|
186
|
-
使用 `DataClient.connect()` 创建实例后可用的方法:
|
|
151
|
+
使用 `DataClient.create()` 或 `new DataClient(options)` 创建实例后可用的方法:
|
|
187
152
|
|
|
188
153
|
| 方法 | 说明 | 参数 | 返回值 |
|
|
189
154
|
|------|------|------|--------|
|
|
@@ -461,43 +426,7 @@ const notes = await client.query({ table_name: 'notes' });
|
|
|
461
426
|
|
|
462
427
|
### 无缝接入场景
|
|
463
428
|
|
|
464
|
-
#### React
|
|
465
|
-
|
|
466
|
-
```typescript
|
|
467
|
-
import { query, create, update } from '@seaverse/data-sdk';
|
|
468
|
-
import { useEffect, useState } from 'react';
|
|
469
|
-
|
|
470
|
-
function NotesApp() {
|
|
471
|
-
const [notes, setNotes] = useState([]);
|
|
472
|
-
|
|
473
|
-
useEffect(() => {
|
|
474
|
-
// 直接调用函数,首次调用会自动初始化 SDK
|
|
475
|
-
loadNotes();
|
|
476
|
-
}, []);
|
|
477
|
-
|
|
478
|
-
const loadNotes = async () => {
|
|
479
|
-
// 无需初始化,直接使用
|
|
480
|
-
const data = await query({
|
|
481
|
-
table_name: 'notes',
|
|
482
|
-
order: { field: 'created_at', direction: 'desc' },
|
|
483
|
-
});
|
|
484
|
-
setNotes(data);
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
const addNote = async (title: string) => {
|
|
488
|
-
const newNote = await create({
|
|
489
|
-
table_name: 'notes',
|
|
490
|
-
data_value: { title },
|
|
491
|
-
visibility: 'private',
|
|
492
|
-
});
|
|
493
|
-
setNotes([newNote, ...notes]);
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
return <div>...</div>;
|
|
497
|
-
}
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
**React 应用中使用(DataClient 类 - 多实例场景):**
|
|
429
|
+
#### React 应用中使用
|
|
501
430
|
|
|
502
431
|
```typescript
|
|
503
432
|
import { DataClient } from '@seaverse/data-sdk';
|
|
@@ -508,15 +437,18 @@ function App() {
|
|
|
508
437
|
|
|
509
438
|
useEffect(() => {
|
|
510
439
|
// 应用启动时初始化客户端
|
|
511
|
-
|
|
440
|
+
// SDK 会自动通过 PostMessage 从父页面获取配置
|
|
441
|
+
DataClient.create({ debug: true }).then(setClient);
|
|
512
442
|
}, []);
|
|
513
443
|
|
|
514
444
|
const loadNotes = async () => {
|
|
515
445
|
if (!client) return [];
|
|
516
|
-
|
|
446
|
+
|
|
447
|
+
const notes = await client.query({
|
|
517
448
|
table_name: 'notes',
|
|
518
449
|
order: { field: 'created_at', direction: 'desc' },
|
|
519
450
|
});
|
|
451
|
+
return notes;
|
|
520
452
|
};
|
|
521
453
|
|
|
522
454
|
return <div>...</div>;
|
|
@@ -573,27 +505,13 @@ window.addEventListener('message', (event) => {
|
|
|
573
505
|
});
|
|
574
506
|
```
|
|
575
507
|
|
|
576
|
-
**iframe
|
|
577
|
-
|
|
578
|
-
```typescript
|
|
579
|
-
import { query, create } from '@seaverse/data-sdk';
|
|
580
|
-
|
|
581
|
-
// 直接使用,SDK 会自动通过 PostMessage 从父页面获取 appId 和 token
|
|
582
|
-
const notes = await query({ table_name: 'notes' });
|
|
583
|
-
|
|
584
|
-
const newNote = await create({
|
|
585
|
-
table_name: 'notes',
|
|
586
|
-
data_value: { title: 'Hello' },
|
|
587
|
-
});
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
**iframe 页面 - DataClient 类:**
|
|
508
|
+
**iframe 页面(自动获取):**
|
|
591
509
|
|
|
592
510
|
```typescript
|
|
593
511
|
import { DataClient } from '@seaverse/data-sdk';
|
|
594
512
|
|
|
595
|
-
//
|
|
596
|
-
const client = await DataClient.
|
|
513
|
+
// 零配置创建客户端,SDK 会自动通过 PostMessage 从父页面获取 appId 和 token
|
|
514
|
+
const client = await DataClient.create();
|
|
597
515
|
const notes = await client.query({ table_name: 'notes' });
|
|
598
516
|
```
|
|
599
517
|
|
|
@@ -628,20 +546,6 @@ export async function getServerSideProps() {
|
|
|
628
546
|
|
|
629
547
|
### 动态更新 Token
|
|
630
548
|
|
|
631
|
-
**函数式 API(推荐):**
|
|
632
|
-
|
|
633
|
-
```typescript
|
|
634
|
-
import { reinit } from '@seaverse/data-sdk';
|
|
635
|
-
|
|
636
|
-
// 用户登录后,使用新 token
|
|
637
|
-
await reinit({ token: newToken });
|
|
638
|
-
|
|
639
|
-
// 完全重置(重新从 PostMessage 获取)
|
|
640
|
-
await reinit();
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
**DataClient 类:**
|
|
644
|
-
|
|
645
549
|
```typescript
|
|
646
550
|
// JWT 过期后更新 token
|
|
647
551
|
client.updateToken('new-jwt-token');
|
|
@@ -649,31 +553,96 @@ client.updateToken('new-jwt-token');
|
|
|
649
553
|
|
|
650
554
|
### 切换应用
|
|
651
555
|
|
|
652
|
-
**函数式 API(推荐):**
|
|
653
|
-
|
|
654
556
|
```typescript
|
|
655
|
-
import { reinit } from '@seaverse/data-sdk';
|
|
656
|
-
|
|
657
557
|
// 切换到其他应用
|
|
658
|
-
|
|
558
|
+
client.updateAppId('another-app-id');
|
|
659
559
|
```
|
|
660
560
|
|
|
661
|
-
|
|
561
|
+
### 配额限制
|
|
562
|
+
|
|
563
|
+
Data SDK 在数据库层实现了自动配额限制,防止单个应用占用过多资源。
|
|
564
|
+
|
|
565
|
+
#### 配额说明
|
|
566
|
+
|
|
567
|
+
SDK 支持以下配额限制(在数据库层通过触发器强制执行,无法绕过):
|
|
568
|
+
|
|
569
|
+
| 配额类型 | 默认值 | 说明 |
|
|
570
|
+
|---------|-------|------|
|
|
571
|
+
| 总数据条数 | 100,000 条 | 单个应用的最大数据总量 |
|
|
572
|
+
| 单表数据条数 | 50,000 条 | 单个 table_name 的最大数据量 |
|
|
573
|
+
| 存储空间 | 1 GB | 单个应用的最大存储空间 |
|
|
574
|
+
| 每分钟操作 | 1,000 次 | 防止短时间内大量操作 |
|
|
575
|
+
| 每小时操作 | 10,000 次 | 防止持续高频操作 |
|
|
576
|
+
| 每天操作 | 100,000 次 | 防止每日滥用 |
|
|
577
|
+
|
|
578
|
+
**配额检查机制:**
|
|
579
|
+
- 创建操作(CREATE):检查数据总量、单表数据量、存储空间、操作频率
|
|
580
|
+
- 更新操作(UPDATE):检查存储空间变化、操作频率
|
|
581
|
+
- 删除操作(DELETE):检查操作频率
|
|
582
|
+
|
|
583
|
+
超限时会自动抛出错误,操作被拒绝并回滚。
|
|
584
|
+
|
|
585
|
+
#### 错误处理
|
|
662
586
|
|
|
663
587
|
```typescript
|
|
664
|
-
|
|
665
|
-
client.
|
|
588
|
+
try {
|
|
589
|
+
await client.create({
|
|
590
|
+
table_name: 'notes',
|
|
591
|
+
data_value: { title: 'New Note' },
|
|
592
|
+
});
|
|
593
|
+
} catch (error) {
|
|
594
|
+
// 检查是否是配额超限错误
|
|
595
|
+
if (error.message && error.message.includes('Quota exceeded')) {
|
|
596
|
+
console.error('配额超限:', error.message);
|
|
597
|
+
// 示例错误信息:
|
|
598
|
+
// "Quota exceeded: Total records quota exceeded: 100000/100000 records"
|
|
599
|
+
// "Quota exceeded: Storage quota exceeded: 1024.5 MB/1024 MB"
|
|
600
|
+
// "Quota exceeded: Rate limit exceeded: 1234/1000 operations in last minute"
|
|
601
|
+
} else {
|
|
602
|
+
console.error('操作失败:', error);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
666
605
|
```
|
|
667
606
|
|
|
668
|
-
|
|
607
|
+
#### 平台管理员监控
|
|
608
|
+
|
|
609
|
+
配额的监控和调整由平台管理员通过数据库查询完成:
|
|
610
|
+
|
|
611
|
+
```sql
|
|
612
|
+
-- 查看所有应用的使用情况
|
|
613
|
+
SELECT
|
|
614
|
+
u.app_id,
|
|
615
|
+
u.total_records,
|
|
616
|
+
ROUND(u.total_storage_bytes::numeric / 1024 / 1024, 2) as storage_mb,
|
|
617
|
+
q.max_total_records,
|
|
618
|
+
ROUND((u.total_records::numeric / q.max_total_records * 100), 2) as usage_percent
|
|
619
|
+
FROM app_usage_stats u
|
|
620
|
+
LEFT JOIN app_quotas q ON u.app_id = q.app_id
|
|
621
|
+
ORDER BY usage_percent DESC;
|
|
622
|
+
|
|
623
|
+
-- 查找接近配额的应用(预警)
|
|
624
|
+
SELECT app_id, usage_percent
|
|
625
|
+
FROM (
|
|
626
|
+
SELECT
|
|
627
|
+
u.app_id,
|
|
628
|
+
ROUND((u.total_records::numeric / q.max_total_records * 100), 2) as usage_percent
|
|
629
|
+
FROM app_usage_stats u
|
|
630
|
+
INNER JOIN app_quotas q ON u.app_id = q.app_id
|
|
631
|
+
) t
|
|
632
|
+
WHERE usage_percent > 80;
|
|
633
|
+
|
|
634
|
+
-- 为应用调整配额
|
|
635
|
+
UPDATE app_quotas
|
|
636
|
+
SET max_total_records = 500000,
|
|
637
|
+
max_storage_bytes = 5368709120 -- 5GB
|
|
638
|
+
WHERE app_id = 'your-app-id';
|
|
639
|
+
```
|
|
669
640
|
|
|
670
|
-
|
|
641
|
+
### JSONB 字段查询
|
|
671
642
|
|
|
672
643
|
```typescript
|
|
673
|
-
import { query } from '@seaverse/data-sdk';
|
|
674
|
-
|
|
675
644
|
// 查询 data_value 中的字段
|
|
676
|
-
const results = await query({
|
|
645
|
+
const results = await client.query({
|
|
677
646
|
table_name: 'posts',
|
|
678
647
|
filters: {
|
|
679
648
|
data_value: {
|
|
@@ -684,26 +653,11 @@ const results = await query({
|
|
|
684
653
|
});
|
|
685
654
|
```
|
|
686
655
|
|
|
687
|
-
**DataClient 类:**
|
|
688
|
-
|
|
689
|
-
```typescript
|
|
690
|
-
const results = await client.query({
|
|
691
|
-
table_name: 'posts',
|
|
692
|
-
filters: {
|
|
693
|
-
data_value: { title: 'Hello World', author: 'Alice' },
|
|
694
|
-
},
|
|
695
|
-
});
|
|
696
|
-
```
|
|
697
|
-
|
|
698
656
|
### 标签查询
|
|
699
657
|
|
|
700
|
-
**函数式 API(推荐):**
|
|
701
|
-
|
|
702
658
|
```typescript
|
|
703
|
-
import { query } from '@seaverse/data-sdk';
|
|
704
|
-
|
|
705
659
|
// 查询包含指定标签的数据
|
|
706
|
-
const results = await query({
|
|
660
|
+
const results = await client.query({
|
|
707
661
|
table_name: 'posts',
|
|
708
662
|
filters: {
|
|
709
663
|
tags: ['javascript', 'typescript'], // 包含任一标签
|
|
@@ -713,13 +667,9 @@ const results = await query({
|
|
|
713
667
|
|
|
714
668
|
### 时间范围查询
|
|
715
669
|
|
|
716
|
-
**函数式 API(推荐):**
|
|
717
|
-
|
|
718
670
|
```typescript
|
|
719
|
-
import { query } from '@seaverse/data-sdk';
|
|
720
|
-
|
|
721
671
|
// 查询指定时间范围的数据
|
|
722
|
-
const results = await query({
|
|
672
|
+
const results = await client.query({
|
|
723
673
|
table_name: 'logs',
|
|
724
674
|
filters: {
|
|
725
675
|
created_after: '2026-01-01T00:00:00Z',
|
|
@@ -730,13 +680,9 @@ const results = await query({
|
|
|
730
680
|
|
|
731
681
|
### 分页查询
|
|
732
682
|
|
|
733
|
-
**函数式 API(推荐):**
|
|
734
|
-
|
|
735
683
|
```typescript
|
|
736
|
-
import { queryWithPagination } from '@seaverse/data-sdk';
|
|
737
|
-
|
|
738
684
|
// 查询带总数的分页数据
|
|
739
|
-
const result = await queryWithPagination({
|
|
685
|
+
const result = await client.queryWithPagination({
|
|
740
686
|
table_name: 'posts',
|
|
741
687
|
pagination: { limit: 10, offset: 0 },
|
|
742
688
|
});
|
|
@@ -745,15 +691,6 @@ console.log(`总共 ${result.total} 条,当前返回 ${result.data.length} 条
|
|
|
745
691
|
console.log(`是否还有更多: ${result.hasMore}`);
|
|
746
692
|
```
|
|
747
693
|
|
|
748
|
-
**DataClient 类:**
|
|
749
|
-
|
|
750
|
-
```typescript
|
|
751
|
-
const result = await client.queryWithPagination({
|
|
752
|
-
table_name: 'posts',
|
|
753
|
-
pagination: { limit: 10, offset: 0 },
|
|
754
|
-
});
|
|
755
|
-
```
|
|
756
|
-
|
|
757
694
|
## 性能说明
|
|
758
695
|
|
|
759
696
|
基于新的 RLS 架构,SDK 具有以下性能特性:
|
|
@@ -763,22 +700,6 @@ const result = await client.queryWithPagination({
|
|
|
763
700
|
- ⚡ **短路求值**:优先检查 visibility 和 owner
|
|
764
701
|
- ⚡ **哈希分区**:256 个分区,支持并行查询
|
|
765
702
|
|
|
766
|
-
## 核心概念
|
|
767
|
-
|
|
768
|
-
### App ID 是什么?
|
|
769
|
-
|
|
770
|
-
**App ID** 是您在 SeaVerse 平台上创建的应用的唯一标识符。它用于:
|
|
771
|
-
- ✅ 多租户数据隔离(每个应用的数据互不干扰)
|
|
772
|
-
- ✅ 权限控制(RLS 策略基于 app_id)
|
|
773
|
-
- ✅ 资源配额管理
|
|
774
|
-
|
|
775
|
-
**如何获取 App ID?**
|
|
776
|
-
1. 登录 [SeaVerse 控制台](https://console.seaverse.ai)
|
|
777
|
-
2. 进入"应用管理"
|
|
778
|
-
3. 复制您的 App ID
|
|
779
|
-
|
|
780
|
-
> ⚠️ **重要**:所有 SDK 操作都需要提供有效的 `appId`,否则无法访问数据。
|
|
781
|
-
|
|
782
703
|
## 架构说明
|
|
783
704
|
|
|
784
705
|
### JWT Token 架构
|
|
@@ -820,43 +741,6 @@ psql -h <host> -U postgres -d <database> -f init/init-schema.sql
|
|
|
820
741
|
|
|
821
742
|
## 版本历史
|
|
822
743
|
|
|
823
|
-
### v0.3.0 (2026-01-19)
|
|
824
|
-
|
|
825
|
-
**🎉 重大功能更新**
|
|
826
|
-
|
|
827
|
-
- ✅ **新增函数式 API**(推荐使用 ⭐):提供 `query()`, `create()`, `update()`, `deleteData()` 等零配置便捷函数
|
|
828
|
-
- ✅ **零配置优先**:首次调用函数时自动初始化 SDK(通过 PostMessage 获取配置)
|
|
829
|
-
- ✅ **全局单例管理**:自动懒加载初始化,防止并发初始化问题
|
|
830
|
-
- ✅ **新增 `DataClient.connect()`**:语义更清晰的连接方法(推荐使用,`create()` 保留作为别名)
|
|
831
|
-
- ✅ **新增 `reinit()` 函数**:支持重新初始化(用于切换用户 token 或应用 appId)
|
|
832
|
-
- ✅ **新增 `getClientStatus()` 函数**:查询客户端初始化状态
|
|
833
|
-
|
|
834
|
-
**使用方式对比:**
|
|
835
|
-
|
|
836
|
-
```typescript
|
|
837
|
-
// ⭐ 方式 1: 函数式 API(推荐 - 最简单)
|
|
838
|
-
import { query, create } from '@seaverse/data-sdk';
|
|
839
|
-
const notes = await query({ table_name: 'notes' });
|
|
840
|
-
|
|
841
|
-
// 方式 2: DataClient 类(高级场景 - 多实例)
|
|
842
|
-
import { DataClient } from '@seaverse/data-sdk';
|
|
843
|
-
const client = await DataClient.connect();
|
|
844
|
-
const notes = await client.query({ table_name: 'notes' });
|
|
845
|
-
```
|
|
846
|
-
|
|
847
|
-
**架构改进:**
|
|
848
|
-
|
|
849
|
-
- 新增 `src/singleton.ts` - 全局单例管理
|
|
850
|
-
- 新增 `src/api.ts` - 函数式 API 封装
|
|
851
|
-
- 优化 `src/client.ts` - 添加 `connect()` 方法
|
|
852
|
-
- 更新 `src/index.ts` - 统一导出函数式 API 和类 API
|
|
853
|
-
|
|
854
|
-
**向后兼容:**
|
|
855
|
-
|
|
856
|
-
- ✅ `DataClient.create()` 方法保留(现为 `connect()` 的别名)
|
|
857
|
-
- ✅ 所有原有的类方法继续支持
|
|
858
|
-
- ✅ 无需修改现有代码,完全向后兼容
|
|
859
|
-
|
|
860
744
|
### v0.2.0 (2026-01-15)
|
|
861
745
|
|
|
862
746
|
**🔄 重大变更(Breaking Changes)**
|