shogun-core 5.2.0 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js +1220 -0
  2. package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js.map +1 -0
  3. package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js +844 -0
  4. package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js.map +1 -0
  5. package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js +2335 -0
  6. package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js.map +1 -0
  7. package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js +999 -0
  8. package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js.map +1 -0
  9. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js +1651 -0
  10. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js.map +1 -0
  11. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js +825 -0
  12. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js.map +1 -0
  13. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js +508 -0
  14. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js.map +1 -0
  15. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js +747 -0
  16. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js.map +1 -0
  17. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js +1608 -0
  18. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js.map +1 -0
  19. package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js +2117 -0
  20. package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js.map +1 -0
  21. package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js +86 -0
  22. package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js.map +1 -0
  23. package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js +539 -0
  24. package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js.map +1 -0
  25. package/dist/browser/shogun-core.js +160386 -0
  26. package/dist/browser/shogun-core.js.map +1 -0
  27. package/dist/config/simplified-config.js +236 -0
  28. package/dist/core.js +329 -0
  29. package/dist/crypto/asymmetric.js +99 -0
  30. package/dist/crypto/double-ratchet.js +370 -0
  31. package/dist/crypto/file-encryption.js +213 -0
  32. package/dist/crypto/hashing.js +87 -0
  33. package/dist/crypto/index.js +34 -0
  34. package/dist/crypto/mls-codec.js +202 -0
  35. package/dist/crypto/mls.js +550 -0
  36. package/dist/crypto/pgp.js +390 -0
  37. package/dist/crypto/random-generation.js +341 -0
  38. package/dist/crypto/sframe.js +350 -0
  39. package/dist/crypto/signal-protocol.js +376 -0
  40. package/dist/crypto/symmetric.js +91 -0
  41. package/dist/crypto/types.js +2 -0
  42. package/dist/crypto/utils.js +140 -0
  43. package/dist/examples/auth-test.js +253 -0
  44. package/dist/examples/crypto-identity-example.js +151 -0
  45. package/dist/examples/crypto-working-test.js +83 -0
  46. package/dist/examples/double-ratchet-test.js +155 -0
  47. package/dist/examples/mls-advanced-example.js +294 -0
  48. package/dist/examples/mls-sframe-test.js +304 -0
  49. package/dist/examples/pgp-example.js +200 -0
  50. package/dist/examples/quick-auth-test.js +61 -0
  51. package/dist/examples/random-generation-test.js +151 -0
  52. package/dist/examples/signal-protocol-test.js +38 -0
  53. package/dist/examples/simple-api-test.js +114 -0
  54. package/dist/examples/simple-crypto-identity-example.js +84 -0
  55. package/dist/examples/timeout-test.js +227 -0
  56. package/dist/examples/zkproof-credentials-example.js +212 -0
  57. package/dist/examples/zkproof-example.js +201 -0
  58. package/dist/gundb/api.js +435 -0
  59. package/dist/gundb/crypto.js +283 -0
  60. package/dist/gundb/db.js +1946 -0
  61. package/dist/gundb/derive.js +232 -0
  62. package/dist/gundb/errors.js +76 -0
  63. package/dist/gundb/index.js +22 -0
  64. package/dist/gundb/rxjs.js +447 -0
  65. package/dist/gundb/types.js +5 -0
  66. package/dist/index.js +58 -0
  67. package/dist/interfaces/common.js +2 -0
  68. package/dist/interfaces/events.js +40 -0
  69. package/dist/interfaces/plugin.js +2 -0
  70. package/dist/interfaces/shogun.js +37 -0
  71. package/dist/managers/AuthManager.js +226 -0
  72. package/dist/managers/CoreInitializer.js +228 -0
  73. package/dist/managers/CryptoIdentityManager.js +366 -0
  74. package/dist/managers/EventManager.js +70 -0
  75. package/dist/managers/PluginManager.js +299 -0
  76. package/dist/plugins/base.js +50 -0
  77. package/dist/plugins/index.js +32 -0
  78. package/dist/plugins/nostr/index.js +20 -0
  79. package/dist/plugins/nostr/nostrConnector.js +419 -0
  80. package/dist/plugins/nostr/nostrConnectorPlugin.js +453 -0
  81. package/dist/plugins/nostr/nostrSigner.js +319 -0
  82. package/dist/plugins/nostr/types.js +2 -0
  83. package/dist/plugins/smartwallet/index.js +18 -0
  84. package/dist/plugins/smartwallet/smartWalletPlugin.js +511 -0
  85. package/dist/plugins/smartwallet/types.js +2 -0
  86. package/dist/plugins/web3/index.js +20 -0
  87. package/dist/plugins/web3/types.js +2 -0
  88. package/dist/plugins/web3/web3Connector.js +533 -0
  89. package/dist/plugins/web3/web3ConnectorPlugin.js +455 -0
  90. package/dist/plugins/web3/web3Signer.js +314 -0
  91. package/dist/plugins/webauthn/index.js +19 -0
  92. package/dist/plugins/webauthn/types.js +14 -0
  93. package/dist/plugins/webauthn/webauthn.js +496 -0
  94. package/dist/plugins/webauthn/webauthnPlugin.js +489 -0
  95. package/dist/plugins/webauthn/webauthnSigner.js +310 -0
  96. package/dist/plugins/zkproof/index.js +53 -0
  97. package/dist/plugins/zkproof/types.js +2 -0
  98. package/dist/plugins/zkproof/zkCredentials.js +213 -0
  99. package/dist/plugins/zkproof/zkProofConnector.js +198 -0
  100. package/dist/plugins/zkproof/zkProofPlugin.js +272 -0
  101. package/dist/storage/storage.js +145 -0
  102. package/dist/types/config/simplified-config.d.ts +114 -0
  103. package/dist/types/core.d.ts +305 -0
  104. package/dist/types/crypto/asymmetric.d.ts +6 -0
  105. package/dist/types/crypto/double-ratchet.d.ts +22 -0
  106. package/dist/types/crypto/file-encryption.d.ts +19 -0
  107. package/dist/types/crypto/hashing.d.ts +9 -0
  108. package/dist/types/crypto/index.d.ts +13 -0
  109. package/dist/types/crypto/mls-codec.d.ts +39 -0
  110. package/dist/types/crypto/mls.d.ts +130 -0
  111. package/dist/types/crypto/pgp.d.ts +95 -0
  112. package/dist/types/crypto/random-generation.d.ts +35 -0
  113. package/dist/types/crypto/sframe.d.ts +102 -0
  114. package/dist/types/crypto/signal-protocol.d.ts +26 -0
  115. package/dist/types/crypto/symmetric.d.ts +9 -0
  116. package/dist/types/crypto/types.d.ts +144 -0
  117. package/dist/types/crypto/utils.d.ts +22 -0
  118. package/dist/types/examples/auth-test.d.ts +8 -0
  119. package/dist/types/examples/crypto-identity-example.d.ts +5 -0
  120. package/dist/types/examples/crypto-working-test.d.ts +1 -0
  121. package/dist/types/examples/double-ratchet-test.d.ts +1 -0
  122. package/dist/types/examples/mls-advanced-example.d.ts +53 -0
  123. package/dist/types/examples/mls-sframe-test.d.ts +1 -0
  124. package/dist/types/examples/pgp-example.d.ts +75 -0
  125. package/dist/types/examples/quick-auth-test.d.ts +8 -0
  126. package/dist/types/examples/random-generation-test.d.ts +1 -0
  127. package/dist/types/examples/signal-protocol-test.d.ts +1 -0
  128. package/dist/types/examples/simple-api-test.d.ts +10 -0
  129. package/dist/types/examples/simple-crypto-identity-example.d.ts +6 -0
  130. package/dist/types/examples/timeout-test.d.ts +8 -0
  131. package/dist/types/examples/zkproof-credentials-example.d.ts +12 -0
  132. package/dist/types/examples/zkproof-example.d.ts +11 -0
  133. package/dist/types/gundb/api.d.ts +185 -0
  134. package/dist/types/gundb/crypto.d.ts +95 -0
  135. package/dist/types/gundb/db.d.ts +397 -0
  136. package/dist/types/gundb/derive.d.ts +21 -0
  137. package/dist/types/gundb/errors.d.ts +42 -0
  138. package/dist/types/gundb/index.d.ts +3 -0
  139. package/dist/types/gundb/rxjs.d.ts +110 -0
  140. package/dist/types/gundb/types.d.ts +255 -0
  141. package/dist/types/index.d.ts +16 -0
  142. package/dist/types/interfaces/common.d.ts +85 -0
  143. package/dist/types/interfaces/events.d.ts +131 -0
  144. package/dist/types/interfaces/plugin.d.ts +162 -0
  145. package/dist/types/interfaces/shogun.d.ts +208 -0
  146. package/dist/types/managers/AuthManager.d.ts +72 -0
  147. package/dist/types/managers/CoreInitializer.d.ts +40 -0
  148. package/dist/types/managers/CryptoIdentityManager.d.ts +102 -0
  149. package/dist/types/managers/EventManager.d.ts +49 -0
  150. package/dist/types/managers/PluginManager.d.ts +145 -0
  151. package/dist/types/plugins/base.d.ts +35 -0
  152. package/dist/types/plugins/index.d.ts +18 -0
  153. package/dist/types/plugins/nostr/index.d.ts +4 -0
  154. package/dist/types/plugins/nostr/nostrConnector.d.ts +119 -0
  155. package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +163 -0
  156. package/dist/types/plugins/nostr/nostrSigner.d.ts +105 -0
  157. package/dist/types/plugins/nostr/types.d.ts +122 -0
  158. package/dist/types/plugins/smartwallet/index.d.ts +2 -0
  159. package/dist/types/plugins/smartwallet/smartWalletPlugin.d.ts +67 -0
  160. package/dist/types/plugins/smartwallet/types.d.ts +80 -0
  161. package/dist/types/plugins/web3/index.d.ts +4 -0
  162. package/dist/types/plugins/web3/types.d.ts +107 -0
  163. package/dist/types/plugins/web3/web3Connector.d.ts +129 -0
  164. package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +160 -0
  165. package/dist/types/plugins/web3/web3Signer.d.ts +114 -0
  166. package/dist/types/plugins/webauthn/index.d.ts +3 -0
  167. package/dist/types/plugins/webauthn/types.d.ts +183 -0
  168. package/dist/types/plugins/webauthn/webauthn.d.ts +129 -0
  169. package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +179 -0
  170. package/dist/types/plugins/webauthn/webauthnSigner.d.ts +91 -0
  171. package/dist/types/plugins/zkproof/index.d.ts +48 -0
  172. package/dist/types/plugins/zkproof/types.d.ts +123 -0
  173. package/dist/types/plugins/zkproof/zkCredentials.d.ts +112 -0
  174. package/dist/types/plugins/zkproof/zkProofConnector.d.ts +46 -0
  175. package/dist/types/plugins/zkproof/zkProofPlugin.d.ts +76 -0
  176. package/dist/types/storage/storage.d.ts +51 -0
  177. package/dist/types/utils/errorHandler.d.ts +119 -0
  178. package/dist/types/utils/eventEmitter.d.ts +39 -0
  179. package/dist/types/utils/seedPhrase.d.ts +50 -0
  180. package/dist/types/utils/validation.d.ts +27 -0
  181. package/dist/utils/errorHandler.js +246 -0
  182. package/dist/utils/eventEmitter.js +79 -0
  183. package/dist/utils/seedPhrase.js +97 -0
  184. package/dist/utils/validation.js +81 -0
  185. package/package.json +10 -1
