[READ-ONLY] a fast, modern browser for the npm registry
at main 168 lines 4.6 kB view raw
1#!/usr/bin/env node 2/** 3 * Mock connector CLI — starts a pre-populated mock server for developing 4 * authenticated features without a real npm account. 5 */ 6 7import process from 'node:process' 8import crypto from 'node:crypto' 9import { styleText } from 'node:util' 10import * as p from '@clack/prompts' 11import { defineCommand, runMain } from 'citty' 12import { 13 MockConnectorStateManager, 14 createMockConnectorState, 15 type MockConnectorConfig, 16} from './mock-state.ts' 17import { MockConnectorServer } from './mock-app.ts' 18 19const DEFAULT_PORT = 31415 20const DEV_FRONTEND_URL = 'http://127.0.0.1:3000/' 21const PROD_FRONTEND_URL = 'https://npmx.dev/' 22 23function generateToken(): string { 24 return crypto.randomBytes(16).toString('hex') 25} 26 27/** 28 * Pre-populate with sample data using real npm orgs so the registry 29 * API calls don't 404. Members/teams are fictional. 30 */ 31function populateDefaultData(stateManager: MockConnectorStateManager): void { 32 const npmUser = stateManager.config.npmUser 33 34 stateManager.setOrgData('@nuxt', { 35 users: { 36 [npmUser]: 'owner', 37 danielroe: 'owner', 38 pi0: 'admin', 39 antfu: 'developer', 40 }, 41 teams: ['core', 'docs', 'triage'], 42 teamMembers: { 43 core: [npmUser, 'danielroe', 'pi0'], 44 docs: ['antfu'], 45 triage: ['pi0', 'antfu'], 46 }, 47 }) 48 49 stateManager.setOrgData('@unjs', { 50 users: { 51 [npmUser]: 'admin', 52 pi0: 'owner', 53 }, 54 teams: ['maintainers'], 55 teamMembers: { 56 maintainers: [npmUser, 'pi0'], 57 }, 58 }) 59 60 stateManager.setUserOrgs(['nuxt', 'unjs']) 61 62 stateManager.setPackageData('@nuxt/kit', { 63 collaborators: { 64 [npmUser]: 'read-write', 65 'danielroe': 'read-write', 66 'nuxt:core': 'read-write', 67 'nuxt:docs': 'read-only', 68 }, 69 }) 70 stateManager.setPackageData('@nuxt/schema', { 71 collaborators: { 72 [npmUser]: 'read-write', 73 'nuxt:core': 'read-write', 74 }, 75 }) 76 stateManager.setPackageData('@unjs/nitro', { 77 collaborators: { 78 [npmUser]: 'read-write', 79 'pi0': 'read-write', 80 'unjs:maintainers': 'read-write', 81 }, 82 }) 83 84 stateManager.setUserPackages({ 85 '@nuxt/kit': 'read-write', 86 '@nuxt/schema': 'read-write', 87 '@unjs/nitro': 'read-write', 88 }) 89} 90 91const main = defineCommand({ 92 meta: { 93 name: 'npmx-connector-mock', 94 version: '0.0.1', 95 description: 'Mock connector for npmx.dev development and testing', 96 }, 97 args: { 98 port: { 99 type: 'string', 100 description: 'Port to listen on', 101 default: String(DEFAULT_PORT), 102 }, 103 user: { 104 type: 'string', 105 description: 'Simulated npm username', 106 default: 'mock-user', 107 }, 108 empty: { 109 type: 'boolean', 110 description: 'Start with empty state (no pre-populated data)', 111 default: false, 112 }, 113 }, 114 async run({ args }) { 115 const port = Number.parseInt(args.port as string, 10) || DEFAULT_PORT 116 const npmUser = args.user as string 117 const empty = args.empty as boolean 118 const frontendUrl = process.env.NPMX_CLI_DEV === 'true' ? DEV_FRONTEND_URL : PROD_FRONTEND_URL 119 120 p.intro(styleText(['bgMagenta', 'white'], ' npmx mock connector ')) 121 122 const token = generateToken() 123 const config: MockConnectorConfig = { 124 token, 125 npmUser, 126 avatar: null, 127 port, 128 } 129 const stateManager = new MockConnectorStateManager(createMockConnectorState(config)) 130 131 if (!empty) { 132 populateDefaultData(stateManager) 133 p.log.info(`Pre-populated with sample data for ${styleText('cyan', npmUser)}`) 134 p.log.info(styleText('dim', ` Orgs: @nuxt (4 members, 3 teams), @unjs (2 members, 1 team)`)) 135 p.log.info(styleText('dim', ` Packages: @nuxt/kit, @nuxt/schema, @unjs/nitro`)) 136 } else { 137 p.log.info('Starting with empty state') 138 } 139 140 stateManager.connect(token) 141 const server = new MockConnectorServer(stateManager) 142 143 try { 144 await server.start() 145 } catch (error) { 146 p.log.error(error instanceof Error ? error.message : 'Failed to start mock connector server') 147 process.exit(1) 148 } 149 150 const connectUrl = `${frontendUrl}?token=${token}&port=${port}` 151 152 p.note( 153 [ 154 `Open: ${styleText(['bold', 'underline', 'cyan'], connectUrl)}`, 155 '', 156 styleText('dim', `Or paste token manually: ${token}`), 157 '', 158 styleText('dim', `User: ${npmUser} | Port: ${port}`), 159 styleText('dim', 'Operations will succeed immediately (no real npm calls)'), 160 ].join('\n'), 161 'Click to connect', 162 ) 163 164 p.log.info('Waiting for connection... (Press Ctrl+C to stop)') 165 }, 166}) 167 168runMain(main)