shogun-core
Version:
SHOGUN CORE - Core library for Shogun Ecosystem
957 lines (956 loc) โข 46.3 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import { RxJS } from './rxjs.js';
import { EventEmitter } from '../utils/eventEmitter.js';
import * as GunErrors from './errors.js';
import * as crypto from './crypto.js';
/**
* GunDB configuration constants.
* @internal
*/
var CONFIG = {
PASSWORD: {
MIN_LENGTH: 8,
},
};
/**
* DataBase
*
* Manages GunDB user authentication and various utility helpers for
* session, alias/username, SEA cryptography, event handling, and reactive streams.
*/
var DataBase = /** @class */ (function () {
/**
* Constructs a new DataBase instance connected to a GunDB instance.
* @param gun The main GunDB instance.
* @param core Optionally, the root Gun instance (unused in this context).
* @param sea Optional cryptography (Gun SEA) instance; will be auto-discovered if not provided.
* @throws If gun or gun.user() is not provided.
*/
function DataBase(gun, core, sea) {
var _a;
/** Cached user instance or `null` if not logged in */
this.user = null;
/** Registered callbacks for auth state changes */
this.onAuthCallbacks = [];
/** Whether the database instance has been destroyed */
this._isDestroyed = false;
this.eventEmitter = new EventEmitter();
this.core = core;
if (!gun) {
throw new Error('Gun instance is required but was not provided');
}
if (typeof gun.user !== 'function') {
throw new Error('Gun instance is invalid: gun.user is not a function');
}
this.gun = gun;
this.user = this.gun.user().recall({ sessionStorage: true });
this.subscribeToAuthEvents();
this.crypto = crypto;
this.sea = sea || null;
if (!this.sea) {
if (this.gun.SEA) {
this.sea = this.gun.SEA;
}
else if ((_a = globalThis.Gun) === null || _a === void 0 ? void 0 : _a.SEA) {
this.sea = globalThis.Gun.SEA;
}
else if (globalThis.SEA) {
this.sea = globalThis.SEA;
}
}
this._rxjs = new RxJS(this.gun);
this.usernamesNode = this.gun.get('usernames');
console.log('[DB] DataBase initialization completed');
}
/**
* Initialize the database instance.
*/
DataBase.prototype.initialize = function () {
// Database is already initialized in constructor
};
/**
* Internal: subscribe to GunDB "auth" events and notify listeners.
* Listeners are invoked on authentication status change.
* @internal
*/
DataBase.prototype.subscribeToAuthEvents = function () {
var _this = this;
this.gun.on('auth', function (ack) {
var _a;
if (ack.err) {
console.error('[DB] Auth event error:', ack.err);
}
else {
_this.notifyAuthListeners(((_a = ack.sea) === null || _a === void 0 ? void 0 : _a.pub) || '');
}
});
};
/**
* Internal: notify all onAuth callbacks with current user.
* @param pub User's public key (pub).
* @internal
*/
DataBase.prototype.notifyAuthListeners = function (pub) {
var user = this.gun.user();
this.onAuthCallbacks.forEach(function (cb) { return cb(user); });
};
/**
* Listen for authentication/sign-in events (login, logout, etc).
* @param callback Function to call with new user instance.
* @returns Function to remove the registered callback.
*/
DataBase.prototype.onAuth = function (callback) {
var _this = this;
this.onAuthCallbacks.push(callback);
var user = this.gun.user();
if (user && user.is)
callback(user);
return function () {
var i = _this.onAuthCallbacks.indexOf(callback);
if (i !== -1)
_this.onAuthCallbacks.splice(i, 1);
};
};
/**
* Check if a user is currently logged in (there is a valid session).
* @returns `true` if logged in; otherwise `false`.
*/
DataBase.prototype.isLoggedIn = function () {
try {
var user = this.gun.user();
return !!(user && user.is && user.is.pub);
}
catch (error) {
return false;
}
};
/**
* Attempt to restore a previously saved session from sessionStorage.
* @returns Object indicating success, error, and userPub if restored.
*/
DataBase.prototype.restoreSession = function () {
try {
if (typeof sessionStorage === 'undefined') {
return { success: false, error: 'sessionStorage not available' };
}
var sessionData = sessionStorage.getItem('gunSessionData');
if (!sessionData) {
return { success: false, error: 'No saved session' };
}
var session = JSON.parse(sessionData);
if (!session.userPub) {
return { success: false, error: 'Invalid session data' };
}
// Check if session is expired
if (session.expiresAt && Date.now() > session.expiresAt) {
sessionStorage.removeItem('gunSessionData');
return { success: false, error: 'Session expired' };
}
// Verify session restoration
var user = this.gun.user();
if (user.is && user.is.pub === session.userPub) {
this.user = user;
return { success: true, userPub: session.userPub };
}
return { success: false, error: 'Session verification failed' };
}
catch (error) {
return { success: false, error: String(error) };
}
};
/**
* Log out the current user, clear local state and remove session from storage.
*/
DataBase.prototype.logout = function () {
try {
var wasLoggedIn = !!this.user;
var currentUser = this.gun.user();
if (currentUser && currentUser.is) {
currentUser.leave();
}
this.user = null;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.removeItem('gunSessionData');
}
// Emit auth:logout event if core is available and user was logged in
if (wasLoggedIn && this.core && typeof this.core.emit === 'function') {
this.core.emit('auth:logout', undefined);
}
}
catch (error) {
console.error('[DB] Error during logout:', error);
}
};
/**
* Validate that a provided password meets minimum length requirements.
* @param password Password string to validate.
* @returns Object indicating validity and, if invalid, an error.
*/
DataBase.prototype.validatePasswordStrength = function (password) {
if (password.length < CONFIG.PASSWORD.MIN_LENGTH) {
return {
valid: false,
error: "Password must be at least ".concat(CONFIG.PASSWORD.MIN_LENGTH, " characters long"),
};
}
return { valid: true };
};
/**
* Validate a signup request's username, password, and/or cryptographic pair.
* @param username Username string.
* @param password Password string.
* @param pair Optional cryptographic SEA pair.
* @returns Object with validation status and optional error.
*/
DataBase.prototype.validateSignupCredentials = function (username, password, pair) {
if (!username || username.length < 1) {
return {
valid: false,
error: 'Username must be more than 0 characters long',
};
}
if (!/^[a-zA-Z0-9._-]+$/.test(username)) {
return {
valid: false,
error: 'Username can only contain letters, numbers, dots, underscores, and hyphens',
};
}
if (pair) {
if (!pair.pub || !pair.priv || !pair.epub || !pair.epriv) {
return { valid: false, error: 'Invalid pair provided' };
}
return { valid: true };
}
return this.validatePasswordStrength(password);
};
/**
* Ensures that an alias/username is available in GunDB for registration.
* @param alias Username to check.
* @param timeout Timeout in milliseconds (default 5000ms).
* @throws If the alias is already taken.
*/
DataBase.prototype.ensureAliasAvailable = function (alias_1) {
return __awaiter(this, arguments, void 0, function (alias, timeout) {
var available;
if (timeout === void 0) { timeout = 5000; }
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.isAliasAvailable(alias, timeout)];
case 1:
available = _a.sent();
if (!available) {
throw new Error("Alias \"".concat(alias, "\" is already registered in Gun"));
}
return [2 /*return*/];
}
});
});
};
/**
* Checks if a given alias/username is available on GunDB.
* @param alias Username to check for availability.
* @param timeout Timeout in ms (default: 5000).
* @returns Promise resolving to `true` if available; otherwise `false`.
* @throws If alias is invalid or on I/O error.
*/
DataBase.prototype.isAliasAvailable = function (alias_1) {
return __awaiter(this, arguments, void 0, function (alias, timeout) {
var normalizedAlias;
var _this = this;
if (timeout === void 0) { timeout = 5000; }
return __generator(this, function (_a) {
if (typeof alias !== 'string' || !alias.trim()) {
throw new Error('Alias must be a non-empty string');
}
normalizedAlias = alias.trim().toLowerCase();
return [2 /*return*/, new Promise(function (resolve, reject) {
var settled = false;
var timer = setTimeout(function () {
if (settled)
return;
settled = true;
reject(new Error('Timeout while checking alias availability'));
}, timeout);
_this.usernamesNode.get(normalizedAlias).once(function (existingPub) {
if (settled)
return;
settled = true;
clearTimeout(timer);
resolve(!existingPub);
});
})];
});
});
};
/**
* Checks if a given alias/username is taken on GunDB.
* @param alias Username to check for availability.
* @returns Promise resolving to `true` if taken; otherwise `false`.
* @throws If alias is invalid or on I/O error.
*/
DataBase.prototype.isAliasTaken = function (alias) {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve, reject) {
// Check if username exists by looking up ~@username
_this.gun.get("~@".concat(alias)).once(function (user) {
// If user exists, alias is taken (return true)
// If user is null/undefined, alias is available (return false)
resolve(!!user);
});
})];
});
});
};
/**
* Register a new alias (username) โ public key mapping on GunDB.
* @param alias The username/alias to register.
* @param userPub The user's public key.
* @param timeout Timeout in ms (default 5000).
* @throws If alias/userPub is invalid or the alias cannot be registered.
*/
DataBase.prototype.registerAlias = function (alias_1, userPub_1) {
return __awaiter(this, arguments, void 0, function (alias, userPub, timeout) {
var normalizedAlias, available, taken;
var _this = this;
if (timeout === void 0) { timeout = 5000; }
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!alias || !alias.trim()) {
throw new Error('Alias must be provided for registration');
}
if (!userPub) {
throw new Error('userPub must be provided for alias registration');
}
normalizedAlias = alias.trim().toLowerCase();
return [4 /*yield*/, this.isAliasAvailable(normalizedAlias, timeout).catch(function (error) {
console.error('[DB] Alias availability check failed:', error);
throw error;
})];
case 1:
available = _a.sent();
return [4 /*yield*/, this.isAliasTaken(normalizedAlias)];
case 2:
taken = _a.sent();
if (taken) {
throw new Error("Alias \"".concat(normalizedAlias, "\" is already taken"));
}
if (!available) {
throw new Error("Alias \"".concat(normalizedAlias, "\" is no longer available for registration"));
}
return [4 /*yield*/, new Promise(function (resolve, reject) {
var settled = false;
var timer = setTimeout(function () {
if (settled)
return;
settled = true;
reject(new Error('Timeout while registering alias'));
}, timeout);
_this.usernamesNode.get(normalizedAlias).put(userPub, function (ack) {
if (settled)
return;
settled = true;
clearTimeout(timer);
if (ack && ack.err) {
reject(new Error(String(ack.err)));
return;
}
resolve();
});
}).catch(function (error) {
console.error('[DB] Failed to register alias:', error);
throw error;
})];
case 3:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* Reset gun.user() authentication state and clear cached user.
* @internal
*/
DataBase.prototype.resetAuthState = function () {
try {
var user = this.gun.user();
if (user && user._) {
var cat = user._;
cat.ing = false;
cat.auth = null;
cat.act = null;
if (cat.auth) {
cat.auth = null;
}
}
try {
user.leave();
}
catch (leaveError) {
// Ignore leave errors
}
this.user = null;
}
catch (e) {
// Ignore
}
};
/**
* Assemble a standard AuthResult object after a successful login.
* @param username Resulting username.
* @param userPub Public key (pub) for logged-in user.
* @returns AuthResult.
* @internal
*/
DataBase.prototype.buildLoginResult = function (username, userPub) {
var _a, _b;
var seaPair = (_b = (_a = this.gun.user()) === null || _a === void 0 ? void 0 : _a._) === null || _b === void 0 ? void 0 : _b.sea;
return {
success: true,
userPub: userPub,
username: username,
sea: seaPair
? {
pub: seaPair.pub,
priv: seaPair.priv,
epub: seaPair.epub,
epriv: seaPair.epriv,
}
: undefined,
};
};
/**
* Save credentials for the current session to sessionStorage, if available.
* @param userInfo The credentials and user identity to store.
*/
DataBase.prototype.saveCredentials = function (userInfo) {
try {
if (typeof sessionStorage !== 'undefined') {
var sessionInfo = {
username: userInfo.alias,
pair: userInfo.pair,
userPub: userInfo.userPub,
timestamp: Date.now(),
expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days
};
sessionStorage.setItem('gunSessionData', JSON.stringify(sessionInfo));
}
}
catch (error) {
console.error('[DB] Error saving credentials:', error);
}
};
/**
* Register and authenticate a new user account.
* @param username The username to create/account for.
* @param password The user's password.
* @param pair Optional cryptographic pair (for `auth` instead of password).
* @returns SignUpResult Promise.
*/
DataBase.prototype.signUp = function (username, password, pair) {
return __awaiter(this, void 0, void 0, function () {
var validation, normalizedUsername, user, loginResult, e_1, aliasError_1, result;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
validation = this.validateSignupCredentials(username, password, pair);
if (!validation.valid) {
return [2 /*return*/, { success: false, error: validation.error }];
}
this.resetAuthState();
normalizedUsername = username.trim().toLowerCase();
user = this.gun.user();
if (!pair) return [3 /*break*/, 4];
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, new Promise(function (resolve) {
var callbackInvoked = false;
user.auth(pair, function (ack) {
var _a, _b, _c;
if (callbackInvoked) {
return;
}
callbackInvoked = true;
if (ack.err) {
resolve({ success: false, error: ack.err });
return;
}
var userPub = (_a = user === null || user === void 0 ? void 0 : user.is) === null || _a === void 0 ? void 0 : _a.pub;
if (!userPub) {
_this.resetAuthState();
resolve({ success: false, error: 'No userPub available' });
return;
}
_this.user = user;
var alias = (_b = user === null || user === void 0 ? void 0 : user.is) === null || _b === void 0 ? void 0 : _b.alias;
var userPair = (_c = user === null || user === void 0 ? void 0 : user._) === null || _c === void 0 ? void 0 : _c.sea;
_this.saveCredentials({
alias: alias || normalizedUsername,
pair: pair !== null && pair !== void 0 ? pair : userPair,
userPub: userPub,
});
// Emit auth:signup event if core is available (pair-based signup)
if (_this.core && typeof _this.core.emit === 'function') {
_this.core.emit('auth:signup', {
userPub: userPub,
username: normalizedUsername,
method: 'pair',
});
}
resolve(_this.buildLoginResult(alias || normalizedUsername, userPub));
});
})];
case 2:
loginResult = _a.sent();
if (loginResult && loginResult.success) {
return [2 /*return*/, loginResult];
}
return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
return [3 /*break*/, 4];
case 4:
_a.trys.push([4, 6, , 7]);
return [4 /*yield*/, this.ensureAliasAvailable(normalizedUsername)];
case 5:
_a.sent();
return [3 /*break*/, 7];
case 6:
aliasError_1 = _a.sent();
return [2 /*return*/, {
success: false,
error: aliasError_1 instanceof Error ? aliasError_1.message : String(aliasError_1),
}];
case 7: return [4 /*yield*/, new Promise(function (resolve) {
var callbackInvoked = false;
user.create(normalizedUsername, password, function (createAck) {
if (callbackInvoked) {
return;
}
if (createAck.err ||
(createAck.ok !== undefined && createAck.ok !== 0)) {
callbackInvoked = true;
_this.resetAuthState();
resolve({ success: false, error: createAck.err || 'Signup failed' });
return;
}
var userPub = createAck.pub;
if (!userPub) {
callbackInvoked = true;
_this.resetAuthState();
resolve({
success: false,
error: 'No userPub available from signup',
});
return;
}
user.auth(normalizedUsername, password, function (authAck) { return __awaiter(_this, void 0, void 0, function () {
var authenticatedUserPub, alias, userPair, registerError_1, sea;
var _a, _b, _c, _d;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
if (callbackInvoked) {
return [2 /*return*/];
}
callbackInvoked = true;
if (authAck.err) {
this.resetAuthState();
resolve({
success: false,
error: authAck.err || 'Authentication after signup failed',
});
return [2 /*return*/];
}
authenticatedUserPub = (_a = user === null || user === void 0 ? void 0 : user.is) === null || _a === void 0 ? void 0 : _a.pub;
if (!authenticatedUserPub) {
this.resetAuthState();
resolve({
success: false,
error: 'User not authenticated after signup',
});
return [2 /*return*/];
}
this.user = user;
alias = (_b = user === null || user === void 0 ? void 0 : user.is) === null || _b === void 0 ? void 0 : _b.alias;
userPair = (_c = user === null || user === void 0 ? void 0 : user._) === null || _c === void 0 ? void 0 : _c.sea;
try {
this.saveCredentials({
alias: alias || normalizedUsername,
pair: pair !== null && pair !== void 0 ? pair : userPair,
userPub: authenticatedUserPub,
});
}
catch (saveError) {
// Ignore save errors
}
_e.label = 1;
case 1:
_e.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.registerAlias(alias || normalizedUsername, authenticatedUserPub)];
case 2:
_e.sent();
return [3 /*break*/, 4];
case 3:
registerError_1 = _e.sent();
console.error('[DB] Alias registration failed:', registerError_1);
return [3 /*break*/, 4];
case 4:
// Emit auth:signup event if core is available
if (this.core && typeof this.core.emit === 'function') {
this.core.emit('auth:signup', {
userPub: authenticatedUserPub,
username: normalizedUsername,
method: pair ? 'pair' : 'password',
});
}
sea = (_d = user === null || user === void 0 ? void 0 : user._) === null || _d === void 0 ? void 0 : _d.sea;
resolve({
success: true,
userPub: authenticatedUserPub,
username: normalizedUsername,
isNewUser: true,
sea: sea
? {
pub: sea.pub,
priv: sea.priv,
epub: sea.epub,
epriv: sea.epriv,
}
: undefined,
});
return [2 /*return*/];
}
});
}); });
});
})];
case 8:
result = _a.sent();
return [2 /*return*/, result];
}
});
});
};
/**
* Sign in (authenticate) as an existing user by username/password or SEA pair.
* @param username Username to log in as.
* @param password User's password (or "" if using pair).
* @param pair Optional cryptographic SEA pair.
* @returns AuthResult Promise.
*/
DataBase.prototype.login = function (username, password, pair) {
return __awaiter(this, void 0, void 0, function () {
var normalizedUsername, user;
var _this = this;
return __generator(this, function (_a) {
this.resetAuthState();
normalizedUsername = username.trim().toLowerCase();
user = this.gun.user();
return [2 /*return*/, new Promise(function (resolve) {
if (pair) {
user.auth(pair, function (ack) {
var _a, _b, _c;
if (ack.err) {
_this.resetAuthState();
resolve({ success: false, error: ack.err });
return;
}
var userPub = (_a = user === null || user === void 0 ? void 0 : user.is) === null || _a === void 0 ? void 0 : _a.pub;
if (!userPub) {
_this.resetAuthState();
resolve({ success: false, error: 'No userPub available' });
return;
}
_this.user = user;
var alias = (_b = user === null || user === void 0 ? void 0 : user.is) === null || _b === void 0 ? void 0 : _b.alias;
var userPair = (_c = user === null || user === void 0 ? void 0 : user._) === null || _c === void 0 ? void 0 : _c.sea;
try {
_this.saveCredentials({
alias: alias || normalizedUsername,
pair: pair !== null && pair !== void 0 ? pair : userPair,
userPub: userPub,
});
}
catch (saveError) {
// Ignore save errors
}
// Emit auth:login event if core is available (pair-based login)
if (_this.core && typeof _this.core.emit === 'function') {
_this.core.emit('auth:login', {
userPub: userPub,
username: alias || normalizedUsername,
method: 'pair',
});
}
resolve(_this.buildLoginResult(alias || normalizedUsername, userPub));
});
}
else {
user.auth(normalizedUsername, password, function (ack) {
var _a, _b, _c;
if (ack.err) {
_this.resetAuthState();
resolve({ success: false, error: ack.err });
return;
}
var userPub = (_a = user === null || user === void 0 ? void 0 : user.is) === null || _a === void 0 ? void 0 : _a.pub;
if (!userPub) {
_this.resetAuthState();
resolve({ success: false, error: 'No userPub available' });
return;
}
_this.user = user;
var alias = (_b = user === null || user === void 0 ? void 0 : user.is) === null || _b === void 0 ? void 0 : _b.alias;
var userPair = (_c = user === null || user === void 0 ? void 0 : user._) === null || _c === void 0 ? void 0 : _c.sea;
try {
_this.saveCredentials({
alias: alias || normalizedUsername,
pair: pair !== null && pair !== void 0 ? pair : userPair,
userPub: userPub,
});
}
catch (saveError) {
// Ignore save errors
}
// Emit auth:login event if core is available (password-based login)
if (_this.core && typeof _this.core.emit === 'function') {
_this.core.emit('auth:login', {
userPub: userPub,
username: alias || normalizedUsername,
method: 'password',
});
}
resolve(_this.buildLoginResult(alias || normalizedUsername, userPub));
});
}
})];
});
});
};
/**
* Returns the currently authenticated user's public key and Gun user instance, if logged in.
* @returns Object containing `pub` (public key) and optionally `user`, or `null`.
*/
DataBase.prototype.getCurrentUser = function () {
try {
var user = this.gun.user();
if (user && user.is && user.is.pub) {
return {
pub: user.is.pub,
user: user,
};
}
return null;
}
catch (error) {
return null;
}
};
/**
* Get current user's public key.
* @returns User's public key or null if not logged in.
*/
DataBase.prototype.getUserPub = function () {
var _a;
try {
var user = this.gun.user();
return ((_a = user === null || user === void 0 ? void 0 : user.is) === null || _a === void 0 ? void 0 : _a.pub) || null;
}
catch (error) {
return null;
}
};
/**
* Authenticate using a SEA pair directly (no password required).
* @param username The user's username for identification (not cryptographically enforced).
* @param pair GunDB SEA pair for authentication.
* @returns Promise with authentication result.
* @description Authenticates user using a GunDB pair directly without password.
*/
DataBase.prototype.loginWithPair = function (username, pair) {
return __awaiter(this, void 0, void 0, function () {
var normalizedUsername, user;
var _this = this;
return __generator(this, function (_a) {
// Validate pair structure
if (!pair || !pair.pub || !pair.priv || !pair.epub || !pair.epriv) {
return [2 /*return*/, {
success: false,
error: 'Invalid pair structure - missing required keys',
}];
}
this.resetAuthState();
normalizedUsername = username.trim().toLowerCase();
user = this.gun.user();
console.log('[DB] Login with pair for username:', normalizedUsername);
return [2 /*return*/, new Promise(function (resolve) {
user.auth(pair, function (ack) {
var _a, _b, _c;
if (ack.err) {
_this.resetAuthState();
resolve({ success: false, error: ack.err });
return;
}
var userPub = (_a = user === null || user === void 0 ? void 0 : user.is) === null || _a === void 0 ? void 0 : _a.pub;
if (!userPub) {
_this.resetAuthState();
resolve({ success: false, error: 'No userPub available' });
return;
}
_this.user = user;
var alias = (_b = user === null || user === void 0 ? void 0 : user.is) === null || _b === void 0 ? void 0 : _b.alias;
var userPair = (_c = user === null || user === void 0 ? void 0 : user._) === null || _c === void 0 ? void 0 : _c.sea;
try {
_this.saveCredentials({
alias: alias || normalizedUsername,
pair: pair !== null && pair !== void 0 ? pair : userPair,
userPub: userPub,
});
}
catch (saveError) {
// Ignore save errors
}
// Emit auth:login event if core is available (loginWithPair)
if (_this.core && typeof _this.core.emit === 'function') {
_this.core.emit('auth:login', {
userPub: userPub,
username: alias || normalizedUsername,
method: 'pair',
});
}
resolve(_this.buildLoginResult(alias || normalizedUsername, userPub));
});
})];
});
});
};
/**
* Legacy API: Sign in using a username and SEA pair (password parameter is unused).
* @param username Username to sign in as.
* @param pair SEA key pair.
* @returns AuthResult Promise.
*/
DataBase.prototype.loginWithPairLegacy = function (username, pair) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.login(username, '', pair)];
});
});
};
/**
* Returns the bound RxJS GunDB helper (reactive streams).
* @returns RxJS instance.
*/
DataBase.prototype.rx = function () {
return this._rxjs;
};
/**
* Tears down the DataBase instance and performs cleanup of all resources/listeners.
* No further actions should be performed on this instance after destruction.
*/
DataBase.prototype.destroy = function () {
if (this._isDestroyed)
return;
console.log('[DB] Destroying DataBase instance...');
this._isDestroyed = true;
this.onAuthCallbacks.length = 0;
this.eventEmitter.removeAllListeners();
if (this.user) {
try {
this.user.leave();
}
catch (error) {
// Ignore
}
this.user = null;
}
this._rxjs = undefined;
console.log('[DB] DataBase instance destroyed');
};
/**
* Aggressively clean up authentication state and session. Typically used for error recovery.
*/
DataBase.prototype.aggressiveAuthCleanup = function () {
console.log('๐งน Performing aggressive auth cleanup...');
this.resetAuthState();
this.logout();
console.log('โ Aggressive auth cleanup completed');
};
/**
* Register an event handler.
* @param event Event name.
* @param listener Listener function.
*/
DataBase.prototype.on = function (event, listener) {
this.eventEmitter.on(event, listener);
};
/**
* Remove an event handler.
* @param event Event name.
* @param listener Listener function.
*/
DataBase.prototype.off = function (event, listener) {
this.eventEmitter.off(event, listener);
};
/**
* Register an event handler for a single event occurrence.
* @param event Event name.
* @param listener Listener function.
*/
DataBase.prototype.once = function (event, listener) {
this.eventEmitter.once(event, listener);
};
/**
* Emit a custom event.
* @param event Event name.
* @param data Optional associated data.
* @returns `true` if listeners were notified; otherwise `false`.
*/
DataBase.prototype.emit = function (event, data) {
return this.eventEmitter.emit(event, data);
};
return DataBase;
}());
export { DataBase, RxJS, crypto, GunErrors };
export { default as derive } from './derive.js';