/** * Simple script to create NOSTR_LOGIN_LITE bundle * For the new two-file architecture: * 1. nostr.bundle.js (official nostr-tools bundle - static file) * 2. nip46-extension.js (NIP-46 extension - static file) * 3. nostr-lite.js (NOSTR_LOGIN_LITE library - built by this script) */ const fs = require('fs'); const path = require('path'); function createNostrLoginLiteBundle() { console.log('šŸ”§ Creating NOSTR_LOGIN_LITE bundle for two-file architecture...'); const outputPath = path.join(__dirname, 'nostr-lite.js'); // Remove old bundle try { if (fs.existsSync(outputPath)) { fs.unlinkSync(outputPath); } } catch (e) { console.log('No old bundle to remove'); } // Start with the bundle header let bundle = `/** * NOSTR_LOGIN_LITE - Authentication Library * Two-file architecture: * 1. Load nostr.bundle.js (official nostr-tools bundle) * 2. Load nip46-extension.js (extends NostrTools with NIP-46) * 3. Load nostr-lite.js (this file - NOSTR_LOGIN_LITE library) * Generated on: ${new Date().toISOString()} */ // Verify dependencies are loaded if (typeof window !== 'undefined') { if (!window.NostrTools) { console.error('NOSTR_LOGIN_LITE: nostr.bundle.js must be loaded first'); throw new Error('Missing dependency: nostr.bundle.js'); } if (!window.NostrTools.nip46) { console.error('NOSTR_LOGIN_LITE: nip46-extension.js must be loaded after nostr.bundle.js'); throw new Error('Missing dependency: nip46-extension.js'); } console.log('NOSTR_LOGIN_LITE: Dependencies verified āœ“'); console.log('NOSTR_LOGIN_LITE: NostrTools available with keys:', Object.keys(window.NostrTools)); console.log('NOSTR_LOGIN_LITE: NIP-46 available:', !!window.NostrTools.nip46); } // ====================================== // NOSTR_LOGIN_LITE Components // ====================================== `; // Add Modal UI const modalPath = path.join(__dirname, 'ui/modal.js'); if (fs.existsSync(modalPath)) { console.log('šŸ“„ Adding Modal UI...'); let modalContent = fs.readFileSync(modalPath, 'utf8'); // Skip header comments let lines = modalContent.split('\n'); let contentStartIndex = 0; for (let i = 0; i < Math.min(15, lines.length); i++) { const line = lines[i].trim(); if (line.startsWith('/**') || line.startsWith('*') || line.startsWith('/*') || line.startsWith('//')) { contentStartIndex = i + 1; } else if (line && !line.startsWith('*') && !line.startsWith('//')) { break; } } if (contentStartIndex > 0) { lines = lines.slice(contentStartIndex); } bundle += `// ======================================\n`; bundle += `// Modal UI Component\n`; bundle += `// ======================================\n\n`; bundle += lines.join('\n'); bundle += '\n\n'; } else { console.warn('āš ļø Modal UI not found: ui/modal.js'); } // Add main library code console.log('šŸ“„ Adding Main Library...'); bundle += ` // ====================================== // Main NOSTR_LOGIN_LITE Library // ====================================== // Extension Bridge for managing browser extensions class ExtensionBridge { constructor() { this.extensions = new Map(); this.primaryExtension = null; this._detectExtensions(); } _detectExtensions() { // Common extension locations const locations = [ { path: 'window.nostr', name: 'Generic' }, { path: 'window.alby?.nostr', name: 'Alby' }, { path: 'window.nos2x?.nostr', name: 'nos2x' }, { path: 'window.flamingo?.nostr', name: 'Flamingo' }, { path: 'window.getAlby?.nostr', name: 'Alby Legacy' }, { path: 'window.mutiny?.nostr', name: 'Mutiny' } ]; for (const location of locations) { try { const obj = eval(location.path); if (obj && typeof obj.getPublicKey === 'function') { this.extensions.set(location.name, { name: location.name, extension: obj, constructor: obj.constructor?.name || 'Unknown' }); if (!this.primaryExtension) { this.primaryExtension = this.extensions.get(location.name); } } } catch (e) { // Extension not available } } } getAllExtensions() { return Array.from(this.extensions.values()); } getExtensionCount() { return this.extensions.size; } } // Main NostrLite class class NostrLite { constructor() { this.options = {}; this.extensionBridge = new ExtensionBridge(); this.initialized = false; } async init(options = {}) { console.log('NOSTR_LOGIN_LITE: Initializing with options:', options); this.options = { theme: 'light', darkMode: false, relays: ['wss://relay.damus.io', 'wss://nos.lol'], methods: { extension: true, local: true, readonly: true, connect: false, otp: false }, ...options }; // Set up window.nostr facade if no extension detected if (this.extensionBridge.getExtensionCount() === 0) { this._setupWindowNostrFacade(); } this.initialized = true; console.log('NOSTR_LOGIN_LITE: Initialization complete'); return this; } _setupWindowNostrFacade() { if (typeof window !== 'undefined' && !window.nostr) { window.nostr = new WindowNostr(this); console.log('NOSTR_LOGIN_LITE: window.nostr facade installed'); } } launch(startScreen = 'login') { console.log('NOSTR_LOGIN_LITE: Launching with screen:', startScreen); if (typeof Modal !== 'undefined') { const modal = new Modal(this.options); modal.open({ startScreen }); } else { console.error('NOSTR_LOGIN_LITE: Modal component not available'); } } logout() { console.log('NOSTR_LOGIN_LITE: Logout called'); // Clear stored data if (typeof localStorage !== 'undefined') { localStorage.removeItem('nl_current'); } // Dispatch logout event if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent('nlLogout', { detail: { timestamp: Date.now() } })); } } } // Window.nostr facade for when no extension is available class WindowNostr { constructor(nostrLite) { this.nostrLite = nostrLite; } async getPublicKey() { throw new Error('Authentication required - use NOSTR_LOGIN_LITE.launch()'); } async signEvent(event) { throw new Error('Authentication required - use NOSTR_LOGIN_LITE.launch()'); } async getRelays() { throw new Error('Authentication required - use NOSTR_LOGIN_LITE.launch()'); } get nip04() { return { async encrypt(pubkey, plaintext) { throw new Error('Authentication required - use NOSTR_LOGIN_LITE.launch()'); }, async decrypt(pubkey, ciphertext) { throw new Error('Authentication required - use NOSTR_LOGIN_LITE.launch()'); } }; } get nip44() { return { async encrypt(pubkey, plaintext) { throw new Error('Authentication required - use NOSTR_LOGIN_LITE.launch()'); }, async decrypt(pubkey, ciphertext) { throw new Error('Authentication required - use NOSTR_LOGIN_LITE.launch()'); } }; } } // Initialize and export if (typeof window !== 'undefined') { const nostrLite = new NostrLite(); // Export main API window.NOSTR_LOGIN_LITE = { init: (options) => nostrLite.init(options), launch: (startScreen) => nostrLite.launch(startScreen), logout: () => nostrLite.logout(), // Expose for debugging _extensionBridge: nostrLite.extensionBridge, _instance: nostrLite }; console.log('NOSTR_LOGIN_LITE: Library loaded and ready'); console.log('NOSTR_LOGIN_LITE: Use window.NOSTR_LOGIN_LITE.init(options) to initialize'); console.log('NOSTR_LOGIN_LITE: Detected', nostrLite.extensionBridge.getExtensionCount(), 'browser extensions'); } else { // Node.js environment module.exports = { NostrLite }; } `; // Write the complete bundle fs.writeFileSync(outputPath, bundle, 'utf8'); const sizeKB = (bundle.length / 1024).toFixed(2); console.log(`\nāœ… nostr-lite.js bundle created: ${outputPath}`); console.log(`šŸ“ Bundle size: ${sizeKB} KB`); console.log(`šŸ“„ Total lines: ${bundle.split('\n').length}`); // Check what's included const hasModal = bundle.includes('class Modal'); const hasNostrLite = bundle.includes('NOSTR_LOGIN_LITE'); console.log('\nšŸ“‹ Bundle contents:'); console.log(` Modal UI: ${hasModal ? 'āœ… Included' : 'āŒ Missing'}`); console.log(` NOSTR_LOGIN_LITE: ${hasNostrLite ? 'āœ… Included' : 'āŒ Missing'}`); console.log(` Extension Bridge: āœ… Included`); console.log(` Window.nostr facade: āœ… Included`); console.log('\nšŸ“‹ Two-file architecture:'); console.log(' 1. nostr.bundle.js (official nostr-tools - 220KB)'); console.log(' 2. nip46-extension.js (NIP-46 support - ~15KB)'); console.log(` 3. nostr-lite.js (NOSTR_LOGIN_LITE - ${sizeKB}KB)`); return bundle; } // Run if called directly if (typeof require !== 'undefined' && require.main === module) { createNostrLoginLiteBundle(); } module.exports = { createNostrLoginLiteBundle };