@seaverse/data-sdk 0.2.6 → 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 +128 -20
- package/dist/index.cjs +353 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +228 -3
- package/dist/index.js +344 -115
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
- ✅ 支持过滤、排序、分页
|
|
33
33
|
- ✅ 支持标签和 JSONB 字段查询
|
|
34
34
|
- ✅ **零配置使用** - 自动从 PostMessage 获取配置
|
|
35
|
+
- ✅ **自动配额限制** - 数据库层强制执行配额,防止资源滥用
|
|
35
36
|
|
|
36
37
|
## 安装
|
|
37
38
|
|
|
@@ -41,20 +42,63 @@ npm install @seaverse/data-sdk
|
|
|
41
42
|
|
|
42
43
|
## 快速开始
|
|
43
44
|
|
|
44
|
-
###
|
|
45
|
+
### 方式1: 函数式 API(推荐 ⭐)
|
|
46
|
+
|
|
47
|
+
最简单的使用方式 - 零配置,直接调用函数:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { query, create, update, deleteData } from '@seaverse/data-sdk';
|
|
51
|
+
|
|
52
|
+
// 第一次调用自动初始化 SDK(从 PostMessage 获取 appId 和 token)
|
|
53
|
+
// 无需任何配置代码!
|
|
54
|
+
|
|
55
|
+
// 查询数据
|
|
56
|
+
const notes = await query({
|
|
57
|
+
table_name: 'notes',
|
|
58
|
+
filters: { category: 'work' },
|
|
59
|
+
order: { field: 'created_at', direction: 'desc' }
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 创建数据
|
|
63
|
+
const newNote = await create({
|
|
64
|
+
table_name: 'notes',
|
|
65
|
+
data_value: { title: 'My Note', content: '...' },
|
|
66
|
+
visibility: 'private'
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// 更新数据
|
|
70
|
+
await update(newNote.id, {
|
|
71
|
+
data_value: { title: 'Updated Title' }
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 删除数据
|
|
75
|
+
await deleteData(newNote.id);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**为什么推荐函数式 API:**
|
|
79
|
+
- ✅ **零配置** - 无需创建客户端实例,直接调用函数
|
|
80
|
+
- ✅ **自动初始化** - 第一次调用自动获取配置(PostMessage)
|
|
81
|
+
- ✅ **代码简洁** - 代码更简洁,更易理解
|
|
82
|
+
- ✅ **单例管理** - SDK 自动管理全局实例,避免重复初始化
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### 方式2: DataClient 类 API(高级场景)
|
|
87
|
+
|
|
88
|
+
适用于需要多个实例或自定义配置的场景:
|
|
45
89
|
|
|
46
90
|
```typescript
|
|
47
91
|
import { DataClient } from '@seaverse/data-sdk';
|
|
48
92
|
|
|
49
|
-
// 方式
|
|
93
|
+
// 方式2a: 零配置(推荐)- 自动从 PostMessage 获取 appId 和 token
|
|
50
94
|
const client = await DataClient.create();
|
|
51
95
|
|
|
52
|
-
// 方式
|
|
96
|
+
// 方式2b: 只提供 appId,token 自动获取
|
|
53
97
|
const client = await DataClient.create({
|
|
54
98
|
appId: 'my-app-123',
|
|
55
99
|
});
|
|
56
100
|
|
|
57
|
-
// 方式
|
|
101
|
+
// 方式2c: 显式提供所有配置
|
|
58
102
|
const client = new DataClient({
|
|
59
103
|
appId: 'my-app-123',
|
|
60
104
|
token: 'user-jwt-token',
|
|
@@ -64,23 +108,7 @@ const client = new DataClient({
|
|
|
64
108
|
const notes = await client.query({
|
|
65
109
|
table_name: 'notes',
|
|
66
110
|
filters: { category: 'work' },
|
|
67
|
-
order: { field: 'created_at', direction: 'desc' },
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// 创建数据
|
|
71
|
-
const newNote = await client.create({
|
|
72
|
-
table_name: 'notes',
|
|
73
|
-
data_value: { title: 'My Note', content: '...' },
|
|
74
|
-
visibility: 'private',
|
|
75
111
|
});
|
|
76
|
-
|
|
77
|
-
// 更新数据
|
|
78
|
-
await client.update('note-id-123', {
|
|
79
|
-
data_value: { title: 'Updated' },
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// 删除数据
|
|
83
|
-
await client.delete('note-id-123');
|
|
84
112
|
```
|
|
85
113
|
|
|
86
114
|
**配置选项说明:**
|
|
@@ -530,6 +558,86 @@ client.updateToken('new-jwt-token');
|
|
|
530
558
|
client.updateAppId('another-app-id');
|
|
531
559
|
```
|
|
532
560
|
|
|
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
|
+
#### 错误处理
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
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
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
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
|
+
```
|
|
640
|
+
|
|
533
641
|
### JSONB 字段查询
|
|
534
642
|
|
|
535
643
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -160,52 +160,6 @@ function resolveBaseURL(options) {
|
|
|
160
160
|
const detectedEnv = detectEnvironment();
|
|
161
161
|
return ENVIRONMENT_CONFIGS[detectedEnv].baseURL;
|
|
162
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
|
-
}
|
|
209
163
|
/**
|
|
210
164
|
* 默认超时时间(毫秒)
|
|
211
165
|
*/
|
|
@@ -338,59 +292,6 @@ async function getAppIdFromParent(timeout = 5000) {
|
|
|
338
292
|
}
|
|
339
293
|
});
|
|
340
294
|
}
|
|
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();
|
|
390
|
-
resolve(null);
|
|
391
|
-
}
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
295
|
/**
|
|
395
296
|
* 动态获取 API Token
|
|
396
297
|
* 优先级:
|
|
@@ -476,40 +377,42 @@ async function getAppId(providedAppId) {
|
|
|
476
377
|
*/
|
|
477
378
|
class DataClient {
|
|
478
379
|
/**
|
|
479
|
-
*
|
|
380
|
+
* 连接到 Data SDK 服务(推荐 - 零配置优先)
|
|
480
381
|
*
|
|
481
|
-
* 支持自动从
|
|
382
|
+
* 支持自动从 PostMessage 获取 appId 和 token,自动检测环境
|
|
482
383
|
*
|
|
483
|
-
* @param options -
|
|
384
|
+
* @param options - 连接选项(所有参数都是可选的)
|
|
484
385
|
* @returns DataClient 实例
|
|
485
386
|
*
|
|
486
387
|
* @example
|
|
487
388
|
* ```typescript
|
|
488
|
-
* //
|
|
489
|
-
* const client = await DataClient.
|
|
389
|
+
* // 🎯 零配置(推荐 - 最简单)
|
|
390
|
+
* const client = await DataClient.connect();
|
|
490
391
|
*
|
|
491
|
-
* //
|
|
492
|
-
* const client = await DataClient.
|
|
392
|
+
* // 部分配置
|
|
393
|
+
* const client = await DataClient.connect({
|
|
394
|
+
* appId: 'my-app-123' // 只提供 appId,其他自动获取
|
|
395
|
+
* });
|
|
493
396
|
*
|
|
494
|
-
* //
|
|
495
|
-
* const client = await DataClient.
|
|
397
|
+
* // 完整配置
|
|
398
|
+
* const client = await DataClient.connect({
|
|
496
399
|
* appId: 'my-app-123',
|
|
497
400
|
* token: 'user-jwt-token',
|
|
498
401
|
* environment: 'production'
|
|
499
402
|
* });
|
|
500
403
|
* ```
|
|
501
404
|
*/
|
|
502
|
-
static async
|
|
405
|
+
static async connect(options = {}) {
|
|
503
406
|
const appId = options.appId || (await getAppId());
|
|
504
407
|
const token = options.token || (await getApiToken());
|
|
505
408
|
if (!appId) {
|
|
506
|
-
throw new ValidationError('appId is required. Please
|
|
409
|
+
throw new ValidationError('appId is required. Please provide it explicitly or configure parent page to send it via PostMessage.');
|
|
507
410
|
}
|
|
508
411
|
if (!token) {
|
|
509
|
-
throw new ValidationError('token is required. Please
|
|
412
|
+
throw new ValidationError('token is required. Please provide it explicitly or configure parent page to send it via PostMessage.');
|
|
510
413
|
}
|
|
511
|
-
//
|
|
512
|
-
const baseURL =
|
|
414
|
+
// 解析 baseURL
|
|
415
|
+
const baseURL = resolveBaseURL({
|
|
513
416
|
baseURL: options.baseURL,
|
|
514
417
|
environment: options.environment,
|
|
515
418
|
});
|
|
@@ -517,9 +420,36 @@ class DataClient {
|
|
|
517
420
|
...options,
|
|
518
421
|
appId,
|
|
519
422
|
token,
|
|
520
|
-
baseURL,
|
|
423
|
+
baseURL,
|
|
521
424
|
});
|
|
522
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* 创建 DataClient 实例(异步工厂方法)
|
|
428
|
+
*
|
|
429
|
+
* @deprecated 推荐使用 connect() 方法
|
|
430
|
+
* @param options - 配置选项(appId 和 token 可选)
|
|
431
|
+
* @returns DataClient 实例
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```typescript
|
|
435
|
+
* // 自动获取 appId 和 token,并通过 PostMessage 获取环境配置
|
|
436
|
+
* const client = await DataClient.create();
|
|
437
|
+
*
|
|
438
|
+
* // 提供 appId,自动获取 token
|
|
439
|
+
* const client = await DataClient.create({ appId: 'my-app-123' });
|
|
440
|
+
*
|
|
441
|
+
* // 显式提供所有配置
|
|
442
|
+
* const client = await DataClient.create({
|
|
443
|
+
* appId: 'my-app-123',
|
|
444
|
+
* token: 'user-jwt-token',
|
|
445
|
+
* environment: 'production'
|
|
446
|
+
* });
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
static async create(options = {}) {
|
|
450
|
+
// 直接调用 connect 方法
|
|
451
|
+
return DataClient.connect(options);
|
|
452
|
+
}
|
|
523
453
|
constructor(options) {
|
|
524
454
|
const { timeout = DEFAULT_TIMEOUT, headers = {}, appId, token, debug = false, } = options;
|
|
525
455
|
if (!appId) {
|
|
@@ -1106,6 +1036,305 @@ class DataClient {
|
|
|
1106
1036
|
}
|
|
1107
1037
|
}
|
|
1108
1038
|
|
|
1039
|
+
/**
|
|
1040
|
+
* 全局单例管理
|
|
1041
|
+
*
|
|
1042
|
+
* 为函数式 API 提供自动初始化和实例复用能力
|
|
1043
|
+
*/
|
|
1044
|
+
/**
|
|
1045
|
+
* 全局单例客户端实例
|
|
1046
|
+
*/
|
|
1047
|
+
let globalClient = null;
|
|
1048
|
+
/**
|
|
1049
|
+
* 初始化 Promise(防止并发初始化)
|
|
1050
|
+
*/
|
|
1051
|
+
let initPromise = null;
|
|
1052
|
+
/**
|
|
1053
|
+
* 初始化状态标志
|
|
1054
|
+
*/
|
|
1055
|
+
let isInitializing = false;
|
|
1056
|
+
/**
|
|
1057
|
+
* 获取全局客户端实例(自动初始化)
|
|
1058
|
+
*
|
|
1059
|
+
* 工作流程:
|
|
1060
|
+
* 1. 如果已初始化 → 直接返回实例
|
|
1061
|
+
* 2. 如果正在初始化 → 等待初始化完成
|
|
1062
|
+
* 3. 如果未初始化 → 触发初始化
|
|
1063
|
+
*
|
|
1064
|
+
* @returns DataClient 实例
|
|
1065
|
+
* @throws 如果初始化失败(缺少 appId 或 token)
|
|
1066
|
+
*/
|
|
1067
|
+
async function getGlobalClient() {
|
|
1068
|
+
// 快速路径:已经初始化完成
|
|
1069
|
+
if (globalClient) {
|
|
1070
|
+
return globalClient;
|
|
1071
|
+
}
|
|
1072
|
+
// 中速路径:正在初始化,等待完成
|
|
1073
|
+
if (initPromise) {
|
|
1074
|
+
return initPromise;
|
|
1075
|
+
}
|
|
1076
|
+
// 慢速路径:首次调用,需要初始化
|
|
1077
|
+
if (!isInitializing) {
|
|
1078
|
+
isInitializing = true;
|
|
1079
|
+
initPromise = (async () => {
|
|
1080
|
+
try {
|
|
1081
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1082
|
+
console.log('[DataSDK] 🚀 自动初始化...');
|
|
1083
|
+
}
|
|
1084
|
+
// 调用 DataClient.connect() 进行零配置初始化
|
|
1085
|
+
const client = await DataClient.connect();
|
|
1086
|
+
// 缓存实例
|
|
1087
|
+
globalClient = client;
|
|
1088
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1089
|
+
console.log('[DataSDK] ✅ 自动初始化成功');
|
|
1090
|
+
}
|
|
1091
|
+
return client;
|
|
1092
|
+
}
|
|
1093
|
+
catch (error) {
|
|
1094
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1095
|
+
console.error('[DataSDK] ❌ 自动初始化失败:', error);
|
|
1096
|
+
}
|
|
1097
|
+
// 重置状态,允许下次重试
|
|
1098
|
+
isInitializing = false;
|
|
1099
|
+
initPromise = null;
|
|
1100
|
+
throw error;
|
|
1101
|
+
}
|
|
1102
|
+
})();
|
|
1103
|
+
}
|
|
1104
|
+
return initPromise;
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* 重新初始化全局客户端
|
|
1108
|
+
*
|
|
1109
|
+
* 用于以下场景:
|
|
1110
|
+
* - 用户登录后切换 token
|
|
1111
|
+
* - 切换到其他应用
|
|
1112
|
+
* - 需要更新配置
|
|
1113
|
+
*
|
|
1114
|
+
* @param options 连接选项(可选)
|
|
1115
|
+
*
|
|
1116
|
+
* @example
|
|
1117
|
+
* ```typescript
|
|
1118
|
+
* import { reinit } from '@seaverse/data-sdk';
|
|
1119
|
+
*
|
|
1120
|
+
* // 用户登录后,使用新 token
|
|
1121
|
+
* await reinit({ token: newToken });
|
|
1122
|
+
*
|
|
1123
|
+
* // 切换到其他应用
|
|
1124
|
+
* await reinit({ appId: 'another-app', token: anotherToken });
|
|
1125
|
+
*
|
|
1126
|
+
* // 完全重置(重新从 PostMessage 获取)
|
|
1127
|
+
* await reinit();
|
|
1128
|
+
* ```
|
|
1129
|
+
*/
|
|
1130
|
+
async function reinit(options = {}) {
|
|
1131
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1132
|
+
console.log('[DataSDK] 🔄 重新初始化...');
|
|
1133
|
+
}
|
|
1134
|
+
// 清除旧实例和状态
|
|
1135
|
+
globalClient = null;
|
|
1136
|
+
initPromise = null;
|
|
1137
|
+
isInitializing = false;
|
|
1138
|
+
// 创建新实例
|
|
1139
|
+
const client = await DataClient.connect(options);
|
|
1140
|
+
globalClient = client;
|
|
1141
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1142
|
+
console.log('[DataSDK] ✅ 重新初始化成功');
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* 获取当前客户端状态
|
|
1147
|
+
*
|
|
1148
|
+
* @returns 客户端状态信息
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* ```typescript
|
|
1152
|
+
* import { getClientStatus } from '@seaverse/data-sdk';
|
|
1153
|
+
*
|
|
1154
|
+
* const status = getClientStatus();
|
|
1155
|
+
* console.log('是否已初始化:', status.initialized);
|
|
1156
|
+
* console.log('是否正在初始化:', status.initializing);
|
|
1157
|
+
* ```
|
|
1158
|
+
*/
|
|
1159
|
+
function getClientStatus() {
|
|
1160
|
+
return {
|
|
1161
|
+
initialized: globalClient !== null,
|
|
1162
|
+
initializing: isInitializing,
|
|
1163
|
+
hasInstance: globalClient !== null
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* 函数式 API
|
|
1169
|
+
*
|
|
1170
|
+
* 提供零配置的便捷函数,自动管理全局客户端实例
|
|
1171
|
+
*/
|
|
1172
|
+
/**
|
|
1173
|
+
* 查询数据
|
|
1174
|
+
*
|
|
1175
|
+
* 首次调用会自动初始化 SDK(从 PostMessage 获取配置)
|
|
1176
|
+
*
|
|
1177
|
+
* @param options 查询选项
|
|
1178
|
+
* @returns 数据记录数组
|
|
1179
|
+
*
|
|
1180
|
+
* @example
|
|
1181
|
+
* ```typescript
|
|
1182
|
+
* import { query } from '@seaverse/data-sdk';
|
|
1183
|
+
*
|
|
1184
|
+
* const notes = await query({ table_name: 'notes' });
|
|
1185
|
+
*
|
|
1186
|
+
* const filtered = await query({
|
|
1187
|
+
* table_name: 'notes',
|
|
1188
|
+
* filters: { category: 'work' },
|
|
1189
|
+
* order: { field: 'created_at', direction: 'desc' }
|
|
1190
|
+
* });
|
|
1191
|
+
* ```
|
|
1192
|
+
*/
|
|
1193
|
+
async function query(options) {
|
|
1194
|
+
const client = await getGlobalClient();
|
|
1195
|
+
return client.query(options);
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* 获取单条数据
|
|
1199
|
+
*
|
|
1200
|
+
* @param id 数据 ID
|
|
1201
|
+
* @returns 数据记录,如果不存在返回 null
|
|
1202
|
+
*
|
|
1203
|
+
* @example
|
|
1204
|
+
* ```typescript
|
|
1205
|
+
* import { get } from '@seaverse/data-sdk';
|
|
1206
|
+
*
|
|
1207
|
+
* const note = await get('note-id-123');
|
|
1208
|
+
* if (note) {
|
|
1209
|
+
* console.log('标题:', note.data_value.title);
|
|
1210
|
+
* }
|
|
1211
|
+
* ```
|
|
1212
|
+
*/
|
|
1213
|
+
async function get(id) {
|
|
1214
|
+
const client = await getGlobalClient();
|
|
1215
|
+
return client.get(id);
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* 创建数据
|
|
1219
|
+
*
|
|
1220
|
+
* @param options 创建选项
|
|
1221
|
+
* @returns 创建的数据记录
|
|
1222
|
+
*
|
|
1223
|
+
* @example
|
|
1224
|
+
* ```typescript
|
|
1225
|
+
* import { create } from '@seaverse/data-sdk';
|
|
1226
|
+
*
|
|
1227
|
+
* const note = await create({
|
|
1228
|
+
* table_name: 'notes',
|
|
1229
|
+
* data_value: {
|
|
1230
|
+
* title: 'Hello',
|
|
1231
|
+
* content: 'World'
|
|
1232
|
+
* },
|
|
1233
|
+
* visibility: 'private',
|
|
1234
|
+
* tags: ['important']
|
|
1235
|
+
* });
|
|
1236
|
+
* ```
|
|
1237
|
+
*/
|
|
1238
|
+
async function create(options) {
|
|
1239
|
+
const client = await getGlobalClient();
|
|
1240
|
+
return client.create(options);
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* 更新数据
|
|
1244
|
+
*
|
|
1245
|
+
* @param id 数据 ID
|
|
1246
|
+
* @param options 更新选项
|
|
1247
|
+
* @returns 更新后的数据记录
|
|
1248
|
+
*
|
|
1249
|
+
* @example
|
|
1250
|
+
* ```typescript
|
|
1251
|
+
* import { update } from '@seaverse/data-sdk';
|
|
1252
|
+
*
|
|
1253
|
+
* await update('note-id-123', {
|
|
1254
|
+
* data_value: { title: 'Updated Title' }
|
|
1255
|
+
* });
|
|
1256
|
+
* ```
|
|
1257
|
+
*/
|
|
1258
|
+
async function update(id, options) {
|
|
1259
|
+
const client = await getGlobalClient();
|
|
1260
|
+
return client.update(id, options);
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* 删除数据
|
|
1264
|
+
*
|
|
1265
|
+
* @param id 数据 ID
|
|
1266
|
+
*
|
|
1267
|
+
* @example
|
|
1268
|
+
* ```typescript
|
|
1269
|
+
* import { deleteData } from '@seaverse/data-sdk';
|
|
1270
|
+
*
|
|
1271
|
+
* await deleteData('note-id-123');
|
|
1272
|
+
* ```
|
|
1273
|
+
*/
|
|
1274
|
+
async function deleteData(id) {
|
|
1275
|
+
const client = await getGlobalClient();
|
|
1276
|
+
return client.delete(id);
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* 批量删除数据
|
|
1280
|
+
*
|
|
1281
|
+
* @param ids 数据 ID 数组
|
|
1282
|
+
*
|
|
1283
|
+
* @example
|
|
1284
|
+
* ```typescript
|
|
1285
|
+
* import { batchDelete } from '@seaverse/data-sdk';
|
|
1286
|
+
*
|
|
1287
|
+
* await batchDelete(['id1', 'id2', 'id3']);
|
|
1288
|
+
* ```
|
|
1289
|
+
*/
|
|
1290
|
+
async function batchDelete(ids) {
|
|
1291
|
+
const client = await getGlobalClient();
|
|
1292
|
+
return client.batchDelete(ids);
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* 分页查询
|
|
1296
|
+
*
|
|
1297
|
+
* @param options 查询选项(包含分页参数)
|
|
1298
|
+
* @returns 分页响应(包含数据、总数等)
|
|
1299
|
+
*
|
|
1300
|
+
* @example
|
|
1301
|
+
* ```typescript
|
|
1302
|
+
* import { queryWithPagination } from '@seaverse/data-sdk';
|
|
1303
|
+
*
|
|
1304
|
+
* const result = await queryWithPagination({
|
|
1305
|
+
* table_name: 'notes',
|
|
1306
|
+
* pagination: { limit: 20, offset: 0 }
|
|
1307
|
+
* });
|
|
1308
|
+
*
|
|
1309
|
+
* console.log('数据:', result.data);
|
|
1310
|
+
* console.log('总数:', result.total);
|
|
1311
|
+
* console.log('是否有更多:', result.hasMore);
|
|
1312
|
+
* ```
|
|
1313
|
+
*/
|
|
1314
|
+
async function queryWithPagination(options) {
|
|
1315
|
+
const client = await getGlobalClient();
|
|
1316
|
+
return client.queryWithPagination(options);
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* 检查当前用户是否为管理员
|
|
1320
|
+
*
|
|
1321
|
+
* @returns 是否为管理员
|
|
1322
|
+
*
|
|
1323
|
+
* @example
|
|
1324
|
+
* ```typescript
|
|
1325
|
+
* import { isAdmin } from '@seaverse/data-sdk';
|
|
1326
|
+
*
|
|
1327
|
+
* const admin = await isAdmin();
|
|
1328
|
+
* if (admin) {
|
|
1329
|
+
* console.log('当前用户是管理员');
|
|
1330
|
+
* }
|
|
1331
|
+
* ```
|
|
1332
|
+
*/
|
|
1333
|
+
async function isAdmin() {
|
|
1334
|
+
const client = await getGlobalClient();
|
|
1335
|
+
return client.isAdmin();
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1109
1338
|
exports.DataClient = DataClient;
|
|
1110
1339
|
exports.DataSDKError = DataSDKError;
|
|
1111
1340
|
exports.NotFoundError = NotFoundError;
|
|
@@ -1113,4 +1342,14 @@ exports.PermissionError = PermissionError;
|
|
|
1113
1342
|
exports.RateLimitError = RateLimitError;
|
|
1114
1343
|
exports.SDKError = SDKError;
|
|
1115
1344
|
exports.ValidationError = ValidationError;
|
|
1345
|
+
exports.batchDelete = batchDelete;
|
|
1346
|
+
exports.create = create;
|
|
1347
|
+
exports.deleteData = deleteData;
|
|
1348
|
+
exports.get = get;
|
|
1349
|
+
exports.getClientStatus = getClientStatus;
|
|
1350
|
+
exports.isAdmin = isAdmin;
|
|
1351
|
+
exports.query = query;
|
|
1352
|
+
exports.queryWithPagination = queryWithPagination;
|
|
1353
|
+
exports.reinit = reinit;
|
|
1354
|
+
exports.update = update;
|
|
1116
1355
|
//# sourceMappingURL=index.cjs.map
|