last commit before bundling nostr-tools ourselves
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* NOSTR_LOGIN_LITE
|
* NOSTR_LOGIN_LITE
|
||||||
* Single-file Nostr authentication library
|
* Single-file Nostr authentication library
|
||||||
* Generated on: 2025-09-13T13:03:05.960Z
|
* Generated on: 2025-09-13T13:54:03.650Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// ======================================
|
// ======================================
|
||||||
@@ -291,85 +291,166 @@ class Modal {
|
|||||||
// Detect all available real extensions
|
// Detect all available real extensions
|
||||||
const availableExtensions = this._detectAllExtensions();
|
const availableExtensions = this._detectAllExtensions();
|
||||||
|
|
||||||
|
console.log(`Modal: Found ${availableExtensions.length} extensions:`, availableExtensions.map(e => e.displayName));
|
||||||
|
|
||||||
if (availableExtensions.length === 0) {
|
if (availableExtensions.length === 0) {
|
||||||
console.log('Modal: No real extensions found');
|
console.log('Modal: No real extensions found');
|
||||||
this._showExtensionRequired();
|
this._showExtensionRequired();
|
||||||
} else if (availableExtensions.length === 1) {
|
} else if (availableExtensions.length === 1) {
|
||||||
console.log('Modal detected single extension:', availableExtensions[0].name);
|
// Single extension - use it directly without showing choice UI
|
||||||
|
console.log('Modal: Single extension detected, using it directly:', availableExtensions[0].displayName);
|
||||||
this._tryExtensionLogin(availableExtensions[0].extension);
|
this._tryExtensionLogin(availableExtensions[0].extension);
|
||||||
} else {
|
} else {
|
||||||
console.log('Modal detected multiple extensions:', availableExtensions.map(e => e.name));
|
// Multiple extensions - show choice UI
|
||||||
|
console.log('Modal: Multiple extensions detected, showing choice UI for', availableExtensions.length, 'extensions');
|
||||||
this._showExtensionChoice(availableExtensions);
|
this._showExtensionChoice(availableExtensions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_detectAllExtensions() {
|
_detectAllExtensions() {
|
||||||
const extensions = [];
|
const extensions = [];
|
||||||
|
const seenExtensions = new Set(); // Track extensions by object reference to avoid duplicates
|
||||||
|
|
||||||
// Check navigator.nostr (NIP-07 standard location)
|
// Extension locations to check (in priority order)
|
||||||
if (window.navigator?.nostr && typeof window.navigator.nostr.getPublicKey === 'function') {
|
const locations = [
|
||||||
|
{ path: 'window.navigator?.nostr', name: 'navigator.nostr', displayName: 'Standard Extension (navigator.nostr)', icon: '🌐', getter: () => window.navigator?.nostr },
|
||||||
|
{ path: 'window.webln?.nostr', name: 'webln.nostr', displayName: 'Alby WebLN Extension', icon: '⚡', getter: () => window.webln?.nostr },
|
||||||
|
{ path: 'window.alby?.nostr', name: 'alby.nostr', displayName: 'Alby Extension (Direct)', icon: '🐝', getter: () => window.alby?.nostr },
|
||||||
|
{ path: 'window.nos2x', name: 'nos2x', displayName: 'nos2x Extension', icon: '🔌', getter: () => window.nos2x },
|
||||||
|
{ path: 'window.flamingo?.nostr', name: 'flamingo.nostr', displayName: 'Flamingo Extension', icon: '🦩', getter: () => window.flamingo?.nostr },
|
||||||
|
{ path: 'window.mutiny?.nostr', name: 'mutiny.nostr', displayName: 'Mutiny Extension', icon: '⚔️', getter: () => window.mutiny?.nostr },
|
||||||
|
{ path: 'window.nostrich?.nostr', name: 'nostrich.nostr', displayName: 'Nostrich Extension', icon: '🐦', getter: () => window.nostrich?.nostr },
|
||||||
|
{ path: 'window.getAlby?.nostr', name: 'getAlby.nostr', displayName: 'getAlby Extension', icon: '🔧', getter: () => window.getAlby?.nostr }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check each location
|
||||||
|
for (const location of locations) {
|
||||||
|
try {
|
||||||
|
const obj = location.getter();
|
||||||
|
|
||||||
|
console.log(`Modal: Checking ${location.name}:`, !!obj, obj?.constructor?.name);
|
||||||
|
|
||||||
|
if (obj && this._isRealExtension(obj) && !seenExtensions.has(obj)) {
|
||||||
extensions.push({
|
extensions.push({
|
||||||
name: 'navigator.nostr',
|
name: location.name,
|
||||||
displayName: 'Standard Extension (navigator.nostr)',
|
displayName: location.displayName,
|
||||||
icon: '🌐',
|
icon: location.icon,
|
||||||
extension: window.navigator.nostr
|
extension: obj
|
||||||
});
|
});
|
||||||
|
seenExtensions.add(obj);
|
||||||
|
console.log(`Modal: ✓ Detected extension at ${location.name} (${obj.constructor?.name})`);
|
||||||
|
} else if (obj) {
|
||||||
|
console.log(`Modal: ✗ Filtered out ${location.name} (${obj.constructor?.name})`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Location doesn't exist or can't be accessed
|
||||||
|
console.log(`Modal: ${location.name} not accessible:`, e.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check webln.nostr (Alby WebLN)
|
// Also check window.nostr but be extra careful to avoid our library
|
||||||
if (window.webln?.nostr && typeof window.webln.nostr.getPublicKey === 'function') {
|
console.log('Modal: Checking window.nostr:', !!window.nostr, window.nostr?.constructor?.name);
|
||||||
extensions.push({
|
if (window.nostr && this._isRealExtension(window.nostr) && !seenExtensions.has(window.nostr)) {
|
||||||
name: 'webln.nostr',
|
|
||||||
displayName: 'Alby WebLN Extension',
|
|
||||||
icon: '⚡',
|
|
||||||
extension: window.webln.nostr
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check alby.nostr (Alby direct)
|
|
||||||
if (window.alby?.nostr && typeof window.alby.nostr.getPublicKey === 'function') {
|
|
||||||
extensions.push({
|
|
||||||
name: 'alby.nostr',
|
|
||||||
displayName: 'Alby Extension (Direct)',
|
|
||||||
icon: '🐝',
|
|
||||||
extension: window.alby.nostr
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check nos2x
|
|
||||||
if (window.nos2x && typeof window.nos2x.getPublicKey === 'function') {
|
|
||||||
extensions.push({
|
|
||||||
name: 'nos2x',
|
|
||||||
displayName: 'nos2x Extension',
|
|
||||||
icon: '🔌',
|
|
||||||
extension: window.nos2x
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check window.nostr but make sure it's not our library
|
|
||||||
if (window.nostr && typeof window.nostr.getPublicKey === 'function') {
|
|
||||||
const isRealExtension = (
|
|
||||||
typeof window.nostr._hexToUint8Array !== 'function' && // Our library has this method
|
|
||||||
window.nostr.constructor.name !== 'Object' // Real extensions usually have proper constructors
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRealExtension) {
|
|
||||||
// Don't add if we already detected it via another path
|
|
||||||
const alreadyDetected = extensions.some(ext => ext.extension === window.nostr);
|
|
||||||
if (!alreadyDetected) {
|
|
||||||
extensions.push({
|
extensions.push({
|
||||||
name: 'window.nostr',
|
name: 'window.nostr',
|
||||||
displayName: 'Extension (window.nostr)',
|
displayName: 'Extension (window.nostr)',
|
||||||
icon: '🔑',
|
icon: '🔑',
|
||||||
extension: window.nostr
|
extension: window.nostr
|
||||||
});
|
});
|
||||||
}
|
seenExtensions.add(window.nostr);
|
||||||
}
|
console.log(`Modal: ✓ Detected extension at window.nostr: ${window.nostr.constructor?.name}`);
|
||||||
|
} else if (window.nostr) {
|
||||||
|
console.log(`Modal: ✗ Filtered out window.nostr (${window.nostr.constructor?.name}) - likely our library`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isRealExtension(obj) {
|
||||||
|
console.log(`Modal: EXTENSIVE DEBUG - _isRealExtension called with:`, obj);
|
||||||
|
console.log(`Modal: Object type: ${typeof obj}`);
|
||||||
|
console.log(`Modal: Object truthy: ${!!obj}`);
|
||||||
|
|
||||||
|
if (!obj || typeof obj !== 'object') {
|
||||||
|
console.log(`Modal: REJECT - Not an object`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Modal: getPublicKey type: ${typeof obj.getPublicKey}`);
|
||||||
|
console.log(`Modal: signEvent type: ${typeof obj.signEvent}`);
|
||||||
|
|
||||||
|
// Must have required Nostr methods
|
||||||
|
if (typeof obj.getPublicKey !== 'function' || typeof obj.signEvent !== 'function') {
|
||||||
|
console.log(`Modal: REJECT - Missing required methods`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude NostrTools library object
|
||||||
|
if (obj === window.NostrTools) {
|
||||||
|
console.log(`Modal: REJECT - Is NostrTools object`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the EXACT SAME logic as the comprehensive test (lines 804-809)
|
||||||
|
// This is the key fix - match the comprehensive test's successful detection logic
|
||||||
|
const constructorName = obj.constructor?.name;
|
||||||
|
const objectKeys = Object.keys(obj);
|
||||||
|
|
||||||
|
console.log(`Modal: Constructor name: "${constructorName}"`);
|
||||||
|
console.log(`Modal: Object keys: [${objectKeys.join(', ')}]`);
|
||||||
|
|
||||||
|
// COMPREHENSIVE TEST LOGIC - Accept anything with required methods that's not our specific library classes
|
||||||
|
const isRealExtension = (
|
||||||
|
typeof obj.getPublicKey === 'function' &&
|
||||||
|
typeof obj.signEvent === 'function' &&
|
||||||
|
constructorName !== 'WindowNostr' && // Our library class
|
||||||
|
constructorName !== 'NostrLite' // Our main class
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Modal: Using comprehensive test logic:`);
|
||||||
|
console.log(` Has getPublicKey: ${typeof obj.getPublicKey === 'function'}`);
|
||||||
|
console.log(` Has signEvent: ${typeof obj.signEvent === 'function'}`);
|
||||||
|
console.log(` Not WindowNostr: ${constructorName !== 'WindowNostr'}`);
|
||||||
|
console.log(` Not NostrLite: ${constructorName !== 'NostrLite'}`);
|
||||||
|
console.log(` Constructor: "${constructorName}"`);
|
||||||
|
|
||||||
|
// Additional debugging for comparison
|
||||||
|
const extensionPropChecks = {
|
||||||
|
_isEnabled: !!obj._isEnabled,
|
||||||
|
enabled: !!obj.enabled,
|
||||||
|
kind: !!obj.kind,
|
||||||
|
_eventEmitter: !!obj._eventEmitter,
|
||||||
|
_scope: !!obj._scope,
|
||||||
|
_requests: !!obj._requests,
|
||||||
|
_pubkey: !!obj._pubkey,
|
||||||
|
name: !!obj.name,
|
||||||
|
version: !!obj.version,
|
||||||
|
description: !!obj.description
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`Modal: Extension property analysis:`, extensionPropChecks);
|
||||||
|
|
||||||
|
const hasExtensionProps = !!(
|
||||||
|
obj._isEnabled || obj.enabled || obj.kind ||
|
||||||
|
obj._eventEmitter || obj._scope || obj._requests || obj._pubkey ||
|
||||||
|
obj.name || obj.version || obj.description
|
||||||
|
);
|
||||||
|
|
||||||
|
const underscoreKeys = objectKeys.filter(key => key.startsWith('_'));
|
||||||
|
const hexToUint8Keys = objectKeys.filter(key => key.startsWith('_hex'));
|
||||||
|
console.log(`Modal: Underscore keys: [${underscoreKeys.join(', ')}]`);
|
||||||
|
console.log(`Modal: _hex* keys: [${hexToUint8Keys.join(', ')}]`);
|
||||||
|
|
||||||
|
console.log(`Modal: Additional analysis:`);
|
||||||
|
console.log(` hasExtensionProps: ${hasExtensionProps}`);
|
||||||
|
console.log(` hasLibraryMethod (_hexToUint8Array): ${objectKeys.includes('_hexToUint8Array')}`);
|
||||||
|
|
||||||
|
console.log(`Modal: COMPREHENSIVE TEST LOGIC RESULT: ${isRealExtension ? 'ACCEPT' : 'REJECT'}`);
|
||||||
|
console.log(`Modal: FINAL DECISION for ${constructorName}: ${isRealExtension ? 'ACCEPT' : 'REJECT'}`);
|
||||||
|
|
||||||
|
return isRealExtension;
|
||||||
|
}
|
||||||
|
|
||||||
_showExtensionChoice(extensions) {
|
_showExtensionChoice(extensions) {
|
||||||
this.modalBody.innerHTML = '';
|
this.modalBody.innerHTML = '';
|
||||||
|
|
||||||
@@ -1684,7 +1765,8 @@ class ExtensionBridge {
|
|||||||
this.checking = false;
|
this.checking = false;
|
||||||
this.checkInterval = null;
|
this.checkInterval = null;
|
||||||
this.originalNostr = null;
|
this.originalNostr = null;
|
||||||
this.foundExtension = null;
|
this.foundExtensions = new Map(); // Store multiple extensions by location
|
||||||
|
this.primaryExtension = null; // The currently selected extension
|
||||||
}
|
}
|
||||||
|
|
||||||
startChecking(nostrLite) {
|
startChecking(nostrLite) {
|
||||||
@@ -1692,7 +1774,7 @@ class ExtensionBridge {
|
|||||||
this.checking = true;
|
this.checking = true;
|
||||||
|
|
||||||
const check = () => {
|
const check = () => {
|
||||||
this.initExtension(nostrLite);
|
this.detectAllExtensions(nostrLite);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check immediately
|
// Check immediately
|
||||||
@@ -1708,50 +1790,170 @@ class ExtensionBridge {
|
|||||||
}, 30000);
|
}, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
initExtension(nostrLite, lastTry = false) {
|
detectAllExtensions(nostrLite) {
|
||||||
const extension = window.nostr;
|
// Extension locations to check (in priority order)
|
||||||
|
const locations = [
|
||||||
|
{ path: 'window.navigator?.nostr', name: 'navigator.nostr', getter: () => window.navigator?.nostr },
|
||||||
|
{ path: 'window.webln?.nostr', name: 'webln.nostr', getter: () => window.webln?.nostr },
|
||||||
|
{ path: 'window.alby?.nostr', name: 'alby.nostr', getter: () => window.alby?.nostr },
|
||||||
|
{ path: 'window.nos2x', name: 'nos2x', getter: () => window.nos2x },
|
||||||
|
{ path: 'window.flamingo?.nostr', name: 'flamingo.nostr', getter: () => window.flamingo?.nostr },
|
||||||
|
{ path: 'window.mutiny?.nostr', name: 'mutiny.nostr', getter: () => window.mutiny?.nostr },
|
||||||
|
{ path: 'window.nostrich?.nostr', name: 'nostrich.nostr', getter: () => window.nostrich?.nostr },
|
||||||
|
{ path: 'window.getAlby?.nostr', name: 'getAlby.nostr', getter: () => window.getAlby?.nostr }
|
||||||
|
];
|
||||||
|
|
||||||
if (extension && !this.foundExtension) {
|
let foundNew = false;
|
||||||
// Check if this is actually a real extension, not our own library
|
|
||||||
const isRealExtension = (
|
// Check each location
|
||||||
extension !== nostrLite && // Not the same object we're about to assign
|
for (const location of locations) {
|
||||||
extension !== windowNostr && // Not our windowNostr object
|
try {
|
||||||
typeof extension._hexToUint8Array !== 'function' && // Our library has this internal method
|
const obj = location.getter();
|
||||||
extension.constructor.name !== 'Object' // Real extensions usually have proper constructors
|
|
||||||
|
if (obj && this.isRealExtension(obj, nostrLite)) {
|
||||||
|
if (!this.foundExtensions.has(location.name)) {
|
||||||
|
this.foundExtensions.set(location.name, {
|
||||||
|
name: location.name,
|
||||||
|
path: location.path,
|
||||||
|
extension: obj,
|
||||||
|
constructor: obj.constructor?.name || 'Unknown'
|
||||||
|
});
|
||||||
|
console.log(`Real Nostr extension detected: ${location.name} (${obj.constructor?.name})`);
|
||||||
|
foundNew = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Location doesn't exist or can't be accessed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check window.nostr but be extra careful to avoid our library
|
||||||
|
if (window.nostr && this.isRealExtension(window.nostr, nostrLite)) {
|
||||||
|
// Make sure we haven't already detected this extension via another path
|
||||||
|
const existingExtension = Array.from(this.foundExtensions.values()).find(
|
||||||
|
ext => ext.extension === window.nostr
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isRealExtension) {
|
if (!existingExtension && !this.foundExtensions.has('window.nostr')) {
|
||||||
this.foundExtension = extension;
|
this.foundExtensions.set('window.nostr', {
|
||||||
|
name: 'window.nostr',
|
||||||
|
path: 'window.nostr',
|
||||||
|
extension: window.nostr,
|
||||||
|
constructor: window.nostr.constructor?.name || 'Unknown'
|
||||||
|
});
|
||||||
|
console.log(`Real Nostr extension detected at window.nostr: ${window.nostr.constructor?.name}`);
|
||||||
|
foundNew = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set primary extension if we don't have one and found extensions
|
||||||
|
if (!this.primaryExtension && this.foundExtensions.size > 0) {
|
||||||
|
// Prefer navigator.nostr if available, otherwise use first found
|
||||||
|
this.primaryExtension = this.foundExtensions.get('navigator.nostr') ||
|
||||||
|
Array.from(this.foundExtensions.values())[0];
|
||||||
|
|
||||||
// Cache the extension and reassign window.nostr to our lite version
|
// Cache the extension and reassign window.nostr to our lite version
|
||||||
this.originalNostr = window.nostr;
|
this.originalNostr = this.primaryExtension.extension;
|
||||||
|
if (window.nostr !== nostrLite) {
|
||||||
window.nostr = nostrLite;
|
window.nostr = nostrLite;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Real Nostr extension detected and bridged:', extension.constructor.name);
|
console.log(`Primary extension set: ${this.primaryExtension.name}`);
|
||||||
|
|
||||||
// If currently authenticated, reconcile state
|
// If currently authenticated, reconcile state
|
||||||
if (LiteState.auth?.signer?.method === 'extension') {
|
if (LiteState.auth?.signer?.method === 'extension') {
|
||||||
this.reconcileExtension();
|
this.reconcileExtension();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log('Skipping non-extension object on window.nostr:', extension.constructor.name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isRealExtension(obj, nostrLite) {
|
||||||
|
if (!obj || typeof obj !== 'object') return false;
|
||||||
|
|
||||||
|
// Must have required Nostr methods
|
||||||
|
if (typeof obj.getPublicKey !== 'function' || typeof obj.signEvent !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude our own library objects
|
||||||
|
if (obj === nostrLite || obj === windowNostr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude objects with our library's internal methods
|
||||||
|
if (typeof obj._hexToUint8Array === 'function' || typeof obj._call === 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude NostrTools library object
|
||||||
|
if (obj === window.NostrTools) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real extensions typically have proper constructors (not plain Object)
|
||||||
|
const constructorName = obj.constructor?.name;
|
||||||
|
if (constructorName === 'Object' && !obj._isEnabled && !obj.enabled) {
|
||||||
|
// Plain objects without extension-specific properties are likely our library
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllExtensions() {
|
||||||
|
return Array.from(this.foundExtensions.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
getExtensionCount() {
|
||||||
|
return this.foundExtensions.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasExtension() {
|
hasExtension() {
|
||||||
return !!this.foundExtension;
|
return this.foundExtensions.size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy compatibility - return primary extension
|
||||||
|
get foundExtension() {
|
||||||
|
return this.primaryExtension?.extension || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to properly set primary extension
|
||||||
|
setPrimaryExtension(extension, name = 'selected') {
|
||||||
|
// Find the extension in our map or create new entry
|
||||||
|
let extensionInfo = null;
|
||||||
|
|
||||||
|
// Check if this extension is already in our map
|
||||||
|
for (const [key, info] of this.foundExtensions) {
|
||||||
|
if (info.extension === extension) {
|
||||||
|
extensionInfo = info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not found, create a new entry
|
||||||
|
if (!extensionInfo) {
|
||||||
|
extensionInfo = {
|
||||||
|
name: name,
|
||||||
|
path: name,
|
||||||
|
extension: extension,
|
||||||
|
constructor: extension?.constructor?.name || 'Unknown'
|
||||||
|
};
|
||||||
|
this.foundExtensions.set(name, extensionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.primaryExtension = extensionInfo;
|
||||||
|
console.log(`Primary extension set to: ${extensionInfo.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setExtensionReadPubkey(expectedPubkey = null) {
|
async setExtensionReadPubkey(expectedPubkey = null) {
|
||||||
if (!this.foundExtension) return false;
|
if (!this.primaryExtension) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Temporarily set window.nostr to extension
|
// Temporarily set window.nostr to extension
|
||||||
const temp = window.nostr;
|
const temp = window.nostr;
|
||||||
window.nostr = this.foundExtension;
|
window.nostr = this.primaryExtension.extension;
|
||||||
|
|
||||||
const pubkey = await this.foundExtension.getPublicKey();
|
const pubkey = await this.primaryExtension.extension.getPublicKey();
|
||||||
|
|
||||||
// Restore our lite implementation
|
// Restore our lite implementation
|
||||||
window.nostr = temp;
|
window.nostr = temp;
|
||||||
@@ -1780,8 +1982,8 @@ class ExtensionBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setExtension() {
|
setExtension() {
|
||||||
if (!this.foundExtension) return;
|
if (!this.primaryExtension) return;
|
||||||
window.nostr = this.foundExtension;
|
window.nostr = this.primaryExtension.extension;
|
||||||
this.setExtensionReadPubkey().then(pubkey => {
|
this.setExtensionReadPubkey().then(pubkey => {
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
LiteState.bus?.emit('extensionSet', { pubkey });
|
LiteState.bus?.emit('extensionSet', { pubkey });
|
||||||
@@ -1877,7 +2079,7 @@ class NostrLite {
|
|||||||
// Handle extension detection
|
// Handle extension detection
|
||||||
this.bus?.on('extensionDetected', (extension) => {
|
this.bus?.on('extensionDetected', (extension) => {
|
||||||
console.log('Extension detected');
|
console.log('Extension detected');
|
||||||
LiteState.extensionBridge.foundExtension = extension;
|
LiteState.extensionBridge.setPrimaryExtension(extension, 'detected');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle auth URL from NIP-46
|
// Handle auth URL from NIP-46
|
||||||
@@ -1915,7 +2117,7 @@ class NostrLite {
|
|||||||
case 'extension':
|
case 'extension':
|
||||||
if (pubkey && extension) {
|
if (pubkey && extension) {
|
||||||
// Store the extension object in the ExtensionBridge for future use
|
// Store the extension object in the ExtensionBridge for future use
|
||||||
LiteState.extensionBridge.foundExtension = extension;
|
LiteState.extensionBridge.setPrimaryExtension(extension, 'modal-selected');
|
||||||
LiteState.extensionBridge.originalNostr = extension;
|
LiteState.extensionBridge.originalNostr = extension;
|
||||||
|
|
||||||
// Set up extension authentication
|
// Set up extension authentication
|
||||||
|
|||||||
@@ -264,7 +264,8 @@ class ExtensionBridge {
|
|||||||
this.checking = false;
|
this.checking = false;
|
||||||
this.checkInterval = null;
|
this.checkInterval = null;
|
||||||
this.originalNostr = null;
|
this.originalNostr = null;
|
||||||
this.foundExtension = null;
|
this.foundExtensions = new Map(); // Store multiple extensions by location
|
||||||
|
this.primaryExtension = null; // The currently selected extension
|
||||||
}
|
}
|
||||||
|
|
||||||
startChecking(nostrLite) {
|
startChecking(nostrLite) {
|
||||||
@@ -272,7 +273,7 @@ class ExtensionBridge {
|
|||||||
this.checking = true;
|
this.checking = true;
|
||||||
|
|
||||||
const check = () => {
|
const check = () => {
|
||||||
this.initExtension(nostrLite);
|
this.detectAllExtensions(nostrLite);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check immediately
|
// Check immediately
|
||||||
@@ -288,50 +289,170 @@ class ExtensionBridge {
|
|||||||
}, 30000);
|
}, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
initExtension(nostrLite, lastTry = false) {
|
detectAllExtensions(nostrLite) {
|
||||||
const extension = window.nostr;
|
// Extension locations to check (in priority order)
|
||||||
|
const locations = [
|
||||||
|
{ path: 'window.navigator?.nostr', name: 'navigator.nostr', getter: () => window.navigator?.nostr },
|
||||||
|
{ path: 'window.webln?.nostr', name: 'webln.nostr', getter: () => window.webln?.nostr },
|
||||||
|
{ path: 'window.alby?.nostr', name: 'alby.nostr', getter: () => window.alby?.nostr },
|
||||||
|
{ path: 'window.nos2x', name: 'nos2x', getter: () => window.nos2x },
|
||||||
|
{ path: 'window.flamingo?.nostr', name: 'flamingo.nostr', getter: () => window.flamingo?.nostr },
|
||||||
|
{ path: 'window.mutiny?.nostr', name: 'mutiny.nostr', getter: () => window.mutiny?.nostr },
|
||||||
|
{ path: 'window.nostrich?.nostr', name: 'nostrich.nostr', getter: () => window.nostrich?.nostr },
|
||||||
|
{ path: 'window.getAlby?.nostr', name: 'getAlby.nostr', getter: () => window.getAlby?.nostr }
|
||||||
|
];
|
||||||
|
|
||||||
if (extension && !this.foundExtension) {
|
let foundNew = false;
|
||||||
// Check if this is actually a real extension, not our own library
|
|
||||||
const isRealExtension = (
|
// Check each location
|
||||||
extension !== nostrLite && // Not the same object we're about to assign
|
for (const location of locations) {
|
||||||
extension !== windowNostr && // Not our windowNostr object
|
try {
|
||||||
typeof extension._hexToUint8Array !== 'function' && // Our library has this internal method
|
const obj = location.getter();
|
||||||
extension.constructor.name !== 'Object' // Real extensions usually have proper constructors
|
|
||||||
|
if (obj && this.isRealExtension(obj, nostrLite)) {
|
||||||
|
if (!this.foundExtensions.has(location.name)) {
|
||||||
|
this.foundExtensions.set(location.name, {
|
||||||
|
name: location.name,
|
||||||
|
path: location.path,
|
||||||
|
extension: obj,
|
||||||
|
constructor: obj.constructor?.name || 'Unknown'
|
||||||
|
});
|
||||||
|
console.log(`Real Nostr extension detected: ${location.name} (${obj.constructor?.name})`);
|
||||||
|
foundNew = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Location doesn't exist or can't be accessed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check window.nostr but be extra careful to avoid our library
|
||||||
|
if (window.nostr && this.isRealExtension(window.nostr, nostrLite)) {
|
||||||
|
// Make sure we haven't already detected this extension via another path
|
||||||
|
const existingExtension = Array.from(this.foundExtensions.values()).find(
|
||||||
|
ext => ext.extension === window.nostr
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isRealExtension) {
|
if (!existingExtension && !this.foundExtensions.has('window.nostr')) {
|
||||||
this.foundExtension = extension;
|
this.foundExtensions.set('window.nostr', {
|
||||||
|
name: 'window.nostr',
|
||||||
|
path: 'window.nostr',
|
||||||
|
extension: window.nostr,
|
||||||
|
constructor: window.nostr.constructor?.name || 'Unknown'
|
||||||
|
});
|
||||||
|
console.log(`Real Nostr extension detected at window.nostr: ${window.nostr.constructor?.name}`);
|
||||||
|
foundNew = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set primary extension if we don't have one and found extensions
|
||||||
|
if (!this.primaryExtension && this.foundExtensions.size > 0) {
|
||||||
|
// Prefer navigator.nostr if available, otherwise use first found
|
||||||
|
this.primaryExtension = this.foundExtensions.get('navigator.nostr') ||
|
||||||
|
Array.from(this.foundExtensions.values())[0];
|
||||||
|
|
||||||
// Cache the extension and reassign window.nostr to our lite version
|
// Cache the extension and reassign window.nostr to our lite version
|
||||||
this.originalNostr = window.nostr;
|
this.originalNostr = this.primaryExtension.extension;
|
||||||
|
if (window.nostr !== nostrLite) {
|
||||||
window.nostr = nostrLite;
|
window.nostr = nostrLite;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Real Nostr extension detected and bridged:', extension.constructor.name);
|
console.log(`Primary extension set: ${this.primaryExtension.name}`);
|
||||||
|
|
||||||
// If currently authenticated, reconcile state
|
// If currently authenticated, reconcile state
|
||||||
if (LiteState.auth?.signer?.method === 'extension') {
|
if (LiteState.auth?.signer?.method === 'extension') {
|
||||||
this.reconcileExtension();
|
this.reconcileExtension();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log('Skipping non-extension object on window.nostr:', extension.constructor.name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isRealExtension(obj, nostrLite) {
|
||||||
|
if (!obj || typeof obj !== 'object') return false;
|
||||||
|
|
||||||
|
// Must have required Nostr methods
|
||||||
|
if (typeof obj.getPublicKey !== 'function' || typeof obj.signEvent !== 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude our own library objects
|
||||||
|
if (obj === nostrLite || obj === windowNostr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude objects with our library's internal methods
|
||||||
|
if (typeof obj._hexToUint8Array === 'function' || typeof obj._call === 'function') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude NostrTools library object
|
||||||
|
if (obj === window.NostrTools) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real extensions typically have proper constructors (not plain Object)
|
||||||
|
const constructorName = obj.constructor?.name;
|
||||||
|
if (constructorName === 'Object' && !obj._isEnabled && !obj.enabled) {
|
||||||
|
// Plain objects without extension-specific properties are likely our library
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllExtensions() {
|
||||||
|
return Array.from(this.foundExtensions.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
getExtensionCount() {
|
||||||
|
return this.foundExtensions.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasExtension() {
|
hasExtension() {
|
||||||
return !!this.foundExtension;
|
return this.foundExtensions.size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy compatibility - return primary extension
|
||||||
|
get foundExtension() {
|
||||||
|
return this.primaryExtension?.extension || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to properly set primary extension
|
||||||
|
setPrimaryExtension(extension, name = 'selected') {
|
||||||
|
// Find the extension in our map or create new entry
|
||||||
|
let extensionInfo = null;
|
||||||
|
|
||||||
|
// Check if this extension is already in our map
|
||||||
|
for (const [key, info] of this.foundExtensions) {
|
||||||
|
if (info.extension === extension) {
|
||||||
|
extensionInfo = info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not found, create a new entry
|
||||||
|
if (!extensionInfo) {
|
||||||
|
extensionInfo = {
|
||||||
|
name: name,
|
||||||
|
path: name,
|
||||||
|
extension: extension,
|
||||||
|
constructor: extension?.constructor?.name || 'Unknown'
|
||||||
|
};
|
||||||
|
this.foundExtensions.set(name, extensionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.primaryExtension = extensionInfo;
|
||||||
|
console.log(`Primary extension set to: ${extensionInfo.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setExtensionReadPubkey(expectedPubkey = null) {
|
async setExtensionReadPubkey(expectedPubkey = null) {
|
||||||
if (!this.foundExtension) return false;
|
if (!this.primaryExtension) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Temporarily set window.nostr to extension
|
// Temporarily set window.nostr to extension
|
||||||
const temp = window.nostr;
|
const temp = window.nostr;
|
||||||
window.nostr = this.foundExtension;
|
window.nostr = this.primaryExtension.extension;
|
||||||
|
|
||||||
const pubkey = await this.foundExtension.getPublicKey();
|
const pubkey = await this.primaryExtension.extension.getPublicKey();
|
||||||
|
|
||||||
// Restore our lite implementation
|
// Restore our lite implementation
|
||||||
window.nostr = temp;
|
window.nostr = temp;
|
||||||
@@ -360,8 +481,8 @@ class ExtensionBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setExtension() {
|
setExtension() {
|
||||||
if (!this.foundExtension) return;
|
if (!this.primaryExtension) return;
|
||||||
window.nostr = this.foundExtension;
|
window.nostr = this.primaryExtension.extension;
|
||||||
this.setExtensionReadPubkey().then(pubkey => {
|
this.setExtensionReadPubkey().then(pubkey => {
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
LiteState.bus?.emit('extensionSet', { pubkey });
|
LiteState.bus?.emit('extensionSet', { pubkey });
|
||||||
@@ -457,7 +578,7 @@ class NostrLite {
|
|||||||
// Handle extension detection
|
// Handle extension detection
|
||||||
this.bus?.on('extensionDetected', (extension) => {
|
this.bus?.on('extensionDetected', (extension) => {
|
||||||
console.log('Extension detected');
|
console.log('Extension detected');
|
||||||
LiteState.extensionBridge.foundExtension = extension;
|
LiteState.extensionBridge.setPrimaryExtension(extension, 'detected');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle auth URL from NIP-46
|
// Handle auth URL from NIP-46
|
||||||
@@ -495,7 +616,7 @@ class NostrLite {
|
|||||||
case 'extension':
|
case 'extension':
|
||||||
if (pubkey && extension) {
|
if (pubkey && extension) {
|
||||||
// Store the extension object in the ExtensionBridge for future use
|
// Store the extension object in the ExtensionBridge for future use
|
||||||
LiteState.extensionBridge.foundExtension = extension;
|
LiteState.extensionBridge.setPrimaryExtension(extension, 'modal-selected');
|
||||||
LiteState.extensionBridge.originalNostr = extension;
|
LiteState.extensionBridge.originalNostr = extension;
|
||||||
|
|
||||||
// Set up extension authentication
|
// Set up extension authentication
|
||||||
|
|||||||
183
lite/ui/modal.js
183
lite/ui/modal.js
@@ -281,85 +281,166 @@ class Modal {
|
|||||||
// Detect all available real extensions
|
// Detect all available real extensions
|
||||||
const availableExtensions = this._detectAllExtensions();
|
const availableExtensions = this._detectAllExtensions();
|
||||||
|
|
||||||
|
console.log(`Modal: Found ${availableExtensions.length} extensions:`, availableExtensions.map(e => e.displayName));
|
||||||
|
|
||||||
if (availableExtensions.length === 0) {
|
if (availableExtensions.length === 0) {
|
||||||
console.log('Modal: No real extensions found');
|
console.log('Modal: No real extensions found');
|
||||||
this._showExtensionRequired();
|
this._showExtensionRequired();
|
||||||
} else if (availableExtensions.length === 1) {
|
} else if (availableExtensions.length === 1) {
|
||||||
console.log('Modal detected single extension:', availableExtensions[0].name);
|
// Single extension - use it directly without showing choice UI
|
||||||
|
console.log('Modal: Single extension detected, using it directly:', availableExtensions[0].displayName);
|
||||||
this._tryExtensionLogin(availableExtensions[0].extension);
|
this._tryExtensionLogin(availableExtensions[0].extension);
|
||||||
} else {
|
} else {
|
||||||
console.log('Modal detected multiple extensions:', availableExtensions.map(e => e.name));
|
// Multiple extensions - show choice UI
|
||||||
|
console.log('Modal: Multiple extensions detected, showing choice UI for', availableExtensions.length, 'extensions');
|
||||||
this._showExtensionChoice(availableExtensions);
|
this._showExtensionChoice(availableExtensions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_detectAllExtensions() {
|
_detectAllExtensions() {
|
||||||
const extensions = [];
|
const extensions = [];
|
||||||
|
const seenExtensions = new Set(); // Track extensions by object reference to avoid duplicates
|
||||||
|
|
||||||
// Check navigator.nostr (NIP-07 standard location)
|
// Extension locations to check (in priority order)
|
||||||
if (window.navigator?.nostr && typeof window.navigator.nostr.getPublicKey === 'function') {
|
const locations = [
|
||||||
|
{ path: 'window.navigator?.nostr', name: 'navigator.nostr', displayName: 'Standard Extension (navigator.nostr)', icon: '🌐', getter: () => window.navigator?.nostr },
|
||||||
|
{ path: 'window.webln?.nostr', name: 'webln.nostr', displayName: 'Alby WebLN Extension', icon: '⚡', getter: () => window.webln?.nostr },
|
||||||
|
{ path: 'window.alby?.nostr', name: 'alby.nostr', displayName: 'Alby Extension (Direct)', icon: '🐝', getter: () => window.alby?.nostr },
|
||||||
|
{ path: 'window.nos2x', name: 'nos2x', displayName: 'nos2x Extension', icon: '🔌', getter: () => window.nos2x },
|
||||||
|
{ path: 'window.flamingo?.nostr', name: 'flamingo.nostr', displayName: 'Flamingo Extension', icon: '🦩', getter: () => window.flamingo?.nostr },
|
||||||
|
{ path: 'window.mutiny?.nostr', name: 'mutiny.nostr', displayName: 'Mutiny Extension', icon: '⚔️', getter: () => window.mutiny?.nostr },
|
||||||
|
{ path: 'window.nostrich?.nostr', name: 'nostrich.nostr', displayName: 'Nostrich Extension', icon: '🐦', getter: () => window.nostrich?.nostr },
|
||||||
|
{ path: 'window.getAlby?.nostr', name: 'getAlby.nostr', displayName: 'getAlby Extension', icon: '🔧', getter: () => window.getAlby?.nostr }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check each location
|
||||||
|
for (const location of locations) {
|
||||||
|
try {
|
||||||
|
const obj = location.getter();
|
||||||
|
|
||||||
|
console.log(`Modal: Checking ${location.name}:`, !!obj, obj?.constructor?.name);
|
||||||
|
|
||||||
|
if (obj && this._isRealExtension(obj) && !seenExtensions.has(obj)) {
|
||||||
extensions.push({
|
extensions.push({
|
||||||
name: 'navigator.nostr',
|
name: location.name,
|
||||||
displayName: 'Standard Extension (navigator.nostr)',
|
displayName: location.displayName,
|
||||||
icon: '🌐',
|
icon: location.icon,
|
||||||
extension: window.navigator.nostr
|
extension: obj
|
||||||
});
|
});
|
||||||
|
seenExtensions.add(obj);
|
||||||
|
console.log(`Modal: ✓ Detected extension at ${location.name} (${obj.constructor?.name})`);
|
||||||
|
} else if (obj) {
|
||||||
|
console.log(`Modal: ✗ Filtered out ${location.name} (${obj.constructor?.name})`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Location doesn't exist or can't be accessed
|
||||||
|
console.log(`Modal: ${location.name} not accessible:`, e.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check webln.nostr (Alby WebLN)
|
// Also check window.nostr but be extra careful to avoid our library
|
||||||
if (window.webln?.nostr && typeof window.webln.nostr.getPublicKey === 'function') {
|
console.log('Modal: Checking window.nostr:', !!window.nostr, window.nostr?.constructor?.name);
|
||||||
extensions.push({
|
if (window.nostr && this._isRealExtension(window.nostr) && !seenExtensions.has(window.nostr)) {
|
||||||
name: 'webln.nostr',
|
|
||||||
displayName: 'Alby WebLN Extension',
|
|
||||||
icon: '⚡',
|
|
||||||
extension: window.webln.nostr
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check alby.nostr (Alby direct)
|
|
||||||
if (window.alby?.nostr && typeof window.alby.nostr.getPublicKey === 'function') {
|
|
||||||
extensions.push({
|
|
||||||
name: 'alby.nostr',
|
|
||||||
displayName: 'Alby Extension (Direct)',
|
|
||||||
icon: '🐝',
|
|
||||||
extension: window.alby.nostr
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check nos2x
|
|
||||||
if (window.nos2x && typeof window.nos2x.getPublicKey === 'function') {
|
|
||||||
extensions.push({
|
|
||||||
name: 'nos2x',
|
|
||||||
displayName: 'nos2x Extension',
|
|
||||||
icon: '🔌',
|
|
||||||
extension: window.nos2x
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check window.nostr but make sure it's not our library
|
|
||||||
if (window.nostr && typeof window.nostr.getPublicKey === 'function') {
|
|
||||||
const isRealExtension = (
|
|
||||||
typeof window.nostr._hexToUint8Array !== 'function' && // Our library has this method
|
|
||||||
window.nostr.constructor.name !== 'Object' // Real extensions usually have proper constructors
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRealExtension) {
|
|
||||||
// Don't add if we already detected it via another path
|
|
||||||
const alreadyDetected = extensions.some(ext => ext.extension === window.nostr);
|
|
||||||
if (!alreadyDetected) {
|
|
||||||
extensions.push({
|
extensions.push({
|
||||||
name: 'window.nostr',
|
name: 'window.nostr',
|
||||||
displayName: 'Extension (window.nostr)',
|
displayName: 'Extension (window.nostr)',
|
||||||
icon: '🔑',
|
icon: '🔑',
|
||||||
extension: window.nostr
|
extension: window.nostr
|
||||||
});
|
});
|
||||||
}
|
seenExtensions.add(window.nostr);
|
||||||
}
|
console.log(`Modal: ✓ Detected extension at window.nostr: ${window.nostr.constructor?.name}`);
|
||||||
|
} else if (window.nostr) {
|
||||||
|
console.log(`Modal: ✗ Filtered out window.nostr (${window.nostr.constructor?.name}) - likely our library`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isRealExtension(obj) {
|
||||||
|
console.log(`Modal: EXTENSIVE DEBUG - _isRealExtension called with:`, obj);
|
||||||
|
console.log(`Modal: Object type: ${typeof obj}`);
|
||||||
|
console.log(`Modal: Object truthy: ${!!obj}`);
|
||||||
|
|
||||||
|
if (!obj || typeof obj !== 'object') {
|
||||||
|
console.log(`Modal: REJECT - Not an object`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Modal: getPublicKey type: ${typeof obj.getPublicKey}`);
|
||||||
|
console.log(`Modal: signEvent type: ${typeof obj.signEvent}`);
|
||||||
|
|
||||||
|
// Must have required Nostr methods
|
||||||
|
if (typeof obj.getPublicKey !== 'function' || typeof obj.signEvent !== 'function') {
|
||||||
|
console.log(`Modal: REJECT - Missing required methods`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude NostrTools library object
|
||||||
|
if (obj === window.NostrTools) {
|
||||||
|
console.log(`Modal: REJECT - Is NostrTools object`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the EXACT SAME logic as the comprehensive test (lines 804-809)
|
||||||
|
// This is the key fix - match the comprehensive test's successful detection logic
|
||||||
|
const constructorName = obj.constructor?.name;
|
||||||
|
const objectKeys = Object.keys(obj);
|
||||||
|
|
||||||
|
console.log(`Modal: Constructor name: "${constructorName}"`);
|
||||||
|
console.log(`Modal: Object keys: [${objectKeys.join(', ')}]`);
|
||||||
|
|
||||||
|
// COMPREHENSIVE TEST LOGIC - Accept anything with required methods that's not our specific library classes
|
||||||
|
const isRealExtension = (
|
||||||
|
typeof obj.getPublicKey === 'function' &&
|
||||||
|
typeof obj.signEvent === 'function' &&
|
||||||
|
constructorName !== 'WindowNostr' && // Our library class
|
||||||
|
constructorName !== 'NostrLite' // Our main class
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Modal: Using comprehensive test logic:`);
|
||||||
|
console.log(` Has getPublicKey: ${typeof obj.getPublicKey === 'function'}`);
|
||||||
|
console.log(` Has signEvent: ${typeof obj.signEvent === 'function'}`);
|
||||||
|
console.log(` Not WindowNostr: ${constructorName !== 'WindowNostr'}`);
|
||||||
|
console.log(` Not NostrLite: ${constructorName !== 'NostrLite'}`);
|
||||||
|
console.log(` Constructor: "${constructorName}"`);
|
||||||
|
|
||||||
|
// Additional debugging for comparison
|
||||||
|
const extensionPropChecks = {
|
||||||
|
_isEnabled: !!obj._isEnabled,
|
||||||
|
enabled: !!obj.enabled,
|
||||||
|
kind: !!obj.kind,
|
||||||
|
_eventEmitter: !!obj._eventEmitter,
|
||||||
|
_scope: !!obj._scope,
|
||||||
|
_requests: !!obj._requests,
|
||||||
|
_pubkey: !!obj._pubkey,
|
||||||
|
name: !!obj.name,
|
||||||
|
version: !!obj.version,
|
||||||
|
description: !!obj.description
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`Modal: Extension property analysis:`, extensionPropChecks);
|
||||||
|
|
||||||
|
const hasExtensionProps = !!(
|
||||||
|
obj._isEnabled || obj.enabled || obj.kind ||
|
||||||
|
obj._eventEmitter || obj._scope || obj._requests || obj._pubkey ||
|
||||||
|
obj.name || obj.version || obj.description
|
||||||
|
);
|
||||||
|
|
||||||
|
const underscoreKeys = objectKeys.filter(key => key.startsWith('_'));
|
||||||
|
const hexToUint8Keys = objectKeys.filter(key => key.startsWith('_hex'));
|
||||||
|
console.log(`Modal: Underscore keys: [${underscoreKeys.join(', ')}]`);
|
||||||
|
console.log(`Modal: _hex* keys: [${hexToUint8Keys.join(', ')}]`);
|
||||||
|
|
||||||
|
console.log(`Modal: Additional analysis:`);
|
||||||
|
console.log(` hasExtensionProps: ${hasExtensionProps}`);
|
||||||
|
console.log(` hasLibraryMethod (_hexToUint8Array): ${objectKeys.includes('_hexToUint8Array')}`);
|
||||||
|
|
||||||
|
console.log(`Modal: COMPREHENSIVE TEST LOGIC RESULT: ${isRealExtension ? 'ACCEPT' : 'REJECT'}`);
|
||||||
|
console.log(`Modal: FINAL DECISION for ${constructorName}: ${isRealExtension ? 'ACCEPT' : 'REJECT'}`);
|
||||||
|
|
||||||
|
return isRealExtension;
|
||||||
|
}
|
||||||
|
|
||||||
_showExtensionChoice(extensions) {
|
_showExtensionChoice(extensions) {
|
||||||
this.modalBody.innerHTML = '';
|
this.modalBody.innerHTML = '';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user