last commit before bundling nostr-tools ourselves

This commit is contained in:
Your Name
2025-09-13 10:22:57 -04:00
parent 025d66c096
commit bad361a686
3 changed files with 579 additions and 175 deletions

View File

@@ -264,7 +264,8 @@ class ExtensionBridge {
this.checking = false;
this.checkInterval = 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) {
@@ -272,7 +273,7 @@ class ExtensionBridge {
this.checking = true;
const check = () => {
this.initExtension(nostrLite);
this.detectAllExtensions(nostrLite);
};
// Check immediately
@@ -288,50 +289,170 @@ class ExtensionBridge {
}, 30000);
}
initExtension(nostrLite, lastTry = false) {
const extension = window.nostr;
detectAllExtensions(nostrLite) {
// 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) {
// Check if this is actually a real extension, not our own library
const isRealExtension = (
extension !== nostrLite && // Not the same object we're about to assign
extension !== windowNostr && // Not our windowNostr object
typeof extension._hexToUint8Array !== 'function' && // Our library has this internal method
extension.constructor.name !== 'Object' // Real extensions usually have proper constructors
let foundNew = false;
// Check each location
for (const location of locations) {
try {
const obj = location.getter();
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) {
this.foundExtension = extension;
if (!existingExtension && !this.foundExtensions.has('window.nostr')) {
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;
}
}
// Cache the extension and reassign window.nostr to our lite version
this.originalNostr = window.nostr;
// 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
this.originalNostr = this.primaryExtension.extension;
if (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 (LiteState.auth?.signer?.method === 'extension') {
this.reconcileExtension();
}
} else {
console.log('Skipping non-extension object on window.nostr:', extension.constructor.name);
// If currently authenticated, reconcile state
if (LiteState.auth?.signer?.method === 'extension') {
this.reconcileExtension();
}
}
}
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() {
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) {
if (!this.foundExtension) return false;
if (!this.primaryExtension) return false;
try {
// Temporarily set window.nostr to extension
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
window.nostr = temp;
@@ -360,8 +481,8 @@ class ExtensionBridge {
}
setExtension() {
if (!this.foundExtension) return;
window.nostr = this.foundExtension;
if (!this.primaryExtension) return;
window.nostr = this.primaryExtension.extension;
this.setExtensionReadPubkey().then(pubkey => {
if (pubkey) {
LiteState.bus?.emit('extensionSet', { pubkey });
@@ -457,7 +578,7 @@ class NostrLite {
// Handle extension detection
this.bus?.on('extensionDetected', (extension) => {
console.log('Extension detected');
LiteState.extensionBridge.foundExtension = extension;
LiteState.extensionBridge.setPrimaryExtension(extension, 'detected');
});
// Handle auth URL from NIP-46
@@ -495,7 +616,7 @@ class NostrLite {
case 'extension':
if (pubkey && extension) {
// Store the extension object in the ExtensionBridge for future use
LiteState.extensionBridge.foundExtension = extension;
LiteState.extensionBridge.setPrimaryExtension(extension, 'modal-selected');
LiteState.extensionBridge.originalNostr = extension;
// Set up extension authentication