@seaverseai/auth-sdk 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/dist/index.js ADDED
@@ -0,0 +1,2534 @@
1
+ import axios from 'axios';
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ import * as os from 'os';
5
+
6
+ /**
7
+ * 生成UUID的跨平台函数
8
+ * 支持 Node.js 和浏览器环境
9
+ */
10
+ function generateUUID() {
11
+ // Node.js 环境
12
+ if (typeof globalThis !== 'undefined' && globalThis.crypto?.randomUUID) {
13
+ return globalThis.crypto.randomUUID();
14
+ }
15
+ // 浏览器环境 - crypto.randomUUID (现代浏览器)
16
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
17
+ return crypto.randomUUID();
18
+ }
19
+ // 浏览器环境 - crypto.getRandomValues (备用方案)
20
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
21
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
22
+ const r = (crypto.getRandomValues(new Uint8Array(1))[0] % 16) | 0;
23
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
24
+ return v.toString(16);
25
+ });
26
+ }
27
+ // Fallback - 使用 Math.random() (不推荐用于生产环境)
28
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
29
+ const r = (Math.random() * 16) | 0;
30
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
31
+ return v.toString(16);
32
+ });
33
+ }
34
+ /**
35
+ * 内置Hook集合
36
+ */
37
+ class BuiltInHooks {
38
+ /**
39
+ * 创建日志记录Hook
40
+ */
41
+ static createLoggerHook(options) {
42
+ const logLevel = options?.logLevel || 'info';
43
+ const logRequestBody = options?.logRequestBody ?? false;
44
+ const logResponseBody = options?.logResponseBody ?? false;
45
+ const logHeaders = options?.logHeaders ?? false;
46
+ return {
47
+ type: 'beforeRequest',
48
+ name: 'logger',
49
+ priority: 100,
50
+ handler: async (context) => {
51
+ const { operationId, config } = context;
52
+ console.log(`[${logLevel.toUpperCase()}] ${config.method?.toUpperCase()} ${config.url}`);
53
+ console.log(`Operation ID: ${operationId}`);
54
+ if (logHeaders && config.headers) {
55
+ console.log('Headers:', JSON.stringify(config.headers, null, 2));
56
+ }
57
+ if (logRequestBody && config.data) {
58
+ console.log('Request Body:', JSON.stringify(config.data, null, 2));
59
+ }
60
+ // 添加afterResponse日志
61
+ if (context.response) {
62
+ console.log(`Response Status: ${context.response.status}`);
63
+ if (logResponseBody && context.response.data) {
64
+ console.log('Response Body:', JSON.stringify(context.response.data, null, 2));
65
+ }
66
+ }
67
+ },
68
+ };
69
+ }
70
+ /**
71
+ * 创建请求ID Hook
72
+ */
73
+ static createRequestIdHook() {
74
+ return {
75
+ type: 'beforeRequest',
76
+ name: 'request-id',
77
+ priority: 10,
78
+ handler: async (context) => {
79
+ const requestId = generateUUID();
80
+ context.config.headers = {
81
+ ...context.config.headers,
82
+ 'X-Request-ID': requestId,
83
+ };
84
+ context.metadata = context.metadata || {};
85
+ context.metadata.requestId = requestId;
86
+ },
87
+ };
88
+ }
89
+ /**
90
+ * 创建速率限制Hook
91
+ */
92
+ static createRateLimitHook(options) {
93
+ const requestsPerSecond = options?.requestsPerSecond;
94
+ // TODO: 实现基于分钟的速率限制
95
+ // const requestsPerMinute = options?.requestsPerMinute;
96
+ // 简单的令牌桶实现
97
+ let tokens = requestsPerSecond || 10;
98
+ let lastRefill = Date.now();
99
+ const refillTokens = () => {
100
+ const now = Date.now();
101
+ const timePassed = now - lastRefill;
102
+ if (requestsPerSecond && timePassed >= 1000) {
103
+ tokens = Math.min(requestsPerSecond, tokens + requestsPerSecond);
104
+ lastRefill = now;
105
+ }
106
+ };
107
+ return {
108
+ type: 'beforeRequest',
109
+ name: 'rate-limit',
110
+ priority: 5,
111
+ handler: async (_context) => {
112
+ refillTokens();
113
+ if (tokens <= 0) {
114
+ const waitTime = 1000 - (Date.now() - lastRefill);
115
+ if (waitTime > 0) {
116
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
117
+ refillTokens();
118
+ }
119
+ }
120
+ tokens--;
121
+ },
122
+ };
123
+ }
124
+ /**
125
+ * 创建缓存Hook(简化版)
126
+ */
127
+ static createCacheHook(options) {
128
+ const cache = new Map();
129
+ // TODO: 实现缓存过期逻辑
130
+ // const ttl = options?.ttl || 60000; // 默认60秒
131
+ const methods = options?.methods || ['GET'];
132
+ return {
133
+ type: 'beforeRequest',
134
+ name: 'cache',
135
+ priority: 20,
136
+ handler: async (context) => {
137
+ const method = context.config.method?.toUpperCase();
138
+ if (!method || !methods.includes(method)) {
139
+ return;
140
+ }
141
+ const cacheKey = `${method}:${context.config.url}:${JSON.stringify(context.config.params)}`;
142
+ const cached = cache.get(cacheKey);
143
+ if (cached && Date.now() < cached.expiresAt) {
144
+ // 从缓存返回(这里需要特殊处理,通常在拦截器中实现)
145
+ context.metadata = context.metadata || {};
146
+ context.metadata.cacheHit = true;
147
+ }
148
+ else {
149
+ // 缓存miss,需要在afterResponse中缓存结果
150
+ context.metadata = context.metadata || {};
151
+ context.metadata.cacheKey = cacheKey;
152
+ }
153
+ },
154
+ };
155
+ }
156
+ /**
157
+ * 创建性能监控Hook
158
+ */
159
+ static createMetricsHook(options) {
160
+ const collector = options?.collector || ((metrics) => {
161
+ console.log('Metrics:', metrics);
162
+ });
163
+ return {
164
+ type: 'beforeRequest',
165
+ name: 'metrics',
166
+ priority: 1,
167
+ handler: async (context) => {
168
+ context.metadata = context.metadata || {};
169
+ context.metadata.startTime = Date.now();
170
+ // 在afterResponse或onError中计算duration
171
+ if (context.response) {
172
+ const duration = Date.now() - context.metadata.startTime;
173
+ const metrics = {
174
+ operationId: context.operationId,
175
+ method: context.config.method?.toUpperCase() || 'UNKNOWN',
176
+ path: context.config.url || '',
177
+ statusCode: context.response.status,
178
+ duration,
179
+ timestamp: Date.now(),
180
+ };
181
+ collector(metrics);
182
+ }
183
+ else if (context.error) {
184
+ const duration = Date.now() - context.metadata.startTime;
185
+ const metrics = {
186
+ operationId: context.operationId,
187
+ method: context.config.method?.toUpperCase() || 'UNKNOWN',
188
+ path: context.config.url || '',
189
+ duration,
190
+ timestamp: Date.now(),
191
+ error: context.error.message,
192
+ };
193
+ collector(metrics);
194
+ }
195
+ },
196
+ };
197
+ }
198
+ /**
199
+ * 创建重试Hook(需要在interceptor中实现真正的重试逻辑)
200
+ */
201
+ static createRetryHook(maxRetries = 3, retryDelay = 1000) {
202
+ return {
203
+ type: 'onRetry',
204
+ name: 'retry',
205
+ priority: 1,
206
+ handler: async (context) => {
207
+ const retryCount = context.retryCount || 0;
208
+ console.log(`Retrying request (${retryCount}/${maxRetries})...`);
209
+ if (retryCount > 0) {
210
+ // 指数退避
211
+ const delay = retryDelay * Math.pow(2, retryCount - 1);
212
+ await new Promise((resolve) => setTimeout(resolve, delay));
213
+ }
214
+ },
215
+ };
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Hook管理器
221
+ */
222
+ class HookManager {
223
+ constructor(options) {
224
+ this.hooks = new Map();
225
+ // 初始化hook类型映射
226
+ this.hooks.set('beforeRequest', []);
227
+ this.hooks.set('afterResponse', []);
228
+ this.hooks.set('onError', []);
229
+ this.hooks.set('onRetry', []);
230
+ // 注册用户自定义hooks
231
+ if (options?.hooks) {
232
+ for (const hook of options.hooks) {
233
+ this.register(hook);
234
+ }
235
+ }
236
+ // 注册内置hooks
237
+ if (options?.enableBuiltInHooks) {
238
+ this.registerBuiltInHooks(options);
239
+ }
240
+ }
241
+ /**
242
+ * 注册hook
243
+ */
244
+ register(hook) {
245
+ const hooks = this.hooks.get(hook.type) || [];
246
+ hooks.push(hook);
247
+ // 按优先级排序(优先级数字越小越优先)
248
+ hooks.sort((a, b) => a.priority - b.priority);
249
+ this.hooks.set(hook.type, hooks);
250
+ }
251
+ /**
252
+ * 注销hook
253
+ */
254
+ unregister(type, name) {
255
+ const hooks = this.hooks.get(type) || [];
256
+ const filtered = hooks.filter((h) => h.name !== name);
257
+ this.hooks.set(type, filtered);
258
+ }
259
+ /**
260
+ * 执行beforeRequest hooks
261
+ */
262
+ async executeBeforeRequest(context) {
263
+ await this.executeHooks('beforeRequest', context);
264
+ }
265
+ /**
266
+ * 执行afterResponse hooks
267
+ */
268
+ async executeAfterResponse(context) {
269
+ await this.executeHooks('afterResponse', context);
270
+ }
271
+ /**
272
+ * 执行onError hooks
273
+ */
274
+ async executeOnError(context) {
275
+ await this.executeHooks('onError', context);
276
+ }
277
+ /**
278
+ * 执行onRetry hooks
279
+ */
280
+ async executeOnRetry(context) {
281
+ await this.executeHooks('onRetry', context);
282
+ }
283
+ /**
284
+ * 执行指定类型的hooks
285
+ */
286
+ async executeHooks(type, context) {
287
+ const hooks = this.hooks.get(type) || [];
288
+ for (const hook of hooks) {
289
+ // 检查过滤条件
290
+ if (!this.shouldExecuteHook(hook, context)) {
291
+ continue;
292
+ }
293
+ try {
294
+ await hook.handler(context);
295
+ }
296
+ catch (error) {
297
+ // Hook执行失败不应阻断请求
298
+ console.error(`Hook ${hook.name} failed:`, error);
299
+ }
300
+ }
301
+ }
302
+ /**
303
+ * 检查是否应该执行hook
304
+ */
305
+ shouldExecuteHook(hook, context) {
306
+ if (!hook.filter) {
307
+ return true;
308
+ }
309
+ const filter = hook.filter;
310
+ // 检查operationId过滤
311
+ if (filter.operationIds && filter.operationIds.length > 0) {
312
+ if (!filter.operationIds.includes(context.operationId)) {
313
+ return false;
314
+ }
315
+ }
316
+ // 检查method过滤
317
+ if (filter.methods && filter.methods.length > 0) {
318
+ const method = context.config.method?.toUpperCase();
319
+ if (!method || !filter.methods.includes(method)) {
320
+ return false;
321
+ }
322
+ }
323
+ // 检查path pattern过滤
324
+ if (filter.pathPatterns && filter.pathPatterns.length > 0) {
325
+ const url = context.config.url || '';
326
+ const matched = filter.pathPatterns.some((pattern) => pattern.test(url));
327
+ if (!matched) {
328
+ return false;
329
+ }
330
+ }
331
+ return true;
332
+ }
333
+ /**
334
+ * 注册内置hooks
335
+ */
336
+ registerBuiltInHooks(options) {
337
+ const { enableBuiltInHooks } = options;
338
+ if (enableBuiltInHooks?.logger) {
339
+ this.register(BuiltInHooks.createLoggerHook(options.loggerOptions));
340
+ }
341
+ if (enableBuiltInHooks?.requestId) {
342
+ this.register(BuiltInHooks.createRequestIdHook());
343
+ }
344
+ if (enableBuiltInHooks?.retry) {
345
+ // Retry hook需要特殊处理,通常在axios interceptor中实现
346
+ console.warn('Retry hook should be implemented in axios interceptor');
347
+ }
348
+ if (enableBuiltInHooks?.rateLimit) {
349
+ this.register(BuiltInHooks.createRateLimitHook(options.rateLimitOptions));
350
+ }
351
+ if (enableBuiltInHooks?.cache) {
352
+ // Cache hook需要特殊处理
353
+ console.warn('Cache hook requires special implementation');
354
+ }
355
+ if (enableBuiltInHooks?.metrics) {
356
+ this.register(BuiltInHooks.createMetricsHook(options.metricsOptions));
357
+ }
358
+ }
359
+ /**
360
+ * 获取所有注册的hooks
361
+ */
362
+ getAllHooks() {
363
+ return new Map(this.hooks);
364
+ }
365
+ /**
366
+ * 获取指定类型的hooks
367
+ */
368
+ getHooks(type) {
369
+ return this.hooks.get(type) || [];
370
+ }
371
+ /**
372
+ * 清除所有hooks
373
+ */
374
+ clear() {
375
+ this.hooks.clear();
376
+ this.hooks.set('beforeRequest', []);
377
+ this.hooks.set('afterResponse', []);
378
+ this.hooks.set('onError', []);
379
+ this.hooks.set('onRetry', []);
380
+ }
381
+ }
382
+
383
+ /**
384
+ * HTTP客户端,集成认证和Hook系统
385
+ */
386
+ class HttpClient {
387
+ constructor(options) {
388
+ this.auth = options.auth;
389
+ this.hookManager = new HookManager(options.hooks);
390
+ this.retryOptions = {
391
+ maxRetries: 3,
392
+ retryDelay: 1000,
393
+ retryStatusCodes: [408, 429, 500, 502, 503, 504],
394
+ ...options.retryOptions,
395
+ };
396
+ // 创建axios实例
397
+ this.client = axios.create({
398
+ baseURL: options.baseURL,
399
+ timeout: options.timeout || 30000,
400
+ headers: options.headers,
401
+ });
402
+ // 设置拦截器
403
+ this.setupInterceptors();
404
+ }
405
+ /**
406
+ * 设置请求和响应拦截器
407
+ */
408
+ setupInterceptors() {
409
+ // 请求拦截器
410
+ this.client.interceptors.request.use(async (config) => {
411
+ const operationId = config.headers?.['X-Operation-Id'] || 'unknown';
412
+ // 添加认证信息
413
+ if (this.auth) {
414
+ config = await this.auth.attachCredentials(config);
415
+ }
416
+ // 执行beforeRequest hooks
417
+ const hookContext = {
418
+ operationId,
419
+ config,
420
+ metadata: {},
421
+ };
422
+ await this.hookManager.executeBeforeRequest(hookContext);
423
+ return hookContext.config;
424
+ }, (error) => {
425
+ return Promise.reject(error);
426
+ });
427
+ // 响应拦截器
428
+ this.client.interceptors.response.use(async (response) => {
429
+ const operationId = response.config.headers?.['X-Operation-Id'] || 'unknown';
430
+ // 执行afterResponse hooks
431
+ const hookContext = {
432
+ operationId,
433
+ config: response.config,
434
+ response,
435
+ metadata: {},
436
+ };
437
+ await this.hookManager.executeAfterResponse(hookContext);
438
+ return hookContext.response || response;
439
+ }, async (error) => {
440
+ const operationId = error.config?.headers?.['X-Operation-Id'] || 'unknown';
441
+ // 执行onError hooks
442
+ const hookContext = {
443
+ operationId,
444
+ config: error.config,
445
+ error,
446
+ metadata: {},
447
+ };
448
+ await this.hookManager.executeOnError(hookContext);
449
+ // 重试逻辑
450
+ const shouldRetry = this.shouldRetryRequest(error);
451
+ const retryCount = error.config?._retryCount || 0;
452
+ if (shouldRetry && retryCount < this.retryOptions.maxRetries) {
453
+ // 执行onRetry hooks
454
+ hookContext.retryCount = retryCount;
455
+ await this.hookManager.executeOnRetry(hookContext);
456
+ // 计算退避延迟
457
+ const delay = this.retryOptions.retryDelay * Math.pow(2, retryCount);
458
+ await this.delay(delay);
459
+ // 更新重试计数
460
+ const config = error.config;
461
+ config._retryCount = retryCount + 1;
462
+ // 重新发起请求
463
+ return this.client.request(config);
464
+ }
465
+ return Promise.reject(hookContext.error || error);
466
+ });
467
+ }
468
+ /**
469
+ * 判断是否应该重试
470
+ */
471
+ shouldRetryRequest(error) {
472
+ // 自定义重试判断
473
+ if (this.retryOptions.shouldRetry) {
474
+ return this.retryOptions.shouldRetry(error);
475
+ }
476
+ // 没有响应或网络错误
477
+ if (!error.response) {
478
+ return true;
479
+ }
480
+ // 根据状态码判断
481
+ const statusCode = error.response.status;
482
+ return this.retryOptions.retryStatusCodes?.includes(statusCode) || false;
483
+ }
484
+ /**
485
+ * 延迟函数
486
+ */
487
+ delay(ms) {
488
+ return new Promise((resolve) => setTimeout(resolve, ms));
489
+ }
490
+ /**
491
+ * 发起请求
492
+ */
493
+ async request(config) {
494
+ return this.client.request(config);
495
+ }
496
+ /**
497
+ * GET请求
498
+ */
499
+ async get(url, config) {
500
+ return this.client.get(url, config);
501
+ }
502
+ /**
503
+ * POST请求
504
+ */
505
+ async post(url, data, config) {
506
+ return this.client.post(url, data, config);
507
+ }
508
+ /**
509
+ * PUT请求
510
+ */
511
+ async put(url, data, config) {
512
+ return this.client.put(url, data, config);
513
+ }
514
+ /**
515
+ * DELETE请求
516
+ */
517
+ async delete(url, config) {
518
+ return this.client.delete(url, config);
519
+ }
520
+ /**
521
+ * PATCH请求
522
+ */
523
+ async patch(url, data, config) {
524
+ return this.client.patch(url, data, config);
525
+ }
526
+ /**
527
+ * 获取底层axios实例
528
+ */
529
+ getAxiosInstance() {
530
+ return this.client;
531
+ }
532
+ }
533
+
534
+ /**
535
+ * 认证提供者抽象基类
536
+ */
537
+ class AuthProvider {
538
+ constructor(credentials) {
539
+ this.credentials = credentials;
540
+ }
541
+ /**
542
+ * 获取当前凭证
543
+ */
544
+ getCredentials() {
545
+ return this.credentials;
546
+ }
547
+ /**
548
+ * 更新凭证
549
+ */
550
+ updateCredentials(credentials) {
551
+ this.credentials = { ...this.credentials, ...credentials };
552
+ }
553
+ }
554
+
555
+ /**
556
+ * API Key认证提供者
557
+ */
558
+ class ApiKeyProvider extends AuthProvider {
559
+ constructor(credentials) {
560
+ super(credentials);
561
+ this.credentials = credentials;
562
+ }
563
+ async attachCredentials(config) {
564
+ const newConfig = { ...config };
565
+ if (this.credentials.location === 'header') {
566
+ // 添加到Header
567
+ newConfig.headers = {
568
+ ...newConfig.headers,
569
+ [this.credentials.name]: this.credentials.apiKey,
570
+ };
571
+ }
572
+ else if (this.credentials.location === 'query') {
573
+ // 添加到Query参数
574
+ newConfig.params = {
575
+ ...newConfig.params,
576
+ [this.credentials.name]: this.credentials.apiKey,
577
+ };
578
+ }
579
+ return newConfig;
580
+ }
581
+ async refreshCredentials() {
582
+ // API Key通常不需要刷新
583
+ return Promise.resolve();
584
+ }
585
+ async isValid() {
586
+ return Boolean(this.credentials.apiKey);
587
+ }
588
+ }
589
+
590
+ /**
591
+ * OAuth2认证提供者
592
+ */
593
+ class OAuth2Provider extends AuthProvider {
594
+ constructor(credentials) {
595
+ super(credentials);
596
+ this.credentials = credentials;
597
+ }
598
+ async attachCredentials(config) {
599
+ // 确保有有效的access token
600
+ if (!(await this.isValid())) {
601
+ await this.refreshCredentials();
602
+ }
603
+ const newConfig = { ...config };
604
+ newConfig.headers = {
605
+ ...newConfig.headers,
606
+ Authorization: `Bearer ${this.credentials.accessToken}`,
607
+ };
608
+ return newConfig;
609
+ }
610
+ async refreshCredentials() {
611
+ try {
612
+ const params = new URLSearchParams();
613
+ params.append('client_id', this.credentials.clientId);
614
+ params.append('client_secret', this.credentials.clientSecret);
615
+ if (this.credentials.refreshToken) {
616
+ // 使用refresh token刷新
617
+ params.append('grant_type', 'refresh_token');
618
+ params.append('refresh_token', this.credentials.refreshToken);
619
+ }
620
+ else {
621
+ // 使用client credentials流程
622
+ params.append('grant_type', 'client_credentials');
623
+ if (this.credentials.scope) {
624
+ params.append('scope', this.credentials.scope);
625
+ }
626
+ }
627
+ const response = await axios.post(this.credentials.tokenUrl, params, {
628
+ headers: {
629
+ 'Content-Type': 'application/x-www-form-urlencoded',
630
+ },
631
+ });
632
+ const tokenData = response.data;
633
+ // 更新凭证
634
+ this.credentials.accessToken = tokenData.access_token;
635
+ if (tokenData.refresh_token) {
636
+ this.credentials.refreshToken = tokenData.refresh_token;
637
+ }
638
+ if (tokenData.expires_in) {
639
+ this.credentials.expiresAt = Date.now() + tokenData.expires_in * 1000;
640
+ }
641
+ }
642
+ catch (error) {
643
+ throw new Error(`Failed to refresh OAuth2 token: ${error}`);
644
+ }
645
+ }
646
+ async isValid() {
647
+ if (!this.credentials.accessToken) {
648
+ return false;
649
+ }
650
+ if (this.credentials.expiresAt) {
651
+ // 提前5分钟刷新
652
+ const bufferTime = 5 * 60 * 1000;
653
+ return Date.now() < this.credentials.expiresAt - bufferTime;
654
+ }
655
+ return true;
656
+ }
657
+ }
658
+
659
+ /**
660
+ * JWT认证提供者
661
+ */
662
+ class JWTProvider extends AuthProvider {
663
+ constructor(credentials) {
664
+ super(credentials);
665
+ this.credentials = credentials;
666
+ }
667
+ async attachCredentials(config) {
668
+ const newConfig = { ...config };
669
+ const headerName = this.credentials.headerName || 'Authorization';
670
+ const token = this.credentials.token;
671
+ newConfig.headers = {
672
+ ...newConfig.headers,
673
+ [headerName]: headerName === 'Authorization' ? `Bearer ${token}` : token,
674
+ };
675
+ return newConfig;
676
+ }
677
+ async refreshCredentials() {
678
+ // JWT通常由外部系统管理,这里不实现刷新逻辑
679
+ return Promise.resolve();
680
+ }
681
+ async isValid() {
682
+ if (!this.credentials.token) {
683
+ return false;
684
+ }
685
+ // 可以在这里添加JWT过期检查
686
+ try {
687
+ const parts = this.credentials.token.split('.');
688
+ if (parts.length !== 3) {
689
+ return false;
690
+ }
691
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
692
+ if (payload.exp) {
693
+ // 检查是否过期(提前1分钟)
694
+ return Date.now() < payload.exp * 1000 - 60000;
695
+ }
696
+ return true;
697
+ }
698
+ catch {
699
+ return false;
700
+ }
701
+ }
702
+ }
703
+
704
+ /**
705
+ * Basic认证提供者
706
+ */
707
+ class BasicAuthProvider extends AuthProvider {
708
+ constructor(credentials) {
709
+ super(credentials);
710
+ this.credentials = credentials;
711
+ }
712
+ async attachCredentials(config) {
713
+ const newConfig = { ...config };
714
+ const token = Buffer.from(`${this.credentials.username}:${this.credentials.password}`).toString('base64');
715
+ newConfig.headers = {
716
+ ...newConfig.headers,
717
+ Authorization: `Basic ${token}`,
718
+ };
719
+ return newConfig;
720
+ }
721
+ async refreshCredentials() {
722
+ // Basic Auth不需要刷新
723
+ return Promise.resolve();
724
+ }
725
+ async isValid() {
726
+ return Boolean(this.credentials.username && this.credentials.password);
727
+ }
728
+ }
729
+
730
+ /**
731
+ * 自定义认证提供者
732
+ */
733
+ class CustomAuthProvider extends AuthProvider {
734
+ constructor(credentials) {
735
+ super(credentials);
736
+ this.credentials = credentials;
737
+ }
738
+ async attachCredentials(config) {
739
+ return this.credentials.handler(config);
740
+ }
741
+ async refreshCredentials() {
742
+ // 自定义认证由用户实现
743
+ return Promise.resolve();
744
+ }
745
+ async isValid() {
746
+ // 自定义认证默认认为总是有效
747
+ return true;
748
+ }
749
+ }
750
+
751
+ /**
752
+ * 配置管理器
753
+ */
754
+ class ConfigManager {
755
+ constructor(configDir) {
756
+ this.configDir = configDir || path.join(os.homedir(), '.openapi-sdk');
757
+ this.configPath = path.join(this.configDir, 'config.json');
758
+ }
759
+ /**
760
+ * 保存认证配置
761
+ */
762
+ async saveAuth(profileName, authConfig) {
763
+ await this.ensureConfigDir();
764
+ const config = await this.loadConfig();
765
+ config.profiles[profileName] = authConfig;
766
+ await fs.writeFile(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
767
+ }
768
+ /**
769
+ * 加载认证配置
770
+ */
771
+ async loadAuth(profileName) {
772
+ const config = await this.loadConfig();
773
+ const authConfig = config.profiles[profileName];
774
+ if (!authConfig) {
775
+ throw new Error(`Profile '${profileName}' not found`);
776
+ }
777
+ return authConfig;
778
+ }
779
+ /**
780
+ * 列出所有profile
781
+ */
782
+ async listProfiles() {
783
+ const config = await this.loadConfig();
784
+ return Object.keys(config.profiles);
785
+ }
786
+ /**
787
+ * 删除profile
788
+ */
789
+ async removeProfile(profileName) {
790
+ const config = await this.loadConfig();
791
+ delete config.profiles[profileName];
792
+ await fs.writeFile(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
793
+ }
794
+ /**
795
+ * 从文件加载配置
796
+ */
797
+ async loadFromFile(filePath) {
798
+ try {
799
+ const content = await fs.readFile(filePath, 'utf-8');
800
+ return JSON.parse(content);
801
+ }
802
+ catch (error) {
803
+ throw new Error(`Failed to load config from ${filePath}: ${error}`);
804
+ }
805
+ }
806
+ /**
807
+ * 保存配置到文件
808
+ */
809
+ async saveToFile(filePath, authConfig) {
810
+ try {
811
+ await fs.writeFile(filePath, JSON.stringify(authConfig, null, 2), 'utf-8');
812
+ }
813
+ catch (error) {
814
+ throw new Error(`Failed to save config to ${filePath}: ${error}`);
815
+ }
816
+ }
817
+ /**
818
+ * 加载完整配置文件
819
+ */
820
+ async loadConfig() {
821
+ try {
822
+ const content = await fs.readFile(this.configPath, 'utf-8');
823
+ return JSON.parse(content);
824
+ }
825
+ catch (error) {
826
+ if (error.code === 'ENOENT') {
827
+ // 文件不存在,返回默认配置
828
+ return { profiles: {} };
829
+ }
830
+ throw error;
831
+ }
832
+ }
833
+ /**
834
+ * 确保配置目录存在
835
+ */
836
+ async ensureConfigDir() {
837
+ try {
838
+ await fs.mkdir(this.configDir, { recursive: true });
839
+ }
840
+ catch (error) {
841
+ // 忽略错误,可能目录已存在
842
+ }
843
+ }
844
+ /**
845
+ * 获取配置目录路径
846
+ */
847
+ getConfigDir() {
848
+ return this.configDir;
849
+ }
850
+ /**
851
+ * 获取配置文件路径
852
+ */
853
+ getConfigPath() {
854
+ return this.configPath;
855
+ }
856
+ }
857
+
858
+ /**
859
+ * 跨平台环境配置工具
860
+ * 支持 Node.js 和浏览器环境
861
+ */
862
+ /**
863
+ * 全局配置存储(用于浏览器环境)
864
+ */
865
+ const globalConfig = {};
866
+ /**
867
+ * 获取环境变量或配置值
868
+ * 优先级:globalConfig > process.env (仅 Node.js)
869
+ */
870
+ function getEnvConfig(key) {
871
+ // 优先使用全局配置
872
+ if (globalConfig[key] !== undefined) {
873
+ return globalConfig[key];
874
+ }
875
+ // Node.js 环境
876
+ if (typeof process !== 'undefined' && process.env) {
877
+ return process.env[key];
878
+ }
879
+ return undefined;
880
+ }
881
+
882
+ /**
883
+ * 认证提供者工厂
884
+ */
885
+ class AuthFactory {
886
+ /**
887
+ * 根据配置创建认证提供者
888
+ */
889
+ static create(config) {
890
+ return this.createFromCredentials(config.credentials);
891
+ }
892
+ /**
893
+ * 根据凭证创建认证提供者
894
+ */
895
+ static createFromCredentials(credentials) {
896
+ switch (credentials.type) {
897
+ case 'apiKey':
898
+ return new ApiKeyProvider(credentials);
899
+ case 'oauth2':
900
+ return new OAuth2Provider(credentials);
901
+ case 'jwt':
902
+ return new JWTProvider(credentials);
903
+ case 'basic':
904
+ return new BasicAuthProvider(credentials);
905
+ case 'custom':
906
+ return new CustomAuthProvider(credentials);
907
+ default:
908
+ throw new Error(`Unsupported auth type: ${credentials.type}`);
909
+ }
910
+ }
911
+ /**
912
+ * 从环境变量创建认证提供者
913
+ */
914
+ static fromEnv() {
915
+ const authType = getEnvConfig('AUTH_TYPE');
916
+ if (!authType) {
917
+ throw new Error('AUTH_TYPE environment variable is not set');
918
+ }
919
+ let credentials;
920
+ switch (authType) {
921
+ case 'apiKey': {
922
+ const apiKey = getEnvConfig('API_KEY');
923
+ const location = getEnvConfig('API_KEY_LOCATION') || 'header';
924
+ const name = getEnvConfig('API_KEY_NAME') || 'X-API-Key';
925
+ if (!apiKey) {
926
+ throw new Error('API_KEY environment variable is required for apiKey auth');
927
+ }
928
+ credentials = {
929
+ type: 'apiKey',
930
+ apiKey,
931
+ location,
932
+ name,
933
+ };
934
+ break;
935
+ }
936
+ case 'oauth2': {
937
+ const clientId = getEnvConfig('OAUTH_CLIENT_ID');
938
+ const clientSecret = getEnvConfig('OAUTH_CLIENT_SECRET');
939
+ const tokenUrl = getEnvConfig('OAUTH_TOKEN_URL');
940
+ const accessToken = getEnvConfig('ACCESS_TOKEN');
941
+ const refreshToken = getEnvConfig('REFRESH_TOKEN');
942
+ const scope = getEnvConfig('OAUTH_SCOPE');
943
+ if (!clientId || !clientSecret || !tokenUrl) {
944
+ throw new Error('OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, and OAUTH_TOKEN_URL are required for oauth2 auth');
945
+ }
946
+ credentials = {
947
+ type: 'oauth2',
948
+ clientId,
949
+ clientSecret,
950
+ tokenUrl,
951
+ accessToken,
952
+ refreshToken,
953
+ scope,
954
+ };
955
+ break;
956
+ }
957
+ case 'jwt': {
958
+ const token = getEnvConfig('JWT_TOKEN');
959
+ const headerName = getEnvConfig('JWT_HEADER_NAME');
960
+ if (!token) {
961
+ throw new Error('JWT_TOKEN environment variable is required for jwt auth');
962
+ }
963
+ credentials = {
964
+ type: 'jwt',
965
+ token,
966
+ headerName,
967
+ };
968
+ break;
969
+ }
970
+ case 'basic': {
971
+ const username = getEnvConfig('BASIC_USERNAME');
972
+ const password = getEnvConfig('BASIC_PASSWORD');
973
+ if (!username || !password) {
974
+ throw new Error('BASIC_USERNAME and BASIC_PASSWORD are required for basic auth');
975
+ }
976
+ credentials = {
977
+ type: 'basic',
978
+ username,
979
+ password,
980
+ };
981
+ break;
982
+ }
983
+ default:
984
+ throw new Error(`Unsupported AUTH_TYPE: ${authType}`);
985
+ }
986
+ return this.createFromCredentials(credentials);
987
+ }
988
+ /**
989
+ * 从配置文件创建认证提供者
990
+ */
991
+ static async fromFile(path) {
992
+ const configManager = new ConfigManager();
993
+ const config = await configManager.loadFromFile(path);
994
+ return this.create(config);
995
+ }
996
+ /**
997
+ * 从profile创建认证提供者
998
+ */
999
+ static async fromProfile(profileName = 'default') {
1000
+ const configManager = new ConfigManager();
1001
+ const config = await configManager.loadAuth(profileName);
1002
+ return this.create(config);
1003
+ }
1004
+ }
1005
+
1006
+ /**
1007
+ * Environment Configuration for SeaVerse SDK
1008
+ * 环境配置模块 - 支持多环境部署
1009
+ */
1010
+ /**
1011
+ * 预定义的环境配置
1012
+ *
1013
+ * 使用说明:
1014
+ * - production: 正式生产环境
1015
+ * - staging: 预发布环境(当前默认)
1016
+ * - development: 开发测试环境
1017
+ * - local: 本地开发环境
1018
+ */
1019
+ const ENVIRONMENT_CONFIGS = {
1020
+ production: {
1021
+ name: 'production',
1022
+ baseURL: 'https://api.seaverse.com',
1023
+ wsURL: 'wss://api.seaverse.com',
1024
+ isProduction: true,
1025
+ },
1026
+ staging: {
1027
+ name: 'staging',
1028
+ baseURL: 'https://account-hub.sg.seaverse.dev',
1029
+ wsURL: 'wss://account-hub.sg.seaverse.dev',
1030
+ isProduction: false,
1031
+ },
1032
+ development: {
1033
+ name: 'development',
1034
+ baseURL: 'https://api-dev.seaverse.dev',
1035
+ wsURL: 'wss://api-dev.seaverse.dev',
1036
+ isProduction: false,
1037
+ },
1038
+ local: {
1039
+ name: 'local',
1040
+ baseURL: 'http://localhost:8001',
1041
+ wsURL: 'ws://localhost:8000',
1042
+ isProduction: false,
1043
+ },
1044
+ };
1045
+ /**
1046
+ * 根据当前运行环境自动检测应该使用的环境配置
1047
+ *
1048
+ * 检测规则:
1049
+ * 1. localhost/127.0.0.1 → local
1050
+ * 2. 包含 -dev 或 .dev → development
1051
+ * 3. 包含 staging → staging
1052
+ * 4. 其他 → production (默认)
1053
+ *
1054
+ * @returns 检测到的环境类型
1055
+ */
1056
+ function detectEnvironment() {
1057
+ // Node.js 环境或非浏览器环境,默认返回 staging(安全起见)
1058
+ if (typeof window === 'undefined' || typeof window.location === 'undefined') {
1059
+ return 'staging';
1060
+ }
1061
+ const hostname = window.location.hostname.toLowerCase();
1062
+ // 本地开发环境
1063
+ if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0') {
1064
+ return 'local';
1065
+ }
1066
+ // 开发环境 (包含 -dev 或 .dev)
1067
+ if (hostname.includes('-dev.') || hostname.includes('.dev') || hostname.endsWith('.dev')) {
1068
+ return 'development';
1069
+ }
1070
+ // Staging 环境
1071
+ if (hostname.includes('-staging.') || hostname.includes('staging.')) {
1072
+ return 'staging';
1073
+ }
1074
+ // 默认使用 production
1075
+ return 'production';
1076
+ }
1077
+ /**
1078
+ * 获取环境配置
1079
+ *
1080
+ * @param env 环境类型,如果不提供则自动检测
1081
+ * @returns 环境配置对象
1082
+ */
1083
+ function getEnvironmentConfig(env) {
1084
+ const targetEnv = env || detectEnvironment();
1085
+ return ENVIRONMENT_CONFIGS[targetEnv];
1086
+ }
1087
+ /**
1088
+ * 解析用户配置,返回最终的 baseURL
1089
+ *
1090
+ * 优先级:
1091
+ * 1. 显式传入的 baseURL(最高优先级)
1092
+ * 2. environment 参数指定的环境
1093
+ * 3. 自动检测环境(最低优先级)
1094
+ *
1095
+ * @param options 用户配置选项
1096
+ * @returns 最终使用的 baseURL
1097
+ */
1098
+ function resolveBaseURL(options) {
1099
+ // 优先级 1: 显式传入的 baseURL
1100
+ if (options.baseURL) {
1101
+ return options.baseURL;
1102
+ }
1103
+ // 优先级 2: environment 参数
1104
+ if (options.environment) {
1105
+ return ENVIRONMENT_CONFIGS[options.environment].baseURL;
1106
+ }
1107
+ // 优先级 3: 自动检测
1108
+ const detectedEnv = detectEnvironment();
1109
+ return ENVIRONMENT_CONFIGS[detectedEnv].baseURL;
1110
+ }
1111
+
1112
+ /**
1113
+ * Type definitions for SeaVerse Dispatcher API
1114
+ * Generated from auth.yaml OpenAPI specification
1115
+ */
1116
+
1117
+ var models = /*#__PURE__*/Object.freeze({
1118
+ __proto__: null
1119
+ });
1120
+
1121
+ /**
1122
+ * SeaVerse Backend API Client
1123
+ * SeaVerse Dispatcher API - 云原生 AI Agent 平台的统一网关服务
1124
+ *
1125
+ * @version 2.0.0
1126
+ *
1127
+ * @example
1128
+ * // 方式 1: 自动检测环境(推荐本地开发)
1129
+ * const client = new SeaVerseBackendAPIClient();
1130
+ *
1131
+ * @example
1132
+ * // 方式 2: 指定环境(推荐生产部署)
1133
+ * const client = new SeaVerseBackendAPIClient({
1134
+ * environment: 'production',
1135
+ * });
1136
+ *
1137
+ * @example
1138
+ * // 方式 3: 自定义 URL(特殊需求)
1139
+ * const client = new SeaVerseBackendAPIClient({
1140
+ * baseURL: 'https://custom-api.example.com',
1141
+ * });
1142
+ */
1143
+ class SeaVerseBackendAPIClient {
1144
+ constructor(options = {}) {
1145
+ // 使用智能配置解析,支持三种优先级:
1146
+ // 1. 显式 baseURL(最高)
1147
+ // 2. environment 参数
1148
+ // 3. 自动检测(默认)
1149
+ const finalBaseURL = resolveBaseURL({
1150
+ baseURL: options.baseURL,
1151
+ environment: options.environment,
1152
+ });
1153
+ const httpOptions = {
1154
+ baseURL: finalBaseURL,
1155
+ timeout: options.timeout,
1156
+ headers: options.headers,
1157
+ auth: options.auth || this.getDefaultAuth(),
1158
+ hooks: options.hooks || this.getDefaultHooks(),
1159
+ };
1160
+ this.httpClient = new HttpClient(httpOptions);
1161
+ }
1162
+ /**
1163
+ * Get default authentication configuration
1164
+ */
1165
+ getDefaultAuth() {
1166
+ const token = getEnvConfig('JWT_TOKEN');
1167
+ if (!token)
1168
+ return undefined;
1169
+ return AuthFactory.create({
1170
+ type: 'jwt',
1171
+ credentials: {
1172
+ type: 'jwt',
1173
+ token,
1174
+ },
1175
+ });
1176
+ }
1177
+ /**
1178
+ * Get default hooks configuration
1179
+ */
1180
+ getDefaultHooks() {
1181
+ return {
1182
+ hooks: [
1183
+ BuiltInHooks.createLoggerHook({
1184
+ logLevel: 'info',
1185
+ logRequestBody: true,
1186
+ logResponseBody: true,
1187
+ }),
1188
+ BuiltInHooks.createRequestIdHook(),
1189
+ ],
1190
+ };
1191
+ }
1192
+ // ============================================================================
1193
+ // Authentication & OAuth APIs
1194
+ // ============================================================================
1195
+ /**
1196
+ * Health check
1197
+ * Check if the service is healthy
1198
+ */
1199
+ async getHealth(options) {
1200
+ const config = {
1201
+ method: 'GET',
1202
+ url: `/health`,
1203
+ headers: {
1204
+ 'X-Operation-Id': 'getHealth',
1205
+ ...options?.headers,
1206
+ },
1207
+ ...options,
1208
+ };
1209
+ const response = await this.httpClient.request(config);
1210
+ return response.data;
1211
+ }
1212
+ /**
1213
+ * User registration
1214
+ * Register a new user with email verification
1215
+ */
1216
+ async register(data, options) {
1217
+ const config = {
1218
+ method: 'POST',
1219
+ url: `/api/auth/register`,
1220
+ data,
1221
+ headers: {
1222
+ 'X-Operation-Id': 'register',
1223
+ ...options?.headers,
1224
+ },
1225
+ ...options,
1226
+ };
1227
+ const response = await this.httpClient.request(config);
1228
+ return response.data;
1229
+ }
1230
+ /**
1231
+ * User login
1232
+ * Login with email and password
1233
+ */
1234
+ async login(data, options) {
1235
+ const config = {
1236
+ method: 'POST',
1237
+ url: `/api/auth/login`,
1238
+ data,
1239
+ headers: {
1240
+ 'X-Operation-Id': 'login',
1241
+ ...options?.headers,
1242
+ },
1243
+ ...options,
1244
+ };
1245
+ const response = await this.httpClient.request(config);
1246
+ return response.data;
1247
+ }
1248
+ /**
1249
+ * Get current user
1250
+ * Get the authenticated user's information
1251
+ */
1252
+ async getCurrentUser(options) {
1253
+ const config = {
1254
+ method: 'GET',
1255
+ url: `/api/auth/me`,
1256
+ headers: {
1257
+ 'X-Operation-Id': 'getCurrentUser',
1258
+ ...options?.headers,
1259
+ },
1260
+ ...options,
1261
+ };
1262
+ const response = await this.httpClient.request(config);
1263
+ return response.data;
1264
+ }
1265
+ /**
1266
+ * User logout
1267
+ * Logout the current user
1268
+ */
1269
+ async logout(options) {
1270
+ const config = {
1271
+ method: 'POST',
1272
+ url: `/api/auth/logout`,
1273
+ headers: {
1274
+ 'X-Operation-Id': 'logout',
1275
+ ...options?.headers,
1276
+ },
1277
+ ...options,
1278
+ };
1279
+ const response = await this.httpClient.request(config);
1280
+ return response.data;
1281
+ }
1282
+ /**
1283
+ * Request password reset
1284
+ * Send password reset email
1285
+ */
1286
+ async forgotPassword(data, options) {
1287
+ const config = {
1288
+ method: 'POST',
1289
+ url: `/api/auth/forgot-password`,
1290
+ data,
1291
+ headers: {
1292
+ 'X-Operation-Id': 'forgotPassword',
1293
+ ...options?.headers,
1294
+ },
1295
+ ...options,
1296
+ };
1297
+ const response = await this.httpClient.request(config);
1298
+ return response.data;
1299
+ }
1300
+ /**
1301
+ * Reset password
1302
+ * Reset password with token from email
1303
+ */
1304
+ async resetPassword(data, options) {
1305
+ const config = {
1306
+ method: 'POST',
1307
+ url: `/api/auth/reset-password`,
1308
+ data,
1309
+ headers: {
1310
+ 'X-Operation-Id': 'resetPassword',
1311
+ ...options?.headers,
1312
+ },
1313
+ ...options,
1314
+ };
1315
+ const response = await this.httpClient.request(config);
1316
+ return response.data;
1317
+ }
1318
+ /**
1319
+ * Get api-service token
1320
+ * Generate token for accessing api-service from sandbox
1321
+ */
1322
+ async getApiServiceToken(options) {
1323
+ const config = {
1324
+ method: 'GET',
1325
+ url: `/api/auth/api-service-token`,
1326
+ headers: {
1327
+ 'X-Operation-Id': 'getApiServiceToken',
1328
+ ...options?.headers,
1329
+ },
1330
+ ...options,
1331
+ };
1332
+ const response = await this.httpClient.request(config);
1333
+ return response.data;
1334
+ }
1335
+ // ============================================================================
1336
+ // OAuth APIs
1337
+ // ============================================================================
1338
+ /**
1339
+ * Exchange Google code for token
1340
+ * Exchange Google authorization code for JWT token (for app)
1341
+ */
1342
+ async googleCodeToToken(data, options) {
1343
+ const config = {
1344
+ method: 'POST',
1345
+ url: `/api/auth/google/code2token`,
1346
+ data,
1347
+ headers: {
1348
+ 'X-Operation-Id': 'googleCodeToToken',
1349
+ ...options?.headers,
1350
+ },
1351
+ ...options,
1352
+ };
1353
+ const response = await this.httpClient.request(config);
1354
+ return response.data;
1355
+ }
1356
+ /**
1357
+ * Unlink Google account
1358
+ * Unlink the user's Google account
1359
+ */
1360
+ async unlinkGoogle(options) {
1361
+ const config = {
1362
+ method: 'POST',
1363
+ url: `/api/auth/google/unlink`,
1364
+ headers: {
1365
+ 'X-Operation-Id': 'unlinkGoogle',
1366
+ ...options?.headers,
1367
+ },
1368
+ ...options,
1369
+ };
1370
+ const response = await this.httpClient.request(config);
1371
+ return response.data;
1372
+ }
1373
+ /**
1374
+ * Exchange Discord code for token
1375
+ */
1376
+ async discordCodeToToken(data, options) {
1377
+ const config = {
1378
+ method: 'POST',
1379
+ url: `/api/auth/discord/code2token`,
1380
+ data,
1381
+ headers: {
1382
+ 'X-Operation-Id': 'discordCodeToToken',
1383
+ ...options?.headers,
1384
+ },
1385
+ ...options,
1386
+ };
1387
+ const response = await this.httpClient.request(config);
1388
+ return response.data;
1389
+ }
1390
+ /**
1391
+ * Unlink Discord account
1392
+ */
1393
+ async unlinkDiscord(options) {
1394
+ const config = {
1395
+ method: 'POST',
1396
+ url: `/api/auth/discord/unlink`,
1397
+ headers: {
1398
+ 'X-Operation-Id': 'unlinkDiscord',
1399
+ ...options?.headers,
1400
+ },
1401
+ ...options,
1402
+ };
1403
+ const response = await this.httpClient.request(config);
1404
+ return response.data;
1405
+ }
1406
+ /**
1407
+ * Exchange GitHub code for token
1408
+ */
1409
+ async githubCodeToToken(data, options) {
1410
+ const config = {
1411
+ method: 'POST',
1412
+ url: `/api/auth/github/code2token`,
1413
+ data,
1414
+ headers: {
1415
+ 'X-Operation-Id': 'githubCodeToToken',
1416
+ ...options?.headers,
1417
+ },
1418
+ ...options,
1419
+ };
1420
+ const response = await this.httpClient.request(config);
1421
+ return response.data;
1422
+ }
1423
+ /**
1424
+ * Unlink GitHub account
1425
+ */
1426
+ async unlinkGithub(options) {
1427
+ const config = {
1428
+ method: 'POST',
1429
+ url: `/api/auth/github/unlink`,
1430
+ headers: {
1431
+ 'X-Operation-Id': 'unlinkGithub',
1432
+ ...options?.headers,
1433
+ },
1434
+ ...options,
1435
+ };
1436
+ const response = await this.httpClient.request(config);
1437
+ return response.data;
1438
+ }
1439
+ // ============================================================================
1440
+ // Container APIs
1441
+ // ============================================================================
1442
+ /**
1443
+ * List all containers
1444
+ */
1445
+ async listContainers(options) {
1446
+ const config = {
1447
+ method: 'GET',
1448
+ url: `/api/containers`,
1449
+ headers: {
1450
+ 'X-Operation-Id': 'listContainers',
1451
+ ...options?.headers,
1452
+ },
1453
+ ...options,
1454
+ };
1455
+ const response = await this.httpClient.request(config);
1456
+ return response.data;
1457
+ }
1458
+ /**
1459
+ * Register a container
1460
+ * Internal service call to register a new container
1461
+ */
1462
+ async registerContainer(data, options) {
1463
+ const config = {
1464
+ method: 'POST',
1465
+ url: `/api/containers`,
1466
+ data,
1467
+ headers: {
1468
+ 'X-Operation-Id': 'registerContainer',
1469
+ ...options?.headers,
1470
+ },
1471
+ ...options,
1472
+ };
1473
+ const response = await this.httpClient.request(config);
1474
+ return response.data;
1475
+ }
1476
+ /**
1477
+ * Get container by ID
1478
+ */
1479
+ async getContainer(id, options) {
1480
+ const config = {
1481
+ method: 'GET',
1482
+ url: `/api/containers/${id}`,
1483
+ headers: {
1484
+ 'X-Operation-Id': 'getContainer',
1485
+ ...options?.headers,
1486
+ },
1487
+ ...options,
1488
+ };
1489
+ const response = await this.httpClient.request(config);
1490
+ return response.data;
1491
+ }
1492
+ /**
1493
+ * Unregister a container
1494
+ * Internal service call to unregister a container
1495
+ */
1496
+ async unregisterContainer(id, options) {
1497
+ const config = {
1498
+ method: 'DELETE',
1499
+ url: `/api/containers/${id}`,
1500
+ headers: {
1501
+ 'X-Operation-Id': 'unregisterContainer',
1502
+ ...options?.headers,
1503
+ },
1504
+ ...options,
1505
+ };
1506
+ const response = await this.httpClient.request(config);
1507
+ return response.data;
1508
+ }
1509
+ /**
1510
+ * Get container statistics
1511
+ */
1512
+ async getContainerStats(options) {
1513
+ const config = {
1514
+ method: 'GET',
1515
+ url: `/api/containers/stats`,
1516
+ headers: {
1517
+ 'X-Operation-Id': 'getContainerStats',
1518
+ ...options?.headers,
1519
+ },
1520
+ ...options,
1521
+ };
1522
+ const response = await this.httpClient.request(config);
1523
+ return response.data;
1524
+ }
1525
+ /**
1526
+ * Container heartbeat
1527
+ * Internal service call to update container heartbeat
1528
+ */
1529
+ async containerHeartbeat(id, options) {
1530
+ const config = {
1531
+ method: 'POST',
1532
+ url: `/api/containers/${id}/heartbeat`,
1533
+ headers: {
1534
+ 'X-Operation-Id': 'containerHeartbeat',
1535
+ ...options?.headers,
1536
+ },
1537
+ ...options,
1538
+ };
1539
+ const response = await this.httpClient.request(config);
1540
+ return response.data;
1541
+ }
1542
+ // ============================================================================
1543
+ // Conversation APIs
1544
+ // ============================================================================
1545
+ /**
1546
+ * Get conversation status
1547
+ * Query conversation streaming status from Redis
1548
+ */
1549
+ async getConversationStatus(conversationId, options) {
1550
+ const config = {
1551
+ method: 'GET',
1552
+ url: `/api/conversations/${conversationId}/status`,
1553
+ headers: {
1554
+ 'X-Operation-Id': 'getConversationStatus',
1555
+ ...options?.headers,
1556
+ },
1557
+ ...options,
1558
+ };
1559
+ const response = await this.httpClient.request(config);
1560
+ return response.data;
1561
+ }
1562
+ // ============================================================================
1563
+ // Skills Marketplace APIs
1564
+ // ============================================================================
1565
+ /**
1566
+ * List marketplace skills
1567
+ */
1568
+ async listMarketplaceSkills(params, options) {
1569
+ const config = {
1570
+ method: 'GET',
1571
+ url: `/api/marketplace/skills`,
1572
+ params,
1573
+ headers: {
1574
+ 'X-Operation-Id': 'listMarketplaceSkills',
1575
+ ...options?.headers,
1576
+ },
1577
+ ...options,
1578
+ };
1579
+ const response = await this.httpClient.request(config);
1580
+ return response.data;
1581
+ }
1582
+ /**
1583
+ * Get skill details
1584
+ */
1585
+ async getMarketplaceSkill(skillId, options) {
1586
+ const config = {
1587
+ method: 'GET',
1588
+ url: `/api/marketplace/skills/${skillId}`,
1589
+ headers: {
1590
+ 'X-Operation-Id': 'getMarketplaceSkill',
1591
+ ...options?.headers,
1592
+ },
1593
+ ...options,
1594
+ };
1595
+ const response = await this.httpClient.request(config);
1596
+ return response.data;
1597
+ }
1598
+ /**
1599
+ * Install skill from marketplace
1600
+ */
1601
+ async installSkill(skillId, options) {
1602
+ const config = {
1603
+ method: 'POST',
1604
+ url: `/api/marketplace/skills/${skillId}/download`,
1605
+ headers: {
1606
+ 'X-Operation-Id': 'installSkill',
1607
+ ...options?.headers,
1608
+ },
1609
+ ...options,
1610
+ };
1611
+ const response = await this.httpClient.request(config);
1612
+ return response.data;
1613
+ }
1614
+ /**
1615
+ * List user installed skills
1616
+ */
1617
+ async listUserSkills(options) {
1618
+ const config = {
1619
+ method: 'GET',
1620
+ url: `/api/user/skills`,
1621
+ headers: {
1622
+ 'X-Operation-Id': 'listUserSkills',
1623
+ ...options?.headers,
1624
+ },
1625
+ ...options,
1626
+ };
1627
+ const response = await this.httpClient.request(config);
1628
+ return response.data;
1629
+ }
1630
+ /**
1631
+ * Uninstall skill
1632
+ */
1633
+ async uninstallSkill(localName, options) {
1634
+ const config = {
1635
+ method: 'DELETE',
1636
+ url: `/api/user/skills/${localName}`,
1637
+ headers: {
1638
+ 'X-Operation-Id': 'uninstallSkill',
1639
+ ...options?.headers,
1640
+ },
1641
+ ...options,
1642
+ };
1643
+ await this.httpClient.request(config);
1644
+ }
1645
+ // ============================================================================
1646
+ // Speech Service APIs
1647
+ // ============================================================================
1648
+ /**
1649
+ * Get Azure Speech token
1650
+ * Get temporary token for Azure Speech Service
1651
+ */
1652
+ async getSpeechToken(options) {
1653
+ const config = {
1654
+ method: 'GET',
1655
+ url: `/api/speech/token`,
1656
+ headers: {
1657
+ 'X-Operation-Id': 'getSpeechToken',
1658
+ ...options?.headers,
1659
+ },
1660
+ ...options,
1661
+ };
1662
+ const response = await this.httpClient.request(config);
1663
+ return response.data;
1664
+ }
1665
+ // ============================================================================
1666
+ // Deprecated/Legacy Methods (for backward compatibility)
1667
+ // ============================================================================
1668
+ /**
1669
+ * @deprecated Use register() instead
1670
+ */
1671
+ async postapiauthregister(data, options) {
1672
+ return this.register(data, options);
1673
+ }
1674
+ /**
1675
+ * @deprecated Use login() instead
1676
+ */
1677
+ async postapiauthlogin(data, options) {
1678
+ return this.login(data, options);
1679
+ }
1680
+ /**
1681
+ * @deprecated Use getHealth() instead
1682
+ */
1683
+ async gethealth(options) {
1684
+ return this.getHealth(options);
1685
+ }
1686
+ /**
1687
+ * @deprecated Use listContainers() instead
1688
+ */
1689
+ async getapicontainers(options) {
1690
+ return this.listContainers(options);
1691
+ }
1692
+ }
1693
+
1694
+ class AuthModal {
1695
+ constructor(options) {
1696
+ this.modal = null;
1697
+ this.currentView = 'login';
1698
+ this.client = options.client;
1699
+ this.options = options;
1700
+ }
1701
+ /**
1702
+ * Show the authentication modal
1703
+ */
1704
+ show(initialView = 'login') {
1705
+ this.currentView = initialView;
1706
+ if (!this.modal) {
1707
+ this.createModal();
1708
+ }
1709
+ this.modal.classList.remove('hidden');
1710
+ this.switchView(initialView);
1711
+ }
1712
+ /**
1713
+ * Hide the authentication modal
1714
+ */
1715
+ hide() {
1716
+ if (this.modal) {
1717
+ this.modal.classList.add('hidden');
1718
+ }
1719
+ }
1720
+ /**
1721
+ * Destroy the modal and remove from DOM
1722
+ */
1723
+ destroy() {
1724
+ if (this.modal) {
1725
+ this.modal.remove();
1726
+ this.modal = null;
1727
+ }
1728
+ }
1729
+ createModal() {
1730
+ const theme = this.options.theme || 'dark';
1731
+ // Create modal container
1732
+ this.modal = document.createElement('div');
1733
+ this.modal.id = 'authModal';
1734
+ this.modal.className = `auth-modal auth-modal-${theme}`;
1735
+ // Create backdrop
1736
+ const backdrop = document.createElement('div');
1737
+ backdrop.className = 'auth-modal-backdrop';
1738
+ this.modal.appendChild(backdrop);
1739
+ // Create content container
1740
+ const content = document.createElement('div');
1741
+ content.className = 'auth-modal-content';
1742
+ this.modal.appendChild(content);
1743
+ // Create left panel (branding)
1744
+ const leftPanel = this.createLeftPanel();
1745
+ content.appendChild(leftPanel);
1746
+ // Create right panel (forms)
1747
+ const rightPanel = this.createRightPanel();
1748
+ content.appendChild(rightPanel);
1749
+ // Append to body
1750
+ document.body.appendChild(this.modal);
1751
+ // Bind event listeners
1752
+ this.bindEventListeners();
1753
+ }
1754
+ createLeftPanel() {
1755
+ const leftPanel = document.createElement('div');
1756
+ leftPanel.className = 'auth-modal-left';
1757
+ // Logo
1758
+ const logo = document.createElement('div');
1759
+ logo.className = 'auth-modal-logo';
1760
+ const logoText = document.createElement('span');
1761
+ logoText.className = 'logo-text';
1762
+ logoText.textContent = 'SeaVerse';
1763
+ logo.appendChild(logoText);
1764
+ leftPanel.appendChild(logo);
1765
+ // Decoration
1766
+ const decoration = document.createElement('div');
1767
+ decoration.className = 'auth-modal-decoration';
1768
+ const orb1 = document.createElement('div');
1769
+ orb1.className = 'glow-orb glow-orb-1';
1770
+ const orb2 = document.createElement('div');
1771
+ orb2.className = 'glow-orb glow-orb-2';
1772
+ decoration.appendChild(orb1);
1773
+ decoration.appendChild(orb2);
1774
+ leftPanel.appendChild(decoration);
1775
+ // Branding
1776
+ const branding = document.createElement('div');
1777
+ branding.className = 'auth-modal-branding';
1778
+ const brandingTitle = document.createElement('h2');
1779
+ brandingTitle.className = 'branding-title';
1780
+ brandingTitle.textContent = 'Get Started with Us';
1781
+ const brandingSubtitle = document.createElement('p');
1782
+ brandingSubtitle.className = 'branding-subtitle';
1783
+ brandingSubtitle.textContent = "Every conversation changes this place. You're not just using Infinity. You're creating it.";
1784
+ branding.appendChild(brandingTitle);
1785
+ branding.appendChild(brandingSubtitle);
1786
+ leftPanel.appendChild(branding);
1787
+ return leftPanel;
1788
+ }
1789
+ createRightPanel() {
1790
+ const rightPanel = document.createElement('div');
1791
+ rightPanel.className = 'auth-modal-right';
1792
+ // Close button
1793
+ const closeBtn = document.createElement('button');
1794
+ closeBtn.className = 'auth-modal-close';
1795
+ closeBtn.setAttribute('aria-label', 'Close modal');
1796
+ closeBtn.innerHTML = '<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>';
1797
+ rightPanel.appendChild(closeBtn);
1798
+ // Login form
1799
+ const loginForm = this.createLoginForm();
1800
+ rightPanel.appendChild(loginForm);
1801
+ // Signup form
1802
+ const signupForm = this.createSignupForm();
1803
+ rightPanel.appendChild(signupForm);
1804
+ // Forgot password form
1805
+ const forgotForm = this.createForgotPasswordForm();
1806
+ rightPanel.appendChild(forgotForm);
1807
+ // Success message
1808
+ const successMessage = this.createSuccessMessage();
1809
+ rightPanel.appendChild(successMessage);
1810
+ return rightPanel;
1811
+ }
1812
+ createLoginForm() {
1813
+ const container = document.createElement('div');
1814
+ container.id = 'loginForm';
1815
+ container.className = 'auth-form-view';
1816
+ // Header
1817
+ const header = document.createElement('div');
1818
+ header.className = 'auth-form-header';
1819
+ const title = document.createElement('h2');
1820
+ title.className = 'auth-form-title';
1821
+ title.textContent = 'Welcome back.';
1822
+ const subtitle = document.createElement('p');
1823
+ subtitle.className = 'auth-form-subtitle';
1824
+ subtitle.textContent = "Don't have an account? ";
1825
+ const signupLink = document.createElement('a');
1826
+ signupLink.href = '#';
1827
+ signupLink.id = 'showSignup';
1828
+ signupLink.className = 'link-primary';
1829
+ signupLink.textContent = 'Sign up';
1830
+ subtitle.appendChild(signupLink);
1831
+ header.appendChild(title);
1832
+ header.appendChild(subtitle);
1833
+ container.appendChild(header);
1834
+ // Form
1835
+ const form = document.createElement('form');
1836
+ form.id = 'loginFormElement';
1837
+ form.className = 'auth-form';
1838
+ // Email field
1839
+ const emailGroup = this.createFormGroup('loginEmail', 'Email', 'email', 'Email');
1840
+ form.appendChild(emailGroup);
1841
+ // Password field with forgot link
1842
+ const passwordGroup = document.createElement('div');
1843
+ passwordGroup.className = 'form-group';
1844
+ const groupHeader = document.createElement('div');
1845
+ groupHeader.className = 'form-group-header';
1846
+ const passwordLabel = document.createElement('label');
1847
+ passwordLabel.htmlFor = 'loginPassword';
1848
+ passwordLabel.className = 'form-label';
1849
+ passwordLabel.textContent = 'Password';
1850
+ const forgotLink = document.createElement('a');
1851
+ forgotLink.href = '#';
1852
+ forgotLink.id = 'forgotPasswordLink';
1853
+ forgotLink.className = 'forgot-password-link';
1854
+ forgotLink.textContent = 'Forgot Password?';
1855
+ groupHeader.appendChild(passwordLabel);
1856
+ groupHeader.appendChild(forgotLink);
1857
+ passwordGroup.appendChild(groupHeader);
1858
+ const passwordInputWrapper = this.createPasswordInput('loginPassword', 'Password');
1859
+ passwordGroup.appendChild(passwordInputWrapper);
1860
+ form.appendChild(passwordGroup);
1861
+ // Submit button
1862
+ const submitBtn = document.createElement('button');
1863
+ submitBtn.type = 'submit';
1864
+ submitBtn.id = 'loginButton';
1865
+ submitBtn.className = 'btn-auth-primary';
1866
+ const btnText = document.createElement('span');
1867
+ btnText.className = 'btn-text';
1868
+ btnText.textContent = 'Sign In';
1869
+ const btnLoader = document.createElement('span');
1870
+ btnLoader.className = 'btn-loader hidden';
1871
+ btnLoader.innerHTML = '<svg class="spinner" viewBox="0 0 24 24"><circle class="spinner-track" cx="12" cy="12" r="10"></circle><circle class="spinner-circle" cx="12" cy="12" r="10"></circle></svg>';
1872
+ submitBtn.appendChild(btnText);
1873
+ submitBtn.appendChild(btnLoader);
1874
+ form.appendChild(submitBtn);
1875
+ // Divider
1876
+ const divider = document.createElement('div');
1877
+ divider.className = 'divider';
1878
+ divider.textContent = 'OR SIGN IN WITH';
1879
+ form.appendChild(divider);
1880
+ // Social buttons grid (Google + GitHub)
1881
+ const socialGrid = document.createElement('div');
1882
+ socialGrid.className = 'social-buttons-grid';
1883
+ // Google button
1884
+ const googleBtn = this.createSocialButton('google', 'Google', 'login');
1885
+ socialGrid.appendChild(googleBtn);
1886
+ // GitHub button
1887
+ const githubBtn = this.createSocialButton('github', 'Github', 'login');
1888
+ socialGrid.appendChild(githubBtn);
1889
+ form.appendChild(socialGrid);
1890
+ // Discord button (full width)
1891
+ const discordBtn = this.createSocialButton('discord', 'Discord', 'login', true);
1892
+ form.appendChild(discordBtn);
1893
+ container.appendChild(form);
1894
+ return container;
1895
+ }
1896
+ createSignupForm() {
1897
+ const container = document.createElement('div');
1898
+ container.id = 'signupForm';
1899
+ container.className = 'auth-form-view hidden';
1900
+ // Header
1901
+ const header = document.createElement('div');
1902
+ header.className = 'auth-form-header';
1903
+ const title = document.createElement('h2');
1904
+ title.className = 'auth-form-title';
1905
+ title.textContent = 'Create an account.';
1906
+ const subtitle = document.createElement('p');
1907
+ subtitle.className = 'auth-form-subtitle';
1908
+ subtitle.textContent = 'Already have an account? ';
1909
+ const loginLink = document.createElement('a');
1910
+ loginLink.href = '#';
1911
+ loginLink.id = 'showLogin';
1912
+ loginLink.className = 'link-primary';
1913
+ loginLink.textContent = 'Sign in';
1914
+ subtitle.appendChild(loginLink);
1915
+ header.appendChild(title);
1916
+ header.appendChild(subtitle);
1917
+ container.appendChild(header);
1918
+ // Form
1919
+ const form = document.createElement('form');
1920
+ form.id = 'signupFormElement';
1921
+ form.className = 'auth-form';
1922
+ // Email field
1923
+ const emailGroup = this.createFormGroup('signupEmail', 'Email', 'email', 'Email');
1924
+ form.appendChild(emailGroup);
1925
+ // Password field
1926
+ const passwordGroup = document.createElement('div');
1927
+ passwordGroup.className = 'form-group';
1928
+ const passwordLabel = document.createElement('label');
1929
+ passwordLabel.htmlFor = 'signupPassword';
1930
+ passwordLabel.className = 'form-label';
1931
+ passwordLabel.textContent = 'Password';
1932
+ passwordGroup.appendChild(passwordLabel);
1933
+ const passwordInputWrapper = this.createPasswordInput('signupPassword', 'Password');
1934
+ passwordGroup.appendChild(passwordInputWrapper);
1935
+ const strengthText = document.createElement('p');
1936
+ strengthText.className = 'strength-text';
1937
+ strengthText.textContent = 'Use 8+ characters with mix of letters, numbers & symbols';
1938
+ passwordGroup.appendChild(strengthText);
1939
+ form.appendChild(passwordGroup);
1940
+ // Confirm password field
1941
+ const confirmGroup = document.createElement('div');
1942
+ confirmGroup.className = 'form-group';
1943
+ const confirmLabel = document.createElement('label');
1944
+ confirmLabel.htmlFor = 'signupPasswordConfirm';
1945
+ confirmLabel.className = 'form-label';
1946
+ confirmLabel.textContent = 'Confirm Password';
1947
+ confirmGroup.appendChild(confirmLabel);
1948
+ const confirmInputWrapper = this.createPasswordInput('signupPasswordConfirm', 'Confirm Password');
1949
+ confirmGroup.appendChild(confirmInputWrapper);
1950
+ form.appendChild(confirmGroup);
1951
+ // Submit button
1952
+ const submitBtn = document.createElement('button');
1953
+ submitBtn.type = 'submit';
1954
+ submitBtn.id = 'signupButton';
1955
+ submitBtn.className = 'btn-auth-primary';
1956
+ const btnText = document.createElement('span');
1957
+ btnText.className = 'btn-text';
1958
+ btnText.textContent = 'Sign Up';
1959
+ const btnLoader = document.createElement('span');
1960
+ btnLoader.className = 'btn-loader hidden';
1961
+ btnLoader.innerHTML = '<svg class="spinner" viewBox="0 0 24 24"><circle class="spinner-track" cx="12" cy="12" r="10"></circle><circle class="spinner-circle" cx="12" cy="12" r="10"></circle></svg>';
1962
+ submitBtn.appendChild(btnText);
1963
+ submitBtn.appendChild(btnLoader);
1964
+ form.appendChild(submitBtn);
1965
+ // Divider
1966
+ const divider = document.createElement('div');
1967
+ divider.className = 'divider';
1968
+ divider.textContent = 'OR SIGN UP WITH';
1969
+ form.appendChild(divider);
1970
+ // Social buttons grid (Google + GitHub)
1971
+ const socialGrid = document.createElement('div');
1972
+ socialGrid.className = 'social-buttons-grid';
1973
+ // Google button
1974
+ const googleBtn = this.createSocialButton('google', 'Google', 'signup');
1975
+ socialGrid.appendChild(googleBtn);
1976
+ // GitHub button
1977
+ const githubBtn = this.createSocialButton('github', 'Github', 'signup');
1978
+ socialGrid.appendChild(githubBtn);
1979
+ form.appendChild(socialGrid);
1980
+ // Discord button (full width)
1981
+ const discordBtn = this.createSocialButton('discord', 'Discord', 'signup', true);
1982
+ form.appendChild(discordBtn);
1983
+ container.appendChild(form);
1984
+ return container;
1985
+ }
1986
+ createForgotPasswordForm() {
1987
+ const container = document.createElement('div');
1988
+ container.id = 'forgotPasswordForm';
1989
+ container.className = 'auth-form-view hidden';
1990
+ // Icon
1991
+ const icon = document.createElement('div');
1992
+ icon.className = 'forgot-password-icon';
1993
+ icon.innerHTML = '<div class="icon-glow"></div><svg class="icon-lock" width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path class="lock-shackle" d="M7 11V7a5 5 0 0 1 10 0v4"/><circle class="lock-keyhole" cx="12" cy="16" r="1.5"/></svg>';
1994
+ container.appendChild(icon);
1995
+ // Header
1996
+ const header = document.createElement('div');
1997
+ header.className = 'auth-form-header';
1998
+ const title = document.createElement('h2');
1999
+ title.className = 'auth-form-title';
2000
+ title.textContent = 'Reset Password';
2001
+ const subtitle = document.createElement('p');
2002
+ subtitle.className = 'auth-form-subtitle';
2003
+ subtitle.textContent = "We'll send a magic link to your inbox";
2004
+ header.appendChild(title);
2005
+ header.appendChild(subtitle);
2006
+ container.appendChild(header);
2007
+ // Form
2008
+ const form = document.createElement('form');
2009
+ form.id = 'forgotPasswordFormElement';
2010
+ form.className = 'auth-form';
2011
+ const emailGroup = this.createFormGroup('resetEmail', 'Email Address', 'email', 'Enter your email');
2012
+ form.appendChild(emailGroup);
2013
+ const submitBtn = document.createElement('button');
2014
+ submitBtn.type = 'submit';
2015
+ submitBtn.id = 'resetButton';
2016
+ submitBtn.className = 'btn-auth-primary btn-forgot-password';
2017
+ submitBtn.innerHTML = '<span class="btn-text">Send Reset Link</span><svg class="btn-arrow" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>';
2018
+ form.appendChild(submitBtn);
2019
+ container.appendChild(form);
2020
+ // Footer
2021
+ const footer = document.createElement('div');
2022
+ footer.className = 'auth-footer forgot-footer';
2023
+ const backLink = document.createElement('a');
2024
+ backLink.href = '#';
2025
+ backLink.id = 'backToLogin';
2026
+ backLink.className = 'back-to-login';
2027
+ backLink.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg><span>Back to Sign In</span>';
2028
+ footer.appendChild(backLink);
2029
+ container.appendChild(footer);
2030
+ return container;
2031
+ }
2032
+ createSuccessMessage() {
2033
+ const container = document.createElement('div');
2034
+ container.id = 'authMessage';
2035
+ container.className = 'auth-form-view hidden';
2036
+ const content = document.createElement('div');
2037
+ content.className = 'auth-message-content';
2038
+ const icon = document.createElement('div');
2039
+ icon.className = 'message-icon';
2040
+ icon.innerHTML = '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>';
2041
+ content.appendChild(icon);
2042
+ const title = document.createElement('h3');
2043
+ title.id = 'messageTitle';
2044
+ title.className = 'message-title';
2045
+ title.textContent = 'Success!';
2046
+ content.appendChild(title);
2047
+ const text = document.createElement('p');
2048
+ text.id = 'messageText';
2049
+ text.className = 'message-text';
2050
+ text.textContent = 'Operation completed successfully.';
2051
+ content.appendChild(text);
2052
+ const button = document.createElement('button');
2053
+ button.id = 'messageButton';
2054
+ button.className = 'btn-auth-primary';
2055
+ button.textContent = 'Continue';
2056
+ content.appendChild(button);
2057
+ container.appendChild(content);
2058
+ return container;
2059
+ }
2060
+ createFormGroup(id, label, type, placeholder) {
2061
+ const group = document.createElement('div');
2062
+ group.className = 'form-group';
2063
+ const labelEl = document.createElement('label');
2064
+ labelEl.htmlFor = id;
2065
+ labelEl.className = 'form-label';
2066
+ labelEl.textContent = label;
2067
+ group.appendChild(labelEl);
2068
+ const input = document.createElement('input');
2069
+ input.type = type;
2070
+ input.id = id;
2071
+ input.className = 'form-input';
2072
+ input.placeholder = placeholder;
2073
+ input.required = true;
2074
+ group.appendChild(input);
2075
+ return group;
2076
+ }
2077
+ createPasswordInput(id, placeholder) {
2078
+ const wrapper = document.createElement('div');
2079
+ wrapper.className = 'input-with-icon';
2080
+ const input = document.createElement('input');
2081
+ input.type = 'password';
2082
+ input.id = id;
2083
+ input.className = 'form-input';
2084
+ input.placeholder = placeholder;
2085
+ input.required = true;
2086
+ wrapper.appendChild(input);
2087
+ const toggleBtn = document.createElement('button');
2088
+ toggleBtn.type = 'button';
2089
+ toggleBtn.className = 'input-icon-btn toggle-password';
2090
+ toggleBtn.dataset.target = id;
2091
+ toggleBtn.innerHTML = '<svg class="icon-eye" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg><svg class="icon-eye-off hidden" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>';
2092
+ wrapper.appendChild(toggleBtn);
2093
+ return wrapper;
2094
+ }
2095
+ createSocialButton(provider, label, mode, fullWidth = false) {
2096
+ const button = document.createElement('button');
2097
+ button.type = 'button';
2098
+ button.className = fullWidth ? 'btn-social btn-social-full' : 'btn-social';
2099
+ button.id = `${provider}${mode === 'login' ? 'SignIn' : 'SignUp'}Modal`;
2100
+ // SVG icons for each provider
2101
+ const icons = {
2102
+ google: `<svg width="20" height="20" viewBox="0 0 24 24">
2103
+ <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
2104
+ <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
2105
+ <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
2106
+ <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
2107
+ </svg>`,
2108
+ github: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
2109
+ <path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.463-1.11-1.463-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.831.092-.646.35-1.086.636-1.336-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0 1 12 6.836c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z"/>
2110
+ </svg>`,
2111
+ discord: `<svg width="20" height="20" viewBox="0 0 24 24" fill="#5865F2">
2112
+ <path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"/>
2113
+ </svg>`
2114
+ };
2115
+ button.innerHTML = icons[provider];
2116
+ const span = document.createElement('span');
2117
+ span.textContent = label;
2118
+ button.appendChild(span);
2119
+ return button;
2120
+ }
2121
+ bindEventListeners() {
2122
+ if (!this.modal)
2123
+ return;
2124
+ // Close button
2125
+ const closeBtn = this.modal.querySelector('.auth-modal-close');
2126
+ closeBtn?.addEventListener('click', () => this.hide());
2127
+ // Backdrop click to close
2128
+ const backdrop = this.modal.querySelector('.auth-modal-backdrop');
2129
+ backdrop?.addEventListener('click', () => this.hide());
2130
+ // View switching
2131
+ const showSignup = this.modal.querySelector('#showSignup');
2132
+ showSignup?.addEventListener('click', (e) => {
2133
+ e.preventDefault();
2134
+ this.switchView('signup');
2135
+ });
2136
+ const showLogin = this.modal.querySelector('#showLogin');
2137
+ showLogin?.addEventListener('click', (e) => {
2138
+ e.preventDefault();
2139
+ this.switchView('login');
2140
+ });
2141
+ const forgotPasswordLink = this.modal.querySelector('#forgotPasswordLink');
2142
+ forgotPasswordLink?.addEventListener('click', (e) => {
2143
+ e.preventDefault();
2144
+ this.switchView('forgot');
2145
+ });
2146
+ const backToLogin = this.modal.querySelector('#backToLogin');
2147
+ backToLogin?.addEventListener('click', (e) => {
2148
+ e.preventDefault();
2149
+ this.switchView('login');
2150
+ });
2151
+ // Password toggle
2152
+ const passwordToggles = this.modal.querySelectorAll('.toggle-password');
2153
+ passwordToggles.forEach((toggle) => {
2154
+ toggle.addEventListener('click', (e) => {
2155
+ e.preventDefault();
2156
+ const target = toggle.dataset.target;
2157
+ if (target) {
2158
+ const input = this.modal.querySelector(`#${target}`);
2159
+ const iconEye = toggle.querySelector('.icon-eye');
2160
+ const iconEyeOff = toggle.querySelector('.icon-eye-off');
2161
+ if (input.type === 'password') {
2162
+ input.type = 'text';
2163
+ iconEye?.classList.add('hidden');
2164
+ iconEyeOff?.classList.remove('hidden');
2165
+ }
2166
+ else {
2167
+ input.type = 'password';
2168
+ iconEye?.classList.remove('hidden');
2169
+ iconEyeOff?.classList.add('hidden');
2170
+ }
2171
+ }
2172
+ });
2173
+ });
2174
+ // Form submissions
2175
+ const loginForm = this.modal.querySelector('#loginFormElement');
2176
+ loginForm?.addEventListener('submit', (e) => this.handleLogin(e));
2177
+ const signupForm = this.modal.querySelector('#signupFormElement');
2178
+ signupForm?.addEventListener('submit', (e) => this.handleSignup(e));
2179
+ const forgotForm = this.modal.querySelector('#forgotPasswordFormElement');
2180
+ forgotForm?.addEventListener('submit', (e) => this.handleForgotPassword(e));
2181
+ // OAuth social login buttons
2182
+ this.bindSocialLoginButtons();
2183
+ }
2184
+ /**
2185
+ * Bind click events to social login buttons
2186
+ */
2187
+ bindSocialLoginButtons() {
2188
+ if (!this.modal)
2189
+ return;
2190
+ // Google login buttons
2191
+ const googleSignInBtn = this.modal.querySelector('#googleSignInModal');
2192
+ googleSignInBtn?.addEventListener('click', (e) => {
2193
+ e.preventDefault();
2194
+ this.startOAuthFlow('google');
2195
+ });
2196
+ const googleSignUpBtn = this.modal.querySelector('#googleSignUpModal');
2197
+ googleSignUpBtn?.addEventListener('click', (e) => {
2198
+ e.preventDefault();
2199
+ this.startOAuthFlow('google');
2200
+ });
2201
+ // Discord login buttons
2202
+ const discordSignInBtn = this.modal.querySelector('#discordSignInModal');
2203
+ discordSignInBtn?.addEventListener('click', (e) => {
2204
+ e.preventDefault();
2205
+ this.startOAuthFlow('discord');
2206
+ });
2207
+ const discordSignUpBtn = this.modal.querySelector('#discordSignUpModal');
2208
+ discordSignUpBtn?.addEventListener('click', (e) => {
2209
+ e.preventDefault();
2210
+ this.startOAuthFlow('discord');
2211
+ });
2212
+ // GitHub login buttons
2213
+ const githubSignInBtn = this.modal.querySelector('#githubSignInModal');
2214
+ githubSignInBtn?.addEventListener('click', (e) => {
2215
+ e.preventDefault();
2216
+ this.startOAuthFlow('github');
2217
+ });
2218
+ const githubSignUpBtn = this.modal.querySelector('#githubSignUpModal');
2219
+ githubSignUpBtn?.addEventListener('click', (e) => {
2220
+ e.preventDefault();
2221
+ this.startOAuthFlow('github');
2222
+ });
2223
+ }
2224
+ switchView(view) {
2225
+ if (!this.modal)
2226
+ return;
2227
+ const views = ['loginForm', 'signupForm', 'forgotPasswordForm', 'authMessage'];
2228
+ views.forEach((viewId) => {
2229
+ const element = this.modal.querySelector(`#${viewId}`);
2230
+ element?.classList.add('hidden');
2231
+ });
2232
+ const viewMap = {
2233
+ login: 'loginForm',
2234
+ signup: 'signupForm',
2235
+ forgot: 'forgotPasswordForm',
2236
+ message: 'authMessage',
2237
+ };
2238
+ const targetView = this.modal.querySelector(`#${viewMap[view]}`);
2239
+ targetView?.classList.remove('hidden');
2240
+ this.currentView = view;
2241
+ }
2242
+ async handleLogin(e) {
2243
+ e.preventDefault();
2244
+ const emailInput = this.modal?.querySelector('#loginEmail');
2245
+ const passwordInput = this.modal?.querySelector('#loginPassword');
2246
+ const submitBtn = this.modal?.querySelector('#loginButton');
2247
+ const btnText = submitBtn?.querySelector('.btn-text');
2248
+ const btnLoader = submitBtn?.querySelector('.btn-loader');
2249
+ if (!emailInput || !passwordInput || !submitBtn)
2250
+ return;
2251
+ const email = emailInput.value;
2252
+ const password = passwordInput.value;
2253
+ try {
2254
+ // Show loading state
2255
+ submitBtn.disabled = true;
2256
+ btnText?.classList.add('hidden');
2257
+ btnLoader?.classList.remove('hidden');
2258
+ // Call login API using new typed method
2259
+ const response = await this.client.login({
2260
+ email,
2261
+ password,
2262
+ });
2263
+ // Handle success
2264
+ if (response.token) {
2265
+ if (this.options.onLoginSuccess) {
2266
+ this.options.onLoginSuccess(response.token, response.user);
2267
+ }
2268
+ this.showMessage('Login Successful', 'Welcome back!');
2269
+ }
2270
+ else {
2271
+ throw new Error('Invalid response from server');
2272
+ }
2273
+ }
2274
+ catch (error) {
2275
+ // Handle error
2276
+ const errorMessage = error instanceof Error ? error.message : 'Login failed';
2277
+ this.showError(errorMessage);
2278
+ if (this.options.onError) {
2279
+ this.options.onError(error);
2280
+ }
2281
+ }
2282
+ finally {
2283
+ // Reset loading state
2284
+ submitBtn.disabled = false;
2285
+ btnText?.classList.remove('hidden');
2286
+ btnLoader?.classList.add('hidden');
2287
+ }
2288
+ }
2289
+ async handleSignup(e) {
2290
+ e.preventDefault();
2291
+ const emailInput = this.modal?.querySelector('#signupEmail');
2292
+ const passwordInput = this.modal?.querySelector('#signupPassword');
2293
+ const passwordConfirmInput = this.modal?.querySelector('#signupPasswordConfirm');
2294
+ const submitBtn = this.modal?.querySelector('#signupButton');
2295
+ const btnText = submitBtn?.querySelector('.btn-text');
2296
+ const btnLoader = submitBtn?.querySelector('.btn-loader');
2297
+ if (!emailInput || !passwordInput || !passwordConfirmInput || !submitBtn)
2298
+ return;
2299
+ const email = emailInput.value;
2300
+ const password = passwordInput.value;
2301
+ const passwordConfirm = passwordConfirmInput.value;
2302
+ // Validate passwords match
2303
+ if (password !== passwordConfirm) {
2304
+ this.showError('Passwords do not match');
2305
+ return;
2306
+ }
2307
+ try {
2308
+ // Show loading state
2309
+ submitBtn.disabled = true;
2310
+ btnText?.classList.add('hidden');
2311
+ btnLoader?.classList.remove('hidden');
2312
+ // Call signup API using new typed method
2313
+ const response = await this.client.register({
2314
+ email,
2315
+ password,
2316
+ });
2317
+ // Handle success - Note: register returns different response format
2318
+ if (response.success) {
2319
+ // After successful registration, perform login to get token
2320
+ const loginResponse = await this.client.login({ email, password });
2321
+ if (loginResponse.token) {
2322
+ if (this.options.onSignupSuccess) {
2323
+ this.options.onSignupSuccess(loginResponse.token, loginResponse.user);
2324
+ }
2325
+ this.showMessage('Account Created', response.message || 'Your account has been created successfully!');
2326
+ }
2327
+ }
2328
+ else {
2329
+ throw new Error('Registration failed');
2330
+ }
2331
+ }
2332
+ catch (error) {
2333
+ // Handle error
2334
+ const errorMessage = error instanceof Error ? error.message : 'Signup failed';
2335
+ this.showError(errorMessage);
2336
+ if (this.options.onError) {
2337
+ this.options.onError(error);
2338
+ }
2339
+ }
2340
+ finally {
2341
+ // Reset loading state
2342
+ submitBtn.disabled = false;
2343
+ btnText?.classList.remove('hidden');
2344
+ btnLoader?.classList.add('hidden');
2345
+ }
2346
+ }
2347
+ async handleForgotPassword(e) {
2348
+ e.preventDefault();
2349
+ const emailInput = this.modal?.querySelector('#resetEmail');
2350
+ const submitBtn = this.modal?.querySelector('#resetButton');
2351
+ if (!emailInput || !submitBtn)
2352
+ return;
2353
+ const email = emailInput.value;
2354
+ try {
2355
+ // Show loading state
2356
+ submitBtn.disabled = true;
2357
+ // TODO: Call forgot password API when available
2358
+ // await this.client.postapiauthforgotpassword({ email });
2359
+ // Show success message
2360
+ this.showMessage('Reset Link Sent', `We've sent a password reset link to ${email}`);
2361
+ }
2362
+ catch (error) {
2363
+ // Handle error
2364
+ const errorMessage = error instanceof Error ? error.message : 'Failed to send reset link';
2365
+ this.showError(errorMessage);
2366
+ if (this.options.onError) {
2367
+ this.options.onError(error);
2368
+ }
2369
+ }
2370
+ finally {
2371
+ // Reset loading state
2372
+ submitBtn.disabled = false;
2373
+ }
2374
+ }
2375
+ showMessage(title, message) {
2376
+ if (!this.modal)
2377
+ return;
2378
+ const messageTitle = this.modal.querySelector('#messageTitle');
2379
+ const messageText = this.modal.querySelector('#messageText');
2380
+ const messageButton = this.modal.querySelector('#messageButton');
2381
+ if (messageTitle)
2382
+ messageTitle.textContent = title;
2383
+ if (messageText)
2384
+ messageText.textContent = message;
2385
+ messageButton?.addEventListener('click', () => {
2386
+ this.hide();
2387
+ }, { once: true });
2388
+ this.switchView('message');
2389
+ }
2390
+ showError(message) {
2391
+ // Simple error display - you can enhance this
2392
+ alert(message);
2393
+ }
2394
+ // ============================================================================
2395
+ // OAuth Methods
2396
+ // ============================================================================
2397
+ /**
2398
+ * Start OAuth flow for a given provider
2399
+ */
2400
+ startOAuthFlow(provider) {
2401
+ const config = this.options.oauthConfig?.[provider];
2402
+ if (!config) {
2403
+ this.showError(`${provider} OAuth is not configured`);
2404
+ return;
2405
+ }
2406
+ // Store the current state (to verify callback)
2407
+ const state = this.generateRandomState();
2408
+ sessionStorage.setItem('oauth_state', state);
2409
+ sessionStorage.setItem('oauth_provider', provider);
2410
+ // Build authorization URL
2411
+ const authUrl = this.buildAuthUrl(provider, config, state);
2412
+ // Redirect to OAuth provider
2413
+ window.location.href = authUrl;
2414
+ }
2415
+ /**
2416
+ * Generate random state for CSRF protection
2417
+ */
2418
+ generateRandomState() {
2419
+ const array = new Uint8Array(32);
2420
+ crypto.getRandomValues(array);
2421
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
2422
+ }
2423
+ /**
2424
+ * Build authorization URL for each provider
2425
+ */
2426
+ buildAuthUrl(provider, config, state) {
2427
+ const params = new URLSearchParams({
2428
+ client_id: config.clientId,
2429
+ redirect_uri: config.redirectUri,
2430
+ state,
2431
+ response_type: 'code',
2432
+ });
2433
+ // Provider-specific configurations
2434
+ switch (provider) {
2435
+ case 'google':
2436
+ params.append('scope', config.scope || 'openid email profile');
2437
+ params.append('access_type', 'offline');
2438
+ params.append('prompt', 'consent');
2439
+ return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
2440
+ case 'discord':
2441
+ params.append('scope', config.scope || 'identify email');
2442
+ return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
2443
+ case 'github':
2444
+ params.append('scope', config.scope || 'read:user user:email');
2445
+ return `https://github.com/login/oauth/authorize?${params.toString()}`;
2446
+ default:
2447
+ throw new Error(`Unknown provider: ${provider}`);
2448
+ }
2449
+ }
2450
+ /**
2451
+ * Handle OAuth callback
2452
+ * Call this method from your redirect page with the code and state from URL
2453
+ */
2454
+ async handleOAuthCallback(code, state) {
2455
+ try {
2456
+ // Verify state
2457
+ const storedState = sessionStorage.getItem('oauth_state');
2458
+ const provider = sessionStorage.getItem('oauth_provider');
2459
+ if (!storedState || storedState !== state) {
2460
+ throw new Error('Invalid state parameter - possible CSRF attack');
2461
+ }
2462
+ if (!provider) {
2463
+ throw new Error('No provider stored in session');
2464
+ }
2465
+ // Clear stored state
2466
+ sessionStorage.removeItem('oauth_state');
2467
+ sessionStorage.removeItem('oauth_provider');
2468
+ // Exchange code for token using the appropriate SDK method
2469
+ let response;
2470
+ switch (provider) {
2471
+ case 'google':
2472
+ response = await this.client.googleCodeToToken({ code });
2473
+ break;
2474
+ case 'discord':
2475
+ response = await this.client.discordCodeToToken({ code });
2476
+ break;
2477
+ case 'github':
2478
+ response = await this.client.githubCodeToToken({ code });
2479
+ break;
2480
+ default:
2481
+ throw new Error(`Unknown provider: ${provider}`);
2482
+ }
2483
+ // Handle success
2484
+ if (response.token) {
2485
+ if (this.options.onLoginSuccess) {
2486
+ this.options.onLoginSuccess(response.token, response.user);
2487
+ }
2488
+ return {
2489
+ success: true,
2490
+ token: response.token,
2491
+ user: response.user,
2492
+ };
2493
+ }
2494
+ else {
2495
+ throw new Error('No token received from server');
2496
+ }
2497
+ }
2498
+ catch (error) {
2499
+ const err = error instanceof Error ? error : new Error('OAuth authentication failed');
2500
+ if (this.options.onError) {
2501
+ this.options.onError(err);
2502
+ }
2503
+ return {
2504
+ success: false,
2505
+ error: err,
2506
+ };
2507
+ }
2508
+ }
2509
+ /**
2510
+ * Check if current page is an OAuth callback and handle it automatically
2511
+ */
2512
+ static async handleOAuthCallbackFromUrl(client, options) {
2513
+ const urlParams = new URLSearchParams(window.location.search);
2514
+ const code = urlParams.get('code');
2515
+ const state = urlParams.get('state');
2516
+ if (!code || !state) {
2517
+ return null; // Not an OAuth callback
2518
+ }
2519
+ const modal = new AuthModal({
2520
+ client,
2521
+ ...options,
2522
+ });
2523
+ return await modal.handleOAuthCallback(code, state);
2524
+ }
2525
+ }
2526
+ /**
2527
+ * Create and show auth modal
2528
+ */
2529
+ function createAuthModal(options) {
2530
+ return new AuthModal(options);
2531
+ }
2532
+
2533
+ export { AuthFactory, AuthModal, AuthProvider, BuiltInHooks, ENVIRONMENT_CONFIGS, SeaVerseBackendAPIClient, createAuthModal, detectEnvironment, getEnvironmentConfig, models };
2534
+ //# sourceMappingURL=index.js.map