@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.
Files changed (2) hide show
  1. package/README.md +133 -249
  2. 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
- ### 方式 1: 函数式 API(推荐 ⭐)
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
- // 首次调用会自动初始化 SDK(从 PostMessage 获取 appId 和 token)
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('note-id-123', {
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('note-id-123');
75
+ await deleteData(newNote.id);
59
76
  ```
60
77
 
61
- **✨ 为什么推荐函数式 API?**
62
-
78
+ **为什么推荐函数式 API:**
63
79
  - ✅ **零配置** - 无需创建客户端实例,直接调用函数
64
- - ✅ **自动初始化** - 首次调用自动获取配置(通过 PostMessage)
65
- - ✅ **简洁易用** - 代码更简洁,AI 更容易理解
80
+ - ✅ **自动初始化** - 第一次调用自动获取配置(PostMessage)
81
+ - ✅ **代码简洁** - 代码更简洁,更易理解
66
82
  - ✅ **单例管理** - SDK 自动管理全局实例,避免重复初始化
67
83
 
68
- ### 方式 2: DataClient 类 API(高级场景)
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.connect();
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 = await DataClient.connect({
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
- ### 函数式 API(推荐 ⭐)
133
-
134
- 零配置,直接导入使用:
149
+ ### DataClient 方法
135
150
 
136
- ```typescript
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 应用中使用(推荐:函数式 API)
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
- DataClient.connect({ debug: true }).then(setClient);
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
- return await client.query({
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 页面(自动获取) - 推荐:函数式 API:**
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.connect();
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
- await reinit({ appId: 'another-app', token: anotherToken });
558
+ client.updateAppId('another-app-id');
659
559
  ```
660
560
 
661
- **DataClient 类:**
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.updateAppId('another-app-id');
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
- ### JSONB 字段查询
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
- **函数式 API(推荐):**
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)**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seaverse/data-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "SeaVerse Data SDK - PostgreSQL/AlloyDB data operations with RLS-based permissions",
5
5
  "keywords": [
6
6
  "seaverse",