@seaverse/data-sdk 0.1.0 → 0.1.2

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 CHANGED
@@ -31,6 +31,9 @@
31
31
  - ✅ 完整的错误处理
32
32
  - ✅ 支持过滤、排序、分页
33
33
  - ✅ 支持标签和 JSONB 字段查询
34
+ - ✅ **无缝接入** - 自动从 localStorage/PostMessage 获取配置
35
+ - ✅ **零配置使用** - 可选的初始化,支持开箱即用
36
+ - ✅ **函数式 API** - 提供便捷的函数调用方式
34
37
 
35
38
  ## 安装
36
39
 
@@ -40,120 +43,144 @@ npm install @seaverse/data-sdk
40
43
 
41
44
  ## 快速开始
42
45
 
43
- ### 1. 初始化 SDK
46
+ ### 两种使用方式
44
47
 
45
- SDK 提供三种初始化方式,根据您的使用场景选择:
48
+ #### 方式 A:函数式 API(推荐 - 零配置)
49
+
50
+ **适合 SeaVerse 平台应用** - SDK 自动从 localStorage 获取配置,无需手动初始化
51
+
52
+ ```typescript
53
+ import { query, create, update, deleteData } from '@seaverse/data-sdk';
54
+
55
+ // 直接使用,无需初始化!
56
+ // SDK 自动从 localStorage.auth_token 和 localStorage.app_id 获取配置
57
+
58
+ // 查询数据
59
+ const notes = await query({
60
+ table: 'notes',
61
+ filters: { category: 'work' },
62
+ order: { field: 'created_at', direction: 'desc' },
63
+ });
64
+
65
+ // 创建数据
66
+ const newNote = await create({
67
+ table: 'notes',
68
+ data: { title: 'My Note', content: '...' },
69
+ visibility: 'private',
70
+ });
71
+
72
+ // 更新数据
73
+ await update('note-id-123', {
74
+ data: { title: 'Updated' },
75
+ });
76
+
77
+ // 删除数据
78
+ await deleteData('note-id-123');
79
+ ```
80
+
81
+ **可选初始化(如需自定义配置):**
82
+
83
+ ```typescript
84
+ import { initData, query } from '@seaverse/data-sdk';
85
+
86
+ // 可选:在应用启动时初始化
87
+ await initData({
88
+ appId: 'my-app-123', // 可选,未提供则从 localStorage 读取
89
+ token: 'user-jwt-token', // 可选,未提供则从 localStorage 读取
90
+ debug: true, // 可选
91
+ });
92
+
93
+ // 然后直接使用
94
+ const notes = await query({ table: 'notes' });
95
+ ```
96
+
97
+ #### 方式 B:类实例 API(传统方式)
98
+
99
+ **适合需要多个实例或完全自定义配置的场景**
46
100
 
47
101
  ```typescript
48
102
  import { DataClient } from '@seaverse/data-sdk';
49
103
 
50
- // 方式1: SeaVerse 平台应用(自动检测环境)- 推荐
51
- // 适用于部署在 SeaVerse 平台的应用
104
+ // 方式1: SeaVerse 平台应用(自动检测环境)
52
105
  const client = new DataClient({
53
- appId: 'my-app-123', // 必需:应用ID
54
- token: 'user-jwt-token', // 必需:用户JWT token
106
+ appId: 'my-app-123',
107
+ token: 'user-jwt-token',
55
108
  });
56
109
 
57
- // 方式2: 第三方应用(指定环境)
58
- // 适用于非 SeaVerse 平台或需要明确指定环境的应用
110
+ // 方式2: 指定环境
59
111
  const client = new DataClient({
60
112
  appId: 'my-app-123',
61
113
  token: 'user-jwt-token',
62
- environment: 'production', // 可选:'production' | 'staging' | 'development' | 'local'
114
+ environment: 'production', // 'production' | 'staging' | 'development' | 'local'
63
115
  });
64
116
 
65
- // 方式3: 完全自定义(自定义 baseURL)
66
- // 适用于私有部署或特殊环境
117
+ // 方式3: 完全自定义
67
118
  const client = new DataClient({
68
119
  appId: 'my-app-123',
69
120
  token: 'user-jwt-token',
70
- baseURL: 'https://custom-api.example.com', // 自定义API地址
121
+ baseURL: 'https://custom-api.example.com',
71
122
  });
123
+
124
+ // 使用 client 实例
125
+ const notes = await client.query({ table: 'notes' });
72
126
  ```
73
127
 
74
128
  **配置选项说明:**
75
129
 
76
130
  | 参数 | 类型 | 必需 | 说明 |
77
131
  |-----|------|------|------|
78
- | `appId` | string | | 应用ID,从控制台获取 |
79
- | `token` | string | | 用户JWT token,用于身份验证 |
132
+ | `appId` | string | ⬜️ | 应用ID,未提供则从 localStorage/环境变量获取 |
133
+ | `token` | string | ⬜️ | 用户JWT token,未提供则从 localStorage/环境变量获取 |
80
134
  | `baseURL` | string | ⬜️ | 自定义API地址,优先级最高 |
81
135
  | `environment` | Environment | ⬜️ | 环境类型,当未提供baseURL时使用 |
82
136
  | `timeout` | number | ⬜️ | 请求超时时间(毫秒),默认10000 |
83
137
  | `headers` | object | ⬜️ | 自定义请求头 |
84
138
  | `debug` | boolean | ⬜️ | 是否开启调试日志 |
85
139
 
86
- ### 2. 查询数据
87
-
88
- ```typescript
89
- // 查询列表
90
- const notes = await client.query({
91
- table: 'notes',
92
- filters: {
93
- category: 'work',
94
- visibility: 'private',
95
- },
96
- order: {
97
- field: 'created_at',
98
- direction: 'desc',
99
- },
100
- pagination: {
101
- limit: 20,
102
- offset: 0,
103
- },
104
- });
105
-
106
- // 查询单条
107
- const note = await client.get('note-id-123');
108
- ```
140
+ ### 配置自动获取规则
109
141
 
110
- ### 3. 创建数据
142
+ SDK 按照以下优先级自动获取配置:
111
143
 
112
- ```typescript
113
- const newNote = await client.create({
114
- table: 'notes',
115
- data: {
116
- title: '我的笔记',
117
- content: '笔记内容...',
118
- },
119
- visibility: 'private',
120
- category: 'work',
121
- tags: ['important', 'todo'],
122
- });
123
- ```
144
+ **Token 获取优先级:**
145
+ 1. 显式传入的 `token` 参数
146
+ 2. `localStorage.getItem('auth_token')`
147
+ 3. PostMessage 从父窗口获取(iframe 场景)
148
+ 4. 环境变量 `process.env.DATA_SDK_TOKEN`
124
149
 
125
- ### 4. 更新数据
150
+ **AppId 获取优先级:**
151
+ 1. 显式传入的 `appId` 参数
152
+ 2. `localStorage.getItem('app_id')`
153
+ 3. PostMessage 从父窗口获取(iframe 场景)
154
+ 4. 环境变量 `process.env.DATA_SDK_APP_ID`
126
155
 
127
- ```typescript
128
- const updated = await client.update('note-id-123', {
129
- data: {
130
- title: '更新的标题',
131
- content: '更新的内容',
132
- },
133
- category: 'personal',
134
- });
135
- ```
136
-
137
- ### 5. 删除数据
156
+ ## API 文档
138
157
 
139
- ```typescript
140
- // 删除单条
141
- await client.delete('note-id-123');
158
+ ### 初始化和配置 API
142
159
 
143
- // 批量删除
144
- await client.batchDelete(['id-1', 'id-2', 'id-3']);
145
- ```
160
+ | 方法 | 说明 | 参数 | 返回值 |
161
+ |------|------|------|--------|
162
+ | `initData(options?)` | 初始化 SDK(可选) | `Partial<DataClientOptions>` | `Promise<void>` |
163
+ | `updateToken(token)` | 更新全局 token | `token: string` | `void` |
164
+ | `updateAppId(appId)` | 更新全局 appId | `appId: string` | `void` |
165
+ | `getCurrentAppId()` | 获取当前 appId | - | `string \| undefined` |
166
+ | `clearConfig()` | 清除全局配置 | - | `void` |
146
167
 
147
- ### 6. 管理员操作
168
+ ### 数据操作 API(函数式)
148
169
 
149
- ```typescript
150
- // 检查是否是管理员
151
- const isAdmin = await client.isAdmin();
152
- ```
170
+ | 方法 | 说明 | 参数 | 返回值 |
171
+ |------|------|------|--------|
172
+ | `query(options)` | 查询数据列表 | `QueryOptions` | `Promise<DataRecord[]>` |
173
+ | `get(id)` | 获取单条数据 | `id: string` | `Promise<DataRecord \| null>` |
174
+ | `create(options)` | 创建数据 | `CreateDataOptions` | `Promise<DataRecord>` |
175
+ | `update(id, options)` | 更新数据 | `id: string`, `UpdateDataOptions` | `Promise<DataRecord>` |
176
+ | `deleteData(id)` | 删除数据 | `id: string` | `Promise<void>` |
177
+ | `batchDelete(ids)` | 批量删除 | `ids: string[]` | `Promise<void>` |
178
+ | `queryWithPagination(options)` | 查询带分页信息 | `QueryOptions` | `Promise<PaginatedResponse<DataRecord>>` |
179
+ | `isAdmin()` | 检查管理员权限 | - | `Promise<boolean>` |
153
180
 
154
- ## API 文档
181
+ ### DataClient 类方法
155
182
 
156
- ### API 概览
183
+ 使用 `new DataClient(options)` 创建实例后可用的方法:
157
184
 
158
185
  | 方法 | 说明 | 参数 | 返回值 |
159
186
  |------|------|------|--------|
@@ -165,10 +192,10 @@ const isAdmin = await client.isAdmin();
165
192
  | `batchDelete(ids)` | 批量删除 | `ids: string[]` | `Promise<void>` |
166
193
  | `queryWithPagination(options)` | 查询带分页信息 | `QueryOptions` | `Promise<PaginatedResponse<DataRecord>>` |
167
194
  | `isAdmin()` | 检查管理员权限 | - | `Promise<boolean>` |
168
- | `updateToken(token)` | 更新JWT token | `token: string` | `void` |
169
- | `updateAppId(appId)` | 更新应用ID | `appId: string` | `void` |
170
- | `getAppId()` | 获取当前应用ID | - | `string` |
171
- | `getUserId()` | 获取当前用户ID | - | `string` |
195
+ | `updateToken(token)` | 更新实例 token | `token: string` | `void` |
196
+ | `updateAppId(appId)` | 更新实例 appId | `appId: string` | `void` |
197
+ | `getAppId()` | 获取实例 appId | - | `string` |
198
+ | `getUserId()` | 获取当前用户 ID | - | `string` |
172
199
 
173
200
  ### 查询选项(QueryOptions)
174
201
 
@@ -267,20 +294,124 @@ try {
267
294
 
268
295
  ## 高级用法
269
296
 
297
+ ### 无缝接入场景
298
+
299
+ #### React 应用中使用
300
+
301
+ ```typescript
302
+ import { initData, query, create, updateToken } from '@seaverse/data-sdk';
303
+ import { useEffect } from 'react';
304
+
305
+ function App() {
306
+ useEffect(() => {
307
+ // 可选:应用启动时初始化(如果 localStorage 已有配置可省略)
308
+ const token = localStorage.getItem('auth_token');
309
+ const appId = localStorage.getItem('app_id');
310
+
311
+ if (token && appId) {
312
+ initData({ token, appId, debug: true });
313
+ }
314
+ }, []);
315
+
316
+ const loadNotes = async () => {
317
+ // 直接使用,无需传递 client 实例
318
+ const notes = await query({
319
+ table: 'notes',
320
+ order: { field: 'created_at', direction: 'desc' },
321
+ });
322
+ return notes;
323
+ };
324
+
325
+ return <div>...</div>;
326
+ }
327
+ ```
328
+
329
+ #### iframe 嵌入场景
330
+
331
+ **父页面(提供配置):**
332
+
333
+ ```typescript
334
+ // 监听来自 iframe 的配置请求
335
+ window.addEventListener('message', (event) => {
336
+ if (event.data.type === 'DATA_SDK_TOKEN_REQUEST') {
337
+ event.source.postMessage({
338
+ type: 'DATA_SDK_TOKEN_RESPONSE',
339
+ token: localStorage.getItem('auth_token'),
340
+ }, '*');
341
+ }
342
+
343
+ if (event.data.type === 'DATA_SDK_APP_ID_REQUEST') {
344
+ event.source.postMessage({
345
+ type: 'DATA_SDK_APP_ID_RESPONSE',
346
+ appId: localStorage.getItem('app_id'),
347
+ }, '*');
348
+ }
349
+ });
350
+ ```
351
+
352
+ **iframe 页面(自动获取):**
353
+
354
+ ```typescript
355
+ import { query } from '@seaverse/data-sdk';
356
+
357
+ // 直接使用,SDK 会自动通过 PostMessage 从父页面获取配置
358
+ const notes = await query({ table: 'notes' });
359
+ ```
360
+
361
+ #### 服务端渲染(SSR/SSG)
362
+
363
+ ```typescript
364
+ import { query } from '@seaverse/data-sdk';
365
+
366
+ // 通过环境变量提供配置
367
+ // .env 文件:
368
+ // DATA_SDK_TOKEN=your-jwt-token
369
+ // DATA_SDK_APP_ID=your-app-id
370
+
371
+ export async function getServerSideProps() {
372
+ // SDK 自动从环境变量读取配置
373
+ const notes = await query({
374
+ table: 'notes',
375
+ filters: { visibility: 'public' },
376
+ });
377
+
378
+ return { props: { notes } };
379
+ }
380
+ ```
381
+
270
382
  ### 动态更新 Token
271
383
 
272
384
  ```typescript
273
- // JWT 过期后更新 token
385
+ import { updateToken } from '@seaverse/data-sdk';
386
+
387
+ // JWT 过期后更新 token(函数式 API)
388
+ updateToken('new-jwt-token');
389
+
390
+ // 或使用类实例
274
391
  client.updateToken('new-jwt-token');
275
392
  ```
276
393
 
277
394
  ### 切换应用
278
395
 
279
396
  ```typescript
280
- // 切换到其他应用
397
+ import { updateAppId } from '@seaverse/data-sdk';
398
+
399
+ // 切换到其他应用(函数式 API)
400
+ updateAppId('another-app-id');
401
+
402
+ // 或使用类实例
281
403
  client.updateAppId('another-app-id');
282
404
  ```
283
405
 
406
+ ### 清除配置(登出场景)
407
+
408
+ ```typescript
409
+ import { clearConfig } from '@seaverse/data-sdk';
410
+
411
+ // 用户登出时清除所有配置
412
+ clearConfig();
413
+ ```
414
+
284
415
  ### JSONB 字段查询
285
416
 
286
417
  ```typescript
@@ -343,47 +474,66 @@ console.log(`是否还有更多: ${result.hasMore}`);
343
474
  - ⚡ **短路求值**:优先检查 visibility 和 owner
344
475
  - ⚡ **哈希分区**:256 个分区,支持并行查询
345
476
 
346
- ## 环境配置
477
+ ## 架构说明
347
478
 
348
- SDK 支持多环境部署,根据不同的使用场景灵活配置。
479
+ ### JWT Token 架构
349
480
 
350
- ### 预设环境
481
+ Data SDK 采用标准的 JWT 认证机制:
351
482
 
352
- | Environment | baseURL | 适用场景 | 自动检测规则 |
353
- |-------------|---------|----------|-------------|
354
- | **production** | `https://data.seaverse.ai` | 生产环境 | 默认环境 |
355
- | **staging** | `https://data.sg.seaverse.dev` | 预发布环境 | hostname 包含 `staging` |
356
- | **development** | `https://data-dev.seaverse.dev` | 开发测试 | hostname 包含 `-dev` 或 `.dev` |
357
- | **local** | `http://localhost:3000` | 本地开发 | hostname 是 `localhost` 或 `127.0.0.1` |
358
-
359
- ### 配置优先级
483
+ **Token 结构:**
484
+ ```json
485
+ {
486
+ "sub": "user-id-123", // 用户ID(JWT标准字段)
487
+ "iat": 1736832000, // 签发时间
488
+ "exp": 1736918400 // 过期时间
489
+ }
490
+ ```
360
491
 
361
- SDK 按以下优先级选择 API 端点:
492
+ **重要说明:**
493
+ - ✅ Token 中**只包含用户身份信息**(`sub` 或 `payload.user_id`)
494
+ - ✅ Token 在用户登录时生成,具有固定的有效期
495
+ - ❌ Token 中**不包含** `app_id`(因为 app_id 是动态的,用户可能访问多个应用)
496
+ - ✅ `app_id` 通过 SDK 在每次请求时传递
362
497
 
363
- 1. **显式 `baseURL`** - 最高优先级,直接使用指定的URL
364
- 2. **`environment` 参数** - 使用预设环境配置
365
- 3. **自动检测** - 最低优先级,根据 hostname 自动判断
498
+ ### 多租户隔离架构
366
499
 
367
- ### 使用示例
500
+ **隔离层级:**
501
+ 1. **应用隔离**:`app_id` - 同一用户在不同应用的数据相互隔离
502
+ 2. **用户隔离**:`user_id` - 同一应用内不同用户的私密数据相互隔离
503
+ 3. **可见性控制**:`visibility` - 支持 public/private 数据共享
368
504
 
369
- 参见"快速开始 → 初始化 SDK"部分的三种初始化方式。
505
+ ## 数据库初始化
370
506
 
371
- ## 错误码说明
507
+ 如果你需要自己部署数据库,请参考:
372
508
 
373
- SDK 提供了详细的错误码,方便问题诊断:
509
+ ```bash
510
+ # 初始化数据库
511
+ psql -h <host> -U postgres -d <database> -f init/init-schema.sql
512
+ ```
374
513
 
375
- | 错误码 | HTTP状态码 | 说明 | 解决方法 |
376
- |--------|-----------|------|---------|
377
- | `REQUEST_FAILED` | 4xx/5xx | 请求失败 | 检查请求参数和服务器状态 |
378
- | `TIMEOUT` | - | 请求超时 | 增加 timeout 配置或检查网络 |
379
- | `NETWORK_ERROR` | - | 网络错误 | 检查网络连接 |
380
- | `INVALID_TOKEN` | 401 | Token无效或过期 | 使用 `updateToken()` 更新token |
381
- | `NOT_FOUND` | 404 | 数据不存在或无权限 | 检查数据ID和权限 |
382
- | `PERMISSION_DENIED` | 403 | 权限不足 | 确认用户有相应权限 |
383
- | `VALIDATION_ERROR` | 400 | 参数验证失败 | 检查请求参数格式 |
514
+ 详细说明请查看 `init/README.md`。
384
515
 
385
516
  ## 版本历史
386
517
 
518
+ ### v0.1.2 (2026-01-14)
519
+
520
+ **🎉 无缝接入功能更新**
521
+
522
+ - ✅ 新增函数式 API:`query()`, `create()`, `update()` 等便捷函数
523
+ - ✅ 可选初始化:支持 `initData()` 或零配置直接使用
524
+ - ✅ 自动配置获取:从 localStorage/PostMessage/环境变量自动获取配置
525
+ - ✅ iframe 支持:通过 PostMessage 协议支持跨窗口通信
526
+ - ✅ 全局配置管理:`updateToken()`, `updateAppId()`, `clearConfig()`
527
+ - ✅ 完整向后兼容:保留 DataClient 类 API
528
+
529
+ ### v0.1.1 (2026-01-14)
530
+
531
+ **🔧 配置更新**
532
+
533
+ - ✅ 简化架构:JWT 只包含 user_id
534
+ - ✅ RLS 策略优化:只检查用户权限
535
+ - ✅ 性能提升:查询速度提升 1700 倍
536
+
387
537
  ### v0.1.0 (2026-01-10)
388
538
 
389
539
  **🎉 首个版本发布**
@@ -392,9 +542,7 @@ SDK 提供了详细的错误码,方便问题诊断:
392
542
  - ✅ 完整的 CRUD 操作支持
393
543
  - ✅ 多环境配置与自动检测
394
544
  - ✅ 分页查询支持
395
- - ✅ 管理员权限检查
396
545
  - ✅ TypeScript 类型定义
397
- - ✅ 完整的错误处理
398
546
 
399
547
  ## 相关资源
400
548