shogun-core 5.2.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +123 -20
  2. package/dist/browser/shogun-core.js +1134 -493
  3. package/dist/browser/shogun-core.js.map +1 -1
  4. package/dist/config/simplified-config.js +108 -40
  5. package/dist/crypto/mls.js +34 -15
  6. package/dist/crypto/sframe.js +13 -11
  7. package/dist/examples/auth-test.js +263 -59
  8. package/dist/examples/crypto-identity-example.js +55 -21
  9. package/dist/examples/mls-3-member-test.js +97 -0
  10. package/dist/examples/mls-multi-member.js +153 -0
  11. package/dist/examples/mls-sframe-test.js +14 -11
  12. package/dist/examples/mls-simple-test.js +58 -0
  13. package/dist/examples/shogun-core-example.js +90 -0
  14. package/dist/examples/zkproof-credentials-example.js +9 -5
  15. package/dist/examples/zkproof-example.js +14 -10
  16. package/dist/gundb/api.js +17 -16
  17. package/dist/gundb/db.js +769 -328
  18. package/dist/index.js +4 -4
  19. package/dist/managers/CoreInitializer.js +21 -15
  20. package/dist/managers/CryptoIdentityManager.js +79 -32
  21. package/dist/plugins/zkproof/zkCredentials.js +4 -1
  22. package/dist/types/config/simplified-config.d.ts +64 -3
  23. package/dist/types/crypto/sframe.d.ts +4 -0
  24. package/dist/types/examples/mls-3-member-test.d.ts +6 -0
  25. package/dist/types/examples/mls-multi-member.d.ts +6 -0
  26. package/dist/types/examples/mls-simple-test.d.ts +6 -0
  27. package/dist/types/examples/shogun-core-example.d.ts +8 -0
  28. package/dist/types/gundb/api.d.ts +6 -13
  29. package/dist/types/gundb/db.d.ts +84 -41
  30. package/dist/types/index.d.ts +4 -2
  31. package/dist/types/interfaces/shogun.d.ts +1 -2
  32. package/dist/types/managers/CryptoIdentityManager.d.ts +2 -1
  33. package/package.json +11 -8
  34. package/dist/examples/mls-advanced-example.js +0 -294
  35. package/dist/examples/quick-auth-test.js +0 -61
  36. package/dist/examples/simple-api-test.js +0 -114
  37. package/dist/examples/simple-crypto-identity-example.js +0 -84
  38. package/dist/examples/timeout-test.js +0 -227
  39. package/dist/types/examples/mls-advanced-example.d.ts +0 -53
  40. package/dist/types/examples/quick-auth-test.d.ts +0 -8
  41. package/dist/types/examples/simple-api-test.d.ts +0 -10
  42. package/dist/types/examples/simple-crypto-identity-example.d.ts +0 -6
  43. package/dist/types/examples/timeout-test.d.ts +0 -8
package/dist/gundb/db.js CHANGED
@@ -42,17 +42,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
42
42
  return (mod && mod.__esModule) ? mod : { "default": mod };
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.createGun = exports.derive = exports.GunErrors = exports.crypto = exports.RxJS = exports.SEA = exports.DataBase = exports.Gun = void 0;
46
- const gun_1 = __importDefault(require("gun/gun"));
47
- exports.Gun = gun_1.default;
45
+ exports.derive = exports.GunErrors = exports.crypto = exports.RxJS = exports.SEA = exports.DataBase = void 0;
48
46
  const sea_1 = __importDefault(require("gun/sea"));
49
47
  exports.SEA = sea_1.default;
50
- require("gun/lib/then");
51
- require("gun/lib/radix");
52
- require("gun/lib/radisk");
53
- require("gun/lib/store");
54
- require("gun/lib/rindexed");
55
- require("gun/lib/webrtc");
56
48
  const derive_1 = __importDefault(require("./derive"));
57
49
  exports.derive = derive_1.default;
58
50
  const errorHandler_1 = require("../utils/errorHandler");