@@ -0,0 +1,1946 @@
1
+ "use strict";
2
+ /**
3
+ * GunDB class with enhanced features:
4
+ * - Dynamic peer linking
5
+ * - Support for remove/unset operations
6
+ * - Direct authentication through Gun.user()
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __importDefault = (this && this.__importDefault) || function (mod) {
42
+ return (mod && mod.__esModule) ? mod : { "default": mod };
43
+ };
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;
48
+ const sea_1 = __importDefault(require("gun/sea"));
49
+ 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
+ const derive_1 = __importDefault(require("./derive"));
57
+ exports.derive = derive_1.default;
58
+ const errorHandler_1 = require("../utils/errorHandler");
59
+ const eventEmitter_1 = require("../utils/eventEmitter");
60
+ const rxjs_1 = require("./rxjs");
61
+ Object.defineProperty(exports, "RxJS", { enumerable: true, get: function () { return rxjs_1.RxJS; } });
62
+ const GunErrors = __importStar(require("./errors"));
63
+ exports.GunErrors = GunErrors;
64
+ const crypto = __importStar(require("./crypto"));
65
+ exports.crypto = crypto;
66
+ const CryptoIdentityManager_1 = require("../managers/CryptoIdentityManager");
67
+ /**
68
+ * Configuration constants for timeouts and security
69
+ */
70
+ const CONFIG = {
71
+ TIMEOUTS: {
72
+ USER_DATA_OPERATION: 5000,
73
+ },
74
+ PASSWORD: {
75
+ MIN_LENGTH: 8,
76
+ REQUIRE_UPPERCASE: false,
77
+ REQUIRE_LOWERCASE: false,
78
+ REQUIRE_NUMBERS: false,
79
+ REQUIRE_SPECIAL_CHARS: false,
80
+ },
81
+ };
82
+ class DataBase {
83
+ constructor(gun, appScope = "shogun", core) {
84
+ this.user = null;
85
+ this.onAuthCallbacks = [];
86
+ console.log("[DB] Initializing DataBase");
87
+ // Store core reference
88
+ this.core = core;
89
+ // Initialize event emitter
90
+ this.eventEmitter = new eventEmitter_1.EventEmitter();
91
+ // Validate Gun instance
92
+ if (!gun) {
93
+ throw new Error("Gun instance is required but was not provided");
94
+ }
95
+ if (typeof gun !== "object") {
96
+ throw new Error(`Gun instance must be an object, received: ${typeof gun}`);
97
+ }
98
+ if (typeof gun.user !== "function") {
99
+ throw new Error(`Gun instance is invalid: gun.user is not a function. Received gun.user type: ${typeof gun.user}`);
100
+ }
101
+ if (typeof gun.get !== "function") {
102
+ throw new Error(`Gun instance is invalid: gun.get is not a function. Received gun.get type: ${typeof gun.get}`);
103
+ }
104
+ if (typeof gun.on !== "function") {
105
+ throw new Error(`Gun instance is invalid: gun.on is not a function. Received gun.on type: ${typeof gun.on}`);
106
+ }
107
+ this.gun = gun;
108
+ console.log("[DB] Gun instance validated");
109
+ // Recall only if NOT disabled and there's a "pair" in sessionStorage
110
+ this.user = this.gun.user().recall({ sessionStorage: true });
111
+ console.log("[DB] User recall completed");
112
+ this.subscribeToAuthEvents();
113
+ console.log("[DB] Auth events subscribed");
114
+ this.crypto = crypto;
115
+ this.sea = sea_1.default;
116
+ this._rxjs = new rxjs_1.RxJS(this.gun);
117
+ this.node = null;
118
+ console.log("[DB] DataBase initialization completed");
119
+ }
120
+ /**
121
+ * Initialize the GunInstance asynchronously
122
+ * This method should be called after construction to perform async operations
123
+ */
124
+ initialize(appScope = "shogun") {
125
+ console.log(`[DB] Initializing with appScope: ${appScope}`);
126
+ try {
127
+ const sessionResult = this.restoreSession();
128
+ console.log(`[DB] Session restore result: ${sessionResult.success ? "success" : "failed"}`);
129
+ this.node = this.gun.get(appScope);
130
+ console.log("[DB] App scope node initialized");
131
+ // Initialize CryptoIdentityManager
132
+ this._cryptoIdentityManager = new CryptoIdentityManager_1.CryptoIdentityManager(this.core);
133
+ console.log("[DB] CryptoIdentityManager initialized");
134
+ if (sessionResult.success) {
135
+ console.log("[DB] Session automatically restored");
136
+ }
137
+ else {
138
+ console.log("[DB] No previous session to restore");
139
+ }
140
+ }
141
+ catch (error) {
142
+ console.error("[DB] Error during automatic session restoration:", error);
143
+ }
144
+ }
145
+ subscribeToAuthEvents() {
146
+ this.gun.on("auth", (ack) => {
147
+ // Auth event received
148
+ if (ack.err) {
149
+ errorHandler_1.ErrorHandler.handle(errorHandler_1.ErrorType.GUN, "AUTH_EVENT_ERROR", ack.err, new Error(ack.err));
150
+ }
151
+ else {
152
+ this.notifyAuthListeners(ack.sea?.pub || "");
153
+ }
154
+ });
155
+ }
156
+ notifyAuthListeners(pub) {
157
+ const user = this.gun.user();
158
+ this.onAuthCallbacks.forEach((cb) => cb(user));
159
+ }
160
+ /**
161
+ * Adds a new peer to the network
162
+ * @param peer URL of the peer to add
163
+ */
164
+ addPeer(peer) {
165
+ console.log(`[PEER] Adding peer: ${peer}`);
166
+ this.gun.opt({ peers: [peer] });
167
+ console.log(`[PEER] Peer added successfully`);
168
+ }
169
+ /**
170
+ * Removes a peer from the network
171
+ * @param peer URL of the peer to remove
172
+ */
173
+ removePeer(peer) {
174
+ console.log(`[PEER] Removing peer: ${peer}`);
175
+ try {
176
+ // Get current peers from Gun instance
177
+ const gunOpts = this.gun._.opt;
178
+ if (gunOpts && gunOpts.peers) {
179
+ // Remove the peer from the peers object
180
+ delete gunOpts.peers[peer];
181
+ // Also try to close the connection if it exists
182
+ const peerConnection = gunOpts.peers[peer];
183
+ if (peerConnection && typeof peerConnection.close === "function") {
184
+ peerConnection.close();
185
+ }
186
+ console.log(`[PEER] Peer removed successfully`);
187
+ }
188
+ else {
189
+ console.error(`[PEER] Peer not found in current connections: ${peer}`);
190
+ }
191
+ }
192
+ catch (error) {
193
+ console.error(`[PEER] Error removing peer ${peer}:`, error);
194
+ }
195
+ }
196
+ /**
197
+ * Gets the list of currently connected peers
198
+ * @returns Array of peer URLs
199
+ */
200
+ getCurrentPeers() {
201
+ try {
202
+ const gunOpts = this.gun._.opt;
203
+ if (gunOpts && gunOpts.peers) {
204
+ return Object.keys(gunOpts.peers).filter((peer) => {
205
+ const peerObj = gunOpts.peers[peer];
206
+ // Check if peer is actually connected (not just configured)
207
+ return peerObj && peerObj.wire && peerObj.wire.hied !== "bye";
208
+ });
209
+ }
210
+ return [];
211
+ }
212
+ catch (error) {
213
+ console.error("Error getting current peers:", error);
214
+ return [];
215
+ }
216
+ }
217
+ /**
218
+ * Gets the list of all configured peers (connected and disconnected)
219
+ * @returns Array of peer URLs
220
+ */
221
+ getAllConfiguredPeers() {
222
+ try {
223
+ const gunOpts = this.gun._.opt;
224
+ if (gunOpts && gunOpts.peers) {
225
+ return Object.keys(gunOpts.peers);
226
+ }
227
+ return [];
228
+ }
229
+ catch (error) {
230
+ console.error("Error getting configured peers:", error);
231
+ return [];
232
+ }
233
+ }
234
+ /**
235
+ * Gets detailed information about all peers
236
+ * @returns Object with peer information
237
+ */
238
+ getPeerInfo() {
239
+ try {
240
+ const gunOpts = this.gun._.opt;
241
+ const peerInfo = {};
242
+ if (gunOpts && gunOpts.peers) {
243
+ Object.keys(gunOpts.peers).forEach((peer) => {
244
+ const peerObj = gunOpts.peers[peer];
245
+ const isConnected = peerObj && peerObj.wire && peerObj.wire.hied !== "bye";
246
+ const status = isConnected
247
+ ? "connected"
248
+ : peerObj && peerObj.wire
249
+ ? "disconnected"
250
+ : "not_initialized";
251
+ peerInfo[peer] = {
252
+ connected: isConnected,
253
+ status: status,
254
+ };
255
+ });
256
+ }
257
+ return peerInfo;
258
+ }
259
+ catch (error) {
260
+ console.error("Error getting peer info:", error);
261
+ return {};
262
+ }
263
+ }
264
+ /**
265
+ * Reconnects to a specific peer
266
+ * @param peer URL of the peer to reconnect
267
+ */
268
+ reconnectToPeer(peer) {
269
+ try {
270
+ // First remove the peer
271
+ this.removePeer(peer);
272
+ // Add it back immediately instead of with timeout
273
+ this.addPeer(peer);
274
+ }
275
+ catch (error) {
276
+ console.error(`Error reconnecting to peer ${peer}:`, error);
277
+ }
278
+ }
279
+ /**
280
+ * Clears all peers and optionally adds new ones
281
+ * @param newPeers Optional array of new peers to add
282
+ */
283
+ resetPeers(newPeers) {
284
+ try {
285
+ const gunOpts = this.gun._.opt;
286
+ if (gunOpts && gunOpts.peers) {
287
+ // Clear all existing peers
288
+ Object.keys(gunOpts.peers).forEach((peer) => {
289
+ this.removePeer(peer);
290
+ });
291
+ // Add new peers if provided
292
+ if (newPeers && newPeers.length > 0) {
293
+ newPeers.forEach((peer) => {
294
+ this.addPeer(peer);
295
+ });
296
+ }
297
+ }
298
+ }
299
+ catch (error) {
300
+ console.error("Error resetting peers:", error);
301
+ }
302
+ }
303
+ /**
304
+ * Registers an authentication callback
305
+ * @param callback Function to call on auth events
306
+ * @returns Function to unsubscribe the callback
307
+ */
308
+ onAuth(callback) {
309
+ this.onAuthCallbacks.push(callback);
310
+ const user = this.gun.user();
311
+ if (user && user.is)
312
+ callback(user);
313
+ return () => {
314
+ const i = this.onAuthCallbacks.indexOf(callback);
315
+ if (i !== -1)
316
+ this.onAuthCallbacks.splice(i, 1);
317
+ };
318
+ }
319
+ /**
320
+ * Helper method to navigate to a nested path by splitting and chaining .get() calls
321
+ * @param node Starting Gun node
322
+ * @param path Path string (e.g., "test/data/marco")
323
+ * @returns Gun node at the specified path
324
+ */
325
+ navigateToPath(node, path) {
326
+ if (!path || typeof path !== "string")
327
+ return node;
328
+ // Split path by '/' and filter out empty segments
329
+ const pathSegments = path
330
+ .split("/")
331
+ .map((segment) => segment.trim())
332
+ .filter((segment) => segment.length > 0);
333
+ // Chain .get() calls for each path segment
334
+ let currentNode = node;
335
+ for (const segment of pathSegments) {
336
+ currentNode = currentNode.get(segment);
337
+ }
338
+ return currentNode;
339
+ }
340
+ /**
341
+ * Gets the Gun instance
342
+ * @returns Gun instance
343
+ */
344
+ getGun() {
345
+ return this.gun;
346
+ }
347
+ /**
348
+ * Gets the current user
349
+ * @returns Current user object or null
350
+ */
351
+ getCurrentUser() {
352
+ try {
353
+ const _user = this.gun.user();
354
+ return _user?.is?.pub
355
+ ? {
356
+ pub: _user?.is?.pub,
357
+ epub: _user?.is?.epub,
358
+ alias: _user?.is?.alias,
359
+ user: _user,
360
+ }
361
+ : null;
362
+ }
363
+ catch (error) {
364
+ console.error("Error getting current user:", error);
365
+ return null;
366
+ }
367
+ }
368
+ /**
369
+ * Gets the current user instance
370
+ * @returns User instance
371
+ */
372
+ getUser() {
373
+ return this.gun.user();
374
+ }
375
+ /**
376
+ * Gets a node at the specified path
377
+ * @param path Path to the node
378
+ * @returns Gun node
379
+ */
380
+ get(path) {
381
+ return this.navigateToPath(this.node, path);
382
+ }
383
+ /**
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
401
+ */
402
+ async put(path, data) {
403
+ const node = this.navigateToPath(this.node, path);
404
+ return await node.put(data).then();
405
+ }
406
+ /**
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
411
+ */
412
+ async set(path, data) {
413
+ const node = this.navigateToPath(this.node, path);
414
+ return await node.set(data).then();
415
+ }
416
+ /**
417
+ * Removes data at the specified path
418
+ * @param path Path to remove
419
+ * @returns Promise resolving to operation result
420
+ */
421
+ async remove(path) {
422
+ const node = this.navigateToPath(this.node, path);
423
+ return await node.put(null).then();
424
+ }
425
+ /**
426
+ * Checks if a user is currently logged in
427
+ * @returns True if logged in
428
+ */
429
+ isLoggedIn() {
430
+ try {
431
+ const user = this.gun.user();
432
+ return !!(user && user.is && user.is.pub);
433
+ }
434
+ catch (error) {
435
+ console.error("Error checking login status:", error);
436
+ return false;
437
+ }
438
+ }
439
+ /**
440
+ * Attempts to restore user session from local storage
441
+ * @returns Promise resolving to session restoration result
442
+ */
443
+ restoreSession() {
444
+ try {
445
+ if (typeof localStorage === "undefined") {
446
+ return { success: false, error: "localStorage not available" };
447
+ }
448
+ // Check for session data in sessionStorage first (new format)
449
+ let sessionData = sessionStorage.getItem("gunSessionData");
450
+ if (!sessionData) {
451
+ // Fallback to old localStorage format
452
+ const sessionInfo = localStorage.getItem("gun/session");
453
+ const pairInfo = localStorage.getItem("gun/pair");
454
+ if (!sessionInfo || !pairInfo) {
455
+ // No saved session found
456
+ return { success: false, error: "No saved session" };
457
+ }
458
+ let session, pair;
459
+ try {
460
+ session = JSON.parse(sessionInfo);
461
+ pair = JSON.parse(pairInfo);
462
+ }
463
+ catch (parseError) {
464
+ console.error("Error parsing session data:", parseError);
465
+ // Clear corrupted data
466
+ localStorage.removeItem("gun/session");
467
+ localStorage.removeItem("gun/pair");
468
+ return { success: false, error: "Corrupted session data" };
469
+ }
470
+ // Convert old format to new format
471
+ sessionData = JSON.stringify({
472
+ username: session.username,
473
+ pair: pair,
474
+ userPub: session.userPub,
475
+ timestamp: Date.now(),
476
+ expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days
477
+ });
478
+ }
479
+ let session;
480
+ try {
481
+ session = JSON.parse(sessionData);
482
+ }
483
+ catch (parseError) {
484
+ console.error("Error parsing session data:", parseError);
485
+ // Clear corrupted data
486
+ sessionStorage.removeItem("gunSessionData");
487
+ return { success: false, error: "Corrupted session data" };
488
+ }
489
+ // Validate session data structure
490
+ if (!session.username || !session.userPub) {
491
+ // Invalid session data, clearing storage
492
+ sessionStorage.removeItem("gunSessionData");
493
+ localStorage.removeItem("gun/session");
494
+ localStorage.removeItem("gun/pair");
495
+ return { success: false, error: "Incomplete session data" };
496
+ }
497
+ // Check if session is expired
498
+ if (session.expiresAt && Date.now() > session.expiresAt) {
499
+ // Session expired, clearing storage
500
+ sessionStorage.removeItem("gunSessionData");
501
+ localStorage.removeItem("gun/session");
502
+ localStorage.removeItem("gun/pair");
503
+ return { success: false, error: "Session expired" };
504
+ }
505
+ // Attempt to restore user session
506
+ try {
507
+ const userInstance = this.gun.user();
508
+ if (!userInstance) {
509
+ console.error("Gun user instance not available");
510
+ sessionStorage.removeItem("gunSessionData");
511
+ localStorage.removeItem("gun/session");
512
+ localStorage.removeItem("gun/pair");
513
+ return { success: false, error: "Gun user instance not available" };
514
+ }
515
+ // Set the user pair if available
516
+ if (session.pair) {
517
+ try {
518
+ userInstance._ = { ...userInstance._, sea: session.pair };
519
+ }
520
+ catch (pairError) {
521
+ console.error("Error setting user pair:", pairError);
522
+ }
523
+ }
524
+ // Attempt to recall user session
525
+ try {
526
+ if (typeof sessionStorage !== "undefined" &&
527
+ sessionStorage.getItem("pair")) {
528
+ const recallResult = userInstance.recall({ sessionStorage: true });
529
+ }
530
+ else {
531
+ const recallResult = userInstance;
532
+ }
533
+ }
534
+ catch (recallError) {
535
+ console.error("Error during recall:", recallError);
536
+ }
537
+ // Verify session restoration success
538
+ if (userInstance.is && userInstance.is.pub === session.userPub) {
539
+ this.user = userInstance;
540
+ // Session restored successfully for user
541
+ return {
542
+ success: true,
543
+ userPub: session.userPub,
544
+ };
545
+ }
546
+ else {
547
+ // Session restoration verification failed
548
+ sessionStorage.removeItem("gunSessionData");
549
+ localStorage.removeItem("gun/session");
550
+ localStorage.removeItem("gun/pair");
551
+ return { success: false, error: "Session verification failed" };
552
+ }
553
+ }
554
+ catch (error) {
555
+ console.error(`Error restoring session: ${error}`);
556
+ return {
557
+ success: false,
558
+ error: `Session restoration failed: ${error}`,
559
+ };
560
+ }
561
+ }
562
+ catch (mainError) {
563
+ console.error(`Error in restoreSession: ${mainError}`);
564
+ return {
565
+ success: false,
566
+ error: `Session restoration failed: ${mainError}`,
567
+ };
568
+ }
569
+ return { success: false, error: "No session data available" };
570
+ }
571
+ logout() {
572
+ try {
573
+ const currentUser = this.gun.user();
574
+ if (!currentUser || !currentUser.is) {
575
+ return;
576
+ }
577
+ // Log out user
578
+ try {
579
+ currentUser.leave();
580
+ }
581
+ catch (gunError) {
582
+ console.error("Error during Gun logout:", gunError);
583
+ }
584
+ // Clear user reference
585
+ this.user = null;
586
+ // Clear local session data
587
+ try {
588
+ // Clear session data if needed
589
+ }
590
+ catch (error) {
591
+ console.error("Error clearing local session data:", error);
592
+ }
593
+ // Clear session storage
594
+ try {
595
+ if (typeof sessionStorage !== "undefined") {
596
+ sessionStorage.removeItem("gunSessionData");
597
+ // Session storage cleared
598
+ }
599
+ }
600
+ catch (error) {
601
+ console.error("Error clearing session storage:", error);
602
+ }
603
+ // Logout completed successfully
604
+ }
605
+ catch (error) {
606
+ console.error("Error during logout:", error);
607
+ }
608
+ }
609
+ /**
610
+ * Accesses the RxJS module for reactive programming
611
+ * @returns GunRxJS instance
612
+ */
613
+ rx() {
614
+ return this._rxjs;
615
+ }
616
+ /**
617
+ * Validates password strength according to security requirements
618
+ */
619
+ validatePasswordStrength(password) {
620
+ if (password.length < CONFIG.PASSWORD.MIN_LENGTH) {
621
+ return {
622
+ valid: false,
623
+ error: `Password must be at least ${CONFIG.PASSWORD.MIN_LENGTH} characters long`,
624
+ };
625
+ }
626
+ const validations = [];
627
+ if (CONFIG.PASSWORD.REQUIRE_UPPERCASE && !/[A-Z]/.test(password)) {
628
+ validations.push("uppercase letter");
629
+ }
630
+ if (CONFIG.PASSWORD.REQUIRE_LOWERCASE && !/[a-z]/.test(password)) {
631
+ validations.push("lowercase letter");
632
+ }
633
+ if (CONFIG.PASSWORD.REQUIRE_NUMBERS && !/\d/.test(password)) {
634
+ validations.push("number");
635
+ }
636
+ if (CONFIG.PASSWORD.REQUIRE_SPECIAL_CHARS &&
637
+ !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
638
+ validations.push("special character");
639
+ }
640
+ if (validations.length > 0) {
641
+ return {
642
+ valid: false,
643
+ error: `Password must contain at least one: ${validations.join(", ")}`,
644
+ };
645
+ }
646
+ return { valid: true };
647
+ }
648
+ /**
649
+ * Validates signup credentials with enhanced security
650
+ */
651
+ validateSignupCredentials(username, password, pair) {
652
+ // Validate username
653
+ if (!username || username.length < 1) {
654
+ return {
655
+ valid: false,
656
+ error: "Username must be more than 0 characters long!",
657
+ };
658
+ }
659
+ // Validate username format (alphanumeric and some special chars only)
660
+ if (!/^[a-zA-Z0-9._-]+$/.test(username)) {
661
+ return {
662
+ valid: false,
663
+ error: "Username can only contain letters, numbers, dots, underscores, and hyphens",
664
+ };
665
+ }
666
+ // If using pair authentication, skip password validation
667
+ if (pair) {
668
+ if (!pair.pub ||
669
+ !pair.priv ||
670
+ !pair.epub ||
671
+ !pair.epriv) {
672
+ return {
673
+ valid: false,
674
+ error: "Invalid pair provided",
675
+ };
676
+ }
677
+ if (!pair.pub || !pair.priv || !pair.epub || !pair.epriv) {
678
+ return {
679
+ valid: false,
680
+ error: "Invalid pair provided",
681
+ };
682
+ }
683
+ return { valid: true };
684
+ }
685
+ // Validate password strength
686
+ return this.validatePasswordStrength(password);
687
+ }
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
+ /**
819
+ * Signs up a new user using direct Gun authentication
820
+ * @param username Username
821
+ * @param password Password
822
+ * @param pair Optional SEA pair for Web3 login
823
+ * @returns Promise resolving to signup result
824
+ */
825
+ async signUp(username, password, pair) {
826
+ try {
827
+ // Validate credentials with enhanced security
828
+ const validation = this.validateSignupCredentials(username, password, pair);
829
+ if (!validation.valid) {
830
+ return { success: false, error: validation.error };
831
+ }
832
+ let createResult;
833
+ if (pair) {
834
+ // For Web3/plugin authentication, use pair-based creation
835
+ createResult = await this.createNewUserWithPair(username, pair);
836
+ }
837
+ else {
838
+ // For password authentication, use standard creation
839
+ createResult = await this.createNewUser(username, password);
840
+ }
841
+ if (!createResult.success) {
842
+ return { success: false, error: createResult.error };
843
+ }
844
+ // Add a small delay to ensure user is properly registered
845
+ await new Promise((resolve) => setTimeout(resolve, 100));
846
+ // Authenticate the newly created user
847
+ const authResult = await this.authenticateNewUser(username, password, pair);
848
+ if (!authResult.success) {
849
+ return { success: false, error: authResult.error };
850
+ }
851
+ // Validate that we have a userPub
852
+ if (!authResult.userPub ||
853
+ typeof authResult.userPub !== "string" ||
854
+ authResult.userPub.trim().length === 0) {
855
+ console.error("Authentication successful but no valid userPub returned:", authResult);
856
+ return {
857
+ success: false,
858
+ error: "Authentication successful but no valid userPub returned",
859
+ };
860
+ }
861
+ // Set the user instance
862
+ this.user = this.gun.user();
863
+ // Run post-authentication tasks
864
+ try {
865
+ const postAuthResult = await this.runPostAuthOnAuthResult(username, authResult.userPub, authResult);
866
+ // Return the post-auth result which includes the complete user data
867
+ return postAuthResult;
868
+ }
869
+ catch (postAuthError) {
870
+ console.error(`Post-auth error: ${postAuthError}`);
871
+ // Even if post-auth fails, the user was created and authenticated successfully
872
+ return {
873
+ success: true,
874
+ userPub: authResult.userPub,
875
+ username: username,
876
+ isNewUser: true,
877
+ sea: this.gun.user()?._?.sea
878
+ ? {
879
+ pub: this.gun.user()._?.sea.pub,
880
+ priv: this.gun.user()._?.sea.priv,
881
+ epub: this.gun.user()._?.sea.epub,
882
+ epriv: this.gun.user()._?.sea.epriv,
883
+ }
884
+ : undefined,
885
+ };
886
+ }
887
+ }
888
+ catch (error) {
889
+ console.error(`Exception during signup for ${username}: ${error}`);
890
+ return { success: false, error: `Signup failed: ${error}` };
891
+ }
892
+ }
893
+ /**
894
+ * Creates a new user in Gun with pair-based authentication (for Web3/plugins)
895
+ */
896
+ async createNewUserWithPair(username, pair) {
897
+ return new Promise((resolve) => {
898
+ // Validate inputs before creating user
899
+ if (!username ||
900
+ typeof username !== "string" ||
901
+ username.trim().length === 0) {
902
+ resolve({ success: false, error: "Invalid username provided" });
903
+ return;
904
+ }
905
+ if (!pair || !pair.pub || !pair.priv) {
906
+ resolve({ success: false, error: "Invalid pair provided" });
907
+ return;
908
+ }
909
+ // Normalize username
910
+ const normalizedUsername = username.trim().toLowerCase();
911
+ if (normalizedUsername.length === 0) {
912
+ resolve({
913
+ success: false,
914
+ error: "Username cannot be empty",
915
+ });
916
+ return;
917
+ }
918
+ // For pair-based authentication, we don't need to call gun.user().create()
919
+ // because the pair already contains the cryptographic credentials
920
+ // We just need to validate that the pair is valid and return success
921
+ resolve({ success: true, userPub: pair.pub });
922
+ });
923
+ }
924
+ async runPostAuthOnAuthResult(username, userPub, authResult) {
925
+ console.log(`[POSTAUTH] Starting post-auth setup for user: ${username}, userPub: ${userPub}`);
926
+ try {
927
+ console.log(`[POSTAUTH] Validating parameters for user: ${username}`);
928
+ // Validate required parameters
929
+ if (!username ||
930
+ typeof username !== "string" ||
931
+ username.trim().length === 0) {
932
+ console.error(`[POSTAUTH] Invalid username provided: ${username}`);
933
+ throw new Error("Invalid username provided");
934
+ }
935
+ if (!userPub ||
936
+ typeof userPub !== "string" ||
937
+ userPub.trim().length === 0) {
938
+ console.error("[POSTAUTH] Invalid userPub provided:", {
939
+ userPub,
940
+ type: typeof userPub,
941
+ authResult,
942
+ });
943
+ throw new Error("Invalid userPub provided");
944
+ }
945
+ // Additional validation for userPub format
946
+ if (!userPub.includes(".") || userPub.length < 10) {
947
+ console.error(`[POSTAUTH] Invalid userPub format: ${userPub}`);
948
+ throw new Error("Invalid userPub format");
949
+ }
950
+ console.log(`[POSTAUTH] Parameters validated for user: ${username}`);
951
+ // Normalize username to prevent path issues
952
+ const normalizedUsername = username.trim().toLowerCase();
953
+ if (normalizedUsername.length === 0) {
954
+ console.error(`[POSTAUTH] Normalized username is empty for user: ${username}`);
955
+ throw new Error("Username cannot be empty");
956
+ }
957
+ console.log(`[POSTAUTH] Normalized username: ${normalizedUsername}`);
958
+ console.log(`[POSTAUTH] Checking if user exists: ${userPub}`);
959
+ const existingUser = await this.gun.get(userPub).then();
960
+ const isNewUser = !existingUser || !existingUser.alias;
961
+ console.log(`[POSTAUTH] User is ${isNewUser ? "NEW" : "EXISTING"}: ${userPub}`);
962
+ // Get user's encryption public key (epub) for comprehensive tracking
963
+ const userInstance = this.gun.user();
964
+ const userSea = userInstance?._?.sea;
965
+ const epub = userSea?.epub;
966
+ console.log(`[POSTAUTH] User epub retrieved: ${epub ? "yes" : "no"}`);
967
+ // Enhanced user tracking system
968
+ console.log(`[POSTAUTH] Setting up comprehensive user tracking for: ${normalizedUsername}`);
969
+ const trackingResult = await this.setupComprehensiveUserTracking(normalizedUsername, userPub, epub);
970
+ if (!trackingResult) {
971
+ console.error(`[POSTAUTH] Comprehensive user tracking setup failed for: ${normalizedUsername}`);
972
+ return {
973
+ success: false,
974
+ error: "Comprehensive user tracking setup failed",
975
+ };
976
+ }
977
+ console.log(`[POSTAUTH] User tracking setup completed successfully for: ${normalizedUsername}`);
978
+ // Setup crypto identities for the user
979
+ if (this._cryptoIdentityManager && userSea) {
980
+ console.log(`[POSTAUTH] Setting up crypto identities for: ${normalizedUsername}`);
981
+ try {
982
+ const cryptoSetupResult = await this._cryptoIdentityManager.setupCryptoIdentities(normalizedUsername, userSea, false);
983
+ if (cryptoSetupResult.success) {
984
+ console.log(`✅ [POSTAUTH] Crypto identities setup completed for: ${normalizedUsername}`);
985
+ }
986
+ else {
987
+ console.error(`❌ [POSTAUTH] Crypto identities setup failed for: ${normalizedUsername}`, cryptoSetupResult.error);
988
+ // Don't fail the entire auth process if crypto setup fails
989
+ }
990
+ }
991
+ catch (cryptoError) {
992
+ console.error(`❌ [POSTAUTH] Crypto identities setup error for: ${normalizedUsername}`, cryptoError);
993
+ // Don't fail the entire auth process if crypto setup fails
994
+ }
995
+ }
996
+ else {
997
+ console.log(`ℹ️ [POSTAUTH] Skipping crypto identities setup - manager not available or no SEA pair`);
998
+ }
999
+ const result = {
1000
+ success: true,
1001
+ userPub: userPub,
1002
+ username: normalizedUsername,
1003
+ isNewUser: isNewUser,
1004
+ // Get the SEA pair from the user object
1005
+ sea: userSea
1006
+ ? {
1007
+ pub: userSea.pub,
1008
+ priv: userSea.priv,
1009
+ epub: userSea.epub,
1010
+ epriv: userSea.epriv,
1011
+ }
1012
+ : undefined,
1013
+ };
1014
+ console.log(`[POSTAUTH] Post-auth setup completed successfully for user: ${username}`);
1015
+ return result;
1016
+ }
1017
+ catch (error) {
1018
+ console.error(`[POSTAUTH] Error in post-authentication setup for ${username}:`, error);
1019
+ return {
1020
+ success: false,
1021
+ error: `Post-authentication setup failed: ${error}`,
1022
+ };
1023
+ }
1024
+ }
1025
+ /**
1026
+ * Sets up comprehensive user tracking system for agile user lookup
1027
+ * Creates multiple indexes for efficient user discovery
1028
+ */
1029
+ async setupComprehensiveUserTracking(username, userPub, epub) {
1030
+ console.log(`[TRACKING] Starting comprehensive user tracking setup for: ${username}, userPub: ${userPub}`);
1031
+ try {
1032
+ // 1. Create alias index: ~@alias -> userPub (for GunDB compatibility)
1033
+ console.log(`[TRACKING] Step 1: Creating alias index for ${username}`);
1034
+ const aliasIndexResult = await this.createAliasIndex(username, userPub);
1035
+ if (!aliasIndexResult) {
1036
+ console.error(`[TRACKING] Failed to create alias index for ${username}`);
1037
+ return false;
1038
+ }
1039
+ console.log(`[TRACKING] Step 1 completed: Alias index created for ${username}`);
1040
+ // 2. Create username mapping: usernames/alias -> userPub
1041
+ console.log(`[TRACKING] Step 2: Creating username mapping for ${username}`);
1042
+ const usernameMappingResult = await this.createUsernameMapping(username, userPub);
1043
+ if (!usernameMappingResult) {
1044
+ console.error(`[TRACKING] Failed to create username mapping for ${username}`);
1045
+ return false;
1046
+ }
1047
+ console.log(`[TRACKING] Step 2 completed: Username mapping created for ${username}`);
1048
+ // 3. Create user registry: users/userPub -> user data
1049
+ console.log(`[TRACKING] Step 3: Creating user registry for ${username}`);
1050
+ const userRegistryResult = await this.createUserRegistry(username, userPub, epub);
1051
+ if (!userRegistryResult) {
1052
+ console.error(`[TRACKING] Failed to create user registry for ${username}`);
1053
+ return false;
1054
+ }
1055
+ console.log(`[TRACKING] Step 3 completed: User registry created for ${username}`);
1056
+ // 4. Create reverse lookup: userPub -> alias
1057
+ console.log(`[TRACKING] Step 4: Creating reverse lookup for ${username}`);
1058
+ const reverseLookupResult = await this.createReverseLookup(username, userPub);
1059
+ if (!reverseLookupResult) {
1060
+ console.error(`[TRACKING] Failed to create reverse lookup for ${username}`);
1061
+ return false;
1062
+ }
1063
+ console.log(`[TRACKING] Step 4 completed: Reverse lookup created for ${username}`);
1064
+ // 5. Create epub index: epubKeys/epub -> userPub (for encryption lookups)
1065
+ if (epub) {
1066
+ console.log(`[TRACKING] Step 5: Creating epub index for ${username}`);
1067
+ const epubIndexResult = await this.createEpubIndex(epub, userPub);
1068
+ if (!epubIndexResult) {
1069
+ console.error(`[TRACKING] Failed to create epub index for ${username}`);
1070
+ return false;
1071
+ }
1072
+ console.log(`[TRACKING] Step 5 completed: Epub index created for ${username}`);
1073
+ }
1074
+ else {
1075
+ console.log(`[TRACKING] Step 5 skipped: No epub available for ${username}`);
1076
+ }
1077
+ // 6. Create user metadata in user's own node
1078
+ console.log(`[TRACKING] Step 6: Creating user metadata for ${username}`);
1079
+ const userMetadataResult = await this.createUserMetadata(username, userPub, epub);
1080
+ if (!userMetadataResult) {
1081
+ console.error(`[TRACKING] Failed to create user metadata for ${username}`);
1082
+ return false;
1083
+ }
1084
+ console.log(`[TRACKING] Step 6 completed: User metadata created for ${username}`);
1085
+ console.log(`[TRACKING] Comprehensive user tracking setup completed successfully for: ${username}`);
1086
+ return true;
1087
+ }
1088
+ catch (error) {
1089
+ console.error(`[TRACKING] Error in comprehensive user tracking setup for ${username}:`, error);
1090
+ // Don't throw - continue with other operations
1091
+ return false;
1092
+ }
1093
+ }
1094
+ /**
1095
+ * Creates alias index following GunDB pattern: ~@alias -> userPub
1096
+ */
1097
+ async createAliasIndex(username, userPub) {
1098
+ try {
1099
+ // Create a simple alias mapping without using GunDB's complex alias system
1100
+ // Store username -> userPub mapping in a simple way
1101
+ const aliasData = {
1102
+ username: username,
1103
+ userPub: userPub,
1104
+ createdAt: Date.now(),
1105
+ };
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
+ });
1128
+ }
1129
+ catch (error) {
1130
+ console.error(`Error creating alias index: ${error}`);
1131
+ return false;
1132
+ }
1133
+ }
1134
+ /**
1135
+ * Creates username mapping: usernames/alias -> userPub
1136
+ */
1137
+ async createUsernameMapping(username, userPub) {
1138
+ 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
+ });
1153
+ }
1154
+ catch (error) {
1155
+ console.error(`Error creating username mapping: ${error}`);
1156
+ return false;
1157
+ }
1158
+ }
1159
+ /**
1160
+ * Creates user registry: users/userPub -> user data
1161
+ */
1162
+ async createUserRegistry(username, userPub, epub) {
1163
+ try {
1164
+ const userData = {
1165
+ username: username,
1166
+ userPub: userPub,
1167
+ epub: epub,
1168
+ registeredAt: Date.now().toString(),
1169
+ lastSeen: Date.now().toString(),
1170
+ };
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
+ });
1185
+ }
1186
+ catch (error) {
1187
+ console.error(`Error creating user registry: ${error}`);
1188
+ return false;
1189
+ }
1190
+ }
1191
+ /**
1192
+ * Creates reverse lookup: userPub -> alias
1193
+ */
1194
+ async createReverseLookup(username, userPub) {
1195
+ 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
+ }
1208
+ }
1209
+ catch (error) {
1210
+ console.error(`Error creating reverse lookup: ${error}`);
1211
+ return false;
1212
+ }
1213
+ }
1214
+ /**
1215
+ * Creates epub index: epubKeys/epub -> userPub
1216
+ */
1217
+ async createEpubIndex(epub, userPub) {
1218
+ 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
+ });
1233
+ }
1234
+ catch (error) {
1235
+ console.error(`Error creating epub index: ${error}`);
1236
+ return false;
1237
+ }
1238
+ }
1239
+ /**
1240
+ * Creates user metadata in user's own node
1241
+ */
1242
+ async createUserMetadata(username, userPub, epub) {
1243
+ try {
1244
+ const userMetadata = {
1245
+ username: username,
1246
+ epub: epub,
1247
+ registeredAt: Date.now(),
1248
+ lastSeen: Date.now(),
1249
+ };
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
+ });
1261
+ }
1262
+ catch (error) {
1263
+ console.error(`Error creating user metadata: ${error}`);
1264
+ return false;
1265
+ }
1266
+ }
1267
+ /**
1268
+ * Gets user information by alias using the comprehensive tracking system
1269
+ * @param alias Username/alias to lookup
1270
+ * @returns Promise resolving to user information or null if not found
1271
+ */
1272
+ async getUserByAlias(alias) {
1273
+ try {
1274
+ const normalizedAlias = alias.trim().toLowerCase();
1275
+ if (!normalizedAlias) {
1276
+ return null;
1277
+ }
1278
+ // Method 1: Try GunDB standard alias lookup (~@alias)
1279
+ try {
1280
+ const aliasData = await this.gun.get(`~@${normalizedAlias}`).then();
1281
+ if (aliasData && aliasData["~pubKeyOfUser"]) {
1282
+ const userPub = aliasData["~pubKeyOfUser"]["#"] || aliasData["~pubKeyOfUser"];
1283
+ if (userPub) {
1284
+ const userData = await this.getUserDataByPub(userPub);
1285
+ if (userData) {
1286
+ return userData;
1287
+ }
1288
+ }
1289
+ }
1290
+ }
1291
+ catch (error) {
1292
+ console.error(`GunDB alias lookup failed for ${normalizedAlias}:`, error);
1293
+ }
1294
+ // Method 2: Try username mapping (usernames/alias -> userPub)
1295
+ try {
1296
+ const userPub = await this.node
1297
+ .get("usernames")
1298
+ .get(normalizedAlias)
1299
+ .then();
1300
+ if (userPub) {
1301
+ const userData = await this.getUserDataByPub(userPub);
1302
+ if (userData) {
1303
+ return userData;
1304
+ }
1305
+ }
1306
+ }
1307
+ catch (error) {
1308
+ console.error(`Username mapping lookup failed for ${normalizedAlias}:`, error);
1309
+ }
1310
+ return null;
1311
+ }
1312
+ catch (error) {
1313
+ console.error(`Error looking up user by alias ${alias}:`, error);
1314
+ return null;
1315
+ }
1316
+ }
1317
+ /**
1318
+ * Gets user information by public key
1319
+ * @param userPub User's public key
1320
+ * @returns Promise resolving to user information or null if not found
1321
+ */
1322
+ async getUserDataByPub(userPub) {
1323
+ try {
1324
+ if (!userPub || typeof userPub !== "string") {
1325
+ return null;
1326
+ }
1327
+ // Method 1: Try user registry (users/userPub -> user data)
1328
+ try {
1329
+ const userData = await this.node.get("users").get(userPub).then();
1330
+ if (userData && userData.username) {
1331
+ return {
1332
+ userPub: userData.userPub || userPub,
1333
+ epub: userData.epub || null,
1334
+ username: userData.username,
1335
+ registeredAt: userData.registeredAt || 0,
1336
+ lastSeen: userData.lastSeen || 0,
1337
+ };
1338
+ }
1339
+ }
1340
+ catch (error) {
1341
+ console.error(`User registry lookup failed for ${userPub}:`, error);
1342
+ }
1343
+ // Method 2: Try user's own node
1344
+ try {
1345
+ const userNodeData = await this.gun.get(userPub).then();
1346
+ if (userNodeData && userNodeData.username) {
1347
+ return {
1348
+ userPub: userPub,
1349
+ epub: userNodeData.epub || null,
1350
+ username: userNodeData.username,
1351
+ registeredAt: userNodeData.registeredAt || 0,
1352
+ lastSeen: userNodeData.lastSeen || 0,
1353
+ };
1354
+ }
1355
+ }
1356
+ catch (error) {
1357
+ console.error(`User node lookup failed for ${userPub}:`, error);
1358
+ }
1359
+ return null;
1360
+ }
1361
+ catch (error) {
1362
+ console.error(`Error looking up user data by pub ${userPub}:`, error);
1363
+ return null;
1364
+ }
1365
+ }
1366
+ /**
1367
+ * Gets user public key by encryption public key (epub)
1368
+ * @param epub User's encryption public key
1369
+ * @returns Promise resolving to user public key or null if not found
1370
+ */
1371
+ async getUserPubByEpub(epub) {
1372
+ try {
1373
+ if (!epub || typeof epub !== "string") {
1374
+ return null;
1375
+ }
1376
+ const userPub = await this.node.get("epubKeys").get(epub).then();
1377
+ return userPub || null;
1378
+ }
1379
+ catch (error) {
1380
+ console.error(`Error looking up user pub by epub ${epub}:`, error);
1381
+ return null;
1382
+ }
1383
+ }
1384
+ /**
1385
+ * Gets user alias by public key
1386
+ * @param userPub User's public key
1387
+ * @returns Promise resolving to user alias or null if not found
1388
+ */
1389
+ async getUserAliasByPub(userPub) {
1390
+ try {
1391
+ if (!userPub || typeof userPub !== "string") {
1392
+ return null;
1393
+ }
1394
+ const alias = await this.node.get("userAliases").get(userPub).then();
1395
+ return alias || null;
1396
+ }
1397
+ catch (error) {
1398
+ console.error(`Error looking up user alias by pub ${userPub}:`, error);
1399
+ return null;
1400
+ }
1401
+ }
1402
+ /**
1403
+ * Gets all registered users (for admin purposes)
1404
+ * @returns Promise resolving to array of user information
1405
+ */
1406
+ async getAllRegisteredUsers() {
1407
+ try {
1408
+ const users = [];
1409
+ // Get all users from the users registry
1410
+ const usersNode = this.node.get("users");
1411
+ // Note: This is a simplified approach. In a real implementation,
1412
+ // you might want to use Gun's map functionality or iterate through
1413
+ // known user public keys
1414
+ return users;
1415
+ }
1416
+ catch (error) {
1417
+ console.error(`Error getting all registered users:`, error);
1418
+ return [];
1419
+ }
1420
+ }
1421
+ /**
1422
+ * Updates user's last seen timestamp
1423
+ * @param userPub User's public key
1424
+ */
1425
+ async updateUserLastSeen(userPub) {
1426
+ try {
1427
+ if (!userPub || typeof userPub !== "string") {
1428
+ return;
1429
+ }
1430
+ const timestamp = Date.now();
1431
+ // Update in user registry
1432
+ try {
1433
+ await this.node
1434
+ .get("users")
1435
+ .get(userPub)
1436
+ .get("lastSeen")
1437
+ .put(timestamp)
1438
+ .then();
1439
+ }
1440
+ catch (error) {
1441
+ console.error(`Failed to update lastSeen in user registry:`, error);
1442
+ }
1443
+ // Update in user's own node
1444
+ try {
1445
+ await this.gun.get(userPub).get("lastSeen").put(timestamp).then();
1446
+ }
1447
+ catch (error) {
1448
+ console.error(`Failed to update lastSeen in user node:`, error);
1449
+ }
1450
+ }
1451
+ catch (error) {
1452
+ console.error(`Error updating user last seen for ${userPub}:`, error);
1453
+ }
1454
+ }
1455
+ /**
1456
+ * Performs authentication with Gun
1457
+ */
1458
+ async performAuthentication(username, password, pair) {
1459
+ 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 });
1468
+ }
1469
+ });
1470
+ }
1471
+ 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
+ });
1481
+ }
1482
+ });
1483
+ }
1484
+ /**
1485
+ * Builds login result object
1486
+ */
1487
+ buildLoginResult(username, userPub) {
1488
+ // Get the SEA pair from the user object
1489
+ const seaPair = this.gun.user()?._?.sea;
1490
+ return {
1491
+ success: true,
1492
+ userPub,
1493
+ username,
1494
+ // Include SEA pair for consistency with AuthResult interface
1495
+ sea: seaPair
1496
+ ? {
1497
+ pub: seaPair.pub,
1498
+ priv: seaPair.priv,
1499
+ epub: seaPair.epub,
1500
+ epriv: seaPair.epriv,
1501
+ }
1502
+ : undefined,
1503
+ };
1504
+ }
1505
+ /**
1506
+ * Performs login with username and password
1507
+ * @param username Username
1508
+ * @param password Password
1509
+ * @param pair SEA pair (optional)
1510
+ * @returns Promise resolving to AuthResult object
1511
+ */
1512
+ async login(username, password, pair) {
1513
+ try {
1514
+ const loginResult = await this.performAuthentication(username, password, pair);
1515
+ if (!loginResult.success) {
1516
+ return {
1517
+ success: false,
1518
+ error: `User '${username}' not found. Please check your username or register first.`,
1519
+ };
1520
+ }
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;
1526
+ if (!alias) {
1527
+ alias = username;
1528
+ }
1529
+ if (!userPub) {
1530
+ return {
1531
+ success: false,
1532
+ error: "Authentication failed: No user pub returned.",
1533
+ };
1534
+ }
1535
+ // Pass the userPub to runPostAuthOnAuthResult
1536
+ try {
1537
+ await this.runPostAuthOnAuthResult(alias, userPub, {
1538
+ success: true,
1539
+ userPub: userPub,
1540
+ });
1541
+ }
1542
+ catch (postAuthError) {
1543
+ console.error(`Post-auth error during login: ${postAuthError}`);
1544
+ // Continue with login even if post-auth fails
1545
+ }
1546
+ // Update user's last seen timestamp
1547
+ try {
1548
+ await this.updateUserLastSeen(userPub);
1549
+ }
1550
+ catch (lastSeenError) {
1551
+ console.error(`Error updating last seen: ${lastSeenError}`);
1552
+ // Continue with login even if last seen update fails
1553
+ }
1554
+ // Save credentials for future sessions
1555
+ try {
1556
+ const userInfo = {
1557
+ alias: username,
1558
+ pair: pair ?? userPair,
1559
+ userPub: userPub,
1560
+ };
1561
+ this.saveCredentials(userInfo);
1562
+ }
1563
+ catch (saveError) {
1564
+ console.error(`Error saving credentials:`, saveError);
1565
+ }
1566
+ return this.buildLoginResult(username, userPub);
1567
+ }
1568
+ catch (error) {
1569
+ console.error(`Exception during login for ${username}: ${error}`);
1570
+ return { success: false, error: String(error) };
1571
+ }
1572
+ }
1573
+ /**
1574
+ * Performs login with GunDB pair directly
1575
+ * @param username Username
1576
+ * @param pair SEA pair
1577
+ * @returns Promise resolving to AuthResult object
1578
+ */
1579
+ async loginWithPair(username, pair) {
1580
+ try {
1581
+ const loginResult = await this.performAuthentication(username, "", pair);
1582
+ if (!loginResult.success) {
1583
+ return {
1584
+ success: false,
1585
+ error: `User '${username}' not found. Please check your username or register first.`,
1586
+ };
1587
+ }
1588
+ await this.runPostAuthOnAuthResult(username, pair.pub || "", {
1589
+ success: true,
1590
+ userPub: pair.pub,
1591
+ });
1592
+ try {
1593
+ await this.updateUserLastSeen(pair.pub);
1594
+ }
1595
+ catch (lastSeenError) {
1596
+ console.error(`Error updating last seen: ${lastSeenError}`);
1597
+ // Continue with login even if last seen update fails
1598
+ }
1599
+ return this.buildLoginResult(username, this.gun.user().is?.pub || "");
1600
+ }
1601
+ catch (error) {
1602
+ console.error(`Exception during login with pair: ${error}`);
1603
+ return { success: false, error: String(error) };
1604
+ }
1605
+ }
1606
+ saveCredentials(userInfo) {
1607
+ try {
1608
+ const sessionInfo = {
1609
+ username: userInfo.alias,
1610
+ pair: userInfo.pair,
1611
+ userPub: userInfo.userPub,
1612
+ timestamp: Date.now(),
1613
+ expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days
1614
+ };
1615
+ if (typeof sessionStorage !== "undefined") {
1616
+ // Save session data directly (unencrypted)
1617
+ sessionStorage.setItem("gunSessionData", JSON.stringify(sessionInfo));
1618
+ }
1619
+ }
1620
+ catch (error) {
1621
+ console.error(`Error saving credentials: ${error}`);
1622
+ }
1623
+ }
1624
+ /**
1625
+ * Sets up security questions and password hint
1626
+ * @param username Username
1627
+ * @param password Current password
1628
+ * @param hint Password hint
1629
+ * @param securityQuestions Array of security questions
1630
+ * @param securityAnswers Array of answers to security questions
1631
+ * @returns Promise resolving with the operation result
1632
+ */
1633
+ async setPasswordHintWithSecurity(username, password, hint, securityQuestions, securityAnswers) {
1634
+ // Setting password hint for
1635
+ // Verify that the user is authenticated with password
1636
+ const loginResult = await this.login(username, password);
1637
+ if (!loginResult.success) {
1638
+ return { success: false, error: "Authentication failed" };
1639
+ }
1640
+ // Check if user was authenticated with password (not with other methods)
1641
+ const currentUser = this.getCurrentUser();
1642
+ if (!currentUser || !currentUser.pub) {
1643
+ return { success: false, error: "User not authenticated" };
1644
+ }
1645
+ try {
1646
+ // Generate a proof of work from security question answers
1647
+ const answersText = securityAnswers.join("|");
1648
+ let proofOfWork;
1649
+ try {
1650
+ // Use SEA directly if available
1651
+ if (sea_1.default && sea_1.default.work) {
1652
+ proofOfWork = await sea_1.default.work(answersText, null, null, {
1653
+ name: "SHA-256",
1654
+ });
1655
+ }
1656
+ else if (this.crypto && this.crypto.hashText) {
1657
+ proofOfWork = await this.crypto.hashText(answersText);
1658
+ }
1659
+ else {
1660
+ throw new Error("Cryptographic functions not available");
1661
+ }
1662
+ if (!proofOfWork) {
1663
+ throw new Error("Failed to generate hash");
1664
+ }
1665
+ }
1666
+ catch (hashError) {
1667
+ console.error("Error generating hash:", hashError);
1668
+ return { success: false, error: "Failed to generate security hash" };
1669
+ }
1670
+ // Encrypt the password hint with the proof of work
1671
+ let encryptedHint;
1672
+ try {
1673
+ if (sea_1.default && sea_1.default.encrypt) {
1674
+ encryptedHint = await sea_1.default.encrypt(hint, proofOfWork);
1675
+ }
1676
+ else if (this.crypto && this.crypto.encrypt) {
1677
+ encryptedHint = await this.crypto.encrypt(hint, proofOfWork);
1678
+ }
1679
+ else {
1680
+ throw new Error("Encryption functions not available");
1681
+ }
1682
+ if (!encryptedHint) {
1683
+ throw new Error("Failed to encrypt hint");
1684
+ }
1685
+ }
1686
+ catch (encryptError) {
1687
+ console.error("Error encrypting hint:", encryptError);
1688
+ return { success: false, error: "Failed to encrypt password hint" };
1689
+ }
1690
+ // Save to the public graph, readable by anyone but only decryptable with the right answers.
1691
+ const userPub = currentUser.pub;
1692
+ const securityPayload = {
1693
+ questions: JSON.stringify(securityQuestions),
1694
+ hint: encryptedHint,
1695
+ };
1696
+ const ack = await this.node.get(userPub)
1697
+ .get("security")
1698
+ .put(securityPayload)
1699
+ .then();
1700
+ if (ack.err) {
1701
+ console.error("Error saving security data to public graph:", ack.err);
1702
+ throw new Error(ack.err);
1703
+ }
1704
+ return { success: true };
1705
+ }
1706
+ catch (error) {
1707
+ console.error("Error setting password hint:", error);
1708
+ return { success: false, error: String(error) };
1709
+ }
1710
+ }
1711
+ /**
1712
+ * Recovers password hint using security question answers
1713
+ * @param username Username
1714
+ * @param securityAnswers Array of answers to security questions
1715
+ * @returns Promise resolving with the password hint
1716
+ */
1717
+ async forgotPassword(username, securityAnswers) {
1718
+ // Attempting password recovery for
1719
+ try {
1720
+ // Find the user's data using direct lookup
1721
+ const normalizedUsername = username.trim().toLowerCase();
1722
+ const userPub = (await this.node.get("usernames").get(normalizedUsername).then()) ||
1723
+ null;
1724
+ if (!userPub) {
1725
+ return { success: false, error: "User not found" };
1726
+ }
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();
1731
+ if (!securityData || !securityData.hint) {
1732
+ return {
1733
+ success: false,
1734
+ error: "No password hint found",
1735
+ };
1736
+ }
1737
+ // Generate hash from security answers
1738
+ const answersText = securityAnswers.join("|");
1739
+ let proofOfWork;
1740
+ try {
1741
+ // Use SEA directly if available
1742
+ if (sea_1.default && sea_1.default.work) {
1743
+ proofOfWork = await sea_1.default.work(answersText, null, null, {
1744
+ name: "SHA-256",
1745
+ });
1746
+ }
1747
+ else if (this.crypto && this.crypto.hashText) {
1748
+ proofOfWork = await this.crypto.hashText(answersText);
1749
+ }
1750
+ else {
1751
+ throw new Error("Cryptographic functions not available");
1752
+ }
1753
+ if (!proofOfWork) {
1754
+ throw new Error("Failed to generate hash");
1755
+ }
1756
+ }
1757
+ catch (hashError) {
1758
+ console.error("Error generating hash:", hashError);
1759
+ return { success: false, error: "Failed to generate security hash" };
1760
+ }
1761
+ // Decrypt the password hint with the proof of work
1762
+ let hint;
1763
+ try {
1764
+ if (sea_1.default && sea_1.default.decrypt) {
1765
+ hint = await sea_1.default.decrypt(securityData.hint, proofOfWork);
1766
+ }
1767
+ else if (this.crypto && this.crypto.decrypt) {
1768
+ hint = await this.crypto.decrypt(securityData.hint, proofOfWork);
1769
+ }
1770
+ else {
1771
+ throw new Error("Decryption functions not available");
1772
+ }
1773
+ }
1774
+ catch (decryptError) {
1775
+ return {
1776
+ success: false,
1777
+ error: "Incorrect answers to security questions",
1778
+ };
1779
+ }
1780
+ if (hint === undefined) {
1781
+ return {
1782
+ success: false,
1783
+ error: "Incorrect answers to security questions",
1784
+ };
1785
+ }
1786
+ return { success: true, hint: hint };
1787
+ }
1788
+ catch (error) {
1789
+ console.error("Error recovering password hint:", error);
1790
+ return { success: false, error: String(error) };
1791
+ }
1792
+ }
1793
+ /**
1794
+ * Adds an event listener
1795
+ * @param event Event name
1796
+ * @param listener Event listener function
1797
+ */
1798
+ on(event, listener) {
1799
+ this.eventEmitter.on(event, listener);
1800
+ }
1801
+ /**
1802
+ * Removes an event listener
1803
+ * @param event Event name
1804
+ * @param listener Event listener function
1805
+ */
1806
+ off(event, listener) {
1807
+ this.eventEmitter.off(event, listener);
1808
+ }
1809
+ /**
1810
+ * Adds a one-time event listener
1811
+ * @param event Event name
1812
+ * @param listener Event listener function
1813
+ */
1814
+ once(event, listener) {
1815
+ this.eventEmitter.once(event, listener);
1816
+ }
1817
+ /**
1818
+ * Emits an event
1819
+ * @param event Event name
1820
+ * @param data Event data
1821
+ */
1822
+ emit(event, data) {
1823
+ return this.eventEmitter.emit(event, data);
1824
+ }
1825
+ /**
1826
+ * Recall user session
1827
+ */
1828
+ recall(options) {
1829
+ if (this.user) {
1830
+ if (typeof sessionStorage !== "undefined" &&
1831
+ sessionStorage.getItem("pair")) {
1832
+ this.user.recall({ sessionStorage: true });
1833
+ }
1834
+ else {
1835
+ this.user;
1836
+ }
1837
+ }
1838
+ }
1839
+ /**
1840
+ * Leave user session
1841
+ */
1842
+ leave() {
1843
+ if (this.user) {
1844
+ this.user.leave();
1845
+ }
1846
+ }
1847
+ /**
1848
+ * Set user data
1849
+ */
1850
+ setUserData(data) {
1851
+ if (this.user) {
1852
+ this.user.put(data);
1853
+ }
1854
+ }
1855
+ /**
1856
+ * Set password hint
1857
+ */
1858
+ setPasswordHint(hint) {
1859
+ if (this.user) {
1860
+ try {
1861
+ this.user.get("passwordHint").put(hint);
1862
+ }
1863
+ catch (error) {
1864
+ // Handle case where user.get returns undefined
1865
+ console.warn("Could not set password hint:", error);
1866
+ }
1867
+ }
1868
+ }
1869
+ /**
1870
+ * Get password hint
1871
+ */
1872
+ getPasswordHint() {
1873
+ if (this.user) {
1874
+ // Access passwordHint from user data, not from is object
1875
+ return this.user.passwordHint || null;
1876
+ }
1877
+ return null;
1878
+ }
1879
+ /**
1880
+ * Save session to storage
1881
+ */
1882
+ saveSession(session) {
1883
+ if (this.user) {
1884
+ if (typeof sessionStorage !== "undefined" &&
1885
+ sessionStorage.getItem("pair")) {
1886
+ this.user.recall({ sessionStorage: true });
1887
+ }
1888
+ else {
1889
+ this.user;
1890
+ }
1891
+ }
1892
+ }
1893
+ /**
1894
+ * Load session from storage
1895
+ */
1896
+ loadSession() {
1897
+ if (this.user) {
1898
+ if (typeof sessionStorage !== "undefined" &&
1899
+ sessionStorage.getItem("pair")) {
1900
+ return this.user.recall({ sessionStorage: true });
1901
+ }
1902
+ else {
1903
+ return this.user;
1904
+ }
1905
+ }
1906
+ return null;
1907
+ }
1908
+ /**
1909
+ * Clear session
1910
+ */
1911
+ clearSession() {
1912
+ if (this.user) {
1913
+ this.user.leave();
1914
+ }
1915
+ }
1916
+ /**
1917
+ * Get app scope
1918
+ */
1919
+ getAppScope() {
1920
+ return this.node?._?.soul || "shogun";
1921
+ }
1922
+ /**
1923
+ * Get user public key
1924
+ */
1925
+ getUserPub() {
1926
+ if (this.user) {
1927
+ return this.user.is?.pub || null;
1928
+ }
1929
+ return null;
1930
+ }
1931
+ /**
1932
+ * Check if user is authenticated
1933
+ */
1934
+ isAuthenticated() {
1935
+ return this.user?.is?.pub ? true : false;
1936
+ }
1937
+ }
1938
+ exports.DataBase = DataBase;
1939
+ // Errors
1940
+ 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;