UNPKG

dwnpm

Version:

Decentralized Registry Package Manager (DRPM) helps developers publish, install, find and manage Decentralized Packages (DPKs) published to Decentralized Web Nodes (DWNs). DRPM does this by looking up a Decentralized Identifier (DID) to find its DID docum

339 lines 13 kB
import { mkdir, writeFile } from 'fs/promises'; import * as Inquirer from '@inquirer/prompts'; import { Logger } from '../utils/logger.js'; import { ResponseUtils } from '../utils/response.js'; import dwn from '../utils/dwn/protocol.js'; import { DateSort } from '@tbd54566975/dwn-sdk-js'; // Collecting TypeScript configuration const tsConfigDefault = { compilerOptions: { target: 'ES5', module: 'CommonJS', moduleResolution: 'Node', strict: true, declaration: true, declarationMap: true, sourceMap: true, esModuleInterop: true, resolveJsonModule: true, skipLibCheck: true, declarationDir: 'dist/types', outDir: 'dist', }, include: ['src'], exclude: ['node_modules'] }; export class Package { static async buildTsConfig(options) { const tsConfig = tsConfigDefault; const compilerOptions = options?.compilerOptions ?? tsConfig.compilerOptions; Logger.log('---------- TSConfig Setup ----------'); const useDefaults = await Inquirer.select({ message: 'Would you like to use project defaults for tsconfig.json?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; if (useDefaults) { Logger.log('Using tsconfig.json defaults:', tsConfig); return tsConfig; } compilerOptions.target = await Inquirer.select({ message: 'Compiler Option: target', choices: [ 'ES5', 'ES6', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', 'ES2022', 'ES2023', 'ESNext', ], default: compilerOptions.target, }); compilerOptions.module = await Inquirer.select({ message: 'Compiler Option: module', choices: [ 'CommonJS', 'ES6', 'ES2015', 'ES2020', 'ES2022', 'ESNext', 'Node16', 'NodeNext' ], default: compilerOptions.target === 'ES5' ? compilerOptions.module : 'ES2015', }); compilerOptions.moduleResolution = await Inquirer.select({ message: 'Compiler Option: moduleResolution', choices: [ 'Classic', 'Node', 'Node16', 'ES2022', 'NodeNext', 'Bundler', ], default: ['ES6', 'ES2015'].includes(compilerOptions.module) ? 'Classic' : ['Node16', 'NodeNext'].includes(compilerOptions.module) ? compilerOptions.module : 'Node' }); compilerOptions.strict = await Inquirer.select({ message: 'Compiler Option: enable strict mode?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; compilerOptions.declaration = await Inquirer.select({ message: 'Compiler Option: Generate declaration files?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; compilerOptions.declarationMap = await Inquirer.select({ message: 'Compiler Option: Generate declaration map files?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; compilerOptions.sourceMap = await Inquirer.select({ message: 'Compiler Option: Generate source map files?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; compilerOptions.esModuleInterop = await Inquirer.select({ message: 'Compiler Option: esModuleInterop?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; compilerOptions.resolveJsonModule = await Inquirer.select({ message: 'Compiler Option: resolveJsonModule?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; compilerOptions.skipLibCheck = await Inquirer.select({ message: 'Compiler Option: skipLibCheck?', choices: ['Yes', 'No'], default: 'Yes', }) === 'Yes'; compilerOptions.declarationDir = await Inquirer.input({ message: 'Compiler Option: declarationDir', default: compilerOptions.declarationDir, }); compilerOptions.outDir = await Inquirer.input({ message: 'Compiler Option: outDir', default: compilerOptions.outDir, }); tsConfig.exclude = options.exclude ?? await Inquirer.input({ message: 'TSConfig Option: exclude', default: `[${tsConfig.exclude.join(', ')}]`, }); tsConfig.include = options.include ?? await Inquirer.input({ message: 'TSConfig Option: exclude', default: `[${tsConfig.include.join(', ')}]`, }); return tsConfig; } static async init({ name, version, description, author, license, ts, src, main, type, tsconfig = tsConfigDefault, } = {}) { Logger.info('Welcome to the DRPM DPK template wizard!'); try { tsconfig ??= JSON.parse(tsconfig) ?? tsConfigDefault; } catch (error) { Logger.error(error.message); Logger.warn('PackageCommand: Failed to parse tsconfig from CLI, using default'); } name ??= await Inquirer.input({ message: 'DPK Name:', required: true, default: 'my-dpk', transformer: (value) => value.replace('@drpm', '') }); version ??= await Inquirer.input({ message: 'Version:', required: true, default: '0.1.0' }); description ??= await Inquirer.input({ message: 'Description:', required: true, default: 'My Decentralized Package' }); author ??= await Inquirer.input({ message: 'Author Name or DID:' }); license ??= await Inquirer.input({ message: 'License:', required: true, default: 'Apache-2.0' }); src ??= await Inquirer.select({ message: 'Use src/ directory?', choices: ['Yes', 'No'], default: 'Yes' }) === 'Yes'; ts ??= await Inquirer.select({ message: 'Use TypeScript?', choices: ['Yes', 'No'], default: 'Yes' }) === 'Yes'; main ??= await Inquirer.input({ message: 'Main:', required: true, default: ts ? './dist/cjs/index.js' : './src/index.js' }); type ??= await Inquirer.select({ message: 'Build type:', choices: ['commonjs', 'module'], }); const tsConfigTemplate = ts ? await this.buildTsConfig(tsconfig) : undefined; // homepage // bugs // repository // contributors // keywords // devDependencies const packageContent = { name, version, type, description, main, author, license, dependencies: {}, scripts: { start: `node ${main}` } }; // Create package directory const srcDir = src ? `${name}/src` : name; await mkdir(srcDir, { recursive: true }); await writeFile(`${srcDir}/index.ts`, 'console.log("Hello, World!");'); // Write package.json to disk await writeFile(`${name}/package.json`, JSON.stringify(packageContent, null, 2)); Logger.log('Generated package.json'); // Create a custom .npmrc file with settings await writeFile(`${name}/.npmrc`, `//localhost:2092/:_authToken="dummy-token" @drpm:registry=http://localhost:2092 `); Logger.log('Generated .npmrc with custom settings'); if (tsConfigTemplate) { await writeFile(`${name}/tsconfig.json`, JSON.stringify(tsConfigTemplate, null, 2)); Logger.log('Generated tsconfig.json'); } Logger.log(`New DPK project ${packageContent.name} created successfully!`); } static async publish(options) { try { const publishType = options.type.toLowerCase(); Logger.log(`Publishing ${options.type} record!`); if (publishType === 'package') { await this.metadata(options); } else if (publishType.includes('release')) { await this.release(options); } else { await this.metadata(options); await this.release(options); } } catch (error) { Logger.error(`Error during package metadata or release publishing: ${error.message}`); throw error; } } static async metadata({ data, connection }) { try { const { web5, did } = connection ?? {}; const { 'dist-tags': distTags, name } = data ?? {}; const { record: _package = null, status } = await web5.dwn.records.create({ store: true, data: data, message: { published: true, dataFormat: 'application/json', schema: dwn.types.package.schema, protocolPath: 'package', protocol: dwn.protocol, tags: { name, latest: distTags.latest }, }, }); if (ResponseUtils.dwnFail({ status }) || !_package) { Logger.error('Failed to create local package record', status); throw new Error('Failed to create package record'); } const { status: sent } = await _package.send(did); Logger.log('Package metadata record created and sent to remote!', { status, sent }); } catch (error) { Logger.error('Error during package metadata record creation', error); throw error; } } static async release({ parentId, name, version, integrity, data, connection, }) { try { const { web5, did } = connection ?? {}; if (!parentId) { if (!(name && version)) { throw new Error(`PublishRelease: Either parentId or ${!name ? 'name' : !version ? 'version' : 'name and version'} required`); } Logger.info('No parentId present, querying ...'); const { records = [], status } = await web5.dwn.records.query({ message: { dateSort: DateSort.CreatedDescending, filter: { tags: { name, version } } } }); if (!records.length || ResponseUtils.dwnFail({ status })) { Logger.error('Failed to find parent package metadata record', status); throw new Error('Failed to find parent package metadata record'); } parentId = records[0].parentId; if (!parentId) { throw new Error('Parent package metadata record not found'); } Logger.info('Parent package metadata record found!', parentId); } const { record: release = null, status } = await web5.dwn.records.create({ data, store: true, message: { published: true, parentContextId: parentId, dataFormat: 'application/gzip', schema: dwn.types.release.schema, protocolPath: 'package/release', protocol: dwn.protocol, tags: { name, version, integrity } }, }); if (ResponseUtils.dwnFail({ status }) || !release) { Logger.error('Failed to create local release record', status); throw new Error('Failed to create release record'); } Logger.log('Local release record created!', status); const { status: sent } = await release.send(did); Logger.log('Release record created and sent to remote!', { status, sent }); } catch (error) { Logger.error('Error during release record creation', error); throw error; } } static async run(options) { throw new Error('Method not implemented ' + options); } } //# sourceMappingURL=package.js.map