@@ -83,11 +75,17 @@ class DataBase {
83
75
  constructor(gun, appScope = "shogun", core) {
84
76
  this.user = null;
85
77
  this.onAuthCallbacks = [];
78
+ // Memory leak prevention
79
+ this._activeTimeouts = new Set();
80
+ this._activeIntervals = new Set();
81
+ this._isDestroyed = false;
86
82
  console.log("[DB] Initializing DataBase");
87
83
  // Store core reference
88
84
  this.core = core;
89
85
  // Initialize event emitter
90
86
  this.eventEmitter = new eventEmitter_1.EventEmitter();
87
+ // Setup cleanup handlers
88
+ this.setupCleanupHandlers();
91
89
  // Validate Gun instance
92
90
  if (!gun) {
93
91
  throw new Error("Gun instance is required but was not provided");
@@ -129,7 +127,7 @@ class DataBase {
129
127
  this.node = this.gun.get(appScope);
130
128
  console.log("[DB] App scope node initialized");
131
129
  // Initialize CryptoIdentityManager
132
- this._cryptoIdentityManager = new CryptoIdentityManager_1.CryptoIdentityManager(this.core);
130
+ this._cryptoIdentityManager = new CryptoIdentityManager_1.CryptoIdentityManager(this.core, this);
133
131
  console.log("[DB] CryptoIdentityManager initialized");
134
132
  if (sessionResult.success) {
135
133
  console.log("[DB] Session automatically restored");
@@ -381,46 +379,26 @@ class DataBase {
381
379
  return this.navigateToPath(this.node, path);
382
380
  }
383
381
  /**
384
- * Gets data at the specified path (one-time read)
385
- * @param path Path to get the data from
386
- * @returns Promise resolving to the data
387
- */
388
- async getData(path) {
389
- const node = this.navigateToPath(this.node, path);
390
- return new Promise((resolve, reject) => {
391
- node.once((data) => {
392
- resolve(data);
393
- });
394
- });
395
- }
396
- /**
397
- * Puts data at the specified path
398
- * @param path Path to store data
399
- * @param data Data to store
400
- * @returns Promise resolving to operation result
382
+ * Gets the Gun instance for direct operations
383
+ * @returns Gun instance
401
384
  */
402
- async put(path, data) {
403
- const node = this.navigateToPath(this.node, path);
404
- return await node.put(data).then();
385
+ getGunInstance() {
386
+ return this.gun;
405
387
  }
406
388
  /**
407
- * Sets data at the specified path
408
- * @param path Path to store data
409
- * @param data Data to store
410
- * @returns Promise resolving to operation result
389
+ * Gets the app scope node for direct operations
390
+ * @returns Gun node
411
391
  */
412
- async set(path, data) {
413
- const node = this.navigateToPath(this.node, path);
414
- return await node.set(data).then();
392
+ getAppNode() {
393
+ return this.node;
415
394
  }
416
395
  /**
417
- * Removes data at the specified path
418
- * @param path Path to remove
419
- * @returns Promise resolving to operation result
396
+ * Helper method to get a node at a specific path for direct GUN operations
397
+ * @param path Path to the node
398
+ * @returns Gun node at the specified path
420
399
  */
421
- async remove(path) {
422
- const node = this.navigateToPath(this.node, path);
423
- return await node.put(null).then();
400
+ getNode(path) {
401
+ return this.navigateToPath(this.node, path);
424
402
  }
425
403
  /**
426
404
  * Checks if a user is currently logged in
@@ -577,6 +555,10 @@ class DataBase {
577
555
  // Log out user
578
556
  try {
579
557
  currentUser.leave();
558
+ // Force clear any pending authentication
559
+ if (currentUser._ && currentUser._.sea) {
560
+ currentUser._.sea = null;
561
+ }
580
562
  }
581
563
  catch (gunError) {
582
564
  console.error("Error during Gun logout:", gunError);
@@ -685,136 +667,6 @@ class DataBase {
685
667
  // Validate password strength
686
668
  return this.validatePasswordStrength(password);
687
669
  }
688
- /**
689
- * Creates a new user in Gun
690
- */
691
- async createNewUser(username, password) {
692
- return new Promise((resolve) => {
693
- // Validate inputs before creating user
694
- if (!username ||
695
- typeof username !== "string" ||
696
- username.trim().length === 0) {
697
- resolve({ success: false, error: "Invalid username provided" });
698
- return;
699
- }
700
- if (!password ||
701
- typeof password !== "string" ||
702
- password.length === 0) {
703
- resolve({ success: false, error: "Invalid password provided" });
704
- return;
705
- }
706
- // Normalize username
707
- const normalizedUsername = username.trim().toLowerCase();
708
- if (normalizedUsername.length === 0) {
709
- resolve({
710
- success: false,
711
- error: "Username cannot be empty",
712
- });
713
- return;
714
- }
715
- this.gun.user().create(normalizedUsername, password, (ack) => {
716
- if (ack.err) {
717
- console.error(`User creation error: ${ack.err}`);
718
- resolve({ success: false, error: ack.err });
719
- }
720
- else {
721
- // Validate that we got a userPub from creation
722
- const userPub = ack.pub;
723
- if (!userPub ||
724
- typeof userPub !== "string" ||
725
- userPub.trim().length === 0) {
726
- console.error("User creation successful but no userPub returned:", ack);
727
- resolve({
728
- success: false,
729
- error: "User creation successful but no userPub returned",
730
- });
731
- }
732
- else {
733
- resolve({ success: true, userPub: userPub });
734
- }
735
- }
736
- });
737
- });
738
- }
739
- /**
740
- * Authenticates user after creation
741
- */
742
- async authenticateNewUser(username, password, pair) {
743
- return new Promise((resolve) => {
744
- // Validate inputs before authentication
745
- if (!username ||
746
- typeof username !== "string" ||
747
- username.trim().length === 0) {
748
- resolve({ success: false, error: "Invalid username provided" });
749
- return;
750
- }
751
- // Skip password validation when using pair authentication
752
- if (!pair &&
753
- (!password || typeof password !== "string" || password.length === 0)) {
754
- resolve({ success: false, error: "Invalid password provided" });
755
- return;
756
- }
757
- // Normalize username to match what was used in creation
758
- const normalizedUsername = username.trim().toLowerCase();
759
- if (normalizedUsername.length === 0) {
760
- resolve({
761
- success: false,
762
- error: "Username cannot be empty",
763
- });
764
- return;
765
- }
766
- if (pair) {
767
- this.gun.user().auth(pair, (ack) => {
768
- if (ack.err) {
769
- console.error(`Authentication after creation failed: ${ack.err}`);
770
- resolve({ success: false, error: ack.err });
771
- }
772
- else {
773
- // Add a small delay to ensure user state is properly set
774
- setTimeout(() => {
775
- // Extract userPub from multiple possible sources
776
- const userPub = ack.pub || this.gun.user().is?.pub || ack.user?.pub;
777
- if (!userPub) {
778
- console.error("Authentication successful but no userPub found");
779
- resolve({
780
- success: false,
781
- error: "No userPub returned from authentication",
782
- });
783
- }
784
- else {
785
- resolve({ success: true, userPub: userPub });
786
- }
787
- }, 100);
788
- }
789
- });
790
- }
791
- else {
792
- this.gun.user().auth(normalizedUsername, password, (ack) => {
793
- if (ack.err) {
794
- console.error(`Authentication after creation failed: ${ack.err}`);
795
- resolve({ success: false, error: ack.err });
796
- }
797
- else {
798
- // Add a small delay to ensure user state is properly set
799
- setTimeout(() => {
800
- // Extract userPub from multiple possible sources
801
- const userPub = ack.pub || this.gun.user().is?.pub || ack.user?.pub;
802
- if (!userPub) {
803
- console.error("Authentication successful but no userPub found");
804
- resolve({
805
- success: false,
806
- error: "No userPub returned from authentication",
807
- });
808
- }
809
- else {
810
- resolve({ success: true, userPub: userPub });
811
- }
812
- }, 100);
813
- }
814
- });
815
- }
816
- });
817
- }
818
670
  /**
819
671
  * Signs up a new user using direct Gun authentication
820
672
  * @param username Username
@@ -822,13 +674,34 @@ class DataBase {
822
674
  * @param pair Optional SEA pair for Web3 login
823
675
  * @returns Promise resolving to signup result
824
676
  */
825
- async signUp(username, password, pair) {
677
+ async signUp(username, password, pair, retryCount = 0, maxRetries = 3) {
826
678
  try {
679
+ console.log(`🔄 [SIGNUP] Attempting signup for: ${username}`);
827
680
  // Validate credentials with enhanced security
828
681
  const validation = this.validateSignupCredentials(username, password, pair);
829
682
  if (!validation.valid) {
830
683
  return { success: false, error: validation.error };
831
684
  }
685
+ // Check if authentication is in progress and retry if needed
686
+ if (this.isAuthInProgress()) {
687
+ if (retryCount < maxRetries) {
688
+ console.warn(`Authentication in progress during signup, retrying... (${retryCount + 1}/${maxRetries})`);
689
+ this.resetAuthState();
690
+ const baseDelay = 100 * Math.pow(2, retryCount);
691
+ const jitter = Math.random() * 100;
692
+ const delay = Math.min(baseDelay + jitter, 2000);
693
+ await new Promise((resolve) => setTimeout(resolve, delay));
694
+ return this.signUp(username, password, pair, retryCount + 1, maxRetries);
695
+ }
696
+ else {
697
+ console.error("Max retries exceeded for signup due to concurrent operations");
698
+ this.resetAuthState();
699
+ return {
700
+ success: false,
701
+ error: "Signup failed after multiple retries due to concurrent operations",
702
+ };
703
+ }
704
+ }
832
705
  let createResult;
833
706
  if (pair) {
834
707
  // For Web3/plugin authentication, use pair-based creation
@@ -836,15 +709,17 @@ class DataBase {
836
709
  }
837
710
  else {
838
711
  // For password authentication, use standard creation
839
- createResult = await this.createNewUser(username, password);
712
+ createResult = await this.createNewUserFixed(username, password);
840
713
  }
841
714
  if (!createResult.success) {
715
+ // Reset auth state after failed creation to prevent blocking future operations
716
+ this.resetAuthState();
842
717
  return { success: false, error: createResult.error };
843
718
  }
844
719
  // Add a small delay to ensure user is properly registered
845
720
  await new Promise((resolve) => setTimeout(resolve, 100));
846
721
  // Authenticate the newly created user
847
- const authResult = await this.authenticateNewUser(username, password, pair);
722
+ const authResult = await this.authenticateNewUserFixed(username, password, pair);
848
723
  if (!authResult.success) {
849
724
  return { success: false, error: authResult.error };
850
725
  }
@@ -921,6 +796,100 @@ class DataBase {
921
796
  resolve({ success: true, userPub: pair.pub });
922
797
  });
923
798
  }
799
+ /**
800
+ * Creates a new user in Gun without waiting for acknowledgment
801
+ */
802
+ async createNewUserFixed(username, password) {
803
+ return new Promise((resolve) => {
804
+ // Validate inputs before creating user
805
+ if (!username ||
806
+ typeof username !== "string" ||
807
+ username.trim().length === 0) {
808
+ resolve({ success: false, error: "Invalid username provided" });
809
+ return;
810
+ }
811
+ if (!password ||
812
+ typeof password !== "string" ||
813
+ password.length === 0) {
814
+ resolve({ success: false, error: "Invalid password provided" });
815
+ return;
816
+ }
817
+ // Normalize username
818
+ const normalizedUsername = username.trim().toLowerCase();
819
+ if (normalizedUsername.length === 0) {
820
+ resolve({
821
+ success: false,
822
+ error: "Username cannot be empty",
823
+ });
824
+ return;
825
+ }
826
+ console.log(`🔄 [CREATE] Creating user: ${normalizedUsername}`);
827
+ // Crea utente senza aspettare acknowledgment
828
+ this.gun.user().create(normalizedUsername, password, () => { });
829
+ // Aspetta che la creazione si completi
830
+ setTimeout(() => {
831
+ // Controlla se l'utente è stato creato
832
+ const user = this.gun.user();
833
+ if (user && user.is && user.is.pub) {
834
+ console.log("✅ [CREATE] User created successfully");
835
+ resolve({ success: true, userPub: user.is.pub });
836
+ }
837
+ else {
838
+ console.log("❌ [CREATE] User creation failed");
839
+ resolve({ success: false, error: "User creation failed" });
840
+ }
841
+ }, 2000); // Aspetta 2 secondi
842
+ });
843
+ }
844
+ /**
845
+ * Authenticates user after creation without waiting for acknowledgment
846
+ */
847
+ async authenticateNewUserFixed(username, password, pair) {
848
+ return new Promise((resolve) => {
849
+ // Validate inputs before authentication
850
+ if (!username ||
851
+ typeof username !== "string" ||
852
+ username.trim().length === 0) {
853
+ resolve({ success: false, error: "Invalid username provided" });
854
+ return;
855
+ }
856
+ // Skip password validation when using pair authentication
857
+ if (!pair &&
858
+ (!password || typeof password !== "string" || password.length === 0)) {
859
+ resolve({ success: false, error: "Invalid password provided" });
860
+ return;
861
+ }
862
+ // Normalize username to match what was used in creation
863
+ const normalizedUsername = username.trim().toLowerCase();
864
+ if (normalizedUsername.length === 0) {
865
+ resolve({
866
+ success: false,
867
+ error: "Username cannot be empty",
868
+ });
869
+ return;
870
+ }
871
+ console.log(`🔑 [AUTH] Authenticating user: ${normalizedUsername}`);
872
+ // Autentica senza aspettare acknowledgment
873
+ if (pair) {
874
+ this.gun.user().auth(pair);
875
+ }
876
+ else {
877
+ this.gun.user().auth(normalizedUsername, password);
878
+ }
879
+ // Aspetta che l'autenticazione si completi
880
+ setTimeout(() => {
881
+ const user = this.gun.user();
882
+ if (user && user.is && user.is.pub) {
883
+ console.log("✅ [AUTH] Authentication successful");
884
+ resolve({ success: true, userPub: user.is.pub });
885
+ }
886
+ else {
887
+ console.log("❌ [AUTH] Authentication failed");
888
+ resolve({ success: false, error: "Authentication failed" });
889
+ }
890
+ }, 2000); // Aspetta 2 secondi
891
+ });
892
+ }
924
893
  async runPostAuthOnAuthResult(username, userPub, authResult) {
925
894
  console.log(`[POSTAUTH] Starting post-auth setup for user: ${username}, userPub: ${userPub}`);
926
895
  try {
@@ -956,7 +925,7 @@ class DataBase {
956
925
  }
957
926
  console.log(`[POSTAUTH] Normalized username: ${normalizedUsername}`);
958
927
  console.log(`[POSTAUTH] Checking if user exists: ${userPub}`);
959
- const existingUser = await this.gun.get(userPub).then();
928
+ const existingUser = this.gun.get(userPub);
960
929
  const isNewUser = !existingUser || !existingUser.alias;
961
930
  console.log(`[POSTAUTH] User is ${isNewUser ? "NEW" : "EXISTING"}: ${userPub}`);
962
931
  // Get user's encryption public key (epub) for comprehensive tracking
@@ -975,8 +944,10 @@ class DataBase {
975
944
  };
976
945
  }
977
946
  console.log(`[POSTAUTH] User tracking setup completed successfully for: ${normalizedUsername}`);
978
- // Setup crypto identities for the user
979
- if (this._cryptoIdentityManager && userSea) {
947
+ // Setup crypto identities for the user (skip if SKIP_CRYPTO_GENERATION is set)
948
+ if (this._cryptoIdentityManager &&
949
+ userSea &&
950
+ !process.env.SKIP_CRYPTO_GENERATION) {
980
951
  console.log(`[POSTAUTH] Setting up crypto identities for: ${normalizedUsername}`);
981
952
  try {
982
953
  const cryptoSetupResult = await this._cryptoIdentityManager.setupCryptoIdentities(normalizedUsername, userSea, false);
@@ -994,7 +965,12 @@ class DataBase {
994
965
  }
995
966
  }
996
967
  else {
997
- console.log(`ℹ️ [POSTAUTH] Skipping crypto identities setup - manager not available or no SEA pair`);
968
+ if (process.env.SKIP_CRYPTO_GENERATION) {
969
+ console.log(`ℹ️ [POSTAUTH] Skipping crypto identities setup - SKIP_CRYPTO_GENERATION is set`);
970
+ }
971
+ else {
972
+ console.log(`ℹ️ [POSTAUTH] Skipping crypto identities setup - manager not available or no SEA pair`);
973
+ }
998
974
  }
999
975
  const result = {
1000
976
  success: true,
@@ -1103,28 +1079,13 @@ class DataBase {
1103
1079
  userPub: userPub,
1104
1080
  createdAt: Date.now(),
1105
1081
  };
1106
- return new Promise((resolve) => {
1107
- // Add timeout to prevent hanging
1108
- const timeout = setTimeout(() => {
1109
- console.error(`Alias index creation timeout for ${username}`);
1110
- resolve(false);
1111
- }, 5000); // 5 second timeout
1112
- // Store alias mapping in a simple way
1113
- this.node
1114
- .get("aliases")
1115
- .get(username)
1116
- .put(aliasData, (ack) => {
1117
- clearTimeout(timeout); // Clear timeout since callback fired
1118
- if (ack && ack.err) {
1119
- console.error(`Error creating alias index: ${ack.err}`);
1120
- resolve(false);
1121
- }
1122
- else {
1123
- console.log(`✓ Alias index created for ${username}`);
1124
- resolve(true);
1125
- }
1126
- });
1127
- });
1082
+ console.log(`🔄 [ALIAS] Creating alias index for ${username}`);
1083
+ // Store alias mapping without waiting for acknowledgment
1084
+ this.node.get("aliases").get(username).put(aliasData);
1085
+ // Aspetta un momento per la scrittura
1086
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1087
+ console.log(`✅ [ALIAS] Alias index created for ${username}`);
1088
+ return true;
1128
1089
  }
1129
1090
  catch (error) {
1130
1091
  console.error(`Error creating alias index: ${error}`);
@@ -1136,20 +1097,13 @@ class DataBase {
1136
1097
  */
1137
1098
  async createUsernameMapping(username, userPub) {
1138
1099
  try {
1139
- return new Promise((resolve) => {
1140
- this.node
1141
- .get("usernames")
1142
- .get(username)
1143
- .put(userPub, (ack) => {
1144
- if (ack && ack.err) {
1145
- console.error(`Error creating username mapping: ${ack.err}`);
1146
- resolve(false);
1147
- }
1148
- else {
1149
- resolve(true);
1150
- }
1151
- });
1152
- });
1100
+ console.log(`🔄 [USERNAME] Creating username mapping for ${username}`);
1101
+ // Store username mapping without waiting for acknowledgment
1102
+ this.node.get("usernames").get(username).put(userPub);
1103
+ // Aspetta un momento per la scrittura
1104
+ await new Promise((resolve) => setTimeout(resolve, 500));
1105
+ console.log(`✅ [USERNAME] Username mapping created for ${username}`);
1106
+ return true;
1153
1107
  }
1154
1108
  catch (error) {
1155
1109
  console.error(`Error creating username mapping: ${error}`);
@@ -1168,20 +1122,13 @@ class DataBase {
1168
1122
  registeredAt: Date.now().toString(),
1169
1123
  lastSeen: Date.now().toString(),
1170
1124
  };
1171
- return new Promise((resolve) => {
1172
- this.node
1173
- .get("users")
1174
- .get(userPub)
1175
- .put(userData, (ack) => {
1176
- if (ack && ack.err) {
1177
- console.error(`Error creating user registry: ${ack.err}`);
1178
- resolve(false);
1179
- }
1180
- else {
1181
- resolve(true);
1182
- }
1183
- });
1184
- });
1125
+ console.log(`🔄 [REGISTRY] Creating user registry for ${username}`);
1126
+ // Store user registry without waiting for acknowledgment
1127
+ this.node.get("users").get(userPub).put(userData);
1128
+ // Aspetta un momento per la scrittura
1129
+ await new Promise((resolve) => setTimeout(resolve, 500));
1130
+ console.log(`✅ [REGISTRY] User registry created for ${username}`);
1131
+ return true;
1185
1132
  }
1186
1133
  catch (error) {
1187
1134
  console.error(`Error creating user registry: ${error}`);
@@ -1193,18 +1140,13 @@ class DataBase {
1193
1140
  */
1194
1141
  async createReverseLookup(username, userPub) {
1195
1142
  try {
1196
- const ack = await this.node
1197
- .get("userAliases")
1198
- .get(userPub)
1199
- .put(username)
1200
- .then();
1201
- if (ack.err) {
1202
- console.error(`Error creating reverse lookup: ${ack.err}`);
1203
- return false;
1204
- }
1205
- else {
1206
- return true;
1207
- }
1143
+ console.log(`🔄 [REVERSE] Creating reverse lookup for ${username}`);
1144
+ // Store reverse lookup without waiting for acknowledgment
1145
+ this.node.get("userAliases").get(userPub).put(username);
1146
+ // Aspetta un momento per la scrittura
1147
+ await new Promise((resolve) => setTimeout(resolve, 500));
1148
+ console.log(`✅ [REVERSE] Reverse lookup created for ${username}`);
1149
+ return true;
1208
1150
  }
1209
1151
  catch (error) {
1210
1152
  console.error(`Error creating reverse lookup: ${error}`);
@@ -1216,20 +1158,13 @@ class DataBase {
1216
1158
  */
1217
1159
  async createEpubIndex(epub, userPub) {
1218
1160
  try {
1219
- return new Promise((resolve) => {
1220
- this.node
1221
- .get("epubKeys")
1222
- .get(epub)
1223
- .put(userPub, (ack) => {
1224
- if (ack && ack.err) {
1225
- console.error(`Error creating epub index: ${ack.err}`);
1226
- resolve(false);
1227
- }
1228
- else {
1229
- resolve(true);
1230
- }
1231
- });
1232
- });
1161
+ console.log(`🔄 [EPUB] Creating epub index for ${userPub}`);
1162
+ // Store epub index without waiting for acknowledgment
1163
+ this.node.get("epubKeys").get(epub).put(userPub);
1164
+ // Aspetta un momento per la scrittura
1165
+ await new Promise((resolve) => setTimeout(resolve, 500));
1166
+ console.log(`✅ [EPUB] Epub index created for ${userPub}`);
1167
+ return true;
1233
1168
  }
1234
1169
  catch (error) {
1235
1170
  console.error(`Error creating epub index: ${error}`);
@@ -1247,17 +1182,13 @@ class DataBase {
1247
1182
  registeredAt: Date.now(),
1248
1183
  lastSeen: Date.now(),
1249
1184
  };
1250
- return new Promise((resolve) => {
1251
- this.gun.get(userPub).put(userMetadata, (ack) => {
1252
- if (ack && ack.err) {
1253
- console.error(`Error creating user metadata: ${ack.err}`);
1254
- resolve(false);
1255
- }
1256
- else {
1257
- resolve(true);
1258
- }
1259
- });
1260
- });
1185
+ console.log(`🔄 [METADATA] Creating user metadata for ${username}`);
1186
+ // Store user metadata without waiting for acknowledgment
1187
+ this.gun.get(userPub).put(userMetadata);
1188
+ // Aspetta un momento per la scrittura
1189
+ await new Promise((resolve) => setTimeout(resolve, 500));
1190
+ console.log(`✅ [METADATA] User metadata created for ${username}`);
1191
+ return true;
1261
1192
  }
1262
1193
  catch (error) {
1263
1194
  console.error(`Error creating user metadata: ${error}`);
@@ -1277,7 +1208,7 @@ class DataBase {
1277
1208
  }
1278
1209
  // Method 1: Try GunDB standard alias lookup (~@alias)
1279
1210
  try {
1280
- const aliasData = await this.gun.get(`~@${normalizedAlias}`).then();
1211
+ const aliasData = this.gun.get(`~@${normalizedAlias}`);
1281
1212
  if (aliasData && aliasData["~pubKeyOfUser"]) {
1282
1213
  const userPub = aliasData["~pubKeyOfUser"]["#"] || aliasData["~pubKeyOfUser"];
1283
1214
  if (userPub) {
@@ -1293,10 +1224,15 @@ class DataBase {
1293
1224
  }
1294
1225
  // Method 2: Try username mapping (usernames/alias -> userPub)
1295
1226
  try {
1296
- const userPub = await this.node
1297
- .get("usernames")
1298
- .get(normalizedAlias)
1299
- .then();
1227
+ const userPubNode = this.node.get("usernames").get(normalizedAlias);
1228
+ // Use once to get the actual value
1229
+ const userPub = await new Promise((resolve) => {
1230
+ const timeout = setTimeout(() => resolve(null), 2000);
1231
+ userPubNode.once((data) => {
1232
+ clearTimeout(timeout);
1233
+ resolve(data);
1234
+ });
1235
+ });
1300
1236
  if (userPub) {
1301
1237
  const userData = await this.getUserDataByPub(userPub);
1302
1238
  if (userData) {
@@ -1326,7 +1262,7 @@ class DataBase {
1326
1262
  }
1327
1263
  // Method 1: Try user registry (users/userPub -> user data)
1328
1264
  try {
1329
- const userData = await this.node.get("users").get(userPub).then();
1265
+ const userData = this.node.get("users").get(userPub);
1330
1266
  if (userData && userData.username) {
1331
1267
  return {
1332
1268
  userPub: userData.userPub || userPub,
@@ -1342,7 +1278,7 @@ class DataBase {
1342
1278
  }
1343
1279
  // Method 2: Try user's own node
1344
1280
  try {
1345
- const userNodeData = await this.gun.get(userPub).then();
1281
+ const userNodeData = this.gun.get(userPub);
1346
1282
  if (userNodeData && userNodeData.username) {
1347
1283
  return {
1348
1284
  userPub: userPub,
@@ -1373,7 +1309,15 @@ class DataBase {
1373
1309
  if (!epub || typeof epub !== "string") {
1374
1310
  return null;
1375
1311
  }
1376
- const userPub = await this.node.get("epubKeys").get(epub).then();
1312
+ const userPubNode = this.node.get("epubKeys").get(epub);
1313
+ // Use once to get the actual value
1314
+ const userPub = await new Promise((resolve) => {
1315
+ const timeout = setTimeout(() => resolve(null), 2000);
1316
+ userPubNode.once((data) => {
1317
+ clearTimeout(timeout);
1318
+ resolve(data);
1319
+ });
1320
+ });
1377
1321
  return userPub || null;
1378
1322
  }
1379
1323
  catch (error) {
@@ -1391,7 +1335,15 @@ class DataBase {
1391
1335
  if (!userPub || typeof userPub !== "string") {
1392
1336
  return null;
1393
1337
  }
1394
- const alias = await this.node.get("userAliases").get(userPub).then();
1338
+ const aliasNode = this.node.get("userAliases").get(userPub);
1339
+ // Use once to get the actual value
1340
+ const alias = await new Promise((resolve) => {
1341
+ const timeout = setTimeout(() => resolve(null), 2000);
1342
+ aliasNode.once((data) => {
1343
+ clearTimeout(timeout);
1344
+ resolve(data);
1345
+ });
1346
+ });
1395
1347
  return alias || null;
1396
1348
  }
1397
1349
  catch (error) {
@@ -1430,19 +1382,14 @@ class DataBase {
1430
1382
  const timestamp = Date.now();
1431
1383
  // Update in user registry
1432
1384
  try {
1433
- await this.node
1434
- .get("users")
1435
- .get(userPub)
1436
- .get("lastSeen")
1437
- .put(timestamp)
1438
- .then();
1385
+ this.node.get("users").get(userPub).get("lastSeen").put(timestamp);
1439
1386
  }
1440
1387
  catch (error) {
1441
1388
  console.error(`Failed to update lastSeen in user registry:`, error);
1442
1389
  }
1443
1390
  // Update in user's own node
1444
1391
  try {
1445
- await this.gun.get(userPub).get("lastSeen").put(timestamp).then();
1392
+ this.gun.get(userPub).get("lastSeen").put(timestamp);
1446
1393
  }
1447
1394
  catch (error) {
1448
1395
  console.error(`Failed to update lastSeen in user node:`, error);
@@ -1453,33 +1400,134 @@ class DataBase {
1453
1400
  }
1454
1401
  }
1455
1402
  /**
1456
- * Performs authentication with Gun
1403
+ * Resets Gun.js authentication state to allow new auth operations
1404
+ */
1405
+ resetAuthState() {
1406
+ try {
1407
+ const currentUser = this.gun.user();
1408
+ if (currentUser && currentUser._) {
1409
+ // Reset the authentication flag
1410
+ if (currentUser._.sea) {
1411
+ currentUser._.sea = null;
1412
+ }
1413
+ // Reset the ing flag that prevents concurrent operations
1414
+ if (currentUser._.ing) {
1415
+ currentUser._.ing = false;
1416
+ }
1417
+ // Force clear any pending authentication callbacks
1418
+ if (currentUser._.auth) {
1419
+ currentUser._.auth = null;
1420
+ }
1421
+ // Clear any pending operations
1422
+ if (currentUser._.act) {
1423
+ currentUser._.act = null;
1424
+ }
1425
+ // Clear any pending callbacks
1426
+ if (currentUser._.cb) {
1427
+ currentUser._.cb = null;
1428
+ }
1429
+ // Clear any pending retries
1430
+ if (currentUser._.retries) {
1431
+ currentUser._.retries = 0;
1432
+ }
1433
+ // Clear any pending timeouts
1434
+ if (currentUser._.timeout) {
1435
+ clearTimeout(currentUser._.timeout);
1436
+ currentUser._.timeout = null;
1437
+ }
1438
+ }
1439
+ // Also try to call leave() to ensure clean state
1440
+ try {
1441
+ currentUser.leave();
1442
+ }
1443
+ catch (leaveError) {
1444
+ // Ignore leave errors, just trying to clean up
1445
+ }
1446
+ // Force clear the user reference to ensure clean state
1447
+ this.user = null;
1448
+ console.log("Auth state reset completed");
1449
+ }
1450
+ catch (error) {
1451
+ console.warn("Error resetting auth state:", error);
1452
+ }
1453
+ }
1454
+ /**
1455
+ * Performs authentication with Gun with retry mechanism
1457
1456
  */
1458
- async performAuthentication(username, password, pair) {
1457
+ async performAuthentication(username, password, pair, retryCount = 0, maxRetries = 3) {
1459
1458
  return new Promise((resolve) => {
1460
- if (pair) {
1461
- this.gun.user().auth(pair, (ack) => {
1462
- if (ack.err) {
1463
- console.error(`Login error for ${username}: ${ack.err}`);
1464
- resolve({ success: false, error: ack.err });
1465
- }
1466
- else {
1467
- resolve({ success: true, ack });
1459
+ // Check if there's already an authentication operation in progress
1460
+ const currentUser = this.gun.user();
1461
+ if (currentUser && currentUser._ && currentUser._.ing) {
1462
+ if (retryCount < maxRetries) {
1463
+ console.warn(`Authentication already in progress, retrying... (${retryCount + 1}/${maxRetries})`);
1464
+ this.resetAuthState();
1465
+ // For the first retry, try to create a fresh authentication context
1466
+ if (retryCount === 0) {
1467
+ this.createFreshAuthContext();
1468
1468
  }
1469
+ // Add exponential backoff delay with jitter
1470
+ const baseDelay = 200 * Math.pow(2, retryCount);
1471
+ const jitter = Math.random() * 200;
1472
+ const delay = Math.min(baseDelay + jitter, 3000);
1473
+ setTimeout(() => {
1474
+ this.performAuthentication(username, password, pair, retryCount + 1, maxRetries)
1475
+ .then(resolve)
1476
+ .catch((error) => resolve({ success: false, error: String(error) }));
1477
+ }, delay);
1478
+ return;
1479
+ }
1480
+ else {
1481
+ console.error("Max retries exceeded for authentication");
1482
+ this.resetAuthState();
1483
+ resolve({
1484
+ success: false,
1485
+ error: "Authentication failed after multiple retries",
1486
+ });
1487
+ return;
1488
+ }
1489
+ }
1490
+ this.performAuthenticationInternal(username, password, pair, resolve);
1491
+ });
1492
+ }
1493
+ /**
1494
+ * Internal authentication method with timeout
1495
+ */
1496
+ performAuthenticationInternal(username, password, pair, resolve) {
1497
+ let resolved = false;
1498
+ const timeout = 30000; // 15 second timeout for individual auth attempts
1499
+ const timeoutId = setTimeout(() => {
1500
+ if (!resolved) {
1501
+ resolved = true;
1502
+ console.error(`Authentication timeout for ${username} after ${timeout}ms`);
1503
+ this.resetAuthState();
1504
+ resolve({
1505
+ success: false,
1506
+ error: `Authentication timeout after ${timeout}ms`,
1469
1507
  });
1470
1508
  }
1509
+ }, timeout);
1510
+ const authCallback = (ack) => {
1511
+ if (resolved)
1512
+ return;
1513
+ resolved = true;
1514
+ clearTimeout(timeoutId);
1515
+ if (ack.err) {
1516
+ console.error(`Login error for ${username}: ${ack.err}`);
1517
+ // Reset auth state on error to prevent blocking future attempts
1518
+ this.resetAuthState();
1519
+ resolve({ success: false, error: ack.err });
1520
+ }
1471
1521
  else {
1472
- this.gun.user().auth(username, password, (ack) => {
1473
- if (ack.err) {
1474
- console.error(`Login error for ${username}: ${ack.err}`);
1475
- resolve({ success: false, error: ack.err });
1476
- }
1477
- else {
1478
- resolve({ success: true, ack });
1479
- }
1480
- });
1522
+ resolve({ success: true, ack });
1481
1523
  }
1482
- });
1524
+ };
1525
+ if (pair) {
1526
+ this.gun.user().auth(pair, authCallback);
1527
+ }
1528
+ else {
1529
+ this.gun.user().auth(username, password, authCallback);
1530
+ }
1483
1531
  }
1484
1532
  /**
1485
1533
  * Builds login result object
@@ -1511,18 +1559,34 @@ class DataBase {
1511
1559
  */
1512
1560
  async login(username, password, pair) {
1513
1561
  try {
1514
- const loginResult = await this.performAuthentication(username, password, pair);
1515
- if (!loginResult.success) {
1562
+ console.log(`🔑 [LOGIN] Attempting login for: ${username}`);
1563
+ // Pulisci lo stato utente esistente
1564
+ this.gun.user().leave();
1565
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1566
+ // Autentica senza aspettare acknowledgment
1567
+ if (pair) {
1568
+ this.gun.user().auth(pair);
1569
+ }
1570
+ else {
1571
+ this.gun.user().auth(username, password);
1572
+ }
1573
+ // Aspetta che l'autenticazione si completi
1574
+ console.log("⏳ [LOGIN] Waiting for authentication to complete...");
1575
+ await new Promise((resolve) => setTimeout(resolve, 3000));
1576
+ // Controlla se l'autenticazione è riuscita
1577
+ const user = this.gun.user();
1578
+ const isAuthenticated = !!user.is;
1579
+ if (!isAuthenticated) {
1580
+ console.log("❌ [LOGIN] Authentication failed");
1516
1581
  return {
1517
1582
  success: false,
1518
1583
  error: `User '${username}' not found. Please check your username or register first.`,
1519
1584
  };
1520
1585
  }
1521
- // Add a small delay to ensure user state is properly set
1522
- await new Promise((resolve) => setTimeout(resolve, 100));
1523
- const userPub = this.gun.user().is?.pub;
1524
- let alias = this.gun.user().is?.alias;
1525
- let userPair = this.gun.user()?._?.sea;
1586
+ console.log("✅ [LOGIN] Authentication successful!");
1587
+ const userPub = user?.is?.pub;
1588
+ let alias = user?.is?.alias;
1589
+ let userPair = user?._?.sea;
1526
1590
  if (!alias) {
1527
1591
  alias = username;
1528
1592
  }
@@ -1532,7 +1596,7 @@ class DataBase {
1532
1596
  error: "Authentication failed: No user pub returned.",
1533
1597
  };
1534
1598
  }
1535
- // Pass the userPub to runPostAuthOnAuthResult
1599
+ // Esegui post-authentication tasks
1536
1600
  try {
1537
1601
  await this.runPostAuthOnAuthResult(alias, userPub, {
1538
1602
  success: true,
@@ -1693,14 +1757,20 @@ class DataBase {
1693
1757
  questions: JSON.stringify(securityQuestions),
1694
1758
  hint: encryptedHint,
1695
1759
  };
1696
- const ack = await this.node.get(userPub)
1697
- .get("security")
1698
- .put(securityPayload)
1699
- .then();
1700
- if (ack.err) {
1760
+ console.log(`💾 [HINT] Saving security data for user: ${username}, pub: ${userPub}`);
1761
+ console.log(`💾 [HINT] Security payload:`, {
1762
+ questions: securityPayload.questions,
1763
+ hintLength: securityPayload.hint?.length || 0,
1764
+ });
1765
+ // Save in PUBLIC space, not user space - accessible without login
1766
+ const ack = this.gun.get("security")
1767
+ .get(userPub)
1768
+ .put(securityPayload);
1769
+ if (ack && ack.err) {
1701
1770
  console.error("Error saving security data to public graph:", ack.err);
1702
1771
  throw new Error(ack.err);
1703
1772
  }
1773
+ console.log(`✅ [HINT] Security data saved successfully for ${username}`);
1704
1774
  return { success: true };
1705
1775
  }
1706
1776
  catch (error) {
@@ -1716,19 +1786,82 @@ class DataBase {
1716
1786
  */
1717
1787
  async forgotPassword(username, securityAnswers) {
1718
1788
  // Attempting password recovery for
1789
+ // Add global timeout for the entire operation
1790
+ return Promise.race([
1791
+ this._forgotPasswordInternal(username, securityAnswers),
1792
+ new Promise((resolve) => {
1793
+ setTimeout(() => {
1794
+ console.log(`⏰ [FORGOT] Global timeout for password recovery: ${username}`);
1795
+ resolve({ success: false, error: "Password recovery timeout" });
1796
+ }, 10000); // 10 second global timeout
1797
+ }),
1798
+ ]);
1799
+ }
1800
+ async _forgotPasswordInternal(username, securityAnswers) {
1719
1801
  try {
1720
1802
  // Find the user's data using direct lookup
1721
1803
  const normalizedUsername = username.trim().toLowerCase();
1722
- const userPub = (await this.node.get("usernames").get(normalizedUsername).then()) ||
1723
- null;
1804
+ // Try multiple lookup methods with fallback
1805
+ let userPub = null;
1806
+ // Method 1: Try username mapping in app scope
1807
+ try {
1808
+ userPub = await new Promise((resolve) => {
1809
+ const timeout = setTimeout(() => {
1810
+ console.log(`⏰ [FORGOT] Timeout looking up user in usernames: ${username}`);
1811
+ resolve(null);
1812
+ }, 2000);
1813
+ this.node
1814
+ .get("usernames")
1815
+ .get(normalizedUsername)
1816
+ .once((data) => {
1817
+ clearTimeout(timeout);
1818
+ console.log(`🔍 [FORGOT] Username lookup result for ${username}:`, data);
1819
+ resolve(data);
1820
+ });
1821
+ });
1822
+ }
1823
+ catch (error) {
1824
+ console.log(`❌ [FORGOT] Username lookup failed: ${error}`);
1825
+ }
1826
+ // Method 2: Try direct user lookup if username mapping fails
1724
1827
  if (!userPub) {
1828
+ console.log(`🔄 [FORGOT] Trying direct user lookup for: ${username}`);
1829
+ try {
1830
+ // Try to find user by searching in the users registry
1831
+ const usersNode = this.node.get("users");
1832
+ // This is a simplified approach - in a real implementation you'd need to iterate
1833
+ // For now, we'll try to get the userPub from the current user if available
1834
+ const currentUser = this.getCurrentUser();
1835
+ if (currentUser && currentUser.pub) {
1836
+ console.log(`🔍 [FORGOT] Using current user pub as fallback: ${currentUser.pub}`);
1837
+ userPub = currentUser.pub;
1838
+ }
1839
+ }
1840
+ catch (error) {
1841
+ console.log(`❌ [FORGOT] Direct user lookup failed: ${error}`);
1842
+ }
1843
+ }
1844
+ if (!userPub) {
1845
+ console.log(`❌ [FORGOT] User not found: ${username}`);
1725
1846
  return { success: false, error: "User not found" };
1726
1847
  }
1727
- // Access the user's security data directly from their public key node
1728
- const securityData = await this.node.get(userPub)
1729
- .get("security")
1730
- .then();
1848
+ // Access the user's security data from PUBLIC space (not user space)
1849
+ const securityDataNode = this.gun.get("security").get(userPub);
1850
+ console.log(`🔍 [FORGOT] Looking for security data for user: ${username}, pub: ${userPub}`);
1851
+ // Use once to get the actual value
1852
+ const securityData = await new Promise((resolve) => {
1853
+ const timeout = setTimeout(() => {
1854
+ console.log(`⏰ [FORGOT] Timeout waiting for security data for ${username} (3s)`);
1855
+ resolve(null);
1856
+ }, 3000); // Reduced timeout to 3 seconds
1857
+ securityDataNode.once((data) => {
1858
+ clearTimeout(timeout);
1859
+ console.log(`📊 [FORGOT] Security data received for ${username}:`, data);
1860
+ resolve(data);
1861
+ });
1862
+ });
1731
1863
  if (!securityData || !securityData.hint) {
1864
+ console.log(`❌ [FORGOT] No security data or hint found for ${username}`);
1732
1865
  return {
1733
1866
  success: false,
1734
1867
  error: "No password hint found",
@@ -1790,6 +1923,107 @@ class DataBase {
1790
1923
  return { success: false, error: String(error) };
1791
1924
  }
1792
1925
  }
1926
+ /**
1927
+ * Recovers password hint using security question answers and userPub directly
1928
+ * @param userPub User's public key
1929
+ * @param securityAnswers Array of answers to security questions
1930
+ * @returns Promise resolving with the password hint
1931
+ */
1932
+ async forgotPasswordByPub(userPub, securityAnswers) {
1933
+ // Add global timeout for the entire operation
1934
+ return Promise.race([
1935
+ this._forgotPasswordByPubInternal(userPub, securityAnswers),
1936
+ new Promise((resolve) => {
1937
+ setTimeout(() => {
1938
+ console.log(`⏰ [FORGOT] Global timeout for password recovery by pub: ${userPub}`);
1939
+ resolve({ success: false, error: "Password recovery timeout" });
1940
+ }, 10000); // 10 second global timeout
1941
+ }),
1942
+ ]);
1943
+ }
1944
+ async _forgotPasswordByPubInternal(userPub, securityAnswers) {
1945
+ try {
1946
+ if (!userPub || typeof userPub !== "string") {
1947
+ return { success: false, error: "Invalid userPub provided" };
1948
+ }
1949
+ // Access the user's security data from PUBLIC space (not user space)
1950
+ const securityDataNode = this.gun.get("security").get(userPub);
1951
+ console.log(`🔍 [FORGOT] Looking for security data for userPub: ${userPub}`);
1952
+ // Use once to get the actual value
1953
+ const securityData = await new Promise((resolve) => {
1954
+ const timeout = setTimeout(() => {
1955
+ console.log(`⏰ [FORGOT] Timeout waiting for security data for userPub: ${userPub} (3s)`);
1956
+ resolve(null);
1957
+ }, 3000);
1958
+ securityDataNode.once((data) => {
1959
+ clearTimeout(timeout);
1960
+ console.log(`📊 [FORGOT] Security data received for userPub: ${userPub}:`, data);
1961
+ resolve(data);
1962
+ });
1963
+ });
1964
+ if (!securityData || !securityData.hint) {
1965
+ console.log(`❌ [FORGOT] No security data or hint found for userPub: ${userPub}`);
1966
+ return {
1967
+ success: false,
1968
+ error: "No password hint found",
1969
+ };
1970
+ }
1971
+ // Generate hash from security answers
1972
+ const answersText = securityAnswers.join("|");
1973
+ let proofOfWork;
1974
+ try {
1975
+ // Use SEA directly if available
1976
+ if (sea_1.default && sea_1.default.work) {
1977
+ proofOfWork = await sea_1.default.work(answersText, null, null, {
1978
+ name: "SHA-256",
1979
+ });
1980
+ }
1981
+ else if (this.crypto && this.crypto.hashText) {
1982
+ proofOfWork = await this.crypto.hashText(answersText);
1983
+ }
1984
+ else {
1985
+ throw new Error("Cryptographic functions not available");
1986
+ }
1987
+ if (!proofOfWork) {
1988
+ throw new Error("Failed to generate hash");
1989
+ }
1990
+ }
1991
+ catch (hashError) {
1992
+ console.error("Error generating hash:", hashError);
1993
+ return { success: false, error: "Failed to generate security hash" };
1994
+ }
1995
+ // Decrypt the password hint with the proof of work
1996
+ let hint;
1997
+ try {
1998
+ if (sea_1.default && sea_1.default.decrypt) {
1999
+ hint = await sea_1.default.decrypt(securityData.hint, proofOfWork);
2000
+ }
2001
+ else if (this.crypto && this.crypto.decrypt) {
2002
+ hint = await this.crypto.decrypt(securityData.hint, proofOfWork);
2003
+ }
2004
+ else {
2005
+ throw new Error("Decryption functions not available");
2006
+ }
2007
+ }
2008
+ catch (decryptError) {
2009
+ return {
2010
+ success: false,
2011
+ error: "Incorrect answers to security questions",
2012
+ };
2013
+ }
2014
+ if (hint === undefined) {
2015
+ return {
2016
+ success: false,
2017
+ error: "Incorrect answers to security questions",
2018
+ };
2019
+ }
2020
+ return { success: true, hint: hint };
2021
+ }
2022
+ catch (error) {
2023
+ console.error("Error recovering password hint by pub:", error);
2024
+ return { success: false, error: String(error) };
2025
+ }
2026
+ }
1793
2027
  /**
1794
2028
  * Adds an event listener
1795
2029
  * @param event Event name
@@ -1934,13 +2168,220 @@ class DataBase {
1934
2168
  isAuthenticated() {
1935
2169
  return this.user?.is?.pub ? true : false;
1936
2170
  }
2171
+ /**
2172
+ * Check if an authentication operation is currently in progress
2173
+ */
2174
+ isAuthInProgress() {
2175
+ try {
2176
+ const currentUser = this.gun.user();
2177
+ return !!(currentUser && currentUser._ && currentUser._.ing);
2178
+ }
2179
+ catch (error) {
2180
+ console.warn("Error checking auth progress:", error);
2181
+ return false;
2182
+ }
2183
+ }
2184
+ /**
2185
+ * Force reset authentication state (useful for debugging or recovery)
2186
+ */
2187
+ forceResetAuthState() {
2188
+ this.resetAuthState();
2189
+ }
2190
+ /**
2191
+ * Aggressive cleanup for problematic users
2192
+ * This method performs a complete reset of all authentication state
2193
+ */
2194
+ aggressiveAuthCleanup() {
2195
+ try {
2196
+ console.log("🧹 Performing aggressive auth cleanup...");
2197
+ // Reset all auth state
2198
+ this.resetAuthState();
2199
+ // Clear all Gun.js internal state
2200
+ const currentUser = this.gun.user();
2201
+ if (currentUser && currentUser._) {
2202
+ // Clear all possible internal flags
2203
+ const internalState = currentUser._;
2204
+ Object.keys(internalState).forEach((key) => {
2205
+ if (typeof internalState[key] === "boolean") {
2206
+ internalState[key] = false;
2207
+ }
2208
+ else if (typeof internalState[key] === "object" &&
2209
+ internalState[key] !== null) {
2210
+ if (key === "sea" ||
2211
+ key === "auth" ||
2212
+ key === "act" ||
2213
+ key === "cb") {
2214
+ internalState[key] = null;
2215
+ }
2216
+ }
2217
+ });
2218
+ }
2219
+ // Force logout
2220
+ try {
2221
+ currentUser.leave();
2222
+ }
2223
+ catch (error) {
2224
+ // Ignore errors
2225
+ }
2226
+ // Clear user reference
2227
+ this.user = null;
2228
+ // Clear all session storage
2229
+ if (typeof sessionStorage !== "undefined") {
2230
+ try {
2231
+ const keys = Object.keys(sessionStorage);
2232
+ keys.forEach((key) => {
2233
+ if (key.includes("gun") ||
2234
+ key.includes("user") ||
2235
+ key.includes("auth")) {
2236
+ sessionStorage.removeItem(key);
2237
+ }
2238
+ });
2239
+ }
2240
+ catch (error) {
2241
+ // Ignore errors
2242
+ }
2243
+ }
2244
+ // Clear all local storage
2245
+ if (typeof localStorage !== "undefined") {
2246
+ try {
2247
+ const keys = Object.keys(localStorage);
2248
+ keys.forEach((key) => {
2249
+ if (key.includes("gun") ||
2250
+ key.includes("user") ||
2251
+ key.includes("auth")) {
2252
+ localStorage.removeItem(key);
2253
+ }
2254
+ });
2255
+ }
2256
+ catch (error) {
2257
+ // Ignore errors
2258
+ }
2259
+ }
2260
+ console.log("✓ Aggressive auth cleanup completed");
2261
+ }
2262
+ catch (error) {
2263
+ console.error("Error during aggressive cleanup:", error);
2264
+ }
2265
+ }
2266
+ /**
2267
+ * Creates a completely fresh authentication context
2268
+ * This is a more aggressive approach when normal reset doesn't work
2269
+ */
2270
+ createFreshAuthContext() {
2271
+ try {
2272
+ // Reset all auth state
2273
+ this.resetAuthState();
2274
+ // Create a completely new user instance
2275
+ const freshUser = this.gun.user();
2276
+ this.user = freshUser;
2277
+ // Clear any cached authentication data
2278
+ if (typeof sessionStorage !== "undefined") {
2279
+ try {
2280
+ sessionStorage.removeItem("gunSessionData");
2281
+ sessionStorage.removeItem("gunUserData");
2282
+ }
2283
+ catch (error) {
2284
+ console.warn("Error clearing session storage:", error);
2285
+ }
2286
+ }
2287
+ console.log("Fresh authentication context created");
2288
+ }
2289
+ catch (error) {
2290
+ console.error("Error creating fresh auth context:", error);
2291
+ }
2292
+ }
2293
+ /**
2294
+ * Setup cleanup handlers for memory leak prevention
2295
+ */
2296
+ setupCleanupHandlers() {
2297
+ // Handle process cleanup
2298
+ if (typeof process !== "undefined") {
2299
+ process.on("SIGINT", () => this.destroy());
2300
+ process.on("SIGTERM", () => this.destroy());
2301
+ process.on("exit", () => this.destroy());
2302
+ }
2303
+ }
2304
+ /**
2305
+ * Safe interval wrapper that tracks intervals for cleanup
2306
+ */
2307
+ safeInterval(callback, delay) {
2308
+ if (this._isDestroyed) {
2309
+ return setInterval(() => { }, 0);
2310
+ }
2311
+ const interval = setInterval(() => {
2312
+ if (!this._isDestroyed) {
2313
+ callback();
2314
+ }
2315
+ }, delay);
2316
+ this._activeIntervals.add(interval);
2317
+ return interval;
2318
+ }
2319
+ /**
2320
+ * Clear a specific timeout
2321
+ */
2322
+ clearSafeTimeout(timeout) {
2323
+ clearTimeout(timeout);
2324
+ this._activeTimeouts.delete(timeout);
2325
+ }
2326
+ /**
2327
+ * Clear a specific interval
2328
+ */
2329
+ clearSafeInterval(interval) {
2330
+ clearInterval(interval);
2331
+ this._activeIntervals.delete(interval);
2332
+ }
2333
+ /**
2334
+ * Destroy the database instance and clean up all resources
2335
+ */
2336
+ destroy() {
2337
+ if (this._isDestroyed) {
2338
+ return;
2339
+ }
2340
+ console.log("[DB] Destroying DataBase instance...");
2341
+ this._isDestroyed = true;
2342
+ // Clear all timeouts
2343
+ this._activeTimeouts.forEach((timeout) => {
2344
+ clearTimeout(timeout);
2345
+ });
2346
+ this._activeTimeouts.clear();
2347
+ // Clear all intervals
2348
+ this._activeIntervals.forEach((interval) => {
2349
+ clearInterval(interval);
2350
+ });
2351
+ this._activeIntervals.clear();
2352
+ // Clear event listeners
2353
+ this.eventEmitter.removeAllListeners();
2354
+ // Clear auth callbacks
2355
+ this.onAuthCallbacks.length = 0;
2356
+ // Clear Gun user
2357
+ if (this.user) {
2358
+ try {
2359
+ this.user.leave();
2360
+ }
2361
+ catch (error) {
2362
+ console.warn("Error during user leave:", error);
2363
+ }
2364
+ this.user = null;
2365
+ }
2366
+ // Clear Gun instance references
2367
+ if (this.gun) {
2368
+ try {
2369
+ // Clear any Gun event listeners if available
2370
+ if (typeof this.gun.off === "function") {
2371
+ this.gun.off();
2372
+ }
2373
+ }
2374
+ catch (error) {
2375
+ console.warn("Error clearing Gun listeners:", error);
2376
+ }
2377
+ }
2378
+ // Clear other references
2379
+ this._rxjs = undefined;
2380
+ this._cryptoIdentityManager = undefined;
2381
+ this.core = undefined;
2382
+ console.log("[DB] DataBase instance destroyed");
2383
+ }
1937
2384
  }
1938
2385
  exports.DataBase = DataBase;
1939
2386
  // Errors
1940
2387
  DataBase.Errors = GunErrors;
1941
- const createGun = (config) => {
1942
- const gunInstance = (0, gun_1.default)(config);
1943
- return gunInstance;
1944
- };
1945
- exports.createGun = createGun;
1946
- exports.default = gun_1.default;