@seaverse/data-sdk 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,770 @@
1
+ import axios from 'axios';
2
+
3
+ /**
4
+ * Type definitions for SeaVerse Data SDK
5
+ *
6
+ * 基于 PostgreSQL/AlloyDB + PostgREST + RLS 的数据类型定义
7
+ */
8
+ // ============================================================================
9
+ // Error Types
10
+ // ============================================================================
11
+ /**
12
+ * SDK Error class
13
+ */
14
+ class SDKError extends Error {
15
+ constructor(message, code, statusCode, details) {
16
+ super(message);
17
+ this.code = code;
18
+ this.statusCode = statusCode;
19
+ this.details = details;
20
+ this.name = 'SDKError';
21
+ }
22
+ }
23
+ /**
24
+ * SDK Error base class (legacy alias)
25
+ */
26
+ class DataSDKError extends SDKError {
27
+ constructor(message, code, statusCode) {
28
+ super(message, code || 'SDK_ERROR', statusCode);
29
+ this.name = 'DataSDKError';
30
+ }
31
+ }
32
+ /**
33
+ * Permission denied error (403)
34
+ */
35
+ class PermissionError extends SDKError {
36
+ constructor(message = 'Permission denied') {
37
+ super(message, 'PERMISSION_DENIED', 403);
38
+ this.name = 'PermissionError';
39
+ }
40
+ }
41
+ /**
42
+ * Resource not found error (404)
43
+ */
44
+ class NotFoundError extends SDKError {
45
+ constructor(message = 'Resource not found') {
46
+ super(message, 'NOT_FOUND', 404);
47
+ this.name = 'NotFoundError';
48
+ }
49
+ }
50
+ /**
51
+ * Rate limit exceeded error (429)
52
+ */
53
+ class RateLimitError extends SDKError {
54
+ constructor(message = 'Rate limit exceeded') {
55
+ super(message, 'RATE_LIMIT_EXCEEDED', 429);
56
+ this.name = 'RateLimitError';
57
+ }
58
+ }
59
+ /**
60
+ * Validation error (400)
61
+ */
62
+ class ValidationError extends SDKError {
63
+ constructor(message) {
64
+ super(message, 'VALIDATION_ERROR', 400);
65
+ this.name = 'ValidationError';
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Environment Configuration for Data SDK
71
+ * 环境配置模块 - 支持多环境部署
72
+ */
73
+ /**
74
+ * 预定义的环境配置
75
+ *
76
+ * 使用说明:
77
+ * - production: 正式生产环境
78
+ * - staging: 预发布环境
79
+ * - development: 开发测试环境
80
+ * - local: 本地开发环境
81
+ */
82
+ const ENVIRONMENT_CONFIGS = {
83
+ production: {
84
+ name: 'production',
85
+ baseURL: 'https://data.seaverse.ai',
86
+ isProduction: true,
87
+ },
88
+ staging: {
89
+ name: 'staging',
90
+ baseURL: 'https://data.sg.seaverse.dev',
91
+ isProduction: false,
92
+ },
93
+ development: {
94
+ name: 'development',
95
+ baseURL: 'https://data-dev.seaverse.dev',
96
+ isProduction: false,
97
+ },
98
+ local: {
99
+ name: 'local',
100
+ baseURL: 'http://localhost:3000',
101
+ isProduction: false,
102
+ },
103
+ };
104
+ /**
105
+ * 根据当前运行环境自动检测应该使用的环境配置
106
+ *
107
+ * 检测规则:
108
+ * 1. localhost/127.0.0.1 → local
109
+ * 2. 包含 -dev 或 .dev → development
110
+ * 3. 包含 staging → staging
111
+ * 4. 其他 → production (默认)
112
+ *
113
+ * @returns 检测到的环境类型
114
+ */
115
+ function detectEnvironment() {
116
+ // Node.js 环境或非浏览器环境,默认返回 staging(安全起见)
117
+ if (typeof window === 'undefined' || typeof window.location === 'undefined') {
118
+ return 'staging';
119
+ }
120
+ const hostname = window.location.hostname.toLowerCase();
121
+ // 本地开发环境
122
+ if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0') {
123
+ return 'local';
124
+ }
125
+ // 开发环境 (包含 -dev 或 .dev)
126
+ if (hostname.includes('-dev.') || hostname.includes('.dev') || hostname.endsWith('.dev')) {
127
+ return 'development';
128
+ }
129
+ // Staging 环境
130
+ if (hostname.includes('-staging.') || hostname.includes('staging.')) {
131
+ return 'staging';
132
+ }
133
+ // 默认使用 production
134
+ return 'production';
135
+ }
136
+ /**
137
+ * 获取环境配置
138
+ *
139
+ * @param env 环境类型,如果不提供则自动检测
140
+ * @returns 环境配置对象
141
+ */
142
+ function getEnvironmentConfig(env) {
143
+ const targetEnv = env || detectEnvironment();
144
+ return ENVIRONMENT_CONFIGS[targetEnv];
145
+ }
146
+ /**
147
+ * 解析用户配置,返回最终的 baseURL
148
+ *
149
+ * 优先级:
150
+ * 1. 显式传入的 baseURL(最高优先级)
151
+ * 2. environment 参数指定的环境
152
+ * 3. 自动检测环境(最低优先级)
153
+ *
154
+ * @param options 用户配置选项
155
+ * @returns 最终使用的 baseURL
156
+ */
157
+ function resolveBaseURL(options) {
158
+ // 优先级 1: 显式传入的 baseURL
159
+ if (options.baseURL) {
160
+ return options.baseURL;
161
+ }
162
+ // 优先级 2: environment 参数
163
+ if (options.environment) {
164
+ return ENVIRONMENT_CONFIGS[options.environment].baseURL;
165
+ }
166
+ // 优先级 3: 自动检测
167
+ const detectedEnv = detectEnvironment();
168
+ return ENVIRONMENT_CONFIGS[detectedEnv].baseURL;
169
+ }
170
+ /**
171
+ * 默认超时时间(毫秒)
172
+ */
173
+ const DEFAULT_TIMEOUT = 10000; // 10 seconds
174
+ /**
175
+ * API 端点路径
176
+ */
177
+ const ENDPOINTS = {
178
+ // Data operations (PostgREST)
179
+ APP_DATA: '/app_data',
180
+ // Admin roles
181
+ APP_ADMINS: '/app_admins',
182
+ };
183
+
184
+ /**
185
+ * Data SDK Client
186
+ *
187
+ * 基于 PostgreSQL/AlloyDB + PostgREST + RLS 的数据 SDK
188
+ */
189
+ /**
190
+ * SeaVerse Data Client
191
+ *
192
+ * 高性能数据服务SDK,基于PostgREST + RLS架构
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * const client = new DataClient({
197
+ * appId: 'my-app-id',
198
+ * token: 'user-jwt-token',
199
+ * });
200
+ *
201
+ * // Query data
202
+ * const notes = await client.query({
203
+ * table: 'notes',
204
+ * filters: { category: 'work' },
205
+ * order: { field: 'created_at', direction: 'desc' },
206
+ * });
207
+ *
208
+ * // Create data
209
+ * const note = await client.create({
210
+ * table: 'notes',
211
+ * data: { title: 'My Note', content: '...' },
212
+ * visibility: 'private',
213
+ * });
214
+ *
215
+ * // Update data
216
+ * await client.update('note-id', {
217
+ * data: { title: 'Updated' },
218
+ * });
219
+ *
220
+ * // Delete data
221
+ * await client.delete('note-id');
222
+ * ```
223
+ */
224
+ class DataClient {
225
+ constructor(options) {
226
+ const { timeout = DEFAULT_TIMEOUT, headers = {}, appId, token, debug = false, } = options;
227
+ if (!appId) {
228
+ throw new ValidationError('appId is required');
229
+ }
230
+ if (!token) {
231
+ throw new ValidationError('token is required');
232
+ }
233
+ this.appId = appId;
234
+ this.token = token;
235
+ this.debug = debug;
236
+ // 解析 baseURL(支持环境自动检测)
237
+ const baseURL = resolveBaseURL({
238
+ baseURL: options.baseURL,
239
+ environment: options.environment,
240
+ });
241
+ this.axiosInstance = axios.create({
242
+ baseURL,
243
+ timeout,
244
+ headers: {
245
+ 'Content-Type': 'application/json',
246
+ 'Authorization': `Bearer ${token}`,
247
+ ...headers,
248
+ },
249
+ });
250
+ // Add response interceptor for error handling
251
+ this.axiosInstance.interceptors.response.use((response) => response, (error) => {
252
+ throw this._handleError(error);
253
+ });
254
+ this.log('SDK initialized', { apiUrl: baseURL, appId });
255
+ }
256
+ // ============================================
257
+ // Logging Methods
258
+ // ============================================
259
+ log(message, data) {
260
+ if (this.debug) {
261
+ console.log(`[DataSDK] ${message}`, data || '');
262
+ }
263
+ }
264
+ logError(message, error) {
265
+ console.error(`[DataSDK] ${message}`, error);
266
+ }
267
+ // ============================================
268
+ // Helper Methods
269
+ // ============================================
270
+ /**
271
+ * Handle axios errors and convert to SDK errors
272
+ * @private
273
+ */
274
+ _handleError(error) {
275
+ const status = error.response?.status;
276
+ const message = error.response?.data?.message || error.message;
277
+ switch (status) {
278
+ case 403:
279
+ return new PermissionError(message);
280
+ case 404:
281
+ return new NotFoundError(message);
282
+ case 429:
283
+ return new RateLimitError(message);
284
+ case 400:
285
+ return new ValidationError(message);
286
+ default:
287
+ return new SDKError(message, 'REQUEST_FAILED', status);
288
+ }
289
+ }
290
+ /**
291
+ * Get user ID from JWT token
292
+ * @private
293
+ */
294
+ getUserIdFromToken() {
295
+ try {
296
+ // JWT token format: header.payload.signature
297
+ const parts = this.token.split('.');
298
+ if (parts.length !== 3) {
299
+ throw new Error('Invalid JWT token');
300
+ }
301
+ // Decode payload (base64url)
302
+ const payload = parts[1];
303
+ // Cross-platform base64 decoding
304
+ let decodedString;
305
+ const globalObj = globalThis;
306
+ if (typeof globalObj.atob === 'function') {
307
+ // Browser environment
308
+ decodedString = globalObj.atob(payload);
309
+ }
310
+ else if (typeof globalObj.Buffer !== 'undefined') {
311
+ // Node.js environment
312
+ decodedString = globalObj.Buffer.from(payload, 'base64').toString('utf-8');
313
+ }
314
+ else {
315
+ // Fallback: manual base64 decode
316
+ decodedString = this.base64Decode(payload);
317
+ }
318
+ const claims = JSON.parse(decodedString);
319
+ return claims.sub;
320
+ }
321
+ catch (error) {
322
+ throw new SDKError('Unable to parse JWT token', 'INVALID_TOKEN', 401, error);
323
+ }
324
+ }
325
+ /**
326
+ * Base64 decode fallback
327
+ * @private
328
+ */
329
+ base64Decode(str) {
330
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
331
+ let output = '';
332
+ str = String(str).replace(/=+$/, '');
333
+ if (str.length % 4 === 1) {
334
+ throw new Error('Invalid base64 string');
335
+ }
336
+ for (let bc = 0, bs = 0, buffer, i = 0; (buffer = str.charAt(i++)); ~buffer && ((bs = bc % 4 ? bs * 64 + buffer : buffer), bc++ % 4)
337
+ ? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6))))
338
+ : 0) {
339
+ buffer = chars.indexOf(buffer);
340
+ }
341
+ return output;
342
+ }
343
+ // ============================================
344
+ // Data Operation Methods
345
+ // ============================================
346
+ /**
347
+ * Query data list
348
+ *
349
+ * 查询数据列表,支持过滤、排序、分页
350
+ *
351
+ * @param options - Query options
352
+ * @returns Array of data records
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * const notes = await client.query({
357
+ * table: 'notes',
358
+ * filters: {
359
+ * category: 'work',
360
+ * visibility: 'private',
361
+ * },
362
+ * order: {
363
+ * field: 'created_at',
364
+ * direction: 'desc',
365
+ * },
366
+ * pagination: {
367
+ * limit: 20,
368
+ * offset: 0,
369
+ * },
370
+ * });
371
+ * ```
372
+ */
373
+ async query(options) {
374
+ const query = {
375
+ app_id: `eq.${this.appId}`,
376
+ table_name: `eq.${options.table}`,
377
+ };
378
+ // Add filters
379
+ if (options.filters) {
380
+ const { filters } = options;
381
+ if (filters.id) {
382
+ query.id = `eq.${filters.id}`;
383
+ }
384
+ if (filters.category) {
385
+ query.category = `eq.${filters.category}`;
386
+ }
387
+ if (filters.visibility) {
388
+ query.visibility = `eq.${filters.visibility}`;
389
+ }
390
+ if (filters.tags && filters.tags.length > 0) {
391
+ // PostgREST array contains operator
392
+ query.tags = `cs.{${filters.tags.join(',')}}`;
393
+ }
394
+ if (filters.created_after) {
395
+ query.created_at = `gte.${filters.created_after}`;
396
+ }
397
+ if (filters.created_before) {
398
+ if (query.created_at) {
399
+ // If created_at already exists, use 'and' to combine
400
+ query.and = `(created_at.gte.${filters.created_after},created_at.lt.${filters.created_before})`;
401
+ delete query.created_at;
402
+ }
403
+ else {
404
+ query.created_at = `lt.${filters.created_before}`;
405
+ }
406
+ }
407
+ // JSONB field filters (data_value)
408
+ if (filters.data) {
409
+ Object.entries(filters.data).forEach(([key, value]) => {
410
+ // PostgREST JSONB query syntax: data_value->>key (text extraction)
411
+ query[`data_value->>${key}`] = `eq.${value}`;
412
+ });
413
+ }
414
+ }
415
+ // Add sorting
416
+ if (options.order) {
417
+ const direction = options.order.direction === 'asc' ? '' : '.desc';
418
+ query.order = `${options.order.field}${direction}`;
419
+ }
420
+ else {
421
+ // Default sort by created_at desc
422
+ query.order = 'created_at.desc';
423
+ }
424
+ // Add pagination
425
+ if (options.pagination) {
426
+ query.limit = options.pagination.limit.toString();
427
+ query.offset = options.pagination.offset.toString();
428
+ }
429
+ this.log('Querying data', query);
430
+ const response = await this.axiosInstance.get(ENDPOINTS.APP_DATA, { params: query });
431
+ return Array.isArray(response.data) ? response.data : [response.data];
432
+ }
433
+ /**
434
+ * Get single data record by ID
435
+ *
436
+ * 通过 ID 查询单条数据
437
+ *
438
+ * @param id - Data ID
439
+ * @returns Data record or null if not found
440
+ *
441
+ * @example
442
+ * ```typescript
443
+ * const note = await client.get('note-id-123');
444
+ * console.log(note?.data_value.title);
445
+ * ```
446
+ */
447
+ async get(id) {
448
+ try {
449
+ const response = await this.axiosInstance.get(ENDPOINTS.APP_DATA, {
450
+ params: {
451
+ id: `eq.${id}`,
452
+ },
453
+ headers: {
454
+ 'Accept': 'application/vnd.pgrst.object+json', // 返回单个对象
455
+ },
456
+ });
457
+ const results = Array.isArray(response.data) ? response.data : [response.data];
458
+ return results && results.length > 0 ? results[0] : null;
459
+ }
460
+ catch (error) {
461
+ this._handleError(error);
462
+ return null;
463
+ }
464
+ }
465
+ /**
466
+ * Create data
467
+ *
468
+ * 创建新数据
469
+ *
470
+ * @param options - Create options
471
+ * @returns Created data record
472
+ *
473
+ * @example
474
+ * ```typescript
475
+ * const newNote = await client.create({
476
+ * table: 'notes',
477
+ * data: {
478
+ * title: 'My Note',
479
+ * content: 'Note content...',
480
+ * },
481
+ * visibility: 'private',
482
+ * category: 'work',
483
+ * tags: ['important', 'todo'],
484
+ * });
485
+ * ```
486
+ */
487
+ async create(options) {
488
+ const body = {
489
+ app_id: this.appId,
490
+ table_name: options.table,
491
+ data_value: options.data,
492
+ owner_user_id: this.getUserIdFromToken(),
493
+ visibility: options.visibility || 'private',
494
+ category: options.category,
495
+ tags: options.tags,
496
+ };
497
+ this.log('Creating data', body);
498
+ const response = await this.axiosInstance.post(ENDPOINTS.APP_DATA, body, {
499
+ headers: {
500
+ 'Prefer': 'return=representation', // Return created record
501
+ },
502
+ });
503
+ const results = Array.isArray(response.data) ? response.data : [response.data];
504
+ return results[0];
505
+ }
506
+ /**
507
+ * Update data
508
+ *
509
+ * 更新数据
510
+ *
511
+ * @param id - Data ID
512
+ * @param options - Update options
513
+ * @returns Updated data record
514
+ *
515
+ * @example
516
+ * ```typescript
517
+ * const updated = await client.update('note-id-123', {
518
+ * data: {
519
+ * title: 'Updated Title',
520
+ * content: 'Updated content',
521
+ * },
522
+ * category: 'personal',
523
+ * });
524
+ * ```
525
+ */
526
+ async update(id, options) {
527
+ const body = {
528
+ updated_at: new Date().toISOString(),
529
+ };
530
+ if (options.data !== undefined) {
531
+ body.data_value = options.data;
532
+ }
533
+ if (options.visibility !== undefined) {
534
+ body.visibility = options.visibility;
535
+ }
536
+ if (options.category !== undefined) {
537
+ body.category = options.category;
538
+ }
539
+ if (options.tags !== undefined) {
540
+ body.tags = options.tags;
541
+ }
542
+ this.log('Updating data', { id, body });
543
+ const response = await this.axiosInstance.patch(ENDPOINTS.APP_DATA, body, {
544
+ params: {
545
+ id: `eq.${id}`,
546
+ },
547
+ headers: {
548
+ 'Prefer': 'return=representation',
549
+ },
550
+ });
551
+ const results = Array.isArray(response.data) ? response.data : [response.data];
552
+ if (!results || results.length === 0) {
553
+ throw new NotFoundError('Data not found or no permission to update');
554
+ }
555
+ return results[0];
556
+ }
557
+ /**
558
+ * Delete data
559
+ *
560
+ * 删除数据
561
+ *
562
+ * @param id - Data ID
563
+ *
564
+ * @example
565
+ * ```typescript
566
+ * await client.delete('note-id-123');
567
+ * ```
568
+ */
569
+ async delete(id) {
570
+ this.log('Deleting data', { id });
571
+ await this.axiosInstance.delete(ENDPOINTS.APP_DATA, {
572
+ params: {
573
+ app_id: `eq.${this.appId}`,
574
+ id: `eq.${id}`,
575
+ },
576
+ });
577
+ }
578
+ /**
579
+ * Batch delete data
580
+ *
581
+ * 批量删除数据
582
+ *
583
+ * @param ids - Array of data IDs
584
+ *
585
+ * @example
586
+ * ```typescript
587
+ * await client.batchDelete(['id-1', 'id-2', 'id-3']);
588
+ * ```
589
+ */
590
+ async batchDelete(ids) {
591
+ if (ids.length === 0)
592
+ return;
593
+ this.log('Batch deleting data', { ids });
594
+ await this.axiosInstance.delete(ENDPOINTS.APP_DATA, {
595
+ params: {
596
+ app_id: `eq.${this.appId}`,
597
+ id: `in.(${ids.join(',')})`,
598
+ },
599
+ });
600
+ }
601
+ /**
602
+ * Query with pagination metadata
603
+ *
604
+ * 查询数据并返回分页信息
605
+ *
606
+ * @param options - Query options
607
+ * @returns Paginated response with metadata
608
+ *
609
+ * @example
610
+ * ```typescript
611
+ * const result = await client.queryWithPagination({
612
+ * table: 'posts',
613
+ * pagination: { limit: 10, offset: 0 },
614
+ * });
615
+ *
616
+ * console.log(`Total: ${result.total}, Has more: ${result.hasMore}`);
617
+ * ```
618
+ */
619
+ async queryWithPagination(options) {
620
+ const limit = options.pagination?.limit || 10;
621
+ const offset = options.pagination?.offset || 0;
622
+ const query = {
623
+ app_id: `eq.${this.appId}`,
624
+ table_name: `eq.${options.table}`,
625
+ limit: limit.toString(),
626
+ offset: offset.toString(),
627
+ };
628
+ // Add filters (same as query method)
629
+ if (options.filters) {
630
+ const { filters } = options;
631
+ if (filters.id)
632
+ query.id = `eq.${filters.id}`;
633
+ if (filters.category)
634
+ query.category = `eq.${filters.category}`;
635
+ if (filters.visibility)
636
+ query.visibility = `eq.${filters.visibility}`;
637
+ if (filters.tags && filters.tags.length > 0) {
638
+ query.tags = `cs.{${filters.tags.join(',')}}`;
639
+ }
640
+ if (filters.created_after) {
641
+ query.created_at = `gte.${filters.created_after}`;
642
+ }
643
+ if (filters.created_before) {
644
+ if (query.created_at) {
645
+ query.and = `(created_at.gte.${filters.created_after},created_at.lt.${filters.created_before})`;
646
+ delete query.created_at;
647
+ }
648
+ else {
649
+ query.created_at = `lt.${filters.created_before}`;
650
+ }
651
+ }
652
+ if (filters.data) {
653
+ Object.entries(filters.data).forEach(([key, value]) => {
654
+ query[`data_value->>${key}`] = `eq.${value}`;
655
+ });
656
+ }
657
+ }
658
+ // Add sorting
659
+ if (options.order) {
660
+ const direction = options.order.direction === 'asc' ? '' : '.desc';
661
+ query.order = `${options.order.field}${direction}`;
662
+ }
663
+ else {
664
+ query.order = 'created_at.desc';
665
+ }
666
+ this.log('Querying with pagination', query);
667
+ // Request with count
668
+ const response = await this.axiosInstance.get(ENDPOINTS.APP_DATA, {
669
+ params: query,
670
+ headers: {
671
+ 'Prefer': 'count=exact', // Request total count
672
+ },
673
+ });
674
+ const data = Array.isArray(response.data) ? response.data : [response.data];
675
+ // Extract total count from Content-Range header
676
+ // Format: "0-9/100" means items 0-9, total 100
677
+ let total;
678
+ let hasMore;
679
+ const contentRange = response.headers['content-range'];
680
+ if (contentRange) {
681
+ const match = contentRange.match(/\/(\d+|\*)/);
682
+ if (match && match[1] !== '*') {
683
+ total = parseInt(match[1], 10);
684
+ hasMore = offset + data.length < total;
685
+ }
686
+ }
687
+ return {
688
+ data,
689
+ total,
690
+ limit,
691
+ offset,
692
+ hasMore,
693
+ };
694
+ }
695
+ // ============================================
696
+ // Admin Operation Methods
697
+ // ============================================
698
+ /**
699
+ * Check if current user is admin
700
+ *
701
+ * 检查当前用户是否是管理员
702
+ *
703
+ * @returns True if user is admin
704
+ *
705
+ * @example
706
+ * ```typescript
707
+ * const isAdmin = await client.isAdmin();
708
+ * if (isAdmin) {
709
+ * // User has admin privileges
710
+ * }
711
+ * ```
712
+ */
713
+ async isAdmin() {
714
+ try {
715
+ const response = await this.axiosInstance.get(ENDPOINTS.APP_ADMINS, {
716
+ params: {
717
+ app_id: `eq.${this.appId}`,
718
+ user_id: `eq.${this.getUserIdFromToken()}`,
719
+ },
720
+ });
721
+ const roles = Array.isArray(response.data) ? response.data : [response.data];
722
+ return roles.length > 0;
723
+ }
724
+ catch (error) {
725
+ this.logError('Check admin permission failed', error);
726
+ return false;
727
+ }
728
+ }
729
+ // ============================================
730
+ // Configuration Methods
731
+ // ============================================
732
+ /**
733
+ * Update token
734
+ *
735
+ * 更新 JWT token
736
+ *
737
+ * @param token - New JWT token
738
+ */
739
+ updateToken(token) {
740
+ this.token = token;
741
+ this.axiosInstance.defaults.headers['Authorization'] = `Bearer ${token}`;
742
+ this.log('Token updated');
743
+ }
744
+ /**
745
+ * Update app ID
746
+ *
747
+ * 更新应用 ID
748
+ *
749
+ * @param appId - New application ID
750
+ */
751
+ updateAppId(appId) {
752
+ this.appId = appId;
753
+ this.log('AppId updated', { appId });
754
+ }
755
+ /**
756
+ * Get current app ID
757
+ */
758
+ getAppId() {
759
+ return this.appId;
760
+ }
761
+ /**
762
+ * Get current user ID
763
+ */
764
+ getUserId() {
765
+ return this.getUserIdFromToken();
766
+ }
767
+ }
768
+
769
+ export { DEFAULT_TIMEOUT, DataClient, DataSDKError, ENDPOINTS, ENVIRONMENT_CONFIGS, NotFoundError, PermissionError, RateLimitError, SDKError, ValidationError, detectEnvironment, getEnvironmentConfig, resolveBaseURL };
770
+ //# sourceMappingURL=index.js.map