@seaverse/auth-sdk 0.4.4 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,30 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var axios = require('axios');
4
- var fs = require('fs/promises');
5
- var path = require('path');
6
- var os = require('os');
7
-
8
- function _interopNamespaceDefault(e) {
9
- var n = Object.create(null);
10
- if (e) {
11
- Object.keys(e).forEach(function (k) {
12
- if (k !== 'default') {
13
- var d = Object.getOwnPropertyDescriptor(e, k);
14
- Object.defineProperty(n, k, d.get ? d : {
15
- enumerable: true,
16
- get: function () { return e[k]; }
17
- });
18
- }
19
- });
20
- }
21
- n.default = e;
22
- return Object.freeze(n);
23
- }
24
-
25
- var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
26
- var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
27
- var os__namespace = /*#__PURE__*/_interopNamespaceDefault(os);
28
4
 
29
5
  /**
30
6
  * 生成UUID的跨平台函数
@@ -771,113 +747,6 @@ class CustomAuthProvider extends AuthProvider {
771
747
  }
772
748
  }
773
749
 
774
- /**
775
- * 配置管理器
776
- */
777
- class ConfigManager {
778
- constructor(configDir) {
779
- this.configDir = configDir || path__namespace.join(os__namespace.homedir(), '.openapi-sdk');
780
- this.configPath = path__namespace.join(this.configDir, 'config.json');
781
- }
782
- /**
783
- * 保存认证配置
784
- */
785
- async saveAuth(profileName, authConfig) {
786
- await this.ensureConfigDir();
787
- const config = await this.loadConfig();
788
- config.profiles[profileName] = authConfig;
789
- await fs__namespace.writeFile(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
790
- }
791
- /**
792
- * 加载认证配置
793
- */
794
- async loadAuth(profileName) {
795
- const config = await this.loadConfig();
796
- const authConfig = config.profiles[profileName];
797
- if (!authConfig) {
798
- throw new Error(`Profile '${profileName}' not found`);
799
- }
800
- return authConfig;
801
- }
802
- /**
803
- * 列出所有profile
804
- */
805
- async listProfiles() {
806
- const config = await this.loadConfig();
807
- return Object.keys(config.profiles);
808
- }
809
- /**
810
- * 删除profile
811
- */
812
- async removeProfile(profileName) {
813
- const config = await this.loadConfig();
814
- delete config.profiles[profileName];
815
- await fs__namespace.writeFile(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
816
- }
817
- /**
818
- * 从文件加载配置
819
- */
820
- async loadFromFile(filePath) {
821
- try {
822
- const content = await fs__namespace.readFile(filePath, 'utf-8');
823
- return JSON.parse(content);
824
- }
825
- catch (error) {
826
- throw new Error(`Failed to load config from ${filePath}: ${error}`);
827
- }
828
- }
829
- /**
830
- * 保存配置到文件
831
- */
832
- async saveToFile(filePath, authConfig) {
833
- try {
834
- await fs__namespace.writeFile(filePath, JSON.stringify(authConfig, null, 2), 'utf-8');
835
- }
836
- catch (error) {
837
- throw new Error(`Failed to save config to ${filePath}: ${error}`);
838
- }
839
- }
840
- /**
841
- * 加载完整配置文件
842
- */
843
- async loadConfig() {
844
- try {
845
- const content = await fs__namespace.readFile(this.configPath, 'utf-8');
846
- return JSON.parse(content);
847
- }
848
- catch (error) {
849
- if (error.code === 'ENOENT') {
850
- // 文件不存在,返回默认配置
851
- return { profiles: {} };
852
- }
853
- throw error;
854
- }
855
- }
856
- /**
857
- * 确保配置目录存在
858
- */
859
- async ensureConfigDir() {
860
- try {
861
- await fs__namespace.mkdir(this.configDir, { recursive: true });
862
- }
863
- catch (error) {
864
- // 忽略错误,可能目录已存在
865
- }
866
- }
867
- /**
868
- * 获取配置目录路径
869
- */
870
- getConfigDir() {
871
- return this.configDir;
872
- }
873
- /**
874
- * 获取配置文件路径
875
- */
876
- getConfigPath() {
877
- return this.configPath;
878
- }
879
- }
880
-
881
750
  /**
882
751
  * 跨平台环境配置工具
883
752
  * 支持 Node.js 和浏览器环境
@@ -1010,19 +879,19 @@ class AuthFactory {
1010
879
  }
1011
880
  /**
1012
881
  * 从配置文件创建认证提供者
882
+ * @deprecated This method requires Node.js fs module and should not be used in browser environments.
883
+ * Use AuthFactory.create() with a config object instead.
1013
884
  */
1014
- static async fromFile(path) {
1015
- const configManager = new ConfigManager();
1016
- const config = await configManager.loadFromFile(path);
1017
- return this.create(config);
885
+ static async fromFile(_path) {
886
+ throw new Error('AuthFactory.fromFile() is not available in browser environments. Please use AuthFactory.create() instead.');
1018
887
  }
1019
888
  /**
1020
889
  * 从profile创建认证提供者
890
+ * @deprecated This method requires Node.js fs module and should not be used in browser environments.
891
+ * Use AuthFactory.create() with a config object instead.
1021
892
  */
1022
- static async fromProfile(profileName = 'default') {
1023
- const configManager = new ConfigManager();
1024
- const config = await configManager.loadAuth(profileName);
1025
- return this.create(config);
893
+ static async fromProfile(_profileName = 'default') {
894
+ throw new Error('AuthFactory.fromProfile() is not available in browser environments. Please use AuthFactory.create() instead.');
1026
895
  }
1027
896
  }
1028
897
 
@@ -4455,11 +4324,373 @@ function createAuthModal(options) {
4455
4324
  return new AuthModal(options);
4456
4325
  }
4457
4326
 
4327
+ /**
4328
+ * Google One-Tap Sign-In SDK
4329
+ *
4330
+ * 用于在用户未登录 SeaVerse 但已登录 Google 的情况下,
4331
+ * 自动显示 Google One-Tap 快速登录界面
4332
+ */
4333
+ /**
4334
+ * Google One-Tap 快速登录
4335
+ *
4336
+ * 用法示例:
4337
+ * ```typescript
4338
+ * const googleLogin = new GoogleOneTap({
4339
+ * clientId: 'YOUR_GOOGLE_CLIENT_ID',
4340
+ * isLoggedIn: () => !!localStorage.getItem('token'),
4341
+ * onCredentialReceived: async (credential) => {
4342
+ * // 调用后端验证
4343
+ * const res = await fetch('/api/auth/google', {
4344
+ * method: 'POST',
4345
+ * body: JSON.stringify({ credential })
4346
+ * });
4347
+ * const data = await res.json();
4348
+ * localStorage.setItem('token', data.token);
4349
+ * window.location.reload();
4350
+ * }
4351
+ * });
4352
+ * ```
4353
+ */
4354
+ class GoogleOneTap {
4355
+ constructor(options) {
4356
+ this.isInitialized = false;
4357
+ this.scriptLoaded = false;
4358
+ this.options = options;
4359
+ this.log('GoogleOneTap initialized with options:', options);
4360
+ // 立即检查登录状态并初始化
4361
+ this.checkAndInit();
4362
+ }
4363
+ /**
4364
+ * 检查登录状态并初始化
4365
+ */
4366
+ checkAndInit() {
4367
+ // 检查用户是否已登录 SeaVerse
4368
+ if (this.options.isLoggedIn()) {
4369
+ this.log('User already logged in, skipping Google One-Tap initialization');
4370
+ return;
4371
+ }
4372
+ this.log('User not logged in, initializing Google One-Tap');
4373
+ // 未登录,加载并初始化 Google One-Tap
4374
+ this.loadGoogleScript()
4375
+ .then(() => {
4376
+ this.init();
4377
+ })
4378
+ .catch((error) => {
4379
+ this.handleError(new Error(`Failed to load Google One-Tap script: ${error.message}`));
4380
+ });
4381
+ }
4382
+ /**
4383
+ * 初始化 Google One-Tap API
4384
+ */
4385
+ init() {
4386
+ if (!window.google?.accounts?.id) {
4387
+ this.handleError(new Error('Google One-Tap API not available'));
4388
+ return;
4389
+ }
4390
+ try {
4391
+ // 初始化 Google One-Tap
4392
+ window.google.accounts.id.initialize({
4393
+ client_id: this.options.clientId,
4394
+ callback: (response) => {
4395
+ this.handleCredentialResponse(response);
4396
+ },
4397
+ auto_select: false,
4398
+ cancel_on_tap_outside: this.options.cancelOnTapOutside ?? true,
4399
+ context: this.options.context || 'signin',
4400
+ use_fedcm_for_prompt: this.options.useFedcmForPrompt ?? false
4401
+ });
4402
+ this.isInitialized = true;
4403
+ this.log('Google One-Tap initialized successfully');
4404
+ // 如果启用自动提示,显示 One-Tap
4405
+ if (this.options.autoPrompt !== false) {
4406
+ this.prompt();
4407
+ }
4408
+ }
4409
+ catch (error) {
4410
+ this.handleError(new Error(`Failed to initialize Google One-Tap: ${error.message}`));
4411
+ }
4412
+ }
4413
+ /**
4414
+ * 处理 Google 返回的 credential
4415
+ */
4416
+ async handleCredentialResponse(response) {
4417
+ this.log('Received credential from Google', {
4418
+ select_by: response.select_by,
4419
+ credential_length: response.credential?.length
4420
+ });
4421
+ // 验证 credential 是否存在
4422
+ if (!response.credential) {
4423
+ const error = new Error('No credential received from Google');
4424
+ this.log('Error: No credential in response', response);
4425
+ this.handleError(error);
4426
+ return;
4427
+ }
4428
+ try {
4429
+ await this.options.onCredentialReceived(response.credential);
4430
+ this.log('Credential processed successfully');
4431
+ }
4432
+ catch (error) {
4433
+ this.log('Error processing credential:', error);
4434
+ this.handleError(error);
4435
+ }
4436
+ }
4437
+ /**
4438
+ * 显示 Google One-Tap 提示
4439
+ * 会先检查登录状态
4440
+ */
4441
+ prompt() {
4442
+ // 再次检查登录状态(可能在初始化后用户已登录)
4443
+ if (this.options.isLoggedIn()) {
4444
+ this.log('User already logged in, skipping prompt');
4445
+ return;
4446
+ }
4447
+ if (!this.isInitialized) {
4448
+ this.log('Google One-Tap not initialized yet, skipping prompt');
4449
+ return;
4450
+ }
4451
+ if (!window.google?.accounts?.id) {
4452
+ this.handleError(new Error('Google One-Tap API not available'));
4453
+ return;
4454
+ }
4455
+ try {
4456
+ this.log('Displaying Google One-Tap prompt');
4457
+ window.google.accounts.id.prompt((notification) => {
4458
+ if (notification.isNotDisplayed()) {
4459
+ const reason = notification.getNotDisplayedReason();
4460
+ this.log('One-Tap not displayed:', reason);
4461
+ }
4462
+ else if (notification.isSkippedMoment()) {
4463
+ const reason = notification.getSkippedReason();
4464
+ this.log('One-Tap skipped:', reason);
4465
+ }
4466
+ else if (notification.isDismissedMoment()) {
4467
+ const reason = notification.getDismissedReason();
4468
+ this.log('One-Tap dismissed:', reason);
4469
+ }
4470
+ else if (notification.isDisplayed()) {
4471
+ this.log('One-Tap displayed successfully');
4472
+ }
4473
+ });
4474
+ }
4475
+ catch (error) {
4476
+ this.handleError(new Error(`Failed to show Google One-Tap: ${error.message}`));
4477
+ }
4478
+ }
4479
+ /**
4480
+ * 取消 Google One-Tap 提示
4481
+ */
4482
+ cancel() {
4483
+ if (!this.isInitialized) {
4484
+ this.log('Google One-Tap not initialized, nothing to cancel');
4485
+ return;
4486
+ }
4487
+ if (!window.google?.accounts?.id) {
4488
+ return;
4489
+ }
4490
+ try {
4491
+ window.google.accounts.id.cancel();
4492
+ this.log('Google One-Tap canceled');
4493
+ }
4494
+ catch (error) {
4495
+ this.log('Error canceling Google One-Tap:', error);
4496
+ }
4497
+ }
4498
+ /**
4499
+ * 渲染 Google 登录按钮到指定元素
4500
+ *
4501
+ * @param element - 要渲染按钮的 HTML 元素
4502
+ *
4503
+ * @example
4504
+ * const container = document.getElementById('google-btn');
4505
+ * googleLogin.renderButton(container);
4506
+ */
4507
+ renderButton(element) {
4508
+ // 检查登录状态
4509
+ if (this.options.isLoggedIn()) {
4510
+ this.log('User already logged in, not rendering button');
4511
+ return;
4512
+ }
4513
+ if (!this.isInitialized) {
4514
+ this.log('Google One-Tap not initialized yet, waiting...');
4515
+ // 等待初始化完成后再渲染
4516
+ setTimeout(() => this.renderButton(element), 100);
4517
+ return;
4518
+ }
4519
+ if (!window.google?.accounts?.id) {
4520
+ this.handleError(new Error('Google One-Tap API not available'));
4521
+ return;
4522
+ }
4523
+ try {
4524
+ const buttonConfig = {
4525
+ type: this.options.button?.type || 'standard',
4526
+ theme: this.options.button?.theme || 'outline',
4527
+ size: this.options.button?.size || 'large',
4528
+ text: this.options.button?.text || 'signin_with',
4529
+ shape: this.options.button?.shape || 'rectangular',
4530
+ logo_alignment: this.options.button?.logoAlignment || 'left',
4531
+ width: this.options.button?.width,
4532
+ locale: this.options.button?.locale
4533
+ };
4534
+ window.google.accounts.id.renderButton(element, buttonConfig);
4535
+ this.log('Google login button rendered', buttonConfig);
4536
+ }
4537
+ catch (error) {
4538
+ this.handleError(new Error(`Failed to render Google button: ${error.message}`));
4539
+ }
4540
+ }
4541
+ /**
4542
+ * 禁用自动选择
4543
+ * 用户关闭 One-Tap 后,下次不会自动选择账号
4544
+ */
4545
+ disableAutoSelect() {
4546
+ if (!window.google?.accounts?.id) {
4547
+ return;
4548
+ }
4549
+ try {
4550
+ window.google.accounts.id.disableAutoSelect();
4551
+ this.log('Auto select disabled');
4552
+ }
4553
+ catch (error) {
4554
+ this.log('Error disabling auto select:', error);
4555
+ }
4556
+ }
4557
+ /**
4558
+ * 等待 Google API 完全就绪
4559
+ */
4560
+ waitForGoogleAPI(maxAttempts = 50, interval = 100) {
4561
+ return new Promise((resolve, reject) => {
4562
+ let attempts = 0;
4563
+ const checkAPI = () => {
4564
+ if (window.google?.accounts?.id) {
4565
+ resolve();
4566
+ return;
4567
+ }
4568
+ attempts++;
4569
+ if (attempts >= maxAttempts) {
4570
+ reject(new Error('Google One-Tap API not available after timeout'));
4571
+ return;
4572
+ }
4573
+ setTimeout(checkAPI, interval);
4574
+ };
4575
+ checkAPI();
4576
+ });
4577
+ }
4578
+ /**
4579
+ * 加载 Google One-Tap 脚本
4580
+ */
4581
+ loadGoogleScript() {
4582
+ // 如果已经有加载中的 Promise,直接返回
4583
+ if (GoogleOneTap.scriptLoadPromise) {
4584
+ return GoogleOneTap.scriptLoadPromise;
4585
+ }
4586
+ // 如果脚本已加载完成且 API 可用,创建已 resolve 的 Promise
4587
+ if (this.scriptLoaded && window.google?.accounts?.id) {
4588
+ return Promise.resolve();
4589
+ }
4590
+ // 检查脚本标签是否已存在
4591
+ const existingScript = document.getElementById('google-one-tap-script');
4592
+ if (existingScript) {
4593
+ // 如果脚本标签存在,等待其加载完成
4594
+ GoogleOneTap.scriptLoadPromise = new Promise((resolve, reject) => {
4595
+ if (window.google?.accounts?.id) {
4596
+ this.scriptLoaded = true;
4597
+ resolve();
4598
+ return;
4599
+ }
4600
+ // 等待脚本加载
4601
+ const onLoad = () => {
4602
+ // 等待 Google API 就绪
4603
+ this.waitForGoogleAPI()
4604
+ .then(() => {
4605
+ this.scriptLoaded = true;
4606
+ resolve();
4607
+ })
4608
+ .catch(reject);
4609
+ };
4610
+ // 检查脚本是否已经加载完成(通过检查 API 是否可用)
4611
+ // 如果 API 已经可用,直接调用 onLoad
4612
+ if (window.google?.accounts?.id) {
4613
+ onLoad();
4614
+ }
4615
+ else {
4616
+ // 否则等待脚本加载事件
4617
+ existingScript.addEventListener('load', onLoad);
4618
+ existingScript.addEventListener('error', () => {
4619
+ GoogleOneTap.scriptLoadPromise = null;
4620
+ reject(new Error('Failed to load Google One-Tap script'));
4621
+ });
4622
+ }
4623
+ });
4624
+ return GoogleOneTap.scriptLoadPromise;
4625
+ }
4626
+ // 创建新的脚本加载 Promise
4627
+ GoogleOneTap.scriptLoadPromise = new Promise((resolve, reject) => {
4628
+ this.log('Loading Google One-Tap script');
4629
+ const script = document.createElement('script');
4630
+ script.id = 'google-one-tap-script';
4631
+ script.src = 'https://accounts.google.com/gsi/client';
4632
+ script.async = true;
4633
+ script.defer = true;
4634
+ script.onload = () => {
4635
+ this.log('Google One-Tap script loaded successfully');
4636
+ // 等待 Google API 完全就绪
4637
+ this.waitForGoogleAPI()
4638
+ .then(() => {
4639
+ this.scriptLoaded = true;
4640
+ resolve();
4641
+ })
4642
+ .catch((error) => {
4643
+ GoogleOneTap.scriptLoadPromise = null;
4644
+ reject(error);
4645
+ });
4646
+ };
4647
+ script.onerror = (error) => {
4648
+ this.log('Failed to load Google One-Tap script:', error);
4649
+ GoogleOneTap.scriptLoadPromise = null;
4650
+ reject(new Error('Failed to load Google One-Tap script'));
4651
+ };
4652
+ document.head.appendChild(script);
4653
+ });
4654
+ return GoogleOneTap.scriptLoadPromise;
4655
+ }
4656
+ /**
4657
+ * 处理错误
4658
+ */
4659
+ handleError(error) {
4660
+ this.log('Error:', error);
4661
+ if (this.options.onError) {
4662
+ try {
4663
+ this.options.onError(error);
4664
+ }
4665
+ catch (callbackError) {
4666
+ console.error('[GoogleOneTap] Error in onError callback:', callbackError);
4667
+ }
4668
+ }
4669
+ }
4670
+ /**
4671
+ * 调试日志
4672
+ */
4673
+ log(message, ...args) {
4674
+ if (this.options.debug) {
4675
+ console.log(`[GoogleOneTap] ${message}`, ...args);
4676
+ }
4677
+ }
4678
+ /**
4679
+ * 检查 Google One-Tap 是否可用
4680
+ */
4681
+ static isAvailable() {
4682
+ return typeof window !== 'undefined' && 'google' in window;
4683
+ }
4684
+ }
4685
+ // 静态 Promise,用于避免多个实例重复加载脚本
4686
+ GoogleOneTap.scriptLoadPromise = null;
4687
+
4458
4688
  exports.AuthFactory = AuthFactory;
4459
4689
  exports.AuthModal = AuthModal;
4460
4690
  exports.AuthProvider = AuthProvider;
4461
4691
  exports.BuiltInHooks = BuiltInHooks;
4462
4692
  exports.ENVIRONMENT_CONFIGS = ENVIRONMENT_CONFIGS;
4693
+ exports.GoogleOneTap = GoogleOneTap;
4463
4694
  exports.SeaVerseBackendAPIClient = SeaVerseBackendAPIClient;
4464
4695
  exports.Toast = Toast;
4465
4696
  exports.createAuthModal = createAuthModal;