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

308 lines (266 loc) 12.4 kB
import { Record } from '@web5/api'; import { DWeb5 } from '../../lib/dweb5.js'; import { RegistryUtils } from '../../registry/utils.js'; import { DidResolver } from '../did/resolver.js'; import { DrlBuilder } from '../dwn/drl-builder.js'; import dwn from '../dwn/protocol.js'; import { Logger } from '../logger.js'; import { stringifier } from '../misc.js'; import { ResponseUtils } from '../response.js'; import { CreatePackageParams, CreateReleaseParams, DpkDwnResponse, DpkMetadata, DpkRequest, ReadPackageParams, ReadReleaseParams, RegistryResponse } from '../types.js'; const { web5, did } = DWeb5.connectSync(); export class DManager { // Get DWeb Node endpoints from Did Doc on respective network based on DID Method static async getDwnEndpoints(didToResolve: string = did): Promise<string[]> { const { didDocument } = await DidResolver.resolve(didToResolve) ?? {}; Logger.info(`DManager: Resolved didDocument=${stringifier(didDocument)}`); const services = didDocument?.service; const didServiceEndpoint = services?.find( service => service.type === 'DecentralizedWebNode' )?.serviceEndpoint ?? ( process.env.NODE_ENV === 'development' ? ['http://localhost:3000/'] : ['https://dwn.drpm.tools/'] ); const serviceEndpoints = Array.isArray(didServiceEndpoint) ? didServiceEndpoint : [didServiceEndpoint]; return serviceEndpoints.map(endpoint => endpoint.replace(/\/$/, '')); } static async readPackageRecord({ name }: { name: string }): Promise<RegistryResponse> { for (const endpoint of await this.getDwnEndpoints()) { Logger.info(`DManager: Fetching DPK ${name} from ${endpoint} ...`); const drl = DrlBuilder .create({ did, endpoint }) .buildDrlRead({ protocolPath : 'package', filters : { tags: [{ subKey: 'name', value: name }] } }); const response: Response = await fetch(drl); if (ResponseUtils.fetchFail(response)) { Logger.error('DManager: DWeb Node request failed', response); return RegistryUtils.routeFailure({ error: response.statusText }); } const metadata: DpkMetadata = await response.json(); if (!metadata) { Logger.error('DManager: DWeb Node request failed - no metadata', response); return RegistryUtils.routeFailure({ error: 'No data returned' }); } return RegistryUtils.routeSuccess({ data: { ...metadata, endpoint } }); } return RegistryUtils.routeFailure({ error: 'All DWeb Node requests failed' }); } // Fetch DPK metadata from DWeb Node DRPM protocol at /package protocol path static async readPackage({ builder, name }: ReadPackageParams): Promise<RegistryResponse> { try { const drl = builder.buildDrlRead({ protocolPath: 'package', filters: { tags: [{ subKey: 'name', value: name }], } }); Logger.debug(`DManager: Using DRL ${drl} to fetch DPK ${name} ...`); const response: Response = await fetch(drl); if (ResponseUtils.fetchFail(response)) { Logger.error('DManager: DWeb Node request failed', response); return RegistryUtils.routeFailure({ error: response.statusText }); } const data: DpkDwnResponse = await response.json(); if (!data) { Logger.error('DManager: DWeb Node request failed - no data', response); return RegistryUtils.routeFailure({ error: 'No data returned' }); } return RegistryUtils.routeSuccess({ data }); } catch (error: any) { Logger.error('DManager: DWeb Node request error catch', error); return RegistryUtils.routeFailure({ error: error.message }); } } // Fetch DPK release from DWeb Node DRPM protocol at package/release protocol path static async readRelease({ builder, name, version }: ReadReleaseParams): Promise<RegistryResponse> { try { const drl = builder.buildDrlRead({ protocolPath : 'package/release', filters : { tags : [ { subKey: 'name', value: name }, { subKey: 'version', value: version } ], } }); Logger.debug(`DManager: Using DRL ${drl} to fetch DPK ${name}@${version} ...`); const response: Response = await fetch(drl); if (ResponseUtils.fetchFail(response)) { Logger.error('DManager: DWeb Node request error', response); return RegistryUtils.routeFailure({ error: response.statusText }); } Logger.debug(`DManager: DWeb Node request success`, response); if (response.headers.get('content-type') !== 'application/gzip') { Logger.error('DManager: DWeb Node request error - bad content-type', response); return RegistryUtils.routeFailure({ error: `Bad content-type: ${response.headers.get('content-type')}` }); } const data = response.body; if (!data) { Logger.error('DManager: DWeb Node request failed - no data', response); return RegistryUtils.routeFailure({ error: 'No tarball data returned' }); } return RegistryUtils.routeSuccess({ data }); } catch (error: any) { Logger.error('DManager: DWeb Node request failed', error); return RegistryUtils.routeFailure({ error: error.message }); } } // Fetch DPK from DWeb Node: either metadata or release static async readDpk({ did, dpk: { name, version, path } }: DpkRequest): Promise<RegistryResponse> { try { for (const endpoint of await this.getDwnEndpoints()) { Logger.info(`DManager: Fetching DPK ${name}@${version} from ${endpoint} ...`); const builder = DrlBuilder.create({ did, endpoint }); const response: RegistryResponse = path === 'package/release' && version ? await this.readRelease({ builder, name, version }) : await this.readPackage({ builder, name }); if (ResponseUtils.fail(response)) { Logger.error('DManager: Error during DWeb Node request, continuing ...', response); continue; } if (!response.data) { Logger.error('DManager: No data returned from DWeb Node request, continuing ...', response); continue; } return response; } return RegistryUtils.routeFailure({ error: 'All DWeb Node requests failed' }); } catch (error: any) { Logger.error('DManager: Error catch during DWeb Node request', error); return RegistryUtils.routeFailure({ error: error.message }); } } // static async createPackageDidWeb({ metadata, did }: CreatePackageDidWebParams): Promise<RegistryResponse> { // try { // // const userAgent = await Web5UserAgent.create(); // const _dwn = new DwnApi({ // agent : await Web5UserAgent.create(), // did, // }); // await RecordsWrite.create({ // data : metadata, // published : true, // dataFormat : 'application/json', // schema : dwn.types.package.schema, // protocolPath : 'package', // protocol : dwn.protocol, // tags : { // name : metadata.name, // latest : metadata.version // }, // }); // const {name, version} = metadata; // for (const endpoint of await DManager.getDwnEndpoints(did)) { // Logger.info(`DManager: Creating package record for ${name}@${version} at ${endpoint} ...`); // const builder = DrlBuilder.create({ did, endpoint }); // const response: RegistryResponse = path === 'package' || !version // ? await this.readPackage({ builder, name }) // : await this.readRelease({ builder, name, version }); // if (ResponseUtils.fail(response)) { // Logger.error('DManager: Error during DWeb Node request, continuing ...', response); // continue; // } // if(!response.data) { // Logger.error('DManager: No data returned from DWeb Node request, continuing ...', response); // continue; // } // return RegistryUtils.routeSuccess({ data: response }); // } // return RegistryUtils.routeFailure({ error: 'All DWeb Node requests failed' }); // } catch(error: any) { // Logger.error('DManager: Error catch during DWeb Node request', error); // return RegistryUtils.routeFailure({ error: error.message }); // } // } static async createPackage({ metadata }: CreatePackageParams): Promise<RegistryResponse> { try { const { record, status: create } = await web5.dwn.records.create({ store : true, data : metadata, message : { published : true, dataFormat : 'application/json', schema : dwn.types.package.schema, protocolPath : 'package', protocol : dwn.protocol, tags : { name : metadata.name, latest : metadata['dist-tags'].latest }, }, }); if (ResponseUtils.dwnFail({ status: create })) { const error = 'Failed to create local package record - Failing DWN Status'; Logger.error(`DManager: ${error}`, create); return RegistryUtils.routeFailure({ error: `${error}: ${create.detail}` }); } if (!record) { const error = 'Failed to create local package record - No Record Returned'; Logger.error(`DManager: ${error}`, create); return RegistryUtils.routeFailure({ error: `${error}: ${create.detail}` }); } Logger.log('DManager: Package record created!', create); const data: Record = await record?.data.json(); Logger.debug('DManager: Package record data parsed!', data); const { status: send } = await record.send(did); if (ResponseUtils.dwnFail({ status: send })) { const error = 'Failed to send remote package record - Failing DWN Status'; Logger.error(`DManager: ${error}`, create); return RegistryUtils.routeFailure({ error: `${error}: ${create.detail}` }); } Logger.debug('DManager: Package record sent!', send); return RegistryUtils.routeSuccess({ data: record.id }); } catch (error: any) { Logger.error('DManager: Error catch during DWebNode records create', error); return RegistryUtils.routeFailure({ error: error.message }); } } static async createPackageRelease({ parentId, name, version, integrity, release }: CreateReleaseParams): Promise<RegistryResponse> { try { const { record = undefined, status }: { record?: Record; status: any } = await web5.dwn.records.create({ data : release, 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 })) { const error = 'Failed to create local release record - Failing DWN Status'; Logger.error(`DManager: ${error}`, status); return RegistryUtils.routeFailure({ error: `${error}: ${status.detail}` }); } if (!record) { const error = 'Failed to create local release record - No Record Returned'; Logger.error(`DManager: ${error}`, status); return RegistryUtils.routeFailure({ error: `${error}: ${status.detail}` }); } Logger.log('DManager: Release record created in local!', status); const data = record.toJSON();; Logger.debug('DManager: Release record data parsed!', data); const { status: send } = await record.send(did); if (ResponseUtils.dwnFail({ status: send })) { const error = 'Failed to send release record to remote - Failed Send Status'; Logger.error(`DManager: ${error}`, send); return RegistryUtils.routeFailure({ error: `${error}: ${send.detail}` }); } Logger.debug('DManager: Release record sent to remote!', send); return RegistryUtils.routeSuccess({ data: record }); } catch (error: any) { Logger.error('DManager: Error catch during DWebNode records create', error); return RegistryUtils.routeFailure({ error: error.message }); } } }