@seaverse/data-sdk 0.2.4 → 0.2.6
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 +56 -131
- package/dist/index.cjs +148 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -36
- package/dist/index.js +148 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,32 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
高性能数据服务 SDK,基于 PostgreSQL/AlloyDB + PostgREST + RLS 架构。
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 🚀 核心概念
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
import { DataClient } from '@seaverse/data-sdk';
|
|
14
|
-
|
|
15
|
-
// 无需任何配置,自动获取!
|
|
16
|
-
const client = await DataClient.create();
|
|
17
|
-
|
|
18
|
-
// 直接使用
|
|
19
|
-
const notes = await client.query({ table_name: 'notes' });
|
|
20
|
-
const newNote = await client.create({
|
|
21
|
-
table_name: 'notes',
|
|
22
|
-
data_value: { title: 'My Note', content: '...' },
|
|
23
|
-
});
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**工作原理:**
|
|
27
|
-
- 📡 **自动初始化**:`DataClient.create()` 自动向父页面发送 `seaverse:get_token` 和 `seaverse:get_appId` 消息
|
|
28
|
-
- 🔄 **PostMessage 协议**:父页面响应消息,返回配置信息
|
|
29
|
-
- ⚡ **即时可用**:适用于 iframe 嵌入场景和 SeaVerse 平台应用
|
|
30
|
-
|
|
31
|
-
> 💡 **父页面需要监听并响应 PostMessage 请求**(详见 [PostMessage 协议集成](#postmessage-协议集成))
|
|
32
|
-
|
|
33
|
-
## 🚀 App ID 是什么?
|
|
10
|
+
### App ID 是什么?
|
|
34
11
|
|
|
35
12
|
**App ID** 是您在 SeaVerse 平台上创建的应用的唯一标识符。它用于:
|
|
36
13
|
- ✅ 多租户数据隔离(每个应用的数据互不干扰)
|
|
@@ -55,7 +32,6 @@ const newNote = await client.create({
|
|
|
55
32
|
- ✅ 支持过滤、排序、分页
|
|
56
33
|
- ✅ 支持标签和 JSONB 字段查询
|
|
57
34
|
- ✅ **零配置使用** - 自动从 PostMessage 获取配置
|
|
58
|
-
- ✅ **类实例 API** - 清晰的面向对象设计
|
|
59
35
|
|
|
60
36
|
## 安装
|
|
61
37
|
|
|
@@ -65,17 +41,26 @@ npm install @seaverse/data-sdk
|
|
|
65
41
|
|
|
66
42
|
## 快速开始
|
|
67
43
|
|
|
68
|
-
###
|
|
69
|
-
|
|
70
|
-
**最简单的方式 - 无需传参!** SDK 会自动通过 PostMessage 从父页面获取 `appId` 和 `token`。
|
|
44
|
+
### 使用 DataClient
|
|
71
45
|
|
|
72
46
|
```typescript
|
|
73
47
|
import { DataClient } from '@seaverse/data-sdk';
|
|
74
48
|
|
|
75
|
-
//
|
|
49
|
+
// 方式1: 零配置(推荐)- 自动从 PostMessage 获取 appId 和 token
|
|
76
50
|
const client = await DataClient.create();
|
|
77
51
|
|
|
78
|
-
//
|
|
52
|
+
// 方式2: 只提供 appId,token 自动获取
|
|
53
|
+
const client = await DataClient.create({
|
|
54
|
+
appId: 'my-app-123',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 方式3: 显式提供所有配置
|
|
58
|
+
const client = new DataClient({
|
|
59
|
+
appId: 'my-app-123',
|
|
60
|
+
token: 'user-jwt-token',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 使用 client 实例
|
|
79
64
|
const notes = await client.query({
|
|
80
65
|
table_name: 'notes',
|
|
81
66
|
filters: { category: 'work' },
|
|
@@ -90,7 +75,7 @@ const newNote = await client.create({
|
|
|
90
75
|
});
|
|
91
76
|
|
|
92
77
|
// 更新数据
|
|
93
|
-
|
|
78
|
+
await client.update('note-id-123', {
|
|
94
79
|
data_value: { title: 'Updated' },
|
|
95
80
|
});
|
|
96
81
|
|
|
@@ -98,41 +83,6 @@ const updated = await client.update('note-id-123', {
|
|
|
98
83
|
await client.delete('note-id-123');
|
|
99
84
|
```
|
|
100
85
|
|
|
101
|
-
> 📡 **自动配置获取**:`DataClient.create()` 会自动向父页面发送 PostMessage 请求获取配置
|
|
102
|
-
> 🎯 **适用场景**:iframe 嵌入应用、SeaVerse 平台应用
|
|
103
|
-
> ⚠️ **前置条件**:父页面需要监听并响应 PostMessage 请求(详见 [PostMessage 协议集成](#postmessage-协议集成))
|
|
104
|
-
|
|
105
|
-
### 方式 2:显式传参
|
|
106
|
-
|
|
107
|
-
**适合非 iframe 环境或需要自定义配置的场景**
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
import { DataClient } from '@seaverse/data-sdk';
|
|
111
|
-
|
|
112
|
-
// 方式 2.1: 显式提供所有配置
|
|
113
|
-
const client = await DataClient.create({
|
|
114
|
-
appId: 'my-app-123',
|
|
115
|
-
token: 'user-jwt-token',
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// 方式 2.2: 指定环境
|
|
119
|
-
const client = await DataClient.create({
|
|
120
|
-
appId: 'my-app-123',
|
|
121
|
-
token: 'user-jwt-token',
|
|
122
|
-
environment: 'production', // 'production' | 'staging' | 'development' | 'local'
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// 方式 2.3: 自定义 API 地址
|
|
126
|
-
const client = await DataClient.create({
|
|
127
|
-
appId: 'my-app-123',
|
|
128
|
-
token: 'user-jwt-token',
|
|
129
|
-
baseURL: 'https://custom-api.example.com',
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// 使用 client 实例
|
|
133
|
-
const notes = await client.query({ table_name: 'notes' });
|
|
134
|
-
```
|
|
135
|
-
|
|
136
86
|
**配置选项说明:**
|
|
137
87
|
|
|
138
88
|
| 参数 | 类型 | 必需 | 说明 |
|
|
@@ -151,50 +101,26 @@ const notes = await client.query({ table_name: 'notes' });
|
|
|
151
101
|
> ```
|
|
152
102
|
> 如果启用 `debug: true`,还会打印完整的 token 和详细的配置信息(⚠️ 注意:生产环境建议关闭 debug 模式)。
|
|
153
103
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
### 核心机制
|
|
157
|
-
|
|
158
|
-
**SDK 采用 PostMessage 协议自动获取配置**,首次调用任何 API 时会自动触发:
|
|
159
|
-
|
|
160
|
-
1. **发送请求**:SDK 向父页面发送 `seaverse:get_token` 和 `seaverse:get_appId` 消息
|
|
161
|
-
2. **父页面响应**:父页面监听消息并返回配置信息
|
|
162
|
-
3. **完成初始化**:SDK 接收配置后立即可用
|
|
163
|
-
|
|
164
|
-
### 获取优先级
|
|
104
|
+
### 配置自动获取
|
|
165
105
|
|
|
166
|
-
|
|
167
|
-
1. 显式传入的 `token` 参数(最高优先级)
|
|
168
|
-
2. PostMessage 从父窗口获取(使用 `seaverse:get_token` 协议)
|
|
106
|
+
SDK 支持零配置使用,会自动从以下来源获取配置:
|
|
169
107
|
|
|
170
|
-
**AppId
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### PostMessage 协议说明
|
|
108
|
+
- **Token & AppId**: 通过 PostMessage 从父窗口获取,或显式传入
|
|
109
|
+
- **BaseURL**: 自动检测运行环境,或通过 `environment` 参数指定
|
|
110
|
+
- **环境变量**: Node.js 环境支持 `DATA_SDK_BASE_URL` 覆盖默认配置
|
|
175
111
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
| 错误 | - | `seaverse:error` | `{ error: string }` |
|
|
112
|
+
**支持的环境类型:**
|
|
113
|
+
```typescript
|
|
114
|
+
environment?: 'production' | 'staging' | 'development' | 'local'
|
|
115
|
+
```
|
|
181
116
|
|
|
182
|
-
>
|
|
183
|
-
> - 父页面**必须**监听并响应上述 PostMessage 消息,否则 SDK 无法自动获取配置
|
|
184
|
-
> - 如果父页面未响应,需要显式传入 `appId` 和 `token` 参数
|
|
185
|
-
> - 详细的父页面集成代码请参见 [PostMessage 协议集成](#postmessage-协议集成)
|
|
117
|
+
> 💡 **iframe 集成提示**:在 iframe 中使用时,SDK 会通过 PostMessage 与父页面通信获取配置(token、appId、环境标识),无需手动传递。
|
|
186
118
|
|
|
187
119
|
## API 文档
|
|
188
120
|
|
|
189
|
-
###
|
|
190
|
-
|
|
191
|
-
| 方法 | 说明 | 参数 | 返回值 |
|
|
192
|
-
|------|------|------|--------|
|
|
193
|
-
| `DataClient.create(options?)` | 创建客户端实例(支持零配置) | `DataClientOptions?` | `Promise<DataClient>` |
|
|
194
|
-
|
|
195
|
-
### 数据操作方法
|
|
121
|
+
### DataClient 方法
|
|
196
122
|
|
|
197
|
-
使用 `
|
|
123
|
+
使用 `DataClient.create()` 或 `new DataClient(options)` 创建实例后可用的方法:
|
|
198
124
|
|
|
199
125
|
| 方法 | 说明 | 参数 | 返回值 |
|
|
200
126
|
|------|------|------|--------|
|
|
@@ -206,19 +132,17 @@ const notes = await client.query({ table_name: 'notes' });
|
|
|
206
132
|
| `batchDelete(ids)` | 批量删除 | `ids: string[]` | `Promise<void>` |
|
|
207
133
|
| `queryWithPagination(options)` | 查询带分页信息 | `QueryOptions` | `Promise<PaginatedResponse<DataRecord>>` |
|
|
208
134
|
| `isAdmin()` | 检查管理员权限 | - | `Promise<boolean>` |
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
|
213
|
-
|------|------|------|--------|
|
|
214
|
-
| `getConfig()` | 获取当前 SDK 配置 | - | `{ appId: string; token: string; url: string }` |
|
|
135
|
+
| `updateToken(token)` | 更新实例 token | `token: string` | `void` |
|
|
136
|
+
| `updateAppId(appId)` | 更新实例 appId | `appId: string` | `void` |
|
|
137
|
+
| `getAppId()` | 获取实例 appId | - | `string` |
|
|
138
|
+
| `getUserId()` | 获取当前用户 ID | - | `string` |
|
|
215
139
|
|
|
216
140
|
### 查询选项(QueryOptions)
|
|
217
141
|
|
|
218
142
|
```typescript
|
|
219
143
|
interface QueryOptions {
|
|
220
144
|
// 表名(必填)
|
|
221
|
-
|
|
145
|
+
table_name: string;
|
|
222
146
|
|
|
223
147
|
// 过滤条件
|
|
224
148
|
filters?: {
|
|
@@ -226,7 +150,7 @@ interface QueryOptions {
|
|
|
226
150
|
category?: string; // 分类
|
|
227
151
|
visibility?: 'public' | 'private'; // 可见性
|
|
228
152
|
tags?: string[]; // 标签
|
|
229
|
-
|
|
153
|
+
data_value?: Record<string, any>; // data_value 字段过滤
|
|
230
154
|
created_after?: string; // 创建时间(之后)
|
|
231
155
|
created_before?: string; // 创建时间(之前)
|
|
232
156
|
};
|
|
@@ -291,7 +215,7 @@ const note = await client.create({
|
|
|
291
215
|
title: '我的笔记',
|
|
292
216
|
content: '笔记内容...',
|
|
293
217
|
author: 'Alice',
|
|
294
|
-
|
|
218
|
+
metadata_value: {
|
|
295
219
|
wordCount: 1000,
|
|
296
220
|
tags: ['work']
|
|
297
221
|
}
|
|
@@ -454,25 +378,25 @@ try {
|
|
|
454
378
|
data-sdk 通过 PostMessage 协议与父页面通信获取配置:
|
|
455
379
|
|
|
456
380
|
```typescript
|
|
457
|
-
import {
|
|
381
|
+
import { DataClient } from '@seaverse/data-sdk';
|
|
458
382
|
|
|
459
383
|
// SDK 会自动通过 PostMessage 从父页面获取 appId 和 token
|
|
460
384
|
// 父页面需要响应以下消息:
|
|
461
385
|
// - seaverse:get_token → 返回 token
|
|
462
386
|
// - seaverse:get_appId → 返回 appId
|
|
463
387
|
|
|
464
|
-
//
|
|
465
|
-
await
|
|
388
|
+
// 零配置初始化(会自动通过 PostMessage 获取配置)
|
|
389
|
+
const client = await DataClient.create();
|
|
466
390
|
|
|
467
391
|
// 使用 data-sdk
|
|
468
|
-
const notes = await query({ table_name: 'notes' });
|
|
392
|
+
const notes = await client.query({ table_name: 'notes' });
|
|
469
393
|
```
|
|
470
394
|
|
|
471
|
-
|
|
472
|
-
1.
|
|
473
|
-
2. PostMessage 从父窗口获取(使用 `seaverse:get_token` 协议)
|
|
395
|
+
**配置获取优先级:**
|
|
396
|
+
1. 显式传入的参数(最高优先级)
|
|
397
|
+
2. PostMessage 从父窗口获取(使用 `seaverse:get_token` 和 `seaverse:get_appId` 协议)
|
|
474
398
|
|
|
475
|
-
###
|
|
399
|
+
### 无缝接入场景
|
|
476
400
|
|
|
477
401
|
#### React 应用中使用
|
|
478
402
|
|
|
@@ -484,7 +408,8 @@ function App() {
|
|
|
484
408
|
const [client, setClient] = useState<DataClient | null>(null);
|
|
485
409
|
|
|
486
410
|
useEffect(() => {
|
|
487
|
-
//
|
|
411
|
+
// 应用启动时初始化客户端
|
|
412
|
+
// SDK 会自动通过 PostMessage 从父页面获取配置
|
|
488
413
|
DataClient.create({ debug: true }).then(setClient);
|
|
489
414
|
}, []);
|
|
490
415
|
|
|
@@ -557,7 +482,7 @@ window.addEventListener('message', (event) => {
|
|
|
557
482
|
```typescript
|
|
558
483
|
import { DataClient } from '@seaverse/data-sdk';
|
|
559
484
|
|
|
560
|
-
//
|
|
485
|
+
// 零配置创建客户端,SDK 会自动通过 PostMessage 从父页面获取 appId 和 token
|
|
561
486
|
const client = await DataClient.create();
|
|
562
487
|
const notes = await client.query({ table_name: 'notes' });
|
|
563
488
|
```
|
|
@@ -577,7 +502,7 @@ import { DataClient } from '@seaverse/data-sdk';
|
|
|
577
502
|
|
|
578
503
|
export async function getServerSideProps() {
|
|
579
504
|
// 服务端需要显式提供配置
|
|
580
|
-
const client =
|
|
505
|
+
const client = new DataClient({
|
|
581
506
|
appId: process.env.APP_ID,
|
|
582
507
|
token: process.env.API_SERVICE_TOKEN,
|
|
583
508
|
});
|
|
@@ -591,18 +516,18 @@ export async function getServerSideProps() {
|
|
|
591
516
|
}
|
|
592
517
|
```
|
|
593
518
|
|
|
594
|
-
###
|
|
519
|
+
### 动态更新 Token
|
|
595
520
|
|
|
596
521
|
```typescript
|
|
597
|
-
|
|
522
|
+
// JWT 过期后更新 token
|
|
523
|
+
client.updateToken('new-jwt-token');
|
|
524
|
+
```
|
|
598
525
|
|
|
599
|
-
|
|
526
|
+
### 切换应用
|
|
600
527
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
console.log('Token:', config.token);
|
|
605
|
-
console.log('API URL:', config.url);
|
|
528
|
+
```typescript
|
|
529
|
+
// 切换到其他应用
|
|
530
|
+
client.updateAppId('another-app-id');
|
|
606
531
|
```
|
|
607
532
|
|
|
608
533
|
### JSONB 字段查询
|
package/dist/index.cjs
CHANGED
|
@@ -138,13 +138,14 @@ function detectEnvironment() {
|
|
|
138
138
|
/**
|
|
139
139
|
* 解析用户配置,返回最终的 baseURL
|
|
140
140
|
*
|
|
141
|
-
*
|
|
141
|
+
* 优先级(同步版本,用于向后兼容):
|
|
142
142
|
* 1. 显式传入的 baseURL(最高优先级)
|
|
143
143
|
* 2. environment 参数指定的环境
|
|
144
144
|
* 3. 自动检测环境(最低优先级)
|
|
145
145
|
*
|
|
146
146
|
* @param options 用户配置选项
|
|
147
147
|
* @returns 最终使用的 baseURL
|
|
148
|
+
* @deprecated 推荐使用异步版本 resolveBaseURLAsync 以支持 PostMessage 和环境变量
|
|
148
149
|
*/
|
|
149
150
|
function resolveBaseURL(options) {
|
|
150
151
|
// 优先级 1: 显式传入的 baseURL
|
|
@@ -159,6 +160,52 @@ function resolveBaseURL(options) {
|
|
|
159
160
|
const detectedEnv = detectEnvironment();
|
|
160
161
|
return ENVIRONMENT_CONFIGS[detectedEnv].baseURL;
|
|
161
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* 解析用户配置,返回最终的 baseURL(异步版本)
|
|
165
|
+
*
|
|
166
|
+
* 优先级:
|
|
167
|
+
* 1. 显式传入的 baseURL(最高优先级)
|
|
168
|
+
* 2. 环境变量 DATA_SDK_BASE_URL
|
|
169
|
+
* 3. environment 参数指定的环境
|
|
170
|
+
* 4. iframe 环境 - 从父页面获取环境标识
|
|
171
|
+
* - 父页面返回 'develop' → development 环境
|
|
172
|
+
* - 父页面返回 'production' → production 环境
|
|
173
|
+
* - 父页面返回其他值 → 自动检测环境
|
|
174
|
+
* 5. 非 iframe 环境 - 自动检测环境
|
|
175
|
+
*
|
|
176
|
+
* @param options 用户配置选项
|
|
177
|
+
* @returns 最终使用的 baseURL
|
|
178
|
+
*/
|
|
179
|
+
async function resolveBaseURLAsync(options) {
|
|
180
|
+
// 优先级 1: 显式传入的 baseURL
|
|
181
|
+
if (options.baseURL) {
|
|
182
|
+
return options.baseURL;
|
|
183
|
+
}
|
|
184
|
+
// 优先级 2: 环境变量 DATA_SDK_BASE_URL
|
|
185
|
+
if (typeof process !== 'undefined' && process.env?.DATA_SDK_BASE_URL) {
|
|
186
|
+
return process.env.DATA_SDK_BASE_URL;
|
|
187
|
+
}
|
|
188
|
+
// 优先级 3: environment 参数
|
|
189
|
+
if (options.environment) {
|
|
190
|
+
return ENVIRONMENT_CONFIGS[options.environment].baseURL;
|
|
191
|
+
}
|
|
192
|
+
// 优先级 4: iframe 环境 - 从父页面获取环境标识
|
|
193
|
+
if (isInIframe()) {
|
|
194
|
+
const parentEnv = await getEnvFromParent(3000);
|
|
195
|
+
if (parentEnv === 'develop') {
|
|
196
|
+
console.log('[DataSDK] iframe 环境:父页面返回开发环境,使用 development baseURL');
|
|
197
|
+
return ENVIRONMENT_CONFIGS.development.baseURL;
|
|
198
|
+
}
|
|
199
|
+
else if (parentEnv === 'production') {
|
|
200
|
+
console.log('[DataSDK] iframe 环境:父页面返回生产环境,使用 production baseURL');
|
|
201
|
+
return ENVIRONMENT_CONFIGS.production.baseURL;
|
|
202
|
+
}
|
|
203
|
+
// 如果获取到其他值或没有获取到,继续自动检测
|
|
204
|
+
}
|
|
205
|
+
// 优先级 5: 自动检测环境
|
|
206
|
+
const detectedEnv = detectEnvironment();
|
|
207
|
+
return ENVIRONMENT_CONFIGS[detectedEnv].baseURL;
|
|
208
|
+
}
|
|
162
209
|
/**
|
|
163
210
|
* 默认超时时间(毫秒)
|
|
164
211
|
*/
|
|
@@ -199,10 +246,8 @@ function isInIframe() {
|
|
|
199
246
|
*/
|
|
200
247
|
async function getTokenFromParent(timeout = 5000) {
|
|
201
248
|
if (!isInIframe()) {
|
|
202
|
-
console.log('[DataSDK] Token: not in iframe, skipping PostMessage');
|
|
203
249
|
return null;
|
|
204
250
|
}
|
|
205
|
-
console.log('[DataSDK] Token: requesting from parent via PostMessage');
|
|
206
251
|
return new Promise((resolve) => {
|
|
207
252
|
const messageHandler = (event) => {
|
|
208
253
|
// 验证消息格式
|
|
@@ -210,24 +255,17 @@ async function getTokenFromParent(timeout = 5000) {
|
|
|
210
255
|
cleanup();
|
|
211
256
|
// 从 payload.accessToken 中获取 token
|
|
212
257
|
const token = event.data.payload?.accessToken;
|
|
213
|
-
if (token) {
|
|
214
|
-
console.log('[DataSDK] Token: ✓ received from parent');
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
console.warn('[DataSDK] Token: received but accessToken is empty');
|
|
218
|
-
}
|
|
219
258
|
resolve(token || null);
|
|
220
259
|
}
|
|
221
260
|
else if (event.data && event.data.type === 'seaverse:error') {
|
|
222
261
|
// 处理错误响应
|
|
223
262
|
cleanup();
|
|
224
|
-
console.warn('[DataSDK]
|
|
263
|
+
console.warn('[DataSDK] Error getting token from parent:', event.data.error);
|
|
225
264
|
resolve(null);
|
|
226
265
|
}
|
|
227
266
|
};
|
|
228
267
|
const timeoutId = setTimeout(() => {
|
|
229
268
|
cleanup();
|
|
230
|
-
console.warn('[DataSDK] Token: ✗ timeout (5s), no response from parent');
|
|
231
269
|
resolve(null);
|
|
232
270
|
}, timeout);
|
|
233
271
|
const cleanup = () => {
|
|
@@ -243,7 +281,6 @@ async function getTokenFromParent(timeout = 5000) {
|
|
|
243
281
|
}
|
|
244
282
|
catch (e) {
|
|
245
283
|
cleanup();
|
|
246
|
-
console.error('[DataSDK] Token: ✗ failed to send PostMessage:', e);
|
|
247
284
|
resolve(null);
|
|
248
285
|
}
|
|
249
286
|
});
|
|
@@ -262,10 +299,8 @@ async function getTokenFromParent(timeout = 5000) {
|
|
|
262
299
|
*/
|
|
263
300
|
async function getAppIdFromParent(timeout = 5000) {
|
|
264
301
|
if (!isInIframe()) {
|
|
265
|
-
console.log('[DataSDK] AppId: not in iframe, skipping PostMessage');
|
|
266
302
|
return null;
|
|
267
303
|
}
|
|
268
|
-
console.log('[DataSDK] AppId: requesting from parent via PostMessage');
|
|
269
304
|
return new Promise((resolve) => {
|
|
270
305
|
const messageHandler = (event) => {
|
|
271
306
|
// 验证消息格式
|
|
@@ -273,24 +308,17 @@ async function getAppIdFromParent(timeout = 5000) {
|
|
|
273
308
|
cleanup();
|
|
274
309
|
// 从 payload.appId 中获取 appId
|
|
275
310
|
const appId = event.data.payload?.appId;
|
|
276
|
-
if (appId) {
|
|
277
|
-
console.log('[DataSDK] AppId: ✓ received from parent');
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
console.warn('[DataSDK] AppId: received but appId is empty');
|
|
281
|
-
}
|
|
282
311
|
resolve(appId || null);
|
|
283
312
|
}
|
|
284
313
|
else if (event.data && event.data.type === 'seaverse:error') {
|
|
285
314
|
// 处理错误响应
|
|
286
315
|
cleanup();
|
|
287
|
-
console.warn('[DataSDK]
|
|
316
|
+
console.warn('[DataSDK] Error getting appId from parent:', event.data.error);
|
|
288
317
|
resolve(null);
|
|
289
318
|
}
|
|
290
319
|
};
|
|
291
320
|
const timeoutId = setTimeout(() => {
|
|
292
321
|
cleanup();
|
|
293
|
-
console.warn('[DataSDK] AppId: ✗ timeout (5s), no response from parent');
|
|
294
322
|
resolve(null);
|
|
295
323
|
}, timeout);
|
|
296
324
|
const cleanup = () => {
|
|
@@ -306,7 +334,59 @@ async function getAppIdFromParent(timeout = 5000) {
|
|
|
306
334
|
}
|
|
307
335
|
catch (e) {
|
|
308
336
|
cleanup();
|
|
309
|
-
|
|
337
|
+
resolve(null);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* 通过 PostMessage 从父页面获取环境标识
|
|
343
|
+
*
|
|
344
|
+
* 此函数允许接收来自任何源的 PostMessage 消息,便于跨域 iframe 集成。
|
|
345
|
+
* 使用 seaverse 协议与父页面通信:
|
|
346
|
+
* - 发送: { type: 'seaverse:get_env' }
|
|
347
|
+
* - 接收: { type: 'seaverse:env', payload: { env: string } }
|
|
348
|
+
* - 错误: { type: 'seaverse:error', error: string }
|
|
349
|
+
*
|
|
350
|
+
* @param timeout 超时时间(毫秒),默认 5000ms
|
|
351
|
+
* @returns 环境标识字符串 (如 "develop", "production") 或 null
|
|
352
|
+
*/
|
|
353
|
+
async function getEnvFromParent(timeout = 5000) {
|
|
354
|
+
if (!isInIframe()) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
return new Promise((resolve) => {
|
|
358
|
+
const messageHandler = (event) => {
|
|
359
|
+
// 验证消息格式
|
|
360
|
+
if (event.data && event.data.type === 'seaverse:env') {
|
|
361
|
+
cleanup();
|
|
362
|
+
// 从 payload.env 中获取环境标识
|
|
363
|
+
const env = event.data.payload?.env;
|
|
364
|
+
resolve(env || null);
|
|
365
|
+
}
|
|
366
|
+
else if (event.data && event.data.type === 'seaverse:error') {
|
|
367
|
+
// 处理错误响应
|
|
368
|
+
cleanup();
|
|
369
|
+
console.warn('[DataSDK] Error getting env from parent:', event.data.error);
|
|
370
|
+
resolve(null);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
const timeoutId = setTimeout(() => {
|
|
374
|
+
cleanup();
|
|
375
|
+
resolve(null);
|
|
376
|
+
}, timeout);
|
|
377
|
+
const cleanup = () => {
|
|
378
|
+
clearTimeout(timeoutId);
|
|
379
|
+
globalThis.window.removeEventListener('message', messageHandler);
|
|
380
|
+
};
|
|
381
|
+
// 监听父页面的响应(不限制来源)
|
|
382
|
+
globalThis.window.addEventListener('message', messageHandler);
|
|
383
|
+
// 向父页面发送请求(允许任何源接收)
|
|
384
|
+
try {
|
|
385
|
+
globalThis.window.parent.postMessage({ type: 'seaverse:get_env' }, '*' // 允许任何源,支持跨域场景
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
catch (e) {
|
|
389
|
+
cleanup();
|
|
310
390
|
resolve(null);
|
|
311
391
|
}
|
|
312
392
|
});
|
|
@@ -330,7 +410,6 @@ async function getApiToken(providedApiKey) {
|
|
|
330
410
|
}
|
|
331
411
|
}
|
|
332
412
|
// 3. 返回'',让调用方处理
|
|
333
|
-
console.warn('[DataSDK] Token: not found, returning empty string');
|
|
334
413
|
return '';
|
|
335
414
|
}
|
|
336
415
|
/**
|
|
@@ -352,7 +431,6 @@ async function getAppId(providedAppId) {
|
|
|
352
431
|
}
|
|
353
432
|
}
|
|
354
433
|
// 3. 返回'',让调用方处理
|
|
355
|
-
console.warn('[DataSDK] AppId: not found, returning empty string');
|
|
356
434
|
return '';
|
|
357
435
|
}
|
|
358
436
|
|
|
@@ -375,21 +453,21 @@ async function getAppId(providedAppId) {
|
|
|
375
453
|
*
|
|
376
454
|
* // Query data
|
|
377
455
|
* const notes = await client.query({
|
|
378
|
-
*
|
|
456
|
+
* table_name: 'notes',
|
|
379
457
|
* filters: { category: 'work' },
|
|
380
458
|
* order: { field: 'created_at', direction: 'desc' },
|
|
381
459
|
* });
|
|
382
460
|
*
|
|
383
461
|
* // Create data
|
|
384
462
|
* const note = await client.create({
|
|
385
|
-
*
|
|
386
|
-
*
|
|
463
|
+
* table_name: 'notes',
|
|
464
|
+
* data_value: { title: 'My Note', content: '...' },
|
|
387
465
|
* visibility: 'private',
|
|
388
466
|
* });
|
|
389
467
|
*
|
|
390
468
|
* // Update data
|
|
391
469
|
* await client.update('note-id', {
|
|
392
|
-
*
|
|
470
|
+
* data_value: { title: 'Updated' },
|
|
393
471
|
* });
|
|
394
472
|
*
|
|
395
473
|
* // Delete data
|
|
@@ -400,14 +478,14 @@ class DataClient {
|
|
|
400
478
|
/**
|
|
401
479
|
* 创建 DataClient 实例(异步工厂方法)
|
|
402
480
|
*
|
|
403
|
-
* 支持自动从 localStorage/环境变量/PostMessage 获取 appId 和
|
|
481
|
+
* 支持自动从 localStorage/环境变量/PostMessage 获取 appId、token 和 baseURL
|
|
404
482
|
*
|
|
405
483
|
* @param options - 配置选项(appId 和 token 可选)
|
|
406
484
|
* @returns DataClient 实例
|
|
407
485
|
*
|
|
408
486
|
* @example
|
|
409
487
|
* ```typescript
|
|
410
|
-
* // 自动获取 appId 和 token
|
|
488
|
+
* // 自动获取 appId 和 token,并通过 PostMessage 获取环境配置
|
|
411
489
|
* const client = await DataClient.create();
|
|
412
490
|
*
|
|
413
491
|
* // 提供 appId,自动获取 token
|
|
@@ -416,7 +494,8 @@ class DataClient {
|
|
|
416
494
|
* // 显式提供所有配置
|
|
417
495
|
* const client = await DataClient.create({
|
|
418
496
|
* appId: 'my-app-123',
|
|
419
|
-
* token: 'user-jwt-token'
|
|
497
|
+
* token: 'user-jwt-token',
|
|
498
|
+
* environment: 'production'
|
|
420
499
|
* });
|
|
421
500
|
* ```
|
|
422
501
|
*/
|
|
@@ -429,10 +508,16 @@ class DataClient {
|
|
|
429
508
|
if (!token) {
|
|
430
509
|
throw new ValidationError('token is required. Please ensure it is available in localStorage.auth_token, provide token explicitly, or configure parent page to provide it via PostMessage.');
|
|
431
510
|
}
|
|
511
|
+
// 使用异步版本解析 baseURL,支持环境变量和 PostMessage
|
|
512
|
+
const baseURL = await resolveBaseURLAsync({
|
|
513
|
+
baseURL: options.baseURL,
|
|
514
|
+
environment: options.environment,
|
|
515
|
+
});
|
|
432
516
|
return new DataClient({
|
|
433
517
|
...options,
|
|
434
518
|
appId,
|
|
435
519
|
token,
|
|
520
|
+
baseURL, // 传递已解析的 baseURL
|
|
436
521
|
});
|
|
437
522
|
}
|
|
438
523
|
constructor(options) {
|
|
@@ -590,7 +675,7 @@ class DataClient {
|
|
|
590
675
|
* @example
|
|
591
676
|
* ```typescript
|
|
592
677
|
* const notes = await client.query({
|
|
593
|
-
*
|
|
678
|
+
* table_name: 'notes',
|
|
594
679
|
* filters: {
|
|
595
680
|
* category: 'work',
|
|
596
681
|
* visibility: 'private',
|
|
@@ -860,7 +945,7 @@ class DataClient {
|
|
|
860
945
|
* @example
|
|
861
946
|
* ```typescript
|
|
862
947
|
* const result = await client.queryWithPagination({
|
|
863
|
-
*
|
|
948
|
+
* table: 'posts',
|
|
864
949
|
* pagination: { limit: 10, offset: 0 },
|
|
865
950
|
* });
|
|
866
951
|
*
|
|
@@ -985,26 +1070,39 @@ class DataClient {
|
|
|
985
1070
|
// Configuration Methods
|
|
986
1071
|
// ============================================
|
|
987
1072
|
/**
|
|
988
|
-
*
|
|
1073
|
+
* Update token
|
|
989
1074
|
*
|
|
990
|
-
*
|
|
1075
|
+
* 更新 JWT token
|
|
991
1076
|
*
|
|
992
|
-
* @
|
|
1077
|
+
* @param token - New JWT token
|
|
1078
|
+
*/
|
|
1079
|
+
updateToken(token) {
|
|
1080
|
+
this.token = token;
|
|
1081
|
+
this.axiosInstance.defaults.headers['Authorization'] = `Bearer ${token}`;
|
|
1082
|
+
this.log('Token updated');
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Update app ID
|
|
993
1086
|
*
|
|
994
|
-
*
|
|
995
|
-
*
|
|
996
|
-
*
|
|
997
|
-
* console.log('App ID:', config.appId);
|
|
998
|
-
* console.log('Token:', config.token);
|
|
999
|
-
* console.log('API URL:', config.url);
|
|
1000
|
-
* ```
|
|
1087
|
+
* 更新应用 ID
|
|
1088
|
+
*
|
|
1089
|
+
* @param appId - New application ID
|
|
1001
1090
|
*/
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1091
|
+
updateAppId(appId) {
|
|
1092
|
+
this.appId = appId;
|
|
1093
|
+
this.log('AppId updated', { appId });
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Get current app ID
|
|
1097
|
+
*/
|
|
1098
|
+
getAppId() {
|
|
1099
|
+
return this.appId;
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Get current user ID
|
|
1103
|
+
*/
|
|
1104
|
+
getUserId() {
|
|
1105
|
+
return this.getUserIdFromToken();
|
|
1008
1106
|
}
|
|
1009
1107
|
}
|
|
1010
1108
|
|