diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 10f5ec09..00000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -app/proxy* -**/*.d.ts -node_modules/ -dist/ -coverage/ -mocks/ -.react_entries/ diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 9bcdb468..00000000 --- a/.eslintrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": [ - "eslint-config-egg/typescript", - "eslint-config-egg/lib/rules/enforce-node-prefix" - ] -} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..2312dc58 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.oxlintrc.json b/.oxlintrc.json index e64d0020..1c4b85bf 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,6 +1,52 @@ { - "plugins": ["import"], + "$schema": "./node_modules/oxlint/configuration_schema.json", + "env": { + "node": true, + "mocha": true + }, + "categories": { + "correctness": "error", + "perf": "error" + }, + "plugins": [ + "import", + "typescript", + "unicorn", + "jsdoc", + "node", + "promise", + "oxc" + ], "rules": { - "import/no-cycle": "error" + // eslint + "constructor-super": "error", + "getter-return": "error", + "no-undef": "error", + "no-unreachable": "error", + "no-var": "error", + "no-eq-null": "error", + "no-await-in-loop": "allow", + "eqeqeq": ["error", "smart"], + // import + "import/no-cycle": "error", + "import/no-anonymous-default-export": "error", + "import/no-namespace": "error", + "import/named": "error", + "import/export": "error", + // promise + "promise/no-return-wrap": "error", + "promise/param-names": "error", + "promise/prefer-await-to-callbacks": "error", + "promise/prefer-await-to-then": "error", + "promise/prefer-catch": "error", + "promise/no-return-in-finally": "error", + // unicorn + "unicorn/error-message": "error", + // "unicorn/no-null": "error", + "unicorn/throw-new-error": "error", + // oxc + "oxc/no-map-spread": "error", + // typescript + "typescript/consistent-type-imports": "error" } } diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..43aee1a4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "tabWidth": 2, + "arrowParens": "avoid" +} diff --git a/INTEGRATE.md b/INTEGRATE.md index 5a2bf614..49933dae 100644 --- a/INTEGRATE.md +++ b/INTEGRATE.md @@ -45,9 +45,7 @@ "extends": "@eggjs/tsconfig", "compilerOptions": { "baseUrl": "./", - "moduleResolution": "NodeNext", - "target": "ES2020", - "module": "Node16" + "target": "ES2021" } } ``` diff --git a/app.ts b/app.ts index 8e7fe910..2d804097 100644 --- a/app.ts +++ b/app.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import { readFile } from 'node:fs/promises'; -import { Application, ILifecycleBoot } from 'egg'; +import type { Application, ILifecycleBoot } from 'egg'; import { ChangesStreamService } from './app/core/service/ChangesStreamService.js'; declare module 'egg' { @@ -34,13 +34,17 @@ export default class CnpmcoreAppHook implements ILifecycleBoot { // ready binary.html and replace registry const filepath = path.join(this.app.baseDir, 'app/port/binary.html'); const text = await readFile(filepath, 'utf-8'); - this.app.binaryHTML = text.replace('{{registry}}', this.app.config.cnpmcore.registry); + this.app.binaryHTML = text.replace( + '{{registry}}', + this.app.config.cnpmcore.registry + ); } // 应用退出时执行 // 需要暂停当前执行的 changesStream task async beforeClose() { - const changesStreamService = await this.app.getEggObject(ChangesStreamService); + const changesStreamService = + await this.app.getEggObject(ChangesStreamService); await changesStreamService.suspendSync(true); } } diff --git a/app/common/AbstractService.ts b/app/common/AbstractService.ts index 89a50115..e772c2bd 100644 --- a/app/common/AbstractService.ts +++ b/app/common/AbstractService.ts @@ -1,10 +1,5 @@ -import { - Inject, -} from '@eggjs/tegg'; -import { - EggAppConfig, - EggLogger, -} from 'egg'; +import { Inject } from '@eggjs/tegg'; +import type { EggAppConfig, EggLogger } from 'egg'; export abstract class AbstractService { @Inject() diff --git a/app/common/ErrorUtil.ts b/app/common/ErrorUtil.ts index aa4be1f1..f736b583 100644 --- a/app/common/ErrorUtil.ts +++ b/app/common/ErrorUtil.ts @@ -1,25 +1,25 @@ -const TimeoutErrorNames = [ +const TimeoutErrorNames = new Set([ 'HttpClientRequestTimeoutError', 'HttpClientConnectTimeoutError', 'ConnectionError', 'ConnectTimeoutError', 'BodyTimeoutError', 'ResponseTimeoutError', -]; +]); export function isTimeoutError(err: Error) { - if (TimeoutErrorNames.includes(err.name)) { + if (TimeoutErrorNames.has(err.name)) { return true; } if (err instanceof AggregateError && err.errors) { for (const subError of err.errors) { - if (TimeoutErrorNames.includes(subError.name)) { + if (TimeoutErrorNames.has(subError.name)) { return true; } } } if ('cause' in err && err.cause instanceof Error) { - if (TimeoutErrorNames.includes(err.cause.name)) { + if (TimeoutErrorNames.has(err.cause.name)) { return true; } } diff --git a/app/common/FileUtil.ts b/app/common/FileUtil.ts index 181cf535..dfa2999c 100644 --- a/app/common/FileUtil.ts +++ b/app/common/FileUtil.ts @@ -4,14 +4,14 @@ import { setTimeout } from 'node:timers/promises'; import path from 'node:path'; import url from 'node:url'; import { randomBytes } from 'node:crypto'; -import { EggContextHttpClient, HttpClientResponse } from 'egg'; +import type { EggContextHttpClient, HttpClientResponse } from 'egg'; import mime from 'mime-types'; import dayjs from './dayjs.js'; interface DownloadToTempfileOptionalConfig { - retries?: number, - ignoreDownloadStatuses?: number[], - remoteAuthToken?: string + retries?: number; + ignoreDownloadStatuses?: number[]; + remoteAuthToken?: string; } export async function createTempDir(dataDir: string, dirname?: string) { @@ -28,17 +28,29 @@ export async function createTempfile(dataDir: string, filename: string) { const tmpdir = await createTempDir(dataDir); // The filename is a URL (from dist.tarball), which needs to be truncated, (`getconf NAME_MAX /` # max filename length: 255 bytes) // https://github.com/cnpm/cnpmjs.org/pull/1345 - const tmpfile = path.join(tmpdir, `${randomBytes(10).toString('hex')}-${path.basename(url.parse(filename).pathname!)}`); + const tmpfile = path.join( + tmpdir, + `${randomBytes(10).toString('hex')}-${path.basename(url.parse(filename).pathname!)}` + ); return tmpfile; } -export async function downloadToTempfile(httpclient: EggContextHttpClient, - dataDir: string, url: string, optionalConfig?: DownloadToTempfileOptionalConfig) { +export async function downloadToTempfile( + httpclient: EggContextHttpClient, + dataDir: string, + url: string, + optionalConfig?: DownloadToTempfileOptionalConfig +) { let retries = optionalConfig?.retries || 3; let lastError: any; while (retries > 0) { try { - return await _downloadToTempfile(httpclient, dataDir, url, optionalConfig); + return await _downloadToTempfile( + httpclient, + dataDir, + url, + optionalConfig + ); } catch (err: any) { if (err.name === 'DownloadNotFoundError') throw err; lastError = err; @@ -46,7 +58,8 @@ export async function downloadToTempfile(httpclient: EggContextHttpClient, retries--; if (retries > 0) { // sleep 1s ~ 4s in random - const delay = process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000; + const delay = + process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000; await setTimeout(delay); } } @@ -57,8 +70,12 @@ export interface Tempfile { headers: HttpClientResponse['res']['headers']; timing: HttpClientResponse['res']['timing']; } -async function _downloadToTempfile(httpclient: EggContextHttpClient, - dataDir: string, url: string, optionalConfig?: DownloadToTempfileOptionalConfig): Promise { +async function _downloadToTempfile( + httpclient: EggContextHttpClient, + dataDir: string, + url: string, + optionalConfig?: DownloadToTempfileOptionalConfig +): Promise { const tmpfile = await createTempfile(dataDir, url); const writeStream = createWriteStream(tmpfile); try { @@ -68,14 +85,18 @@ async function _downloadToTempfile(httpclient: EggContextHttpClient, if (optionalConfig?.remoteAuthToken) { requestHeaders.authorization = `Bearer ${optionalConfig.remoteAuthToken}`; } - const { status, headers, res } = await httpclient.request(url, { + const { status, headers, res } = (await httpclient.request(url, { timeout: 60000 * 10, headers: requestHeaders, writeStream, timing: true, followRedirect: true, - }) as HttpClientResponse; - if (status === 404 || (optionalConfig?.ignoreDownloadStatuses && optionalConfig.ignoreDownloadStatuses.includes(status))) { + })) as HttpClientResponse; + if ( + status === 404 || + (optionalConfig?.ignoreDownloadStatuses && + optionalConfig.ignoreDownloadStatuses.includes(status)) + ) { const err = new Error(`Not found, status(${status})`); err.name = 'DownloadNotFoundError'; throw err; @@ -114,7 +135,11 @@ export function mimeLookup(filepath: string) { const filename = path.basename(filepath).toLowerCase(); if (filename.endsWith('.ts')) return PLAIN_TEXT; if (filename.endsWith('.lock')) return PLAIN_TEXT; - return mime.lookup(filename) || - WHITE_FILENAME_CONTENT_TYPES[filename as keyof typeof WHITE_FILENAME_CONTENT_TYPES] || - DEFAULT_CONTENT_TYPE; + return ( + mime.lookup(filename) || + WHITE_FILENAME_CONTENT_TYPES[ + filename as keyof typeof WHITE_FILENAME_CONTENT_TYPES + ] || + DEFAULT_CONTENT_TYPE + ); } diff --git a/app/common/PackageUtil.ts b/app/common/PackageUtil.ts index 1c17fd1a..f34021dc 100644 --- a/app/common/PackageUtil.ts +++ b/app/common/PackageUtil.ts @@ -1,11 +1,14 @@ import { createReadStream } from 'node:fs'; import { Readable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; -import * as ssri from 'ssri'; +import type { HashLike } from 'ssri'; +import { fromData, fromStream } from 'ssri'; // @ts-expect-error type error import tar from '@fengmk2/tar'; -import type { AuthorType, PackageJSONType } from '../repository/PackageRepository.js'; - +import type { + AuthorType, + PackageJSONType, +} from '../repository/PackageRepository.js'; // /@cnpm%2ffoo // /@cnpm%2Ffoo @@ -13,13 +16,14 @@ import type { AuthorType, PackageJSONType } from '../repository/PackageRepositor // /foo // name max length is 214 chars // https://www.npmjs.com/package/path-to-regexp#custom-matching-parameters -export const FULLNAME_REG_STRING = '@[^/]{1,220}/[^/]{1,220}|@[^%]+%2[fF][^/]{1,220}|[^@/]{1,220}'; +export const FULLNAME_REG_STRING = + '@[^/]{1,220}/[^/]{1,220}|@[^%]+%2[fF][^/]{1,220}|[^@/]{1,220}'; export function getScopeAndName(fullname: string): string[] { if (fullname.startsWith('@')) { return fullname.split('/', 2); } - return [ '', fullname ]; + return ['', fullname]; } export function getFullname(scope: string, name: string): string { @@ -35,14 +39,14 @@ export function getPrefixedName(prefix: string, username: string): string { } export async function calculateIntegrity(contentOrFile: Uint8Array | string) { - let integrityObj; + let integrityObj: HashLike; if (typeof contentOrFile === 'string') { - integrityObj = await ssri.fromStream(createReadStream(contentOrFile), { - algorithms: [ 'sha512', 'sha1' ], + integrityObj = await fromStream(createReadStream(contentOrFile), { + algorithms: ['sha512', 'sha1'], }); } else { - integrityObj = ssri.fromData(contentOrFile, { - algorithms: [ 'sha512', 'sha1' ], + integrityObj = fromData(contentOrFile, { + algorithms: ['sha512', 'sha1'], }); } const integrity = integrityObj.sha512[0].toString() as string; @@ -50,7 +54,12 @@ export async function calculateIntegrity(contentOrFile: Uint8Array | string) { return { integrity, shasum }; } -export function formatTarball(registry: string, scope: string, name: string, version: string) { +export function formatTarball( + registry: string, + scope: string, + name: string, + version: string +) { const fullname = getFullname(scope, name); return `${registry}/${fullname}/-/${name}-${version}.tgz`; } @@ -69,7 +78,9 @@ export function detectInstallScript(manifest: any) { } /** 判断一个版本压缩包中是否包含 npm-shrinkwrap.json */ -export async function hasShrinkWrapInTgz(contentOrFile: Uint8Array | string): Promise { +export async function hasShrinkWrapInTgz( + contentOrFile: Uint8Array | string +): Promise { let readable: Readable; if (typeof contentOrFile === 'string') { readable = createReadStream(contentOrFile); @@ -102,12 +113,17 @@ export async function hasShrinkWrapInTgz(contentOrFile: Uint8Array | string): Pr if (e.code === 'ABORT_ERR') { return hasShrinkWrap; } - throw Object.assign(new Error('[hasShrinkWrapInTgz] Fail to parse input file'), { cause: e }); + throw Object.assign( + new Error('[hasShrinkWrapInTgz] Fail to parse input file'), + { cause: e } + ); } } /** 写入 ES 时,格式化 author */ -export function formatAuthor(author: string | AuthorType | undefined): AuthorType | undefined { +export function formatAuthor( + author: string | AuthorType | undefined +): AuthorType | undefined { if (author === undefined) { return author; } @@ -119,10 +135,12 @@ export function formatAuthor(author: string | AuthorType | undefined): AuthorTyp return author; } -export async function extractPackageJSON(tarballBytes: Buffer): Promise { +export async function extractPackageJSON( + tarballBytes: Buffer +): Promise { return new Promise((resolve, reject) => { - Readable.from(tarballBytes) - .pipe(tar.t({ + Readable.from(tarballBytes).pipe( + tar.t({ filter: (name: string) => name === 'package/package.json', onentry: async (entry: any) => { const chunks: Buffer[] = []; @@ -136,6 +154,7 @@ export async function extractPackageJSON(tarballBytes: Buffer): Promise { + async getDownloadUrlOrStream( + storeKey: string + ): Promise { const downloadUrl = await this.getDownloadUrl(storeKey); if (downloadUrl) { return downloadUrl; diff --git a/app/common/adapter/NPMRegistry.ts b/app/common/adapter/NPMRegistry.ts index 314fc05b..4570fc64 100644 --- a/app/common/adapter/NPMRegistry.ts +++ b/app/common/adapter/NPMRegistry.ts @@ -1,17 +1,13 @@ import { setTimeout } from 'node:timers/promises'; -import { - ContextProto, - AccessLevel, - Inject, -} from '@eggjs/tegg'; -import { +import { ContextProto, AccessLevel, Inject } from '@eggjs/tegg'; +import type { EggLogger, EggContextHttpClient, EggAppConfig, HttpClientRequestOptions, HttpClientResponse, } from 'egg'; -import { PackageManifestType } from '../../repository/PackageRepository.js'; +import type { PackageManifestType } from '../../repository/PackageRepository.js'; import { isTimeoutError } from '../ErrorUtil.js'; type HttpMethod = HttpClientRequestOptions['method']; @@ -42,7 +38,10 @@ export class NPMRegistry { this.registryHost = registryHost; } - public async getFullManifests(fullname: string, optionalConfig?: { retries?: number, remoteAuthToken?: string }): Promise<{ method: HttpMethod } & HttpClientResponse> { + public async getFullManifests( + fullname: string, + optionalConfig?: { retries?: number; remoteAuthToken?: string } + ): Promise<{ method: HttpMethod } & HttpClientResponse> { let retries = optionalConfig?.retries || 3; // set query t=timestamp, make sure CDN cache disable // cache=0 is sync worker request flag @@ -52,7 +51,9 @@ export class NPMRegistry { try { // large package: https://r.cnpmjs.org/%40procore%2Fcore-icons // https://r.cnpmjs.org/intraactive-sdk-ui 44s - const authorization = this.genAuthorizationHeader(optionalConfig?.remoteAuthToken); + const authorization = this.genAuthorizationHeader( + optionalConfig?.remoteAuthToken + ); return await this.request('GET', url, undefined, { timeout: 120000, headers: { authorization }, @@ -66,7 +67,8 @@ export class NPMRegistry { retries--; if (retries > 0) { // sleep 1s ~ 4s in random - const delay = process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000; + const delay = + process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000; await setTimeout(delay); } } @@ -74,8 +76,13 @@ export class NPMRegistry { } // app.put('/:name/sync', sync.sync); - public async createSyncTask(fullname: string, optionalConfig?: { remoteAuthToken?:string}): Promise { - const authorization = this.genAuthorizationHeader(optionalConfig?.remoteAuthToken); + public async createSyncTask( + fullname: string, + optionalConfig?: { remoteAuthToken?: string } + ): Promise { + const authorization = this.genAuthorizationHeader( + optionalConfig?.remoteAuthToken + ); const url = `${this.registry}/${encodeURIComponent(fullname)}/sync?sync_upstream=true&nodeps=true`; // { // ok: true, @@ -85,21 +92,41 @@ export class NPMRegistry { } // app.get('/:name/sync/log/:id', sync.getSyncLog); - public async getSyncTask(fullname: string, id: string, offset: number, optionalConfig?:{ remoteAuthToken?:string }): Promise { - const authorization = this.genAuthorizationHeader(optionalConfig?.remoteAuthToken); + public async getSyncTask( + fullname: string, + id: string, + offset: number, + optionalConfig?: { remoteAuthToken?: string } + ): Promise { + const authorization = this.genAuthorizationHeader( + optionalConfig?.remoteAuthToken + ); const url = `${this.registry}/${encodeURIComponent(fullname)}/sync/log/${id}?offset=${offset}`; // { ok: true, syncDone: syncDone, log: log } return await this.request('GET', url, undefined, { authorization }); } - public async getDownloadRanges(registry: string, fullname: string, start: string, end: string, optionalConfig?:{ remoteAuthToken?:string }): Promise { - const authorization = this.genAuthorizationHeader(optionalConfig?.remoteAuthToken); + public async getDownloadRanges( + registry: string, + fullname: string, + start: string, + end: string, + optionalConfig?: { remoteAuthToken?: string } + ): Promise { + const authorization = this.genAuthorizationHeader( + optionalConfig?.remoteAuthToken + ); const url = `${registry}/downloads/range/${start}:${end}/${encodeURIComponent(fullname)}`; return await this.request('GET', url, undefined, { authorization }); } - private async request(method: HttpMethod, url: string, params?: object, options?: object): Promise { - const res = await this.httpclient.request(url, { + private async request( + method: HttpMethod, + url: string, + params?: object, + options?: object + ): Promise { + const res = (await this.httpclient.request(url, { method, data: params, dataType: 'json', @@ -109,15 +136,20 @@ export class NPMRegistry { followRedirect: true, gzip: true, ...options, - }) as HttpClientResponse; - this.logger.info('[NPMRegistry:request] %s %s, status: %s', method, url, res.status); + })) as HttpClientResponse; + this.logger.info( + '[NPMRegistry:request] %s %s, status: %s', + method, + url, + res.status + ); return { method, ...res, }; } - public genAuthorizationHeader(remoteAuthToken?:string) { + public genAuthorizationHeader(remoteAuthToken?: string) { return remoteAuthToken ? `Bearer ${remoteAuthToken}` : ''; } } diff --git a/app/common/adapter/binary/AbstractBinary.ts b/app/common/adapter/binary/AbstractBinary.ts index 6bf538ff..fd10e64d 100644 --- a/app/common/adapter/binary/AbstractBinary.ts +++ b/app/common/adapter/binary/AbstractBinary.ts @@ -1,7 +1,11 @@ -import { ImplDecorator, Inject, QualifierImplDecoratorUtil } from '@eggjs/tegg'; -import { EggHttpClient, EggLogger } from 'egg'; -import { BinaryType } from '../../enum/Binary.js'; -import { BinaryName, BinaryTaskConfig } from '../../../../config/binaries.js'; +import type { ImplDecorator } from '@eggjs/tegg'; +import { Inject, QualifierImplDecoratorUtil } from '@eggjs/tegg'; +import type { EggHttpClient, EggLogger } from 'egg'; +import type { BinaryType } from '../../enum/Binary.js'; +import type { + BinaryName, + BinaryTaskConfig, +} from '../../../../config/binaries.js'; export type BinaryItem = { name: string; @@ -17,7 +21,7 @@ export type FetchResult = { nextParams?: any; }; -const platforms = [ 'darwin', 'linux', 'win32' ] as const; +const platforms = ['darwin', 'linux', 'win32'] as const; export const BINARY_ADAPTER_ATTRIBUTE = Symbol('BINARY_ADAPTER_ATTRIBUTE'); @@ -29,7 +33,10 @@ export abstract class AbstractBinary { protected httpclient: EggHttpClient; abstract initFetch(binaryName: BinaryName): Promise; - abstract fetch(dir: string, binaryName: BinaryName): Promise; + abstract fetch( + dir: string, + binaryName: BinaryName + ): Promise; // eslint-disable-next-line @typescript-eslint/no-unused-vars async finishFetch(_success: boolean, _binaryName: BinaryName): Promise { @@ -44,13 +51,22 @@ export abstract class AbstractBinary { }); const xml = data.toString() as string; if (status !== 200) { - this.logger.warn('[AbstractBinary.requestXml:non-200-status] url: %s, status: %s, headers: %j, xml: %j', url, status, headers, xml); + this.logger.warn( + '[AbstractBinary.requestXml:non-200-status] url: %s, status: %s, headers: %j, xml: %j', + url, + status, + headers, + xml + ); return ''; } return xml; } - protected async requestJSON(url: string, requestHeaders?: Record) { + protected async requestJSON( + url: string, + requestHeaders?: Record + ) { const { status, data, headers } = await this.httpclient.request(url, { timeout: 30000, dataType: 'json', @@ -59,7 +75,12 @@ export abstract class AbstractBinary { headers: requestHeaders, }); if (status !== 200) { - this.logger.warn('[AbstractBinary.requestJSON:non-200-status] url: %s, status: %s, headers: %j', url, status, headers); + this.logger.warn( + '[AbstractBinary.requestJSON:non-200-status] url: %s, status: %s, headers: %j', + url, + status, + headers + ); return data; } return data; @@ -68,7 +89,9 @@ export abstract class AbstractBinary { // https://nodejs.org/api/n-api.html#n_api_node_api_version_matrix protected async listNodeABIVersions() { const nodeABIVersions: number[] = []; - const versions = await this.requestJSON('https://nodejs.org/dist/index.json'); + const versions = await this.requestJSON( + 'https://nodejs.org/dist/index.json' + ); for (const version of versions) { if (!version.modules) continue; const modulesVersion = parseInt(version.modules); @@ -89,21 +112,24 @@ export abstract class AbstractBinary { if (binaryConfig?.options?.nodeArchs) return binaryConfig.options.nodeArchs; // https://nodejs.org/api/os.html#osarch return { - linux: [ 'arm', 'arm64', 's390x', 'ia32', 'x64' ], - darwin: [ 'arm64', 'ia32', 'x64' ], - win32: [ 'ia32', 'x64' ], + linux: ['arm', 'arm64', 's390x', 'ia32', 'x64'], + darwin: ['arm64', 'ia32', 'x64'], + win32: ['ia32', 'x64'], }; } - protected listNodeLibcs(): Record { + protected listNodeLibcs(): Record<(typeof platforms)[number], string[]> { // https://github.com/lovell/detect-libc/blob/master/lib/detect-libc.js#L42 return { - darwin: [ 'unknown' ], - linux: [ 'glibc', 'musl' ], - win32: [ 'unknown' ], + darwin: ['unknown'], + linux: ['glibc', 'musl'], + win32: ['unknown'], }; } } export const BinaryAdapter: ImplDecorator = - QualifierImplDecoratorUtil.generatorDecorator(AbstractBinary, BINARY_ADAPTER_ATTRIBUTE); + QualifierImplDecoratorUtil.generatorDecorator( + AbstractBinary, + BINARY_ADAPTER_ATTRIBUTE + ); diff --git a/app/common/adapter/binary/ApiBinary.ts b/app/common/adapter/binary/ApiBinary.ts index 2350fd1d..ab0bfd29 100644 --- a/app/common/adapter/binary/ApiBinary.ts +++ b/app/common/adapter/binary/ApiBinary.ts @@ -1,7 +1,8 @@ import { Inject, SingletonProto } from '@eggjs/tegg'; -import { EggAppConfig } from 'egg'; +import type { EggAppConfig } from 'egg'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Api) @@ -14,12 +15,20 @@ export class ApiBinary extends AbstractBinary { return; } - async fetch(dir: string, binaryName: string): Promise { - const apiUrl = this.config.cnpmcore.syncBinaryFromAPISource || `${this.config.cnpmcore.sourceRegistry}/-/binary`; + async fetch( + dir: string, + binaryName: string + ): Promise { + const apiUrl = + this.config.cnpmcore.syncBinaryFromAPISource || + `${this.config.cnpmcore.sourceRegistry}/-/binary`; const url = `${apiUrl}/${binaryName}${dir}`; const data = await this.requestJSON(url); if (!Array.isArray(data)) { - this.logger.warn('[ApiBinary.fetch:response-data-not-array] data: %j', data); + this.logger.warn( + '[ApiBinary.fetch:response-data-not-array] data: %j', + data + ); return; } const items: BinaryItem[] = []; diff --git a/app/common/adapter/binary/BucketBinary.ts b/app/common/adapter/binary/BucketBinary.ts index 8862601d..0293d9dd 100644 --- a/app/common/adapter/binary/BucketBinary.ts +++ b/app/common/adapter/binary/BucketBinary.ts @@ -1,8 +1,13 @@ import path from 'node:path'; import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import binaries, { BinaryName, BinaryTaskConfig } from '../../../../config/binaries.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { + BinaryName, + BinaryTaskConfig, +} from '../../../../config/binaries.js'; +import binaries from '../../../../config/binaries.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Bucket) @@ -12,7 +17,10 @@ export class BucketBinary extends AbstractBinary { return; } - async fetch(dir: string, binaryName: BinaryName): Promise { + async fetch( + dir: string, + binaryName: BinaryName + ): Promise { // /foo/ => foo/ const binaryConfig = binaries[binaryName]; const subDir = dir.substring(1); @@ -21,13 +29,18 @@ export class BucketBinary extends AbstractBinary { return { items: this.parseItems(xml, dir, binaryConfig), nextParams: null }; } - protected parseItems(xml: string, dir: string, binaryConfig: BinaryTaskConfig): BinaryItem[] { + protected parseItems( + xml: string, + dir: string, + binaryConfig: BinaryTaskConfig + ): BinaryItem[] { const items: BinaryItem[] = []; // https://nwjs2.s3.amazonaws.com/?prefix=v0.59.0%2Fx64%2F // https://chromedriver.storage.googleapis.com/?delimiter=/&prefix= // 2.0/chromedriver_linux32.zip138014985953000022013-09-25T22:57:39.349Z"c0d96102715c4916b872f91f5bf9b12c"7262134 // v0.59.0/nwjs-v0.59.0-linux-ia32.tar.gz2015-11-02T02:34:18.000Z"b1b7a52928e9f874bad0cabf7f74ba8e"22842STANDARD - const fileRe = /([^<]+?)<\/Key>(?:\d+?<\/Generation>)?(?:\d+?<\/MetaGeneration>)?([^<]+?)<\/LastModified>[^<]+?<\/ETag>(\d+?)<\/Size>/g; + const fileRe = + /([^<]+?)<\/Key>(?:\d+?<\/Generation>)?(?:\d+?<\/MetaGeneration>)?([^<]+?)<\/LastModified>[^<]+?<\/ETag>(\d+?)<\/Size>/g; let matchs = xml.matchAll(fileRe); for (const m of matchs) { const fullname = m[1].trim(); @@ -52,7 +65,8 @@ export class BucketBinary extends AbstractBinary { }); } // v0.59.0/x64/ - const dirRe = /([^<]+?)<\/Prefix><\/CommonPrefixes>/g; + const dirRe = + /([^<]+?)<\/Prefix><\/CommonPrefixes>/g; matchs = xml.matchAll(dirRe); for (const m of matchs) { // AWSLogs/ diff --git a/app/common/adapter/binary/ChromeForTestingBinary.ts b/app/common/adapter/binary/ChromeForTestingBinary.ts index d507aaa8..f5a6385e 100644 --- a/app/common/adapter/binary/ChromeForTestingBinary.ts +++ b/app/common/adapter/binary/ChromeForTestingBinary.ts @@ -1,7 +1,8 @@ import { basename } from 'node:path'; import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.ChromeForTesting) @@ -18,7 +19,11 @@ export class ChromeForTestingBinary extends AbstractBinary { } async finishFetch(success: boolean) { - if (success && this.#timestamp && ChromeForTestingBinary.lastTimestamp !== this.#timestamp) { + if ( + success && + this.#timestamp && + ChromeForTestingBinary.lastTimestamp !== this.#timestamp + ) { ChromeForTestingBinary.lastTimestamp = this.#timestamp; } } @@ -26,22 +31,35 @@ export class ChromeForTestingBinary extends AbstractBinary { async #syncDirItems() { this.dirItems = {}; this.dirItems['/'] = []; - const jsonApiEndpoint = 'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json'; - const { data, status, headers } = await this.httpclient.request(jsonApiEndpoint, { - dataType: 'json', - timeout: 30000, - followRedirect: true, - gzip: true, - }); + const jsonApiEndpoint = + 'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json'; + const { data, status, headers } = await this.httpclient.request( + jsonApiEndpoint, + { + dataType: 'json', + timeout: 30000, + followRedirect: true, + gzip: true, + } + ); if (status !== 200) { - this.logger.warn('[ChromeForTestingBinary.request:non-200-status] url: %s, status: %s, headers: %j, data: %j', - jsonApiEndpoint, status, headers, data); + this.logger.warn( + '[ChromeForTestingBinary.request:non-200-status] url: %s, status: %s, headers: %j, data: %j', + jsonApiEndpoint, + status, + headers, + data + ); return; } this.#timestamp = data.timestamp; const hasNewData = this.#timestamp !== ChromeForTestingBinary.lastTimestamp; - this.logger.info('[ChromeForTestingBinary] remote data timestamp: %j, last timestamp: %j, hasNewData: %s', - this.#timestamp, ChromeForTestingBinary.lastTimestamp, hasNewData); + this.logger.info( + '[ChromeForTestingBinary] remote data timestamp: %j, last timestamp: %j, hasNewData: %s', + this.#timestamp, + ChromeForTestingBinary.lastTimestamp, + hasNewData + ); if (!hasNewData) { return; } diff --git a/app/common/adapter/binary/CypressBinary.ts b/app/common/adapter/binary/CypressBinary.ts index 2e989747..2def97e8 100644 --- a/app/common/adapter/binary/CypressBinary.ts +++ b/app/common/adapter/binary/CypressBinary.ts @@ -1,6 +1,7 @@ import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Cypress) @@ -53,8 +54,10 @@ export class CypressBinary extends AbstractBinary { // { platform: 'win32', arch: 'x64' }, // ] const platforms = [ - 'darwin-x64', 'darwin-arm64', - 'linux-x64', 'linux-arm64', + 'darwin-x64', + 'darwin-arm64', + 'linux-x64', + 'linux-arm64', 'win32-x64', ]; for (const platform of platforms) { diff --git a/app/common/adapter/binary/EdgedriverBinary.ts b/app/common/adapter/binary/EdgedriverBinary.ts index 3a180bb1..b5932465 100644 --- a/app/common/adapter/binary/EdgedriverBinary.ts +++ b/app/common/adapter/binary/EdgedriverBinary.ts @@ -1,8 +1,7 @@ import path from 'node:path'; import { SingletonProto } from '@eggjs/tegg'; -import { - AbstractBinary, FetchResult, BinaryItem, BinaryAdapter, -} from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; import { BinaryType } from '../../enum/Binary.js'; @SingletonProto() @@ -20,15 +19,23 @@ export class EdgedriverBinary extends AbstractBinary { this.dirItems = {}; this.dirItems['/'] = []; const jsonApiEndpoint = 'https://edgeupdates.microsoft.com/api/products'; - const { data, status, headers } = await this.httpclient.request(jsonApiEndpoint, { - dataType: 'json', - timeout: 30000, - followRedirect: true, - gzip: true, - }); + const { data, status, headers } = await this.httpclient.request( + jsonApiEndpoint, + { + dataType: 'json', + timeout: 30000, + followRedirect: true, + gzip: true, + } + ); if (status !== 200) { - this.logger.warn('[EdgedriverBinary.request:non-200-status] url: %s, status: %s, headers: %j, data: %j', - jsonApiEndpoint, status, headers, data); + this.logger.warn( + '[EdgedriverBinary.request:non-200-status] url: %s, status: %s, headers: %j, data: %j', + jsonApiEndpoint, + status, + headers, + data + ); return; } this.logger.info('[EdgedriverBinary] remote data length: %s', data.length); @@ -175,7 +182,8 @@ export class EdgedriverBinary extends AbstractBinary { #parseItems(xml: string): BinaryItem[] { const items: BinaryItem[] = []; // 124.0.2478.97/edgedriver_arm64.ziphttps://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/124.0.2478.97/edgedriver_arm64.zipFri, 10 May 2024 18:35:44 GMT0x8DC712000713C139191362application/octet-stream1tjPTf5JU6KKB06Qf1JOGw==BlockBlobunlocked - const fileRe = /([^<]+?)<\/Name>([^<]+?)<\/Url>([^<]+?)<\/Last-Modified>(?:[^<]+?)<\/Etag>(\d+)<\/Content-Length>/g; + const fileRe = + /([^<]+?)<\/Name>([^<]+?)<\/Url>([^<]+?)<\/Last-Modified>(?:[^<]+?)<\/Etag>(\d+)<\/Content-Length>/g; const matchItems = xml.matchAll(fileRe); for (const m of matchItems) { const fullname = m[1].trim(); diff --git a/app/common/adapter/binary/ElectronBinary.ts b/app/common/adapter/binary/ElectronBinary.ts index bb91cd66..a0102203 100644 --- a/app/common/adapter/binary/ElectronBinary.ts +++ b/app/common/adapter/binary/ElectronBinary.ts @@ -1,13 +1,18 @@ import { SingletonProto } from '@eggjs/tegg'; -import binaries, { BinaryName } from '../../../../config/binaries.js'; +import type { BinaryName } from '../../../../config/binaries.js'; +import binaries from '../../../../config/binaries.js'; import { BinaryType } from '../../enum/Binary.js'; -import { BinaryAdapter, BinaryItem, FetchResult } from './AbstractBinary.js'; +import type { BinaryItem, FetchResult } from './AbstractBinary.js'; +import { BinaryAdapter } from './AbstractBinary.js'; import { GithubBinary } from './GithubBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Electron) export class ElectronBinary extends GithubBinary { - async fetch(dir: string, binaryName: BinaryName = 'electron'): Promise { + async fetch( + dir: string, + binaryName: BinaryName = 'electron' + ): Promise { const releases = await this.initReleases(binaryName, binaries.electron); if (!releases) return; @@ -34,7 +39,10 @@ export class ElectronBinary extends GithubBinary { } } else { for (const item of releases) { - if (dir === `/${item.tag_name}/` || dir === `/${item.tag_name.substring(1)}/`) { + if ( + dir === `/${item.tag_name}/` || + dir === `/${item.tag_name.substring(1)}/` + ) { items = this.formatItems(item, binaries.electron); break; } diff --git a/app/common/adapter/binary/GithubBinary.ts b/app/common/adapter/binary/GithubBinary.ts index 51ec989a..76918079 100644 --- a/app/common/adapter/binary/GithubBinary.ts +++ b/app/common/adapter/binary/GithubBinary.ts @@ -1,7 +1,12 @@ import { SingletonProto } from '@eggjs/tegg'; -import binaries, { BinaryName, BinaryTaskConfig } from '../../../../config/binaries.js'; +import type { + BinaryName, + BinaryTaskConfig, +} from '../../../../config/binaries.js'; +import binaries from '../../../../config/binaries.js'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.GitHub) @@ -12,7 +17,10 @@ export class GithubBinary extends AbstractBinary { delete this.releases[binaryName]; } - protected async initReleases(binaryName: BinaryName, binaryConfig: BinaryTaskConfig) { + protected async initReleases( + binaryName: BinaryName, + binaryConfig: BinaryTaskConfig + ) { if (!this.releases[binaryName]) { // https://docs.github.com/en/rest/reference/releases get three pages // https://api.github.com/repos/electron/electron/releases @@ -28,11 +36,22 @@ export class GithubBinary extends AbstractBinary { const data = await this.requestJSON(url, requestHeaders); if (!Array.isArray(data)) { // {"message":"API rate limit exceeded for 47.57.239.54. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)","documentation_url":"https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting"} - if (typeof data?.message === 'string' && data.message.includes('rate limit')) { - this.logger.info('[GithubBinary.fetch:hit-rate-limit] skip sync this time, data: %j, url: %s', data, url); + if ( + typeof data?.message === 'string' && + data.message.includes('rate limit') + ) { + this.logger.info( + '[GithubBinary.fetch:hit-rate-limit] skip sync this time, data: %j, url: %s', + data, + url + ); return; } - this.logger.warn('[GithubBinary.fetch:response-data-not-array] data: %j, url: %s', data, url); + this.logger.warn( + '[GithubBinary.fetch:response-data-not-array] data: %j, url: %s', + data, + url + ); return; } releases = releases.concat(data); @@ -48,7 +67,10 @@ export class GithubBinary extends AbstractBinary { const maxFileSize = 1024 * 1024 * 250; for (const asset of releaseItem.assets) { if (asset.size > maxFileSize) { - this.logger.info('[GithubBinary.formatItems] asset reach max file size(> 250MB), ignore download it, asset: %j', asset); + this.logger.info( + '[GithubBinary.formatItems] asset reach max file size(> 250MB), ignore download it, asset: %j', + asset + ); continue; } items.push({ @@ -83,7 +105,10 @@ export class GithubBinary extends AbstractBinary { return items; } - async fetch(dir: string, binaryName: BinaryName): Promise { + async fetch( + dir: string, + binaryName: BinaryName + ): Promise { const binaryConfig = binaries[binaryName]; const releases = await this.initReleases(binaryName, binaryConfig); if (!releases) return; diff --git a/app/common/adapter/binary/ImageminBinary.ts b/app/common/adapter/binary/ImageminBinary.ts index efd2abb0..939de1ce 100644 --- a/app/common/adapter/binary/ImageminBinary.ts +++ b/app/common/adapter/binary/ImageminBinary.ts @@ -1,7 +1,9 @@ import { SingletonProto } from '@eggjs/tegg'; -import binaries, { BinaryName } from '../../../../config/binaries.js'; +import type { BinaryName } from '../../../../config/binaries.js'; +import binaries from '../../../../config/binaries.js'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Imagemin) @@ -11,7 +13,10 @@ export class ImageminBinary extends AbstractBinary { return; } - async fetch(dir: string, binaryName: BinaryName): Promise { + async fetch( + dir: string, + binaryName: BinaryName + ): Promise { const binaryConfig = binaries[binaryName]; const dirItems: { [key: string]: BinaryItem[]; @@ -66,7 +71,7 @@ export class ImageminBinary extends AbstractBinary { size: '-', isDir: false, url: `${binaryConfig.distUrl}/${binaryConfig.repo}${platformDir}${name}`, - ignoreDownloadStatuses: [ 404 ], + ignoreDownloadStatuses: [404], }); } } else { @@ -88,7 +93,7 @@ export class ImageminBinary extends AbstractBinary { size: '-', isDir: false, url: `${binaryConfig.distUrl}/${binaryConfig.repo}${platformArchDir}${name}`, - ignoreDownloadStatuses: [ 404 ], + ignoreDownloadStatuses: [404], }); } } diff --git a/app/common/adapter/binary/NodeBinary.ts b/app/common/adapter/binary/NodeBinary.ts index 9bd23cf1..ca8ef59c 100644 --- a/app/common/adapter/binary/NodeBinary.ts +++ b/app/common/adapter/binary/NodeBinary.ts @@ -1,8 +1,10 @@ import { basename } from 'node:path'; import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import binaries, { BinaryName } from '../../../../config/binaries.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { BinaryName } from '../../../../config/binaries.js'; +import binaries from '../../../../config/binaries.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Node) @@ -12,7 +14,10 @@ export class NodeBinary extends AbstractBinary { return; } - async fetch(dir: string, binaryName: BinaryName): Promise { + async fetch( + dir: string, + binaryName: BinaryName + ): Promise { const binaryConfig = binaries[binaryName]; const url = `${binaryConfig.distUrl}${dir}`; const html = await this.requestXml(url); @@ -30,7 +35,8 @@ export class NodeBinary extends AbstractBinary { // SHASUMS256.txt.asc 04-Nov-2024 17:29 3.7 KB // SHASUMS256.txt.sig 04-Nov-2024 17:29 310 B // SHASUMS256.txt 04-Nov-2024 17:29 3.2 KB - const re = /]*?>[^<]+?<\/a>\s+?((?:[\w-]+? \w{2}:\d{2})|-)\s+?([\d.\-\s\w]+)/ig; + const re = + /]*?>[^<]+?<\/a>\s+?((?:[\w-]+? \w{2}:\d{2})|-)\s+?([\d.\-\s\w]+)/gi; const matchs = html.matchAll(re); const items: BinaryItem[] = []; for (const m of matchs) { diff --git a/app/common/adapter/binary/NodePreGypBinary.ts b/app/common/adapter/binary/NodePreGypBinary.ts index 9d6696c3..ddc1a4b0 100644 --- a/app/common/adapter/binary/NodePreGypBinary.ts +++ b/app/common/adapter/binary/NodePreGypBinary.ts @@ -1,8 +1,10 @@ import { join } from 'node:path'; import { SingletonProto } from '@eggjs/tegg'; -import binaries, { BinaryName } from '../../../../config/binaries.js'; +import type { BinaryName } from '../../../../config/binaries.js'; +import binaries from '../../../../config/binaries.js'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.NodePreGyp) @@ -13,7 +15,10 @@ export class NodePreGypBinary extends AbstractBinary { } // https://github.com/mapbox/node-pre-gyp - async fetch(dir: string, binaryName: BinaryName): Promise { + async fetch( + dir: string, + binaryName: BinaryName + ): Promise { const binaryConfig = binaries[binaryName]; const npmPackageName = binaryConfig.options?.npmPackageName ?? binaryName; const pkgUrl = `https://registry.npmjs.com/${npmPackageName}`; @@ -33,20 +38,28 @@ export class NodePreGypBinary extends AbstractBinary { if (!pkgVersion.binary) continue; // https://github.com/mapbox/node-pre-gyp#package_name // defaults to {module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz - let binaryFile = pkgVersion.binary.package_name - || '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz'; + let binaryFile = + pkgVersion.binary.package_name || + '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz'; if (!binaryFile) continue; const moduleName = pkgVersion.binary.module_name || pkgVersion.name; - binaryFile = binaryFile.replace('{version}', version) + binaryFile = binaryFile + .replace('{version}', version) .replace('{module_name}', moduleName); let currentDir = dirItems['/']; let versionPrefix = ''; let remotePath = pkgVersion.binary.remote_path; const napiVersions = pkgVersion.binary.napi_versions ?? []; - if (binaryConfig.options?.requiredNapiVersions && napiVersions.length === 0) continue; + if ( + binaryConfig.options?.requiredNapiVersions && + napiVersions.length === 0 + ) + continue; if (remotePath?.includes('{version}')) { - const dirName = remotePath.includes('v{version}') ? `v${version}` : version; + const dirName = remotePath.includes('v{version}') + ? `v${version}` + : version; versionPrefix = `/${dirName}`; dirItems['/'].push({ name: `${dirName}/`, @@ -67,17 +80,20 @@ export class NodePreGypBinary extends AbstractBinary { // "remote_path": "{name}/v{version}", // "package_name": "{node_abi}-{platform}-{arch}-{libc}.tar.gz" // }, - if (binaryFile.includes('{node_abi}') - && binaryFile.includes('{platform}') - && binaryFile.includes('{arch}') - && binaryFile.includes('{libc}')) { + if ( + binaryFile.includes('{node_abi}') && + binaryFile.includes('{platform}') && + binaryFile.includes('{arch}') && + binaryFile.includes('{libc}') + ) { for (const nodeAbi of nodeABIVersions) { for (const platform of nodePlatforms) { const archs = nodeArchs[platform]; const libcs = nodeLibcs[platform]; for (const arch of archs) { for (const libc of libcs) { - const name = binaryFile.replace('{node_abi}', `node-v${nodeAbi}`) + const name = binaryFile + .replace('{node_abi}', `node-v${nodeAbi}`) .replace('{platform}', platform) .replace('{arch}', arch) .replace('{libc}', libc); @@ -87,20 +103,23 @@ export class NodePreGypBinary extends AbstractBinary { size: '-', isDir: false, url: `${binaryConfig.distUrl}/${binaryName}${versionPrefix}/${name}`, - ignoreDownloadStatuses: [ 404 ], + ignoreDownloadStatuses: [404], }); } } } } - } else if (binaryFile.includes('{node_abi}') - && binaryFile.includes('{platform}') - && binaryFile.includes('{arch}')) { + } else if ( + binaryFile.includes('{node_abi}') && + binaryFile.includes('{platform}') && + binaryFile.includes('{arch}') + ) { for (const nodeAbi of nodeABIVersions) { for (const platform of nodePlatforms) { const archs = nodeArchs[platform]; for (const arch of archs) { - const name = binaryFile.replace('{node_abi}', `node-v${nodeAbi}`) + const name = binaryFile + .replace('{node_abi}', `node-v${nodeAbi}`) .replace('{platform}', platform) .replace('{arch}', arch); currentDir.push({ @@ -109,12 +128,15 @@ export class NodePreGypBinary extends AbstractBinary { size: '-', isDir: false, url: `${binaryConfig.distUrl}/${binaryName}${versionPrefix}/${name}`, - ignoreDownloadStatuses: [ 404 ], + ignoreDownloadStatuses: [404], }); } } } - } else if (binaryFile.includes('{platform}-{arch}-{node_napi_label}-{libc}') && napiVersions.length > 0) { + } else if ( + binaryFile.includes('{platform}-{arch}-{node_napi_label}-{libc}') && + napiVersions.length > 0 + ) { // https://skia-canvas.s3.us-east-1.amazonaws.com/v0.9.30/darwin-arm64-napi-v6-unknown.tar.gz // https://github.com/samizdatco/skia-canvas/blob/2a75801d7cce3b4e4e6ad015a173daefaa8465e6/package.json#L48 // "binary": { @@ -133,7 +155,8 @@ export class NodePreGypBinary extends AbstractBinary { for (const arch of archs) { for (const libc of libcs) { for (const napiVersion of napiVersions) { - const name = binaryFile.replace('{platform}', platform) + const name = binaryFile + .replace('{platform}', platform) .replace('{arch}', arch) .replace('{node_napi_label}', `napi-v${napiVersion}`) .replace('{libc}', libc); @@ -143,7 +166,7 @@ export class NodePreGypBinary extends AbstractBinary { size: '-', isDir: false, url: `${binaryConfig.distUrl}${versionPrefix}/${name}`, - ignoreDownloadStatuses: [ 404, 403 ], + ignoreDownloadStatuses: [404, 403], }); } } @@ -165,10 +188,12 @@ export class NodePreGypBinary extends AbstractBinary { const archs = nodeArchs[platform]; for (const arch of archs) { for (const napiVersion of napiVersions) { - const binaryFileName = binaryFile.replace('{platform}', platform) + const binaryFileName = binaryFile + .replace('{platform}', platform) .replace('{arch}', arch) .replace('{node_napi_label}', napiVersion); - remotePath = remotePath.replace('{module_name}', moduleName) + remotePath = remotePath + .replace('{module_name}', moduleName) .replace('{name}', binaryName) .replace('{version}', version) .replace('{configuration}', 'Release'); @@ -180,12 +205,15 @@ export class NodePreGypBinary extends AbstractBinary { size: '-', isDir: false, url: remoteUrl, - ignoreDownloadStatuses: [ 404 ], + ignoreDownloadStatuses: [404], }); } } } - } else if (binaryFile.includes('{platform}') && binaryFile.includes('{arch}')) { + } else if ( + binaryFile.includes('{platform}') && + binaryFile.includes('{arch}') + ) { // https://github.com/grpc/grpc-node/blob/master/packages/grpc-tools/package.json#L29 // "binary": { // "module_name": "grpc_tools", @@ -205,9 +233,11 @@ export class NodePreGypBinary extends AbstractBinary { for (const platform of nodePlatforms) { const archs = nodeArchs[platform]; for (const arch of archs) { - const binaryFileName = binaryFile.replace('{platform}', platform) + const binaryFileName = binaryFile + .replace('{platform}', platform) .replace('{arch}', arch); - remotePath = remotePath.replace('{module_name}', moduleName) + remotePath = remotePath + .replace('{module_name}', moduleName) .replace('{name}', binaryName) .replace('{version}', version) .replace('{configuration}', 'Release'); @@ -219,7 +249,7 @@ export class NodePreGypBinary extends AbstractBinary { size: '-', isDir: false, url: remoteUrl, - ignoreDownloadStatuses: [ 404 ], + ignoreDownloadStatuses: [404], }); } } diff --git a/app/common/adapter/binary/NwjsBinary.ts b/app/common/adapter/binary/NwjsBinary.ts index a73a5492..2fc624c5 100644 --- a/app/common/adapter/binary/NwjsBinary.ts +++ b/app/common/adapter/binary/NwjsBinary.ts @@ -1,7 +1,8 @@ import { SingletonProto } from '@eggjs/tegg'; import binaries from '../../../../config/binaries.js'; import { BinaryType } from '../../enum/Binary.js'; -import { FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { BinaryAdapter } from './AbstractBinary.js'; import { BucketBinary } from './BucketBinary.js'; @SingletonProto() @@ -14,7 +15,9 @@ export class NwjsBinary extends BucketBinary { const isRootDir = dir === '/'; // /foo/ => foo/ const subDir = dir.substring(1); - const url = isRootDir ? binaryConfig.distUrl : `${this.s3Url}${encodeURIComponent(subDir)}`; + const url = isRootDir + ? binaryConfig.distUrl + : `${this.s3Url}${encodeURIComponent(subDir)}`; const xml = await this.requestXml(url); if (!xml) return; @@ -25,7 +28,8 @@ export class NwjsBinary extends BucketBinary { // [DIR]v0.15.0-rc1/06-May-2016 12:24 -   // [DIR]v0.15.0-rc2/13-May-2016 20:13 -   const items: BinaryItem[] = []; - const re = /]+?>([^<]+?\/)<\/a><\/td>]+?>([^>]+?)<\/td>/ig; + const re = + /]+?>([^<]+?\/)<\/a><\/td>]+?>([^>]+?)<\/td>/gi; const matchs = xml.matchAll(re); for (const m of matchs) { const name = m[1].trim(); diff --git a/app/common/adapter/binary/PlaywrightBinary.ts b/app/common/adapter/binary/PlaywrightBinary.ts index b0f88086..a3f68e52 100644 --- a/app/common/adapter/binary/PlaywrightBinary.ts +++ b/app/common/adapter/binary/PlaywrightBinary.ts @@ -2,7 +2,8 @@ import util from 'node:util'; import path from 'node:path'; import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, BinaryAdapter, BinaryItem, FetchResult } from './AbstractBinary.js'; +import type { BinaryItem, FetchResult } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; const PACKAGE_URL = 'https://registry.npmjs.com/playwright-core'; const DOWNLOAD_HOST = 'https://playwright.azureedge.net/'; @@ -10,7 +11,7 @@ const DOWNLOAD_HOST = 'https://playwright.azureedge.net/'; // https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/registry/index.ts /* eslint-disable quote-props */ const DOWNLOAD_PATHS = { - 'chromium': { + chromium: { '': undefined, 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/chromium/%s/chromium-linux.zip', @@ -27,17 +28,17 @@ const DOWNLOAD_PATHS = { 'mac10.13': 'builds/chromium/%s/chromium-mac.zip', 'mac10.14': 'builds/chromium/%s/chromium-mac.zip', 'mac10.15': 'builds/chromium/%s/chromium-mac.zip', - 'mac11': 'builds/chromium/%s/chromium-mac.zip', + mac11: 'builds/chromium/%s/chromium-mac.zip', 'mac11-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac12': 'builds/chromium/%s/chromium-mac.zip', + mac12: 'builds/chromium/%s/chromium-mac.zip', 'mac12-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac13': 'builds/chromium/%s/chromium-mac.zip', + mac13: 'builds/chromium/%s/chromium-mac.zip', 'mac13-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac14': 'builds/chromium/%s/chromium-mac.zip', + mac14: 'builds/chromium/%s/chromium-mac.zip', 'mac14-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'mac15': 'builds/chromium/%s/chromium-mac.zip', + mac15: 'builds/chromium/%s/chromium-mac.zip', 'mac15-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', - 'win64': 'builds/chromium/%s/chromium-win64.zip', + win64: 'builds/chromium/%s/chromium-win64.zip', }, 'chromium-headless-shell': { '': undefined, @@ -46,87 +47,128 @@ const DOWNLOAD_PATHS = { 'ubuntu22.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', 'ubuntu24.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', 'ubuntu18.04-arm64': undefined, - 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', - 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', - 'ubuntu24.04-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'ubuntu20.04-arm64': + 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'ubuntu22.04-arm64': + 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'ubuntu24.04-arm64': + 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', 'debian11-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', - 'debian11-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'debian11-arm64': + 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', 'debian12-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip', - 'debian12-arm64': 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', + 'debian12-arm64': + 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip', 'mac10.13': undefined, 'mac10.14': undefined, 'mac10.15': undefined, - 'mac11': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + mac11: 'builds/chromium/%s/chromium-headless-shell-mac.zip', 'mac11-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac12': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + mac12: 'builds/chromium/%s/chromium-headless-shell-mac.zip', 'mac12-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac13': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + mac13: 'builds/chromium/%s/chromium-headless-shell-mac.zip', 'mac13-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac14': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + mac14: 'builds/chromium/%s/chromium-headless-shell-mac.zip', 'mac14-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'mac15': 'builds/chromium/%s/chromium-headless-shell-mac.zip', + mac15: 'builds/chromium/%s/chromium-headless-shell-mac.zip', 'mac15-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip', - 'win64': 'builds/chromium/%s/chromium-headless-shell-win64.zip', + win64: 'builds/chromium/%s/chromium-headless-shell-win64.zip', }, 'chromium-tip-of-tree': { '': undefined, 'ubuntu18.04-x64': undefined, - 'ubuntu20.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', - 'ubuntu22.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', - 'ubuntu24.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', + 'ubuntu20.04-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', + 'ubuntu22.04-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', + 'ubuntu24.04-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', 'ubuntu18.04-arm64': undefined, - 'ubuntu20.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', - 'ubuntu22.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', - 'ubuntu24.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', - 'debian11-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', - 'debian11-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', - 'debian12-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', - 'debian12-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', + 'ubuntu20.04-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', + 'ubuntu22.04-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', + 'ubuntu24.04-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', + 'debian11-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', + 'debian11-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', + 'debian12-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', + 'debian12-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', 'mac10.13': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', 'mac10.14': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', 'mac10.15': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', - 'mac11': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', - 'mac11-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', - 'mac12': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', - 'mac12-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', - 'mac13': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', - 'mac13-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', - 'mac14': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', - 'mac14-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', - 'mac15': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', - 'mac15-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', - 'win64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-win64.zip', + mac11: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', + 'mac11-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', + mac12: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', + 'mac12-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', + mac13: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', + 'mac13-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', + mac14: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', + 'mac14-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', + mac15: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', + 'mac15-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', + win64: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-win64.zip', }, 'chromium-tip-of-tree-headless-shell': { '': undefined, 'ubuntu18.04-x64': undefined, - 'ubuntu20.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', - 'ubuntu22.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', - 'ubuntu24.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', + 'ubuntu20.04-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', + 'ubuntu22.04-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', + 'ubuntu24.04-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', 'ubuntu18.04-arm64': undefined, - 'ubuntu20.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', - 'ubuntu22.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', - 'ubuntu24.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', - 'debian11-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', - 'debian11-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', - 'debian12-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', - 'debian12-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', + 'ubuntu20.04-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', + 'ubuntu22.04-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', + 'ubuntu24.04-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', + 'debian11-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', + 'debian11-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', + 'debian12-x64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip', + 'debian12-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip', 'mac10.13': undefined, 'mac10.14': undefined, 'mac10.15': undefined, - 'mac11': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', - 'mac11-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', - 'mac12': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', - 'mac12-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', - 'mac13': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', - 'mac13-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', - 'mac14': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', - 'mac14-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', - 'mac15': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', - 'mac15-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', - 'win64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-win64.zip', + mac11: + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', + 'mac11-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', + mac12: + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', + 'mac12-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', + mac13: + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', + 'mac13-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', + mac14: + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', + 'mac14-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', + mac15: + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip', + 'mac15-arm64': + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip', + win64: + 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-win64.zip', }, - 'firefox': { + firefox: { '': undefined, 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/firefox/%s/firefox-ubuntu-20.04.zip', @@ -143,17 +185,17 @@ const DOWNLOAD_PATHS = { 'mac10.13': 'builds/firefox/%s/firefox-mac.zip', 'mac10.14': 'builds/firefox/%s/firefox-mac.zip', 'mac10.15': 'builds/firefox/%s/firefox-mac.zip', - 'mac11': 'builds/firefox/%s/firefox-mac.zip', + mac11: 'builds/firefox/%s/firefox-mac.zip', 'mac11-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', - 'mac12': 'builds/firefox/%s/firefox-mac.zip', + mac12: 'builds/firefox/%s/firefox-mac.zip', 'mac12-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', - 'mac13': 'builds/firefox/%s/firefox-mac.zip', + mac13: 'builds/firefox/%s/firefox-mac.zip', 'mac13-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', - 'mac14': 'builds/firefox/%s/firefox-mac.zip', + mac14: 'builds/firefox/%s/firefox-mac.zip', 'mac14-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', - 'mac15': 'builds/firefox/%s/firefox-mac.zip', + mac15: 'builds/firefox/%s/firefox-mac.zip', 'mac15-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', - 'win64': 'builds/firefox/%s/firefox-win64.zip', + win64: 'builds/firefox/%s/firefox-win64.zip', }, 'firefox-beta': { '': undefined, @@ -163,8 +205,10 @@ const DOWNLOAD_PATHS = { 'ubuntu24.04-x64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-24.04.zip', 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': undefined, - 'ubuntu22.04-arm64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip', - 'ubuntu24.04-arm64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-24.04-arm64.zip', + 'ubuntu22.04-arm64': + 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip', + 'ubuntu24.04-arm64': + 'builds/firefox-beta/%s/firefox-beta-ubuntu-24.04-arm64.zip', 'debian11-x64': 'builds/firefox-beta/%s/firefox-beta-debian-11.zip', 'debian11-arm64': 'builds/firefox-beta/%s/firefox-beta-debian-11-arm64.zip', 'debian12-x64': 'builds/firefox-beta/%s/firefox-beta-debian-12.zip', @@ -172,19 +216,19 @@ const DOWNLOAD_PATHS = { 'mac10.13': 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac10.14': 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac10.15': 'builds/firefox-beta/%s/firefox-beta-mac.zip', - 'mac11': 'builds/firefox-beta/%s/firefox-beta-mac.zip', + mac11: 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac11-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', - 'mac12': 'builds/firefox-beta/%s/firefox-beta-mac.zip', + mac12: 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac12-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', - 'mac13': 'builds/firefox-beta/%s/firefox-beta-mac.zip', + mac13: 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac13-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', - 'mac14': 'builds/firefox-beta/%s/firefox-beta-mac.zip', + mac14: 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac14-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', - 'mac15': 'builds/firefox-beta/%s/firefox-beta-mac.zip', + mac15: 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac15-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', - 'win64': 'builds/firefox-beta/%s/firefox-beta-win64.zip', + win64: 'builds/firefox-beta/%s/firefox-beta-win64.zip', }, - 'webkit': { + webkit: { '': undefined, 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/webkit/%s/webkit-ubuntu-20.04.zip', @@ -199,21 +243,23 @@ const DOWNLOAD_PATHS = { 'debian12-x64': 'builds/webkit/%s/webkit-debian-12.zip', 'debian12-arm64': 'builds/webkit/%s/webkit-debian-12-arm64.zip', 'mac10.13': undefined, - 'mac10.14': 'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip', - 'mac10.15': 'builds/deprecated-webkit-mac-10.15/%s/deprecated-webkit-mac-10.15.zip', - 'mac11': 'builds/webkit/%s/webkit-mac-11.zip', + 'mac10.14': + 'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip', + 'mac10.15': + 'builds/deprecated-webkit-mac-10.15/%s/deprecated-webkit-mac-10.15.zip', + mac11: 'builds/webkit/%s/webkit-mac-11.zip', 'mac11-arm64': 'builds/webkit/%s/webkit-mac-11-arm64.zip', - 'mac12': 'builds/webkit/%s/webkit-mac-12.zip', + mac12: 'builds/webkit/%s/webkit-mac-12.zip', 'mac12-arm64': 'builds/webkit/%s/webkit-mac-12-arm64.zip', - 'mac13': 'builds/webkit/%s/webkit-mac-13.zip', + mac13: 'builds/webkit/%s/webkit-mac-13.zip', 'mac13-arm64': 'builds/webkit/%s/webkit-mac-13-arm64.zip', - 'mac14': 'builds/webkit/%s/webkit-mac-14.zip', + mac14: 'builds/webkit/%s/webkit-mac-14.zip', 'mac14-arm64': 'builds/webkit/%s/webkit-mac-14-arm64.zip', - 'mac15': 'builds/webkit/%s/webkit-mac-15.zip', + mac15: 'builds/webkit/%s/webkit-mac-15.zip', 'mac15-arm64': 'builds/webkit/%s/webkit-mac-15-arm64.zip', - 'win64': 'builds/webkit/%s/webkit-win64.zip', + win64: 'builds/webkit/%s/webkit-win64.zip', }, - 'ffmpeg': { + ffmpeg: { '': undefined, 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip', @@ -230,19 +276,19 @@ const DOWNLOAD_PATHS = { 'mac10.13': 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac10.14': 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac10.15': 'builds/ffmpeg/%s/ffmpeg-mac.zip', - 'mac11': 'builds/ffmpeg/%s/ffmpeg-mac.zip', + mac11: 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac11-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', - 'mac12': 'builds/ffmpeg/%s/ffmpeg-mac.zip', + mac12: 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac12-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', - 'mac13': 'builds/ffmpeg/%s/ffmpeg-mac.zip', + mac13: 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac13-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', - 'mac14': 'builds/ffmpeg/%s/ffmpeg-mac.zip', + mac14: 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac14-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', - 'mac15': 'builds/ffmpeg/%s/ffmpeg-mac.zip', + mac15: 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac15-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', - 'win64': 'builds/ffmpeg/%s/ffmpeg-win64.zip', + win64: 'builds/ffmpeg/%s/ffmpeg-win64.zip', }, - 'winldd': { + winldd: { '': undefined, 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': undefined, @@ -259,19 +305,19 @@ const DOWNLOAD_PATHS = { 'mac10.13': undefined, 'mac10.14': undefined, 'mac10.15': undefined, - 'mac11': undefined, + mac11: undefined, 'mac11-arm64': undefined, - 'mac12': undefined, + mac12: undefined, 'mac12-arm64': undefined, - 'mac13': undefined, + mac13: undefined, 'mac13-arm64': undefined, - 'mac14': undefined, + mac14: undefined, 'mac14-arm64': undefined, - 'mac15': undefined, + mac15: undefined, 'mac15-arm64': undefined, - 'win64': 'builds/winldd/%s/winldd-win64.zip', + win64: 'builds/winldd/%s/winldd-win64.zip', }, - 'android': { + android: { '': 'builds/android/%s/android.zip', 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/android/%s/android.zip', @@ -288,17 +334,17 @@ const DOWNLOAD_PATHS = { 'mac10.13': 'builds/android/%s/android.zip', 'mac10.14': 'builds/android/%s/android.zip', 'mac10.15': 'builds/android/%s/android.zip', - 'mac11': 'builds/android/%s/android.zip', + mac11: 'builds/android/%s/android.zip', 'mac11-arm64': 'builds/android/%s/android.zip', - 'mac12': 'builds/android/%s/android.zip', + mac12: 'builds/android/%s/android.zip', 'mac12-arm64': 'builds/android/%s/android.zip', - 'mac13': 'builds/android/%s/android.zip', + mac13: 'builds/android/%s/android.zip', 'mac13-arm64': 'builds/android/%s/android.zip', - 'mac14': 'builds/android/%s/android.zip', + mac14: 'builds/android/%s/android.zip', 'mac14-arm64': 'builds/android/%s/android.zip', - 'mac15': 'builds/android/%s/android.zip', + mac15: 'builds/android/%s/android.zip', 'mac15-arm64': 'builds/android/%s/android.zip', - 'win64': 'builds/android/%s/android.zip', + win64: 'builds/android/%s/android.zip', }, } as const; @@ -316,17 +362,37 @@ export class PlaywrightBinary extends AbstractBinary { const nowDateISO = new Date().toISOString(); const buildDirs: BinaryItem[] = []; for (const browserName of Object.keys(DOWNLOAD_PATHS)) { - if (browserName === 'chromium-headless-shell' || browserName === 'chromium-tip-of-tree-headless-shell') { + if ( + browserName === 'chromium-headless-shell' || + browserName === 'chromium-tip-of-tree-headless-shell' + ) { continue; } - buildDirs.push({ name: `${browserName}/`, isDir: true, url: '', size: '-', date: nowDateISO }); + buildDirs.push({ + name: `${browserName}/`, + isDir: true, + url: '', + size: '-', + date: nowDateISO, + }); } this.dirItems = { - '/': [{ name: 'builds/', isDir: true, url: '', size: '-', date: nowDateISO }], + '/': [ + { + name: 'builds/', + isDir: true, + url: '', + size: '-', + date: nowDateISO, + }, + ], '/builds/': buildDirs, }; for (const browserName of Object.keys(DOWNLOAD_PATHS)) { - if (browserName === 'chromium-headless-shell' || browserName === 'chromium-tip-of-tree-headless-shell') { + if ( + browserName === 'chromium-headless-shell' || + browserName === 'chromium-tip-of-tree-headless-shell' + ) { continue; } this.dirItems[`/builds/${browserName}/`] = []; @@ -337,11 +403,16 @@ export class PlaywrightBinary extends AbstractBinary { .filter(version => version.match(/^(?:\d+\.\d+\.\d+)(?:-beta-\d+)?$/)) // select recently update 20 items .slice(-20); - const browsers: { name: keyof typeof DOWNLOAD_PATHS; revision: string; browserVersion: string; revisionOverrides?: Record }[] = []; + const browsers: { + name: keyof typeof DOWNLOAD_PATHS; + revision: string; + browserVersion: string; + revisionOverrides?: Record; + }[] = []; await Promise.all( packageVersions.map(version => this.requestJSON( - `https://unpkg.com/playwright-core@${version}/browsers.json`, + `https://unpkg.com/playwright-core@${version}/browsers.json` ) .then(data => { // browsers: [ @@ -357,13 +428,18 @@ export class PlaywrightBinary extends AbstractBinary { }) .catch(err => { /* c8 ignore next 2 */ - this.logger.warn('[PlaywrightBinary.fetch:error] Playwright version %s browser data request failed: %s', - version, err); - }), - ), + this.logger.warn( + '[PlaywrightBinary.fetch:error] Playwright version %s browser data request failed: %s', + version, + err + ); + }) + ) ); // if chromium-headless-shell not exists on browsers, copy chromium to chromium-headless-shell - if (!browsers.find(browser => browser.name === 'chromium-headless-shell')) { + if ( + !browsers.find(browser => browser.name === 'chromium-headless-shell') + ) { const chromium = browsers.find(browser => browser.name === 'chromium'); // { // "name": "chromium", @@ -379,8 +455,14 @@ export class PlaywrightBinary extends AbstractBinary { } } // if chromium-tip-of-tree-headless-shell not exists on browsers, copy chromium-tip-of-tree to chromium-tip-of-tree-headless-shell - if (!browsers.find(browser => browser.name === 'chromium-tip-of-tree-headless-shell')) { - const chromiumTipOfTree = browsers.find(browser => browser.name === 'chromium-tip-of-tree'); + if ( + !browsers.find( + browser => browser.name === 'chromium-tip-of-tree-headless-shell' + ) + ) { + const chromiumTipOfTree = browsers.find( + browser => browser.name === 'chromium-tip-of-tree' + ); if (chromiumTipOfTree) { browsers.push({ ...chromiumTipOfTree, @@ -402,9 +484,10 @@ export class PlaywrightBinary extends AbstractBinary { // https://playwright.azureedge.net/builds/chromium-tip-of-tree/1293/chromium-tip-of-tree-headless-shell-mac-arm64.zip browserDirname = 'chromium-tip-of-tree'; } - for (const [ platform, remotePath ] of Object.entries(downloadPaths)) { + for (const [platform, remotePath] of Object.entries(downloadPaths)) { if (typeof remotePath !== 'string') continue; - const revision = browser.revisionOverrides?.[platform] ?? browser.revision; + const revision = + browser.revisionOverrides?.[platform] ?? browser.revision; const itemDate = browser.browserVersion || revision; const url = DOWNLOAD_HOST + util.format(remotePath, revision); const name = path.basename(remotePath); @@ -420,7 +503,13 @@ export class PlaywrightBinary extends AbstractBinary { this.dirItems[dir] = []; } if (!this.dirItems[dir].find(item => item.name === name)) { - this.dirItems[dir].push({ name, isDir: false, url, size: '-', date: itemDate }); + this.dirItems[dir].push({ + name, + isDir: false, + url, + size: '-', + date: itemDate, + }); } } } diff --git a/app/common/adapter/binary/PrismaBinary.ts b/app/common/adapter/binary/PrismaBinary.ts index 42b63fbb..3220c5ac 100644 --- a/app/common/adapter/binary/PrismaBinary.ts +++ b/app/common/adapter/binary/PrismaBinary.ts @@ -1,8 +1,10 @@ import path from 'node:path'; import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import binaries, { BinaryName } from '../../../../config/binaries.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { BinaryName } from '../../../../config/binaries.js'; +import binaries from '../../../../config/binaries.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Prisma) @@ -37,8 +39,10 @@ export class PrismaBinary extends AbstractBinary { const pkg = data.versions[version]; // https://registry.npmjs.com/@prisma/engines/4.14.1 // https://registry.npmjs.com/@prisma/engines/5.7.0 should read from dependencies - const enginesVersion = pkg.devDependencies?.['@prisma/engines-version'] - || pkg.dependencies?.['@prisma/engines-version'] || ''; + const enginesVersion = + pkg.devDependencies?.['@prisma/engines-version'] || + pkg.dependencies?.['@prisma/engines-version'] || + ''; // "@prisma/engines-version": "4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c" // "@prisma/engines-version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9" const matched = /\.(\w{30,})$/.exec(enginesVersion); @@ -56,7 +60,10 @@ export class PrismaBinary extends AbstractBinary { } } - async fetch(dir: string, binaryName: BinaryName): Promise { + async fetch( + dir: string, + binaryName: BinaryName + ): Promise { const existsItems = this.dirItems[dir]; if (existsItems) { return { items: existsItems, nextParams: null }; diff --git a/app/common/adapter/binary/PuppeteerBinary.ts b/app/common/adapter/binary/PuppeteerBinary.ts index 4f3bc4f4..5a2ab245 100644 --- a/app/common/adapter/binary/PuppeteerBinary.ts +++ b/app/common/adapter/binary/PuppeteerBinary.ts @@ -1,6 +1,7 @@ import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Puppeteer) @@ -23,7 +24,9 @@ export class PuppeteerBinary extends AbstractBinary { for (const version in data.versions) { // find chromium versions const pkg = data.versions[version]; - const revision = pkg.puppeteer?.chromium_revision ? String(pkg.puppeteer.chromium_revision) : ''; + const revision = pkg.puppeteer?.chromium_revision + ? String(pkg.puppeteer.chromium_revision) + : ''; if (revision && !chromiumRevisions.has(revision)) { chromiumRevisions.set(revision, data.time[version]); } @@ -35,7 +38,8 @@ export class PuppeteerBinary extends AbstractBinary { // chromium: '768783', // firefox: 'latest', // }; - const unpkgURL = 'https://unpkg.com/puppeteer-core@latest/lib/cjs/puppeteer/revisions.js'; + const unpkgURL = + 'https://unpkg.com/puppeteer-core@latest/lib/cjs/puppeteer/revisions.js'; const text = await this.requestXml(unpkgURL); const m = /chromium:\s+'(\d+)',/.exec(text); if (m && !chromiumRevisions.has(m[1])) { @@ -44,7 +48,8 @@ export class PuppeteerBinary extends AbstractBinary { // download LAST_CHANGE // https://github.com/chaopeng/chromium-downloader/blob/master/get-chromium#L28 - const LAST_CHANGE_URL = 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media'; + const LAST_CHANGE_URL = + 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media'; const lastRevision = await this.requestXml(LAST_CHANGE_URL); if (lastRevision) { chromiumRevisions.set(lastRevision, new Date().toISOString()); @@ -77,7 +82,7 @@ export class PuppeteerBinary extends AbstractBinary { // "11.0.0":"2021-11-03T09:29:12.751Z" chromiumRevisions.set('901912', data.time['11.0.0']); - const platforms = [ 'Linux_x64', 'Mac', 'Mac_Arm', 'Win', 'Win_x64' ]; + const platforms = ['Linux_x64', 'Mac', 'Mac_Arm', 'Win', 'Win_x64']; for (const platform of platforms) { this.dirItems['/'].push({ name: `${platform}/`, @@ -88,7 +93,7 @@ export class PuppeteerBinary extends AbstractBinary { }); this.dirItems[`/${platform}/`] = []; } - for (const [ revision, date ] of chromiumRevisions.entries()) { + for (const [revision, date] of chromiumRevisions.entries()) { // https://github.com/puppeteer/puppeteer/blob/eebf452d38b79bb2ea1a1ba84c3d2ea6f2f9f899/src/node/BrowserFetcher.ts#L40 // chrome: { // linux: '%s/chromium-browser-snapshots/Linux_x64/%d/%s.zip', @@ -113,7 +118,7 @@ export class PuppeteerBinary extends AbstractBinary { size: '-', isDir: false, url: `https://storage.googleapis.com/chromium-browser-snapshots/${platform}/${revision}/${name}`, - ignoreDownloadStatuses: [ 404 ], + ignoreDownloadStatuses: [404], }, ]; } @@ -124,10 +129,7 @@ export class PuppeteerBinary extends AbstractBinary { } // https://github.com/puppeteer/puppeteer/blob/eebf452d38b79bb2ea1a1ba84c3d2ea6f2f9f899/src/node/BrowserFetcher.ts#L72 - private archiveName( - platform: string, - revision: string, - ): string { + private archiveName(platform: string, revision: string): string { if (platform === 'Linux_x64') return 'chrome-linux'; if (platform === 'Mac' || platform === 'Mac_Arm') return 'chrome-mac'; if (platform === 'Win' || platform === 'Win_x64') { diff --git a/app/common/adapter/binary/SqlcipherBinary.ts b/app/common/adapter/binary/SqlcipherBinary.ts index a80e4ed8..f72f06fe 100644 --- a/app/common/adapter/binary/SqlcipherBinary.ts +++ b/app/common/adapter/binary/SqlcipherBinary.ts @@ -1,6 +1,7 @@ import { SingletonProto } from '@eggjs/tegg'; import { BinaryType } from '../../enum/Binary.js'; -import { AbstractBinary, FetchResult, BinaryItem, BinaryAdapter } from './AbstractBinary.js'; +import type { FetchResult, BinaryItem } from './AbstractBinary.js'; +import { AbstractBinary, BinaryAdapter } from './AbstractBinary.js'; @SingletonProto() @BinaryAdapter(BinaryType.Sqlcipher) @@ -16,7 +17,8 @@ export class SqlcipherBinary extends AbstractBinary { } = { '/': [], }; - const s3Url = 'https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher'; + const s3Url = + 'https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher'; const pkgUrl = 'https://registry.npmjs.com/@journeyapps/sqlcipher'; const data = await this.requestJSON(pkgUrl); // https://github.com/journeyapps/node-sqlcipher/blob/master/.circleci/config.yml#L407 @@ -48,7 +50,8 @@ export class SqlcipherBinary extends AbstractBinary { if (major < 5) continue; // >= 5.0.0 const pkgVersion = data.versions[version]; - const napiVersions = pkgVersion.binary && pkgVersion.binary.napi_versions || []; + const napiVersions = + (pkgVersion.binary && pkgVersion.binary.napi_versions) || []; const date = data.time[version]; dirItems['/'].push({ name: `v${version}/`, @@ -74,7 +77,7 @@ export class SqlcipherBinary extends AbstractBinary { size: '-', isDir: false, url: `${s3Url}/v${version}/${name}`, - ignoreDownloadStatuses: [ 404, 403 ], + ignoreDownloadStatuses: [404, 403], }); } } diff --git a/app/common/adapter/changesStream/AbstractChangesStream.ts b/app/common/adapter/changesStream/AbstractChangesStream.ts index 5aa2b8fa..ef31905b 100644 --- a/app/common/adapter/changesStream/AbstractChangesStream.ts +++ b/app/common/adapter/changesStream/AbstractChangesStream.ts @@ -1,14 +1,8 @@ -import { - ImplDecorator, - Inject, - QualifierImplDecoratorUtil, -} from '@eggjs/tegg'; -import { RegistryType } from '../../../common/enum/Registry.js'; -import { Registry } from '../../../core/entity/Registry.js'; -import { - EggHttpClient, - EggLogger, -} from 'egg'; +import type { ImplDecorator } from '@eggjs/tegg'; +import { Inject, QualifierImplDecoratorUtil } from '@eggjs/tegg'; +import type { RegistryType } from '../../../common/enum/Registry.js'; +import type { Registry } from '../../../core/entity/Registry.js'; +import type { EggHttpClient, EggLogger } from 'egg'; export const CHANGE_STREAM_ATTRIBUTE = 'CHANGE_STREAM_ATTRIBUTE'; export type ChangesStreamChange = { @@ -24,9 +18,16 @@ export abstract class AbstractChangeStream { protected httpclient: EggHttpClient; abstract getInitialSince(registry: Registry): Promise; - abstract fetchChanges(registry: Registry, since: string): AsyncGenerator; + abstract fetchChanges( + registry: Registry, + since: string + ): AsyncGenerator; - getChangesStreamUrl(registry: Registry, since: string, limit?: number): string { + getChangesStreamUrl( + registry: Registry, + since: string, + limit?: number + ): string { const url = new URL(registry.changeStream); url.searchParams.set('since', since); if (limit) { @@ -36,5 +37,10 @@ export abstract class AbstractChangeStream { } } -export const RegistryChangesStream: ImplDecorator = - QualifierImplDecoratorUtil.generatorDecorator(AbstractChangeStream, CHANGE_STREAM_ATTRIBUTE); +export const RegistryChangesStream: ImplDecorator< + AbstractChangeStream, + typeof RegistryType +> = QualifierImplDecoratorUtil.generatorDecorator( + AbstractChangeStream, + CHANGE_STREAM_ATTRIBUTE +); diff --git a/app/common/adapter/changesStream/CnpmcoreChangesStream.ts b/app/common/adapter/changesStream/CnpmcoreChangesStream.ts index 91ed1944..6fd0f0dc 100644 --- a/app/common/adapter/changesStream/CnpmcoreChangesStream.ts +++ b/app/common/adapter/changesStream/CnpmcoreChangesStream.ts @@ -1,15 +1,17 @@ import { SingletonProto } from '@eggjs/tegg'; import { E500 } from 'egg-errors'; import { RegistryType } from '../../../common/enum/Registry.js'; -import { Registry } from '../../../core/entity/Registry.js'; -import { AbstractChangeStream, RegistryChangesStream } from './AbstractChangesStream.js'; +import type { Registry } from '../../../core/entity/Registry.js'; +import { + AbstractChangeStream, + RegistryChangesStream, +} from './AbstractChangesStream.js'; @SingletonProto() @RegistryChangesStream(RegistryType.Cnpmcore) export class CnpmcoreChangesStream extends AbstractChangeStream { - async getInitialSince(registry: Registry): Promise { - const db = (new URL(registry.changeStream)).origin; + const db = new URL(registry.changeStream).origin; const { status, data } = await this.httpclient.request(db, { followRedirect: true, timeout: 10000, @@ -19,12 +21,17 @@ export class CnpmcoreChangesStream extends AbstractChangeStream { throw new E500(`get getInitialSince failed: ${data.update_seq}`); } const since = String(data.update_seq - 10); - this.logger.warn('[NpmChangesStream.getInitialSince:firstSeq] GET %s status: %s, data: %j, since: %s', - registry.name, status, data, since); + this.logger.warn( + '[NpmChangesStream.getInitialSince:firstSeq] GET %s status: %s, data: %j, since: %s', + registry.name, + status, + data, + since + ); return since; } - async* fetchChanges(registry: Registry, since: string) { + async *fetchChanges(registry: Registry, since: string) { const db = this.getChangesStreamUrl(registry, since); // json mode const { data } = await this.httpclient.request(db, { diff --git a/app/common/adapter/changesStream/CnpmjsorgChangesStream.ts b/app/common/adapter/changesStream/CnpmjsorgChangesStream.ts index d032deae..0e67c3d8 100644 --- a/app/common/adapter/changesStream/CnpmjsorgChangesStream.ts +++ b/app/common/adapter/changesStream/CnpmjsorgChangesStream.ts @@ -1,8 +1,11 @@ import { SingletonProto } from '@eggjs/tegg'; import { E500 } from 'egg-errors'; import { RegistryType } from '../../../common/enum/Registry.js'; -import { Registry } from '../../../core/entity/Registry.js'; -import { AbstractChangeStream, RegistryChangesStream } from './AbstractChangesStream.js'; +import type { Registry } from '../../../core/entity/Registry.js'; +import { + AbstractChangeStream, + RegistryChangesStream, +} from './AbstractChangesStream.js'; const MAX_LIMIT = 10000; @@ -12,25 +15,32 @@ type FetchResults = { type: string; id: string; changes: Record[]; - gmt_modified: Date, + gmt_modified: Date; }[]; }; @SingletonProto() @RegistryChangesStream(RegistryType.Cnpmjsorg) export class CnpmjsorgChangesStream extends AbstractChangeStream { - // cnpmjsorg 未实现 update_seq 字段 // 默认返回当前时间戳字符串 async getInitialSince(registry: Registry): Promise { - const since = String((new Date()).getTime()); - this.logger.warn(`[CnpmjsorgChangesStream.getInitialSince] since: ${since}, skip query ${registry.changeStream}`); + const since = String(new Date().getTime()); + this.logger.warn( + `[CnpmjsorgChangesStream.getInitialSince] since: ${since}, skip query ${registry.changeStream}` + ); return since; } - private async tryFetch(registry: Registry, since: string, limit = 1000): Promise<{ data: FetchResults }> { + private async tryFetch( + registry: Registry, + since: string, + limit = 1000 + ): Promise<{ data: FetchResults }> { if (limit > MAX_LIMIT) { - throw new E500(`limit too large, current since: ${since}, limit: ${limit}`); + throw new E500( + `limit too large, current since: ${since}, limit: ${limit}` + ); } const db = this.getChangesStreamUrl(registry, since, limit); // json mode @@ -42,7 +52,7 @@ export class CnpmjsorgChangesStream extends AbstractChangeStream { }); const { results = [] } = res.data; if (results?.length >= limit) { - const [ first ] = results; + const [first] = results; const last = results[results.length - 1]; if (first.gmt_modified === last.gmt_modified) { return await this.tryFetch(registry, since, limit + 1000); @@ -52,7 +62,7 @@ export class CnpmjsorgChangesStream extends AbstractChangeStream { return res; } - async* fetchChanges(registry: Registry, since: string) { + async *fetchChanges(registry: Registry, since: string) { // ref: https://github.com/cnpm/cnpmjs.org/pull/1734 // 由于 cnpmjsorg 无法计算准确的 seq // since 是一个时间戳,需要确保一次返回的结果中首尾两个 gmtModified 不相等 diff --git a/app/common/adapter/changesStream/NpmChangesStream.ts b/app/common/adapter/changesStream/NpmChangesStream.ts index 36efdc86..d6542c89 100644 --- a/app/common/adapter/changesStream/NpmChangesStream.ts +++ b/app/common/adapter/changesStream/NpmChangesStream.ts @@ -1,15 +1,18 @@ import { SingletonProto } from '@eggjs/tegg'; import { E500 } from 'egg-errors'; import { RegistryType } from '../../../common/enum/Registry.js'; -import { Registry } from '../../../core/entity/Registry.js'; -import { AbstractChangeStream, ChangesStreamChange, RegistryChangesStream } from './AbstractChangesStream.js'; +import type { Registry } from '../../../core/entity/Registry.js'; +import type { ChangesStreamChange } from './AbstractChangesStream.js'; +import { + AbstractChangeStream, + RegistryChangesStream, +} from './AbstractChangesStream.js'; @SingletonProto() @RegistryChangesStream(RegistryType.Npm) export class NpmChangesStream extends AbstractChangeStream { - async getInitialSince(registry: Registry): Promise { - const db = (new URL(registry.changeStream)).origin; + const db = new URL(registry.changeStream).origin; const { status, data } = await this.httpclient.request(db, { followRedirect: true, timeout: 10000, @@ -19,12 +22,18 @@ export class NpmChangesStream extends AbstractChangeStream { if (!data.update_seq) { throw new E500(`get getInitialSince failed: ${data.update_seq}`); } - this.logger.warn('[NpmChangesStream.getInitialSince] GET %s status: %s, data: %j, since: %s', - registry.name, registry.changeStream, status, data, since); + this.logger.warn( + '[NpmChangesStream.getInitialSince] GET %s status: %s, data: %j, since: %s', + registry.name, + registry.changeStream, + status, + data, + since + ); return since; } - async* fetchChanges(registry: Registry, since: string) { + async *fetchChanges(registry: Registry, since: string) { const db = this.getChangesStreamUrl(registry, since); const { res } = await this.httpclient.request(db, { streaming: true, @@ -51,5 +60,4 @@ export class NpmChangesStream extends AbstractChangeStream { } } } - } diff --git a/app/common/aop/AsyncTimer.ts b/app/common/aop/AsyncTimer.ts index 4c779828..e2517d09 100644 --- a/app/common/aop/AsyncTimer.ts +++ b/app/common/aop/AsyncTimer.ts @@ -1,7 +1,8 @@ import { performance } from 'node:perf_hooks'; -import { Advice, AdviceContext, IAdvice } from '@eggjs/tegg/aop'; +import type { AdviceContext, IAdvice } from '@eggjs/tegg/aop'; +import { Advice } from '@eggjs/tegg/aop'; import { Inject } from '@eggjs/tegg'; -import { EggLogger } from 'egg'; +import type { EggLogger } from 'egg'; // auto print async function call performance timer log into logger @Advice() @@ -21,7 +22,12 @@ export class AsyncTimer implements IAdvice { async afterFinally(ctx: AdviceContext) { const ms = Math.floor((performance.now() - this.start) * 1000) / 1000; - this.logger.info('[%s] [%s:%s|%s]', - ms, ctx.that.constructor.name, ctx.method, this.succeed ? 'T' : 'F'); + this.logger.info( + '[%s] [%s:%s|%s]', + ms, + ctx.that.constructor.name, + ctx.method, + this.succeed ? 'T' : 'F' + ); } } diff --git a/app/common/typing.ts b/app/common/typing.ts index 76627c61..234c2ab6 100644 --- a/app/common/typing.ts +++ b/app/common/typing.ts @@ -1,8 +1,8 @@ -import { Readable } from 'node:stream'; -import { IncomingHttpHeaders } from 'node:http'; -import { EggContext } from '@eggjs/tegg'; -import { estypes } from '@elastic/elasticsearch'; -import { CnpmcoreConfig } from '../port/config.js'; +import type { Readable } from 'node:stream'; +import type { IncomingHttpHeaders } from 'node:http'; +import type { EggContext } from '@eggjs/tegg'; +import type { estypes } from '@elastic/elasticsearch'; +import type { CnpmcoreConfig } from '../port/config.js'; export interface UploadResult { key: string; @@ -19,8 +19,8 @@ export interface UploadOptions { export interface AppendOptions { key: string; - position?: string, - headers?: IncomingHttpHeaders, + position?: string; + headers?: IncomingHttpHeaders; } export interface DownloadOptions { @@ -40,7 +40,11 @@ export interface NFSClient { createDownloadStream(key: string): Promise; - download(key: string, filepath: string, options: DownloadOptions): Promise; + download( + key: string, + filepath: string, + options: DownloadOptions + ): Promise; url?(key: string): string; } diff --git a/app/core/entity/Binary.ts b/app/core/entity/Binary.ts index f59b1933..316926c9 100644 --- a/app/core/entity/Binary.ts +++ b/app/core/entity/Binary.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface BinaryData extends EntityData { binaryId: string; diff --git a/app/core/entity/Change.ts b/app/core/entity/Change.ts index fb97bc62..4de1aa1d 100644 --- a/app/core/entity/Change.ts +++ b/app/core/entity/Change.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface ChangeData extends EntityData { changeId: string; diff --git a/app/core/entity/Dist.ts b/app/core/entity/Dist.ts index 1cd18acb..ea362737 100644 --- a/app/core/entity/Dist.ts +++ b/app/core/entity/Dist.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface DistData extends EntityData { distId: string; diff --git a/app/core/entity/Hook.ts b/app/core/entity/Hook.ts index 1f0e2f95..fdeb3592 100644 --- a/app/core/entity/Hook.ts +++ b/app/core/entity/Hook.ts @@ -1,9 +1,14 @@ import crypto from 'node:crypto'; -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; -import { HookType } from '../../common/enum/Hook.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; +import type { HookType } from '../../common/enum/Hook.js'; -export type CreateHookData = Omit, 'enable' | 'latestTaskId'>; +export type CreateHookData = Omit< + EasyData, + 'enable' | 'latestTaskId' +>; export interface HookData extends EntityData { hookId: string; @@ -50,7 +55,8 @@ export class Hook extends Entity { // payload 可能会特别大,如果做多次 stringify 浪费太多 cpu signPayload(payload: object) { const payloadStr = JSON.stringify(payload); - const digest = crypto.createHmac('sha256', this.secret) + const digest = crypto + .createHmac('sha256', this.secret) .update(JSON.stringify(payload)) .digest('hex'); return { diff --git a/app/core/entity/Package.ts b/app/core/entity/Package.ts index e5175750..a2c58d69 100644 --- a/app/core/entity/Package.ts +++ b/app/core/entity/Package.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; import { Dist } from './Dist.js'; import { getFullname } from '../../common/PackageUtil.js'; @@ -23,7 +25,10 @@ export enum DIST_NAMES { } export function isPkgManifest(fileType: DIST_NAMES) { - return fileType === DIST_NAMES.FULL_MANIFESTS || fileType === DIST_NAMES.ABBREVIATED_MANIFESTS; + return ( + fileType === DIST_NAMES.FULL_MANIFESTS || + fileType === DIST_NAMES.ABBREVIATED_MANIFESTS + ); } interface FileInfo { diff --git a/app/core/entity/PackageTag.ts b/app/core/entity/PackageTag.ts index b5850876..df17424a 100644 --- a/app/core/entity/PackageTag.ts +++ b/app/core/entity/PackageTag.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface PackageTagData extends EntityData { packageId: string; diff --git a/app/core/entity/PackageVersion.ts b/app/core/entity/PackageVersion.ts index 549c2677..de1c37a7 100644 --- a/app/core/entity/PackageVersion.ts +++ b/app/core/entity/PackageVersion.ts @@ -1,6 +1,8 @@ -import { Dist } from './Dist.js'; -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { Dist } from './Dist.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; import { PaddingSemVer } from './PaddingSemVer.js'; interface PackageVersionData extends EntityData { @@ -48,7 +50,9 @@ export class PackageVersion extends Entity { } } - static create(data: EasyData): PackageVersion { + static create( + data: EasyData + ): PackageVersion { const newData = EntityUtil.defaultData(data, 'packageVersionId'); return new PackageVersion(newData); } diff --git a/app/core/entity/PackageVersionBlock.ts b/app/core/entity/PackageVersionBlock.ts index b9fe19b3..911ec843 100644 --- a/app/core/entity/PackageVersionBlock.ts +++ b/app/core/entity/PackageVersionBlock.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface PackageVersionBlockData extends EntityData { packageVersionBlockId: string; @@ -22,7 +24,9 @@ export class PackageVersionBlock extends Entity { this.reason = data.reason; } - static create(data: EasyData): PackageVersionBlock { + static create( + data: EasyData + ): PackageVersionBlock { const newData = EntityUtil.defaultData(data, 'packageVersionBlockId'); return new PackageVersionBlock(newData); } diff --git a/app/core/entity/PackageVersionFile.ts b/app/core/entity/PackageVersionFile.ts index 52977cd5..fd8b47a5 100644 --- a/app/core/entity/PackageVersionFile.ts +++ b/app/core/entity/PackageVersionFile.ts @@ -1,6 +1,8 @@ -import { Dist } from './Dist.js'; -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { Dist } from './Dist.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface PackageVersionFileData extends EntityData { packageVersionFileId: string; @@ -33,10 +35,14 @@ export class PackageVersionFile extends Entity { } get path() { - return this.directory === '/' ? `/${this.name}` : `${this.directory}/${this.name}`; + return this.directory === '/' + ? `/${this.name}` + : `${this.directory}/${this.name}`; } - static create(data: EasyData): PackageVersionFile { + static create( + data: EasyData + ): PackageVersionFile { const newData = EntityUtil.defaultData(data, 'packageVersionFileId'); return new PackageVersionFile(newData); } diff --git a/app/core/entity/PackageVersionManifest.ts b/app/core/entity/PackageVersionManifest.ts index 3c26901d..ea7fbb86 100644 --- a/app/core/entity/PackageVersionManifest.ts +++ b/app/core/entity/PackageVersionManifest.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface PackageVersionManifestData extends EntityData { packageId: string; @@ -22,7 +24,9 @@ export class PackageVersionManifest extends Entity { this.manifest = data.manifest; } - static create(data: EasyData): PackageVersionManifest { + static create( + data: EasyData + ): PackageVersionManifest { const newData = EntityUtil.defaultData(data, 'packageVersionManifestId'); return new PackageVersionManifest(newData); } diff --git a/app/core/entity/PaddingSemVer.ts b/app/core/entity/PaddingSemVer.ts index 6ae0b035..cd569673 100644 --- a/app/core/entity/PaddingSemVer.ts +++ b/app/core/entity/PaddingSemVer.ts @@ -37,7 +37,7 @@ export class PaddingSemVer { static paddingVersion(v: number) { const t = String(v); if (t.length <= 16) { - const padding = new Array(16 - t.length).fill(0) + const padding = Array.from({ length: 16 - t.length }).fill(0) .join(''); return padding + t; } diff --git a/app/core/entity/ProxyCache.ts b/app/core/entity/ProxyCache.ts index 61f6921e..cd47bb97 100644 --- a/app/core/entity/ProxyCache.ts +++ b/app/core/entity/ProxyCache.ts @@ -1,6 +1,8 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData } from '../util/EntityUtil.js'; -import { DIST_NAMES, isPkgManifest } from './Package.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import type { DIST_NAMES } from './Package.js'; +import { isPkgManifest } from './Package.js'; import { PROXY_CACHE_DIR_NAME } from '../../common/constants.js'; interface ProxyCacheData extends EntityData { fullname: string; @@ -8,7 +10,10 @@ interface ProxyCacheData extends EntityData { version?: string; } -export type CreateProxyCacheData = Omit, 'id'| 'filePath'>; +export type CreateProxyCacheData = Omit< + EasyData, + 'id' | 'filePath' +>; export class ProxyCache extends Entity { readonly fullname: string; @@ -37,5 +42,4 @@ export class ProxyCache extends Entity { data.updatedAt = new Date(); return data; } - } diff --git a/app/core/entity/Registry.ts b/app/core/entity/Registry.ts index dbc5f9cf..33204ed4 100644 --- a/app/core/entity/Registry.ts +++ b/app/core/entity/Registry.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; import type { RegistryType } from '../../common/enum/Registry.js'; interface RegistryData extends EntityData { @@ -12,7 +14,10 @@ interface RegistryData extends EntityData { authToken?: string; } -export type CreateRegistryData = Omit, 'id'>; +export type CreateRegistryData = Omit< + EasyData, + 'id' +>; export class Registry extends Entity { name: string; @@ -35,7 +40,10 @@ export class Registry extends Entity { } public static create(data: CreateRegistryData): Registry { - const newData = EntityUtil.defaultData(data, 'registryId'); + const newData = EntityUtil.defaultData( + data, + 'registryId' + ); return new Registry(newData); } } diff --git a/app/core/entity/Scope.ts b/app/core/entity/Scope.ts index bda1a8bd..1e836045 100644 --- a/app/core/entity/Scope.ts +++ b/app/core/entity/Scope.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface ScopeData extends EntityData { name: string; diff --git a/app/core/entity/Task.ts b/app/core/entity/Task.ts index 0aa8888c..c9d77fde 100644 --- a/app/core/entity/Task.ts +++ b/app/core/entity/Task.ts @@ -1,13 +1,16 @@ import os from 'node:os'; import path from 'node:path'; import { InternalServerError } from 'egg-errors'; -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; import { TaskType, TaskState } from '../../common/enum/Task.js'; import { PROXY_CACHE_DIR_NAME } from '../../common/constants.js'; import dayjs from '../../common/dayjs.js'; -import { HookEvent } from './HookEvent.js'; -import { DIST_NAMES, isPkgManifest } from './Package.js'; +import type { HookEvent } from './HookEvent.js'; +import type { DIST_NAMES } from './Package.js'; +import { isPkgManifest } from './Package.js'; export const HOST_NAME = os.hostname(); export const PID = process.pid; @@ -44,9 +47,9 @@ export type SyncPackageTaskOptions = { }; export type UpdateProxyCacheTaskOptions = { - fullname: string, - version?: string, - fileType: DIST_NAMES, + fullname: string; + version?: string; + fileType: DIST_NAMES; }; export interface CreateHookTaskData extends TaskBaseData { @@ -68,18 +71,18 @@ export interface CreateSyncPackageTaskData extends TaskBaseData { } export interface CreateUpdateProxyCacheTaskData extends TaskBaseData { - fullname: string, - version?: string, - fileType: DIST_NAMES, - filePath: string + fullname: string; + version?: string; + fileType: DIST_NAMES; + filePath: string; } export interface ChangesStreamTaskData extends TaskBaseData { since: string; - last_package?: string, - last_package_created?: Date, - task_count?: number, - registryId?: string, + last_package?: string; + last_package_created?: Date; + task_count?: number; + registryId?: string; } export interface TaskUpdateCondition { @@ -133,12 +136,17 @@ export class Task extends Entity { this.data.taskWorker = `${HOST_NAME}:${PID}`; } - private static create(data: EasyData, 'taskId'>): Task { + private static create( + data: EasyData, 'taskId'> + ): Task { const newData = EntityUtil.defaultData(data, 'taskId'); return new Task(newData); } - public static createSyncPackage(fullname: string, options?: SyncPackageTaskOptions): CreateSyncPackageTask { + public static createSyncPackage( + fullname: string, + options?: SyncPackageTaskOptions + ): CreateSyncPackageTask { const data = { type: TaskType.SyncPackage, state: TaskState.Waiting, @@ -161,7 +169,11 @@ export class Task extends Entity { return task; } - public static createChangesStream(targetName: string, registryId = '', since = ''): ChangesStreamTask { + public static createChangesStream( + targetName: string, + registryId = '', + since = '' + ): ChangesStreamTask { const data = { type: TaskType.ChangesStream, state: TaskState.Waiting, @@ -209,7 +221,10 @@ export class Task extends Entity { return task; } - public static createTriggerHookTask(hookEvent: HookEvent, hookId: string): TriggerHookTask { + public static createTriggerHookTask( + hookEvent: HookEvent, + hookId: string + ): TriggerHookTask { const data = { type: TaskType.TriggerHook, state: TaskState.Waiting, @@ -249,12 +264,17 @@ export class Task extends Entity { } public static needMergeWhenWaiting(type: TaskType) { - return [ TaskType.SyncBinary, TaskType.SyncPackage ].includes(type); + return [TaskType.SyncBinary, TaskType.SyncPackage].includes(type); } - public static createUpdateProxyCache(targetName: string, options: UpdateProxyCacheTaskOptions):CreateUpdateProxyCacheTask { + public static createUpdateProxyCache( + targetName: string, + options: UpdateProxyCacheTaskOptions + ): CreateUpdateProxyCacheTask { if (!isPkgManifest(options.fileType)) { - throw new InternalServerError('should not update package version manifest.'); + throw new InternalServerError( + 'should not update package version manifest.' + ); } const filePath = `/${PROXY_CACHE_DIR_NAME}/${options.fullname}/${options.fileType}`; const data = { diff --git a/app/core/entity/Token.ts b/app/core/entity/Token.ts index c42f5ec0..0e9581e9 100644 --- a/app/core/entity/Token.ts +++ b/app/core/entity/Token.ts @@ -1,6 +1,8 @@ import dayjs from 'dayjs'; -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; export enum TokenType { granular = 'granular', @@ -17,7 +19,7 @@ interface BaseTokenData extends EntityData { lastUsedAt?: Date; } -interface ClassicTokenData extends BaseTokenData{ +interface ClassicTokenData extends BaseTokenData { isAutomation?: boolean; } interface GranularTokenData extends BaseTokenData { @@ -31,7 +33,9 @@ interface GranularTokenData extends BaseTokenData { type TokenData = ClassicTokenData | GranularTokenData; -export function isGranularToken(data: TokenData | Token): data is GranularTokenData { +export function isGranularToken( + data: TokenData | Token +): data is GranularTokenData { return data.type === TokenType.granular; } @@ -79,9 +83,10 @@ export class Token extends Entity { static create(data: EasyData): Token { const newData = EntityUtil.defaultData(data, 'tokenId'); if (isGranularToken(newData) && !newData.expiredAt) { - newData.expiredAt = dayjs(newData.createdAt).add(newData.expires, 'days').toDate(); + newData.expiredAt = dayjs(newData.createdAt) + .add(newData.expires, 'days') + .toDate(); } return new Token(newData); } - } diff --git a/app/core/entity/User.ts b/app/core/entity/User.ts index 5837ee7a..c37eb1b8 100644 --- a/app/core/entity/User.ts +++ b/app/core/entity/User.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; import { cleanUserPrefix } from '../../common/PackageUtil.js'; interface UserData extends EntityData { diff --git a/app/core/entity/WebauthnCredential.ts b/app/core/entity/WebauthnCredential.ts index 8e8d684a..b3d5aaf4 100644 --- a/app/core/entity/WebauthnCredential.ts +++ b/app/core/entity/WebauthnCredential.ts @@ -1,5 +1,7 @@ -import { Entity, EntityData } from './Entity.js'; -import { EasyData, EntityUtil } from '../util/EntityUtil.js'; +import type { EntityData } from './Entity.js'; +import { Entity } from './Entity.js'; +import type { EasyData } from '../util/EntityUtil.js'; +import { EntityUtil } from '../util/EntityUtil.js'; interface WebauthnCredentialData extends EntityData { wancId: string; @@ -25,7 +27,9 @@ export class WebauthnCredential extends Entity { this.browserType = data.browserType; } - static create(data: EasyData): WebauthnCredential { + static create( + data: EasyData + ): WebauthnCredential { const newData = EntityUtil.defaultData(data, 'wancId'); return new WebauthnCredential(newData); } diff --git a/app/core/event/BugVersionFixHandler.ts b/app/core/event/BugVersionFixHandler.ts index 5d46b612..e776e596 100644 --- a/app/core/event/BugVersionFixHandler.ts +++ b/app/core/event/BugVersionFixHandler.ts @@ -1,8 +1,8 @@ import { Event, Inject } from '@eggjs/tegg'; -import { EggLogger } from 'egg'; +import type { EggLogger } from 'egg'; import { PACKAGE_VERSION_ADDED } from './index.js'; import { BUG_VERSIONS } from '../../common/constants.js'; -import { BugVersionService } from '../service/BugVersionService.js'; +import type { BugVersionService } from '../service/BugVersionService.js'; @Event(PACKAGE_VERSION_ADDED) export class BugVersionFixHandler { diff --git a/app/core/event/CacheCleaner.ts b/app/core/event/CacheCleaner.ts index cf51c4fd..468712b6 100644 --- a/app/core/event/CacheCleaner.ts +++ b/app/core/event/CacheCleaner.ts @@ -12,7 +12,7 @@ import { PACKAGE_MAINTAINER_REMOVED, PACKAGE_META_CHANGED, } from './index.js'; -import { CacheService } from '../../core/service/CacheService.js'; +import type { CacheService } from '../../core/service/CacheService.js'; class CacheCleanerEvent { @Inject() diff --git a/app/core/event/ChangesStream.ts b/app/core/event/ChangesStream.ts index 58b1448a..0accea1d 100644 --- a/app/core/event/ChangesStream.ts +++ b/app/core/event/ChangesStream.ts @@ -1,5 +1,6 @@ -import { EggAppConfig } from 'egg'; +import type { EggAppConfig } from 'egg'; import { Event, Inject } from '@eggjs/tegg'; +import type { PackageMetaChange } from './index.js'; import { PACKAGE_UNPUBLISHED, PACKAGE_VERSION_ADDED, @@ -9,14 +10,14 @@ import { PACKAGE_TAG_REMOVED, PACKAGE_MAINTAINER_CHANGED, PACKAGE_MAINTAINER_REMOVED, - PACKAGE_META_CHANGED, PackageMetaChange, + PACKAGE_META_CHANGED, } from './index.js'; -import { ChangeRepository } from '../../repository/ChangeRepository.js'; +import type { ChangeRepository } from '../../repository/ChangeRepository.js'; import { Change } from '../entity/Change.js'; import { HookEvent } from '../entity/HookEvent.js'; import { Task } from '../entity/Task.js'; -import { User } from '../entity/User.js'; -import { TaskService } from '../service/TaskService.js'; +import type { User } from '../entity/User.js'; +import type { TaskService } from '../service/TaskService.js'; class ChangesStreamEvent { @Inject() @@ -32,7 +33,11 @@ class ChangesStreamEvent { return this.config.cnpmcore.hookEnable; } - protected async addChange(type: string, fullname: string, data: object): Promise { + protected async addChange( + type: string, + fullname: string, + data: object + ): Promise { const change = Change.create({ type, targetName: fullname, @@ -48,7 +53,9 @@ export class PackageUnpublishedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string) { const change = await this.addChange(PACKAGE_UNPUBLISHED, fullname, {}); if (this.hookEnable) { - const task = Task.createCreateHookTask(HookEvent.createUnpublishEvent(fullname, change.changeId)); + const task = Task.createCreateHookTask( + HookEvent.createUnpublishEvent(fullname, change.changeId) + ); await this.taskService.createTask(task, true); } } @@ -57,9 +64,13 @@ export class PackageUnpublishedChangesStreamEvent extends ChangesStreamEvent { @Event(PACKAGE_VERSION_ADDED) export class PackageVersionAddedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, version: string, tag?: string) { - const change = await this.addChange(PACKAGE_VERSION_ADDED, fullname, { version }); + const change = await this.addChange(PACKAGE_VERSION_ADDED, fullname, { + version, + }); if (this.hookEnable) { - const task = Task.createCreateHookTask(HookEvent.createPublishEvent(fullname, change.changeId, version, tag)); + const task = Task.createCreateHookTask( + HookEvent.createPublishEvent(fullname, change.changeId, version, tag) + ); await this.taskService.createTask(task, true); } } @@ -68,9 +79,13 @@ export class PackageVersionAddedChangesStreamEvent extends ChangesStreamEvent { @Event(PACKAGE_VERSION_REMOVED) export class PackageVersionRemovedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, version: string, tag?: string) { - const change = await this.addChange(PACKAGE_VERSION_REMOVED, fullname, { version }); + const change = await this.addChange(PACKAGE_VERSION_REMOVED, fullname, { + version, + }); if (this.hookEnable) { - const task = Task.createCreateHookTask(HookEvent.createUnpublishEvent(fullname, change.changeId, version, tag)); + const task = Task.createCreateHookTask( + HookEvent.createUnpublishEvent(fullname, change.changeId, version, tag) + ); await this.taskService.createTask(task, true); } } @@ -81,7 +96,9 @@ export class PackageTagAddedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, tag: string) { const change = await this.addChange(PACKAGE_TAG_ADDED, fullname, { tag }); if (this.hookEnable) { - const task = Task.createCreateHookTask(HookEvent.createDistTagEvent(fullname, change.changeId, tag)); + const task = Task.createCreateHookTask( + HookEvent.createDistTagEvent(fullname, change.changeId, tag) + ); await this.taskService.createTask(task, true); } } @@ -92,7 +109,9 @@ export class PackageTagChangedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, tag: string) { const change = await this.addChange(PACKAGE_TAG_CHANGED, fullname, { tag }); if (this.hookEnable) { - const task = Task.createCreateHookTask(HookEvent.createDistTagEvent(fullname, change.changeId, tag)); + const task = Task.createCreateHookTask( + HookEvent.createDistTagEvent(fullname, change.changeId, tag) + ); await this.taskService.createTask(task, true); } } @@ -103,7 +122,9 @@ export class PackageTagRemovedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, tag: string) { const change = await this.addChange(PACKAGE_TAG_REMOVED, fullname, { tag }); if (this.hookEnable) { - const task = Task.createCreateHookTask(HookEvent.createDistTagRmEvent(fullname, change.changeId, tag)); + const task = Task.createCreateHookTask( + HookEvent.createDistTagRmEvent(fullname, change.changeId, tag) + ); await this.taskService.createTask(task, true); } } @@ -112,11 +133,17 @@ export class PackageTagRemovedChangesStreamEvent extends ChangesStreamEvent { @Event(PACKAGE_MAINTAINER_CHANGED) export class PackageMaintainerChangedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, maintainers: User[]) { - const change = await this.addChange(PACKAGE_MAINTAINER_CHANGED, fullname, {}); + const change = await this.addChange( + PACKAGE_MAINTAINER_CHANGED, + fullname, + {} + ); // TODO 应该比较差值,而不是全量推送 if (this.hookEnable) { for (const maintainer of maintainers) { - const task = Task.createCreateHookTask(HookEvent.createOwnerEvent(fullname, change.changeId, maintainer.name)); + const task = Task.createCreateHookTask( + HookEvent.createOwnerEvent(fullname, change.changeId, maintainer.name) + ); await this.taskService.createTask(task, true); } } @@ -126,9 +153,13 @@ export class PackageMaintainerChangedChangesStreamEvent extends ChangesStreamEve @Event(PACKAGE_MAINTAINER_REMOVED) export class PackageMaintainerRemovedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, maintainer: string) { - const change = await this.addChange(PACKAGE_MAINTAINER_REMOVED, fullname, { maintainer }); + const change = await this.addChange(PACKAGE_MAINTAINER_REMOVED, fullname, { + maintainer, + }); if (this.hookEnable) { - const task = Task.createCreateHookTask(HookEvent.createOwnerRmEvent(fullname, change.changeId, maintainer)); + const task = Task.createCreateHookTask( + HookEvent.createOwnerRmEvent(fullname, change.changeId, maintainer) + ); await this.taskService.createTask(task, true); } } @@ -137,11 +168,19 @@ export class PackageMaintainerRemovedChangesStreamEvent extends ChangesStreamEve @Event(PACKAGE_META_CHANGED) export class PackageMetaChangedChangesStreamEvent extends ChangesStreamEvent { async handle(fullname: string, meta: PackageMetaChange) { - const change = await this.addChange(PACKAGE_META_CHANGED, fullname, { ...meta }); + const change = await this.addChange(PACKAGE_META_CHANGED, fullname, { + ...meta, + }); const { deprecateds } = meta; if (this.hookEnable) { for (const deprecated of deprecateds || []) { - const task = Task.createCreateHookTask(HookEvent.createDeprecatedEvent(fullname, change.changeId, deprecated.version)); + const task = Task.createCreateHookTask( + HookEvent.createDeprecatedEvent( + fullname, + change.changeId, + deprecated.version + ) + ); await this.taskService.createTask(task, true); } } diff --git a/app/core/event/StoreManifest.ts b/app/core/event/StoreManifest.ts index a5bea817..bded2a13 100644 --- a/app/core/event/StoreManifest.ts +++ b/app/core/event/StoreManifest.ts @@ -1,12 +1,10 @@ import { Event, Inject } from '@eggjs/tegg'; -import { - EggAppConfig, -} from 'egg'; +import type { EggAppConfig } from 'egg'; import { PACKAGE_VERSION_ADDED } from './index.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; import { PackageVersionManifest as PackageVersionManifestEntity } from '../entity/PackageVersionManifest.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; -import { DistRepository } from '../../repository/DistRepository.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { DistRepository } from '../../repository/DistRepository.js'; class StoreManifestEvent { @Inject() @@ -16,15 +14,25 @@ class StoreManifestEvent { @Inject() private readonly distRepository: DistRepository; - protected async savePackageVersionManifest(fullname: string, version: string) { - if (!this.config.cnpmcore.enableStoreFullPackageVersionManifestsToDatabase) return; + protected async savePackageVersionManifest( + fullname: string, + version: string + ) { + if (!this.config.cnpmcore.enableStoreFullPackageVersionManifestsToDatabase) + return; - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const packageId = await this.packageRepository.findPackageId(scope, name); if (!packageId) return; - const packageVersion = await this.packageRepository.findPackageVersion(packageId, version); + const packageVersion = await this.packageRepository.findPackageVersion( + packageId, + version + ); if (!packageVersion) return; - const manifest = await this.distRepository.findPackageVersionManifest(packageId, version); + const manifest = await this.distRepository.findPackageVersionManifest( + packageId, + version + ); if (!manifest) return; const entity = PackageVersionManifestEntity.create({ packageId, diff --git a/app/core/event/SyncESPackage.ts b/app/core/event/SyncESPackage.ts index 63a888c0..8a726813 100644 --- a/app/core/event/SyncESPackage.ts +++ b/app/core/event/SyncESPackage.ts @@ -1,6 +1,6 @@ // TODO sync event /* eslint-disable @typescript-eslint/no-unused-vars */ -import { EggAppConfig } from 'egg'; +import type { EggAppConfig } from 'egg'; import { Event, Inject } from '@eggjs/tegg'; import { PACKAGE_UNPUBLISHED, @@ -15,7 +15,7 @@ import { PACKAGE_BLOCKED, PACKAGE_UNBLOCKED, } from './index.js'; -import { PackageSearchService } from '../service/PackageSearchService.js'; +import type { PackageSearchService } from '../service/PackageSearchService.js'; class SyncESPackage { @Inject() diff --git a/app/core/event/SyncPackageVersionFile.ts b/app/core/event/SyncPackageVersionFile.ts index ae1a2c02..55dbb3f6 100644 --- a/app/core/event/SyncPackageVersionFile.ts +++ b/app/core/event/SyncPackageVersionFile.ts @@ -1,12 +1,14 @@ import { Event, Inject } from '@eggjs/tegg'; -import { - EggAppConfig, EggLogger, -} from 'egg'; +import type { EggAppConfig, EggLogger } from 'egg'; import { ForbiddenError } from 'egg-errors'; -import { PACKAGE_VERSION_ADDED, PACKAGE_TAG_ADDED, PACKAGE_TAG_CHANGED } from './index.js'; +import { + PACKAGE_VERSION_ADDED, + PACKAGE_TAG_ADDED, + PACKAGE_TAG_CHANGED, +} from './index.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; -import { PackageManagerService } from '../service/PackageManagerService.js'; -import { PackageVersionFileService } from '../service/PackageVersionFileService.js'; +import type { PackageManagerService } from '../service/PackageManagerService.js'; +import type { PackageVersionFileService } from '../service/PackageVersionFileService.js'; class SyncPackageVersionFileEvent { @Inject() @@ -23,17 +25,28 @@ class SyncPackageVersionFileEvent { if (!this.config.cnpmcore.enableUnpkg) return; if (!this.config.cnpmcore.enableSyncUnpkgFiles) return; // ignore sync on unittest - if (this.config.env === 'unittest' && fullname !== '@cnpm/unittest-unpkg-demo') return; - const [ scope, name ] = getScopeAndName(fullname); - const { packageVersion } = await this.packageManagerService.showPackageVersionByVersionOrTag( - scope, name, version); + if ( + this.config.env === 'unittest' && + fullname !== '@cnpm/unittest-unpkg-demo' + ) + return; + const [scope, name] = getScopeAndName(fullname); + const { packageVersion } = + await this.packageManagerService.showPackageVersionByVersionOrTag( + scope, + name, + version + ); if (!packageVersion) return; try { - await this.packageVersionFileService.syncPackageVersionFiles(packageVersion); + await this.packageVersionFileService.syncPackageVersionFiles( + packageVersion + ); } catch (err) { if (err instanceof ForbiddenError) { - this.logger.info('[SyncPackageVersionFileEvent.syncPackageVersionFile] ignore sync files, cause: %s', - err.message, + this.logger.info( + '[SyncPackageVersionFileEvent.syncPackageVersionFile] ignore sync files, cause: %s', + err.message ); return; } @@ -42,9 +55,13 @@ class SyncPackageVersionFileEvent { } protected async syncPackageReadmeToLatestVersion(fullname: string) { - const [ scope, name ] = getScopeAndName(fullname); - const { pkg, packageVersion } = await this.packageManagerService.showPackageVersionByVersionOrTag( - scope, name, 'latest'); + const [scope, name] = getScopeAndName(fullname); + const { pkg, packageVersion } = + await this.packageManagerService.showPackageVersionByVersionOrTag( + scope, + name, + 'latest' + ); if (!pkg || !packageVersion) return; await this.packageVersionFileService.syncPackageReadme(pkg, packageVersion); } diff --git a/app/core/event/index.ts b/app/core/event/index.ts index 61a3f9aa..3db1b7dc 100644 --- a/app/core/event/index.ts +++ b/app/core/event/index.ts @@ -1,5 +1,5 @@ import '@eggjs/tegg'; -import { User } from '../entity/User.js'; +import type { User } from '../entity/User.js'; export const PACKAGE_UNPUBLISHED = 'PACKAGE_UNPUBLISHED'; export const PACKAGE_BLOCKED = 'PACKAGE_BLOCKED'; @@ -22,19 +22,35 @@ export interface PackageMetaChange { deprecateds?: Array; } - declare module '@eggjs/tegg' { interface Events { [PACKAGE_UNPUBLISHED]: (fullname: string) => Promise; [PACKAGE_BLOCKED]: (fullname: string) => Promise; [PACKAGE_UNBLOCKED]: (fullname: string) => Promise; - [PACKAGE_VERSION_ADDED]: (fullname: string, version: string, tag?: string) => Promise; - [PACKAGE_VERSION_REMOVED]: (fullname: string, version: string, tag?: string) => Promise; + [PACKAGE_VERSION_ADDED]: ( + fullname: string, + version: string, + tag?: string + ) => Promise; + [PACKAGE_VERSION_REMOVED]: ( + fullname: string, + version: string, + tag?: string + ) => Promise; [PACKAGE_TAG_ADDED]: (fullname: string, tag: string) => Promise; [PACKAGE_TAG_CHANGED]: (fullname: string, tag: string) => Promise; [PACKAGE_TAG_REMOVED]: (fullname: string, tag: string) => Promise; - [PACKAGE_MAINTAINER_CHANGED]: (fullname: string, maintainers: User[]) => Promise; - [PACKAGE_MAINTAINER_REMOVED]: (fullname: string, maintainer: string) => Promise; - [PACKAGE_META_CHANGED]: (fullname: string, meta: PackageMetaChange) => Promise; + [PACKAGE_MAINTAINER_CHANGED]: ( + fullname: string, + maintainers: User[] + ) => Promise; + [PACKAGE_MAINTAINER_REMOVED]: ( + fullname: string, + maintainer: string + ) => Promise; + [PACKAGE_META_CHANGED]: ( + fullname: string, + meta: PackageMetaChange + ) => Promise; } } diff --git a/app/core/service/BinarySyncerService.ts b/app/core/service/BinarySyncerService.ts index ecfe7273..bd8c8870 100644 --- a/app/core/service/BinarySyncerService.ts +++ b/app/core/service/BinarySyncerService.ts @@ -1,23 +1,19 @@ -import { - AccessLevel, - SingletonProto, - Inject, - EggObjectFactory, -} from '@eggjs/tegg'; -import { - EggHttpClient, -} from 'egg'; +import type { EggObjectFactory } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; +import type { EggHttpClient } from 'egg'; import fs from 'node:fs/promises'; import { sortBy } from 'lodash-es'; -import binaries, { BinaryName, CategoryName } from '../../../config/binaries.js'; -import { BinaryRepository } from '../../repository/BinaryRepository.js'; +import type { BinaryName, CategoryName } from '../../../config/binaries.js'; +import binaries from '../../../config/binaries.js'; +import type { BinaryRepository } from '../../repository/BinaryRepository.js'; import { Task } from '../entity/Task.js'; import { Binary } from '../entity/Binary.js'; -import { TaskService } from './TaskService.js'; -import { NFSAdapter } from '../../common/adapter/NFSAdapter.js'; +import type { TaskService } from './TaskService.js'; +import type { NFSAdapter } from '../../common/adapter/NFSAdapter.js'; import { downloadToTempfile } from '../../common/FileUtil.js'; import { isTimeoutError } from '../../common/ErrorUtil.js'; -import { AbstractBinary, BinaryItem } from '../../common/adapter/binary/AbstractBinary.js'; +import type { BinaryItem } from '../../common/adapter/binary/AbstractBinary.js'; +import { AbstractBinary } from '../../common/adapter/binary/AbstractBinary.js'; import { AbstractService } from '../../common/AbstractService.js'; import { BinaryType } from '../../common/enum/Binary.js'; import { TaskType, TaskState } from '../../common/enum/Task.js'; @@ -44,37 +40,37 @@ export class BinarySyncerService extends AbstractService { // canvas/v2.6.1/canvas-v2.6.1-node-v57-linux-glibc-x64.tar.gz // -> node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz // canvas 历史版本的 targetName 可能是 category 需要兼容 - public async findBinary(targetName: BinaryName | CategoryName, parent: string, name: string) { + public async findBinary( + targetName: BinaryName | CategoryName, + parent: string, + name: string + ) { return await this.binaryRepository.findBinary(targetName, parent, name); } public async listDirBinaries(binary: Binary) { - return await this.binaryRepository.listBinaries(binary.category, `${binary.parent}${binary.name}`); + return await this.binaryRepository.listBinaries( + binary.category, + `${binary.parent}${binary.name}` + ); } public async listRootBinaries(binaryName: BinaryName) { // 通常 binaryName 和 category 是一样的,但是有些特殊的 binaryName 会有多个 category,比如 canvas // 所以查询 canvas 的时候,需要将 binaryName 和 category 的数据都查出来 - const { - category, - } = binaries[binaryName]; - const reqs = [ - this.binaryRepository.listBinaries(binaryName, '/'), - ]; + const { category } = binaries[binaryName]; + const reqs = [this.binaryRepository.listBinaries(binaryName, '/')]; if (category && category !== binaryName) { reqs.push(this.binaryRepository.listBinaries(category, '/')); } - const [ - rootBinary, - categoryBinary, - ] = await Promise.all(reqs); + const [rootBinary, categoryBinary] = await Promise.all(reqs); - const versions = rootBinary.map(b => b.name); + const versions = new Set(rootBinary.map(b => b.name)); categoryBinary?.forEach(b => { const version = b.name; // 只将没有的版本添加进去 - if (!versions.includes(version)) { + if (!versions.has(version)) { rootBinary.push(b); } }); @@ -88,9 +84,16 @@ export class BinarySyncerService extends AbstractService { public async createTask(binaryName: BinaryName, lastData?: any) { try { - return await this.taskService.createTask(Task.createSyncBinary(binaryName, lastData), false); + return await this.taskService.createTask( + Task.createSyncBinary(binaryName, lastData), + false + ); } catch (e) { - this.logger.error('[BinarySyncerService.createTask] binaryName: %s, error: %s', binaryName, e); + this.logger.error( + '[BinarySyncerService.createTask] binaryName: %s, error: %s', + binaryName, + e + ); } } @@ -111,41 +114,72 @@ export class BinarySyncerService extends AbstractService { const binaryAdapter = await this.getBinaryAdapter(binaryName); const logUrl = `${this.config.cnpmcore.registry}/-/binary/${binaryName}/syncs/${task.taskId}/log`; let logs: string[] = []; - logs.push(`[${isoNow()}] 🚧🚧🚧🚧🚧 Start sync binary "${binaryName}" 🚧🚧🚧🚧🚧`); + logs.push( + `[${isoNow()}] 🚧🚧🚧🚧🚧 Start sync binary "${binaryName}" 🚧🚧🚧🚧🚧` + ); if (!binaryAdapter) { task.error = 'unknow binaryName'; - logs.push(`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`); + logs.push( + `[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}` + ); logs.push(`[${isoNow()}] ❌❌❌❌❌ "${binaryName}" ❌❌❌❌❌`); - this.logger.error('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.error( + '[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); return; } await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; - this.logger.info('[BinarySyncerService.executeTask:start] taskId: %s, targetName: %s, log: %s', - task.taskId, task.targetName, logUrl); + this.logger.info( + '[BinarySyncerService.executeTask:start] taskId: %s, targetName: %s, log: %s', + task.taskId, + task.targetName, + logUrl + ); try { - const [ hasDownloadError ] = await this.syncDir(binaryAdapter, task, '/'); + const [hasDownloadError] = await this.syncDir(binaryAdapter, task, '/'); logs.push(`[${isoNow()}] 🟢 log: ${logUrl}`); logs.push(`[${isoNow()}] 🟢🟢🟢🟢🟢 "${binaryName}" 🟢🟢🟢🟢🟢`); - await this.taskService.finishTask(task, TaskState.Success, logs.join('\n')); + await this.taskService.finishTask( + task, + TaskState.Success, + logs.join('\n') + ); // 确保没有下载异常才算 success await binaryAdapter.finishFetch(!hasDownloadError, binaryName); - this.logger.info('[BinarySyncerService.executeTask:success] taskId: %s, targetName: %s, log: %s, hasDownloadError: %s', - task.taskId, task.targetName, logUrl, hasDownloadError); + this.logger.info( + '[BinarySyncerService.executeTask:success] taskId: %s, targetName: %s, log: %s, hasDownloadError: %s', + task.taskId, + task.targetName, + logUrl, + hasDownloadError + ); } catch (err: any) { task.error = `${err.name}: ${err.message}`; - logs.push(`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`); + logs.push( + `[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}` + ); logs.push(`[${isoNow()}] ❌❌❌❌❌ "${binaryName}" ❌❌❌❌❌`); if (isTimeoutError(err)) { - this.logger.warn('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.warn( + '[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); this.logger.warn(err); } else { - this.logger.error('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.error( + '[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); this.logger.error(err); } await binaryAdapter.finishFetch(false, binaryName); @@ -153,7 +187,13 @@ export class BinarySyncerService extends AbstractService { } } - private async syncDir(binaryAdapter: AbstractBinary, task: Task, dir: string, parentIndex = '', latestVersionParent = '/') { + private async syncDir( + binaryAdapter: AbstractBinary, + task: Task, + dir: string, + parentIndex = '', + latestVersionParent = '/' + ) { const binaryName = task.targetName as BinaryName; const result = await binaryAdapter.fetch(dir, binaryName); let hasDownloadError = false; @@ -161,15 +201,30 @@ export class BinarySyncerService extends AbstractService { if (result && result.items.length > 0) { hasItems = true; let logs: string[] = []; - const { newItems, latestVersionDir } = await this.diff(binaryName, dir, result.items, latestVersionParent); - logs.push(`[${isoNow()}][${dir}] 🚧 Syncing diff: ${result.items.length} => ${newItems.length}, Binary class: ${binaryAdapter.constructor.name}`); + const { newItems, latestVersionDir } = await this.diff( + binaryName, + dir, + result.items, + latestVersionParent + ); + logs.push( + `[${isoNow()}][${dir}] 🚧 Syncing diff: ${result.items.length} => ${newItems.length}, Binary class: ${binaryAdapter.constructor.name}` + ); // re-check latest version - for (const [ index, { item, reason }] of newItems.entries()) { + for (const [index, { item, reason }] of newItems.entries()) { if (item.isDir) { - logs.push(`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Start sync dir ${JSON.stringify(item)}, reason: ${reason}`); + logs.push( + `[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Start sync dir ${JSON.stringify(item)}, reason: ${reason}` + ); await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; - const [ hasError, hasSubItems ] = await this.syncDir(binaryAdapter, task, `${dir}${item.name}`, `${parentIndex}${index}.`, latestVersionDir); + const [hasError, hasSubItems] = await this.syncDir( + binaryAdapter, + task, + `${dir}${item.name}`, + `${parentIndex}${index}.`, + latestVersionDir + ); if (hasError) { hasDownloadError = true; } else { @@ -181,34 +236,55 @@ export class BinarySyncerService extends AbstractService { } } else { // download to nfs - logs.push(`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Downloading ${JSON.stringify(item)}, reason: ${reason}`); + logs.push( + `[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Downloading ${JSON.stringify(item)}, reason: ${reason}` + ); // skip exists binary file - const existsBinary = await this.binaryRepository.findBinary(item.category, item.parent, item.name); + const existsBinary = await this.binaryRepository.findBinary( + item.category, + item.parent, + item.name + ); if (existsBinary && existsBinary.date === item.date) { - logs.push(`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] binary file exists, skip download, binaryId: ${existsBinary.binaryId}`); - this.logger.info('[BinarySyncerService.syncDir:skipDownload] binaryId: %s exists, storePath: %s', - existsBinary.binaryId, existsBinary.storePath); + logs.push( + `[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] binary file exists, skip download, binaryId: ${existsBinary.binaryId}` + ); + this.logger.info( + '[BinarySyncerService.syncDir:skipDownload] binaryId: %s exists, storePath: %s', + existsBinary.binaryId, + existsBinary.storePath + ); continue; } await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; let localFile = ''; try { - const { tmpfile, headers, timing } = - await downloadToTempfile( - this.httpclient, this.config.dataDir, item.sourceUrl!, { ignoreDownloadStatuses: item.ignoreDownloadStatuses }); + const { tmpfile, headers, timing } = await downloadToTempfile( + this.httpclient, + this.config.dataDir, + item.sourceUrl!, + { ignoreDownloadStatuses: item.ignoreDownloadStatuses } + ); const log = `[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] HTTP content-length: ${headers['content-length']}, timing: ${JSON.stringify(timing)}, ${item.sourceUrl} => ${tmpfile}`; logs.push(log); - this.logger.info('[BinarySyncerService.syncDir:downloadToTempfile] %s', log); + this.logger.info( + '[BinarySyncerService.syncDir:downloadToTempfile] %s', + log + ); localFile = tmpfile; const binary = await this.saveBinaryItem(item, tmpfile); - logs.push(`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] Synced file success, binaryId: ${binary.binaryId}`); + logs.push( + `[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] Synced file success, binaryId: ${binary.binaryId}` + ); await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; } catch (err: any) { if (err.name === 'DownloadNotFoundError') { this.logger.info('Not found %s, skip it', item.sourceUrl); - logs.push(`[${isoNow()}][${dir}] 🧪️ [${parentIndex}${index}] Download ${item.sourceUrl} not found, skip it`); + logs.push( + `[${isoNow()}][${dir}] 🧪️ [${parentIndex}${index}] Download ${item.sourceUrl} not found, skip it` + ); } else { if (err.name === 'DownloadStatusInvalidError') { this.logger.warn('Download binary %s %s', item.sourceUrl, err); @@ -216,7 +292,9 @@ export class BinarySyncerService extends AbstractService { this.logger.error('Download binary %s %s', item.sourceUrl, err); } hasDownloadError = true; - logs.push(`[${isoNow()}][${dir}] ❌ [${parentIndex}${index}] Download ${item.sourceUrl} error: ${err}`); + logs.push( + `[${isoNow()}][${dir}] ❌ [${parentIndex}${index}] Download ${item.sourceUrl} error: ${err}` + ); } await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; @@ -230,20 +308,29 @@ export class BinarySyncerService extends AbstractService { if (hasDownloadError) { logs.push(`[${isoNow()}][${dir}] ❌ Synced dir fail`); } else { - logs.push(`[${isoNow()}][${dir}] 🟢 Synced dir success, hasItems: ${hasItems}`); + logs.push( + `[${isoNow()}][${dir}] 🟢 Synced dir success, hasItems: ${hasItems}` + ); } await this.taskService.appendTaskLog(task, logs.join('\n')); } - return [ hasDownloadError, hasItems ]; + return [hasDownloadError, hasItems]; } - // see https://github.com/cnpm/cnpmcore/issues/556 // 上游可能正在发布新版本、同步流程中断,导致同步的时候,文件列表不一致 // 如果的当前目录命中 latestVersionParent 父目录,那么就再校验一下当前目录 // 如果 existsItems 为空或者经过修改,那么就不需要 revalidate 了 - private async diff(binaryName: BinaryName, dir: string, fetchItems: BinaryItem[], latestVersionParent = '/') { - const existsItems = await this.binaryRepository.listBinaries(binaryName, dir); + private async diff( + binaryName: BinaryName, + dir: string, + fetchItems: BinaryItem[], + latestVersionParent = '/' + ) { + const existsItems = await this.binaryRepository.listBinaries( + binaryName, + dir + ); const existsMap = new Map(); for (const item of existsItems) { existsMap.set(item.name, item); @@ -276,7 +363,7 @@ export class BinarySyncerService extends AbstractService { existsItem.date = item.date; } else if (dir.endsWith(latestVersionParent)) { if (!latestItem) { - latestItem = sortBy(fetchItems, [ 'date' ]).pop(); + latestItem = sortBy(fetchItems, ['date']).pop(); } const isLatestItem = latestItem?.name === item.name; if (isLatestItem && existsItem.isDir) { @@ -289,7 +376,6 @@ export class BinarySyncerService extends AbstractService { } } - return { newItems: diffItems, latestVersionDir: latestVersionParent, @@ -301,22 +387,35 @@ export class BinarySyncerService extends AbstractService { const stat = await fs.stat(tmpfile); binary.size = stat.size; await this.nfsAdapter.uploadFile(binary.storePath, tmpfile); - this.logger.info('[BinarySyncerService.saveBinaryItem:uploadFile] binaryId: %s, size: %d, %s => %s', - binary.binaryId, stat.size, tmpfile, binary.storePath); + this.logger.info( + '[BinarySyncerService.saveBinaryItem:uploadFile] binaryId: %s, size: %d, %s => %s', + binary.binaryId, + stat.size, + tmpfile, + binary.storePath + ); } await this.binaryRepository.saveBinary(binary); return binary; } - private async getBinaryAdapter(binaryName: BinaryName): Promise { + private async getBinaryAdapter( + binaryName: BinaryName + ): Promise { const config = this.config.cnpmcore; const binaryConfig = binaries[binaryName]; let binaryAdapter: AbstractBinary; if (config.sourceRegistryIsCNpm) { - binaryAdapter = await this.eggObjectFactory.getEggObject(AbstractBinary, BinaryType.Api); + binaryAdapter = await this.eggObjectFactory.getEggObject( + AbstractBinary, + BinaryType.Api + ); } else { - binaryAdapter = await this.eggObjectFactory.getEggObject(AbstractBinary, binaryConfig.type); + binaryAdapter = await this.eggObjectFactory.getEggObject( + AbstractBinary, + binaryConfig.type + ); } await binaryAdapter.initFetch(binaryName); return binaryAdapter; diff --git a/app/core/service/BugVersionService.ts b/app/core/service/BugVersionService.ts index 319ef9e1..f0d22baa 100644 --- a/app/core/service/BugVersionService.ts +++ b/app/core/service/BugVersionService.ts @@ -1,13 +1,16 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; -import { EggLogger } from 'egg'; +import type { EggLogger } from 'egg'; import pMap from 'p-map'; import { BugVersion } from '../entity/BugVersion.js'; -import { PackageJSONType, PackageRepository } from '../../repository/PackageRepository.js'; -import { DistRepository } from '../../repository/DistRepository.js'; +import type { + PackageJSONType, + PackageRepository, +} from '../../repository/PackageRepository.js'; +import type { DistRepository } from '../../repository/DistRepository.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; -import { CacheService } from './CacheService.js'; +import type { CacheService } from './CacheService.js'; import { BUG_VERSIONS, LATEST_TAG } from '../../common/constants.js'; -import { BugVersionStore } from '../../common/adapter/BugVersionStore.js'; +import type { BugVersionStore } from '../../common/adapter/BugVersionStore.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -33,11 +36,18 @@ export class BugVersionService { const pkg = await this.packageRepository.findPackage('', BUG_VERSIONS); if (!pkg) return; /* c8 ignore next 10 */ - const tag = await this.packageRepository.findPackageTag(pkg!.packageId, LATEST_TAG); + const tag = await this.packageRepository.findPackageTag( + pkg!.packageId, + LATEST_TAG + ); if (!tag) return; let bugVersion = this.bugVersionStore.getBugVersion(tag!.version); if (!bugVersion) { - const packageVersionJson = (await this.distRepository.findPackageVersionManifest(pkg!.packageId, tag!.version)) as PackageJSONType; + const packageVersionJson = + (await this.distRepository.findPackageVersionManifest( + pkg!.packageId, + tag!.version + )) as PackageJSONType; if (!packageVersionJson) return; const data = packageVersionJson.config?.['bug-versions']; bugVersion = new BugVersion(data || {}); @@ -48,51 +58,83 @@ export class BugVersionService { async cleanBugVersionPackageCaches(bugVersion: BugVersion) { const fullnames = bugVersion.listAllPackagesHasBugs(); - await pMap(fullnames, async fullname => { - await this.cacheService.removeCache(fullname); - }, { - concurrency: 50, - stopOnError: false, - }); + await pMap( + fullnames, + async fullname => { + await this.cacheService.removeCache(fullname); + }, + { + concurrency: 50, + stopOnError: false, + } + ); } - async fixPackageBugVersions(bugVersion: BugVersion, fullname: string, manifests: Record) { + async fixPackageBugVersions( + bugVersion: BugVersion, + fullname: string, + manifests: Record + ) { // If package all version unpublished(like pinyin-tool), versions is undefined if (!manifests) return; for (const manifest of Object.values(manifests)) { - this.fixPackageBugVersionWithAllVersions(fullname, bugVersion, manifest, manifests); + this.fixPackageBugVersionWithAllVersions( + fullname, + bugVersion, + manifest, + manifests + ); } } - async fixPackageBugVersion(bugVersion: BugVersion, fullname: string, manifest: any) { + async fixPackageBugVersion( + bugVersion: BugVersion, + fullname: string, + manifest: any + ) { const advice = bugVersion.fixVersion(fullname, manifest.version); if (!advice) { return manifest; } - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) { return manifest; } - const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, advice.version); + const packageVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + advice.version + ); if (!packageVersion) { return manifest; } - const fixedManifest = await this.distRepository.findPackageVersionManifest(packageVersion.packageId, advice.version); + const fixedManifest = await this.distRepository.findPackageVersionManifest( + packageVersion.packageId, + advice.version + ); if (!fixedManifest) { return manifest; } return bugVersion.fixManifest(manifest, fixedManifest); } - private fixPackageBugVersionWithAllVersions(fullname: string, bugVersion: BugVersion, manifest: any, manifests: Record) { + private fixPackageBugVersionWithAllVersions( + fullname: string, + bugVersion: BugVersion, + manifest: any, + manifests: Record + ) { const advice = bugVersion.fixVersion(fullname, manifest.version); if (!advice) { return; } const fixedManifest = manifests[advice.version]; if (!fixedManifest) { - this.logger.warn('[BugVersionService] not found pkg for %s@%s manifest', fullname, advice.version); + this.logger.warn( + '[BugVersionService] not found pkg for %s@%s manifest', + fullname, + advice.version + ); return; } const newManifest = bugVersion.fixManifest(manifest, fixedManifest); diff --git a/app/core/service/CacheService.ts b/app/core/service/CacheService.ts index 8a8fc143..b2fcbffb 100644 --- a/app/core/service/CacheService.ts +++ b/app/core/service/CacheService.ts @@ -1,11 +1,7 @@ -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; -import { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; +import type { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; import { AbstractService } from '../../common/AbstractService.js'; -import { ChangesStreamTaskData } from '../entity/Task.js'; +import type { ChangesStreamTaskData } from '../entity/Task.js'; type PackageCacheAttribute = 'etag' | 'manifests'; @@ -57,40 +53,56 @@ export class CacheService extends AbstractService { return await this.cacheAdapter.getBytes(key); } - public async savePackageEtagAndManifests(fullname: string, isFullManifests: boolean, etag: string, manifests: Buffer) { + public async savePackageEtagAndManifests( + fullname: string, + isFullManifests: boolean, + etag: string, + manifests: Buffer + ) { await Promise.all([ - await this.cacheAdapter.set(this.cacheKey(fullname, isFullManifests, 'etag'), etag), - await this.cacheAdapter.setBytes(this.cacheKey(fullname, isFullManifests, 'manifests'), manifests), + this.cacheAdapter.set( + this.cacheKey(fullname, isFullManifests, 'etag'), + etag + ), + this.cacheAdapter.setBytes( + this.cacheKey(fullname, isFullManifests, 'manifests'), + manifests + ), ]); } public async getTotalData() { const value = await this.cacheAdapter.get(TOTAL_DATA_KEY); - const totalData: TotalData = value ? JSON.parse(value) : { - packageCount: 0, - packageVersionCount: 0, - lastPackage: '', - lastPackageVersion: '', - download: { - today: 0, - thisweek: 0, - thismonth: 0, - thisyear: 0, - lastday: 0, - lastweek: 0, - lastmonth: 0, - lastyear: 0, - }, - changesStream: {}, - upstreamRegistries: [], - lastChangeId: 0, - cacheTime: '', - }; + const totalData: TotalData = value + ? JSON.parse(value) + : { + packageCount: 0, + packageVersionCount: 0, + lastPackage: '', + lastPackageVersion: '', + download: { + today: 0, + thisweek: 0, + thismonth: 0, + thisyear: 0, + lastday: 0, + lastweek: 0, + lastmonth: 0, + lastyear: 0, + }, + changesStream: {}, + upstreamRegistries: [], + lastChangeId: 0, + cacheTime: '', + }; return totalData; } public async saveTotalData(totalData: TotalData) { - return await this.cacheAdapter.set(TOTAL_DATA_KEY, JSON.stringify(totalData)); + return await this.cacheAdapter.set( + TOTAL_DATA_KEY, + JSON.stringify(totalData) + ); } public async removeCache(fullname: string) { @@ -102,7 +114,11 @@ export class CacheService extends AbstractService { ]); } - private cacheKey(fullname: string, isFullManifests: boolean, attribute: PackageCacheAttribute) { + private cacheKey( + fullname: string, + isFullManifests: boolean, + attribute: PackageCacheAttribute + ) { return `${fullname}|${isFullManifests ? 'full' : 'abbr'}:${attribute}`; } } diff --git a/app/core/service/ChangesStreamService.ts b/app/core/service/ChangesStreamService.ts index 38a1b2a9..d6ccf0e3 100644 --- a/app/core/service/ChangesStreamService.ts +++ b/app/core/service/ChangesStreamService.ts @@ -1,20 +1,18 @@ import os from 'node:os'; import { setTimeout } from 'node:timers/promises'; -import { - AccessLevel, - SingletonProto, - EggObjectFactory, - Inject, -} from '@eggjs/tegg'; +import type { EggObjectFactory } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { E500 } from 'egg-errors'; -import { PackageSyncerService, RegistryNotMatchError } from './PackageSyncerService.js'; -import { TaskService } from './TaskService.js'; -import { RegistryManagerService } from './RegistryManagerService.js'; -import { ScopeManagerService } from './ScopeManagerService.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; -import { TaskRepository } from '../../repository/TaskRepository.js'; -import { HOST_NAME, ChangesStreamTask, Task } from '../entity/Task.js'; -import { Registry } from '../entity/Registry.js'; +import type { PackageSyncerService } from './PackageSyncerService.js'; +import { RegistryNotMatchError } from './PackageSyncerService.js'; +import type { TaskService } from './TaskService.js'; +import type { RegistryManagerService } from './RegistryManagerService.js'; +import type { ScopeManagerService } from './ScopeManagerService.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { TaskRepository } from '../../repository/TaskRepository.js'; +import type { ChangesStreamTask } from '../entity/Task.js'; +import { HOST_NAME, Task } from '../entity/Task.js'; +import type { Registry } from '../entity/Registry.js'; import { AbstractChangeStream } from '../../common/adapter/changesStream/AbstractChangesStream.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; import { isTimeoutError } from '../../common/ErrorUtil.js'; @@ -33,9 +31,9 @@ export class ChangesStreamService extends AbstractService { @Inject() private readonly taskService: TaskService; @Inject() - private readonly registryManagerService : RegistryManagerService; + private readonly registryManagerService: RegistryManagerService; @Inject() - private readonly scopeManagerService : ScopeManagerService; + private readonly scopeManagerService: ScopeManagerService; @Inject() private readonly eggObjectFactory: EggObjectFactory; @Inject() @@ -46,14 +44,22 @@ export class ChangesStreamService extends AbstractService { // `{registryName}_WORKER`: 自定义 scope 的同步源 public async findExecuteTask(): Promise { const targetName = GLOBAL_WORKER; - const globalRegistryTask = await this.taskRepository.findTaskByTargetName(targetName, TaskType.ChangesStream); + const globalRegistryTask = await this.taskRepository.findTaskByTargetName( + targetName, + TaskType.ChangesStream + ); // 如果没有配置默认同步源,先进行初始化 if (!globalRegistryTask) { - await this.taskService.createTask(Task.createChangesStream(targetName), false); + await this.taskService.createTask( + Task.createChangesStream(targetName), + false + ); } // 自定义 scope 由 admin 手动创建 // 根据 TaskType.ChangesStream 从队列中获取 - return await this.taskService.findExecuteTask(TaskType.ChangesStream) as ChangesStreamTask; + return (await this.taskService.findExecuteTask( + TaskType.ChangesStream + )) as ChangesStreamTask; } public async suspendSync(exit = false) { @@ -65,10 +71,16 @@ export class ChangesStreamService extends AbstractService { } const authorIp = os.hostname(); // 暂停当前机器所有的 changesStream 任务 - const tasks = await this.taskRepository.findTaskByAuthorIpAndType(authorIp, TaskType.ChangesStream); + const tasks = await this.taskRepository.findTaskByAuthorIpAndType( + authorIp, + TaskType.ChangesStream + ); for (const task of tasks) { if (task.state === TaskState.Processing) { - this.logger.info('[ChangesStreamService.suspendSync:suspend] taskId: %s', task.taskId); + this.logger.info( + '[ChangesStreamService.suspendSync:suspend] taskId: %s', + task.taskId + ); // 1. 更新任务状态为 waiting // 2. 重新推入任务队列供其他机器执行 await this.taskService.retryTask(task); @@ -93,8 +105,14 @@ export class ChangesStreamService extends AbstractService { // allow disable changesStream dynamic while (since && this.config.cnpmcore.enableChangesStream) { const { lastSince, taskCount } = await this.executeSync(since, task); - this.logger.info('[ChangesStreamService.executeTask:changes] since: %s => %s, %d new tasks, taskId: %s, updatedAt: %j', - since, lastSince, taskCount, task.taskId, task.updatedAt); + this.logger.info( + '[ChangesStreamService.executeTask:changes] since: %s => %s, %d new tasks, taskId: %s, updatedAt: %j', + since, + lastSince, + taskCount, + task.taskId, + task.updatedAt + ); since = lastSince; if (taskCount === 0 && this.config.env === 'unittest') { break; @@ -102,7 +120,10 @@ export class ChangesStreamService extends AbstractService { await setTimeout(this.config.cnpmcore.checkChangesStreamInterval); } } catch (err) { - this.logger.warn('[ChangesStreamService.executeTask:error] %s, exit now', err.message); + this.logger.warn( + '[ChangesStreamService.executeTask:error] %s, exit now', + err.message + ); if (isTimeoutError(err)) { this.logger.warn(err); } else { @@ -119,9 +140,13 @@ export class ChangesStreamService extends AbstractService { const { registryId } = task.data || {}; // 如果已有 registryId, 查询 DB 直接获取 if (registryId) { - const registry = await this.registryManagerService.findByRegistryId(registryId); + const registry = + await this.registryManagerService.findByRegistryId(registryId); if (!registry) { - this.logger.error('[ChangesStreamService.getRegistry:error] registryId %s not found', registryId); + this.logger.error( + '[ChangesStreamService.getRegistry:error] registryId %s not found', + registryId + ); throw new E500(`invalid change stream registry: ${registryId}`); } return registry; @@ -129,7 +154,7 @@ export class ChangesStreamService extends AbstractService { const registry = await this.registryManagerService.ensureDefaultRegistry(); task.data = { - ...(task.data || {}), + ...task.data, registryId: registry.registryId, }; await this.taskRepository.saveTask(task); @@ -141,9 +166,15 @@ export class ChangesStreamService extends AbstractService { // 1. 如果该包已经指定了 registryId 则以 registryId 为准 // 1. 该包的 scope 在当前 registry 下 // 2. 如果 registry 下没有配置 scope (认为是通用 registry 地址) ,且该包的 scope 不在其他 registry 下 - public async needSync(registry: Registry, fullname: string): Promise { - const [ scopeName, name ] = getScopeAndName(fullname); - const packageEntity = await this.packageRepository.findPackage(scopeName, name); + public async needSync( + registry: Registry, + fullname: string + ): Promise { + const [scopeName, name] = getScopeAndName(fullname); + const packageEntity = await this.packageRepository.findPackage( + scopeName, + name + ); // 如果包不存在,且处在 exist 模式下,则不同步 if (this.config.cnpmcore.syncMode === 'exist' && !packageEntity) { @@ -155,18 +186,24 @@ export class ChangesStreamService extends AbstractService { } const scope = await this.scopeManagerService.findByName(scopeName); - const inCurrentRegistry = scope && scope?.registryId === registry.registryId; + const inCurrentRegistry = + scope && scope?.registryId === registry.registryId; if (inCurrentRegistry) { return true; } - const registryScopeCount = await this.scopeManagerService.countByRegistryId(registry.registryId); + const registryScopeCount = await this.scopeManagerService.countByRegistryId( + registry.registryId + ); // 当前包没有 scope 信息,且当前 registry 下没有 scope,是通用 registry,需要同步 return !scope && !registryScopeCount; } public async getInitialSince(task: ChangesStreamTask): Promise { const registry = await this.prepareRegistry(task); - const changesStreamAdapter = await this.eggObjectFactory.getEggObject(AbstractChangeStream, registry.type) as AbstractChangeStream; + const changesStreamAdapter = (await this.eggObjectFactory.getEggObject( + AbstractChangeStream, + registry.type + )) as AbstractChangeStream; const since = await changesStreamAdapter.getInitialSince(registry); return since; } @@ -175,7 +212,10 @@ export class ChangesStreamService extends AbstractService { // 更新任务的 since 和 taskCount 相关字段 public async executeSync(since: string, task: ChangesStreamTask) { const registry = await this.prepareRegistry(task); - const changesStreamAdapter = await this.eggObjectFactory.getEggObject(AbstractChangeStream, registry.type) as AbstractChangeStream; + const changesStreamAdapter = (await this.eggObjectFactory.getEggObject( + AbstractChangeStream, + registry.type + )) as AbstractChangeStream; let taskCount = 0; let lastSince = since; @@ -201,17 +241,29 @@ export class ChangesStreamService extends AbstractService { skipDependencies: true, tips, }); - this.logger.info('[ChangesStreamService.createTask:success] fullname: %s, task: %s, tips: %s', - fullname, task.id, tips); + this.logger.info( + '[ChangesStreamService.createTask:success] fullname: %s, task: %s, tips: %s', + fullname, + task.id, + tips + ); } catch (err) { if (err instanceof RegistryNotMatchError) { - this.logger.warn('[ChangesStreamService.executeSync:skip] fullname: %s, error: %s, tips: %s', - fullname, err, tips); + this.logger.warn( + '[ChangesStreamService.executeSync:skip] fullname: %s, error: %s, tips: %s', + fullname, + err, + tips + ); continue; } // only log error, make sure changes still reading - this.logger.error('[ChangesStreamService.executeSync:error] fullname: %s, error: %s, tips: %s', - fullname, err, tips); + this.logger.error( + '[ChangesStreamService.executeSync:error] fullname: %s, error: %s, tips: %s', + fullname, + err, + tips + ); this.logger.error(err); continue; } diff --git a/app/core/service/CreateHookTriggerService.ts b/app/core/service/CreateHookTriggerService.ts index c96379be..38f846e7 100644 --- a/app/core/service/CreateHookTriggerService.ts +++ b/app/core/service/CreateHookTriggerService.ts @@ -3,12 +3,13 @@ import pMap from 'p-map'; import { AbstractService } from '../../common/AbstractService.js'; import { HookType } from '../../common/enum/Hook.js'; import { TaskState } from '../../common/enum/Task.js'; -import { HookEvent } from '../entity/HookEvent.js'; -import { CreateHookTask, Task } from '../entity/Task.js'; -import { HookRepository } from '../../repository/HookRepository.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; -import { Hook } from '../entity/Hook.js'; -import { TaskService } from './TaskService.js'; +import type { HookEvent } from '../entity/HookEvent.js'; +import type { CreateHookTask } from '../entity/Task.js'; +import { Task } from '../entity/Task.js'; +import type { HookRepository } from '../../repository/HookRepository.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { Hook } from '../entity/Hook.js'; +import type { TaskService } from './TaskService.js'; import { isoNow } from '../../common/LogUtil.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; @@ -27,10 +28,14 @@ export class CreateHookTriggerService extends AbstractService { async executeTask(task: CreateHookTask): Promise { const { hookEvent } = task.data; - const [ scope, name ] = getScopeAndName(hookEvent.fullname); + const [scope, name] = getScopeAndName(hookEvent.fullname); const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) { - await this.taskService.finishTask(task, TaskState.Success, `[${isoNow()}][Hooks] package ${hookEvent.fullname} not exits`); + await this.taskService.finishTask( + task, + TaskState.Success, + `[${isoNow()}][Hooks] package ${hookEvent.fullname} not exits` + ); return; } @@ -38,41 +43,97 @@ export class CreateHookTriggerService extends AbstractService { `[${isoNow()}][Hooks] Start Create Trigger for ${pkg.fullname} ${task.data.hookEvent.changeId}`, `[${isoNow()}][Hooks] change content ${JSON.stringify(task.data.hookEvent.change)}`, ]; - await this.taskService.finishTask(task, TaskState.Processing, startLog.join('\n')); + await this.taskService.finishTask( + task, + TaskState.Processing, + startLog.join('\n') + ); try { - await this.taskService.appendTaskLog(task, `[${isoNow()}][Hooks] PushHooks to ${HookType.Package} ${pkg.fullname}\n`); - await this.createTriggerByMethod(task, HookType.Package, pkg.fullname, hookEvent); - await this.taskService.appendTaskLog(task, `[${isoNow()}][Hooks] PushHooks to ${HookType.Scope} ${pkg.scope}\n`); - await this.createTriggerByMethod(task, HookType.Scope, pkg.scope, hookEvent); + await this.taskService.appendTaskLog( + task, + `[${isoNow()}][Hooks] PushHooks to ${HookType.Package} ${pkg.fullname}\n` + ); + await this.createTriggerByMethod( + task, + HookType.Package, + pkg.fullname, + hookEvent + ); + await this.taskService.appendTaskLog( + task, + `[${isoNow()}][Hooks] PushHooks to ${HookType.Scope} ${pkg.scope}\n` + ); + await this.createTriggerByMethod( + task, + HookType.Scope, + pkg.scope, + hookEvent + ); - const maintainers = await this.packageRepository.listPackageMaintainers(pkg.packageId); + const maintainers = await this.packageRepository.listPackageMaintainers( + pkg.packageId + ); for (const maintainer of maintainers) { - await this.taskService.appendTaskLog(task, `[${isoNow()}][Hooks] PushHooks to ${HookType.Owner} ${maintainer.name}\n`); - await this.createTriggerByMethod(task, HookType.Owner, maintainer.name, hookEvent); + await this.taskService.appendTaskLog( + task, + `[${isoNow()}][Hooks] PushHooks to ${HookType.Owner} ${maintainer.name}\n` + ); + await this.createTriggerByMethod( + task, + HookType.Owner, + maintainer.name, + hookEvent + ); } - await this.taskService.finishTask(task, TaskState.Success, `[${isoNow()}][Hooks] create trigger succeed \n`); + await this.taskService.finishTask( + task, + TaskState.Success, + `[${isoNow()}][Hooks] create trigger succeed \n` + ); } catch (e) { e.message = 'create trigger failed: ' + e.message; - await this.taskService.finishTask(task, TaskState.Fail, `[${isoNow()}][Hooks] ${e.stack} \n`); + await this.taskService.finishTask( + task, + TaskState.Fail, + `[${isoNow()}][Hooks] ${e.stack} \n` + ); return; } } - private async createTriggerByMethod(task: Task, type: HookType, name: string, hookEvent: HookEvent) { + private async createTriggerByMethod( + task: Task, + type: HookType, + name: string, + hookEvent: HookEvent + ) { let hooks = await this.hookRepository.listHooksByTypeAndName(type, name); while (hooks.length) { await this.createTriggerTasks(hooks, hookEvent); - hooks = await this.hookRepository.listHooksByTypeAndName(type, name, hooks[hooks.length - 1].id); - await this.taskService.appendTaskLog(task, - `[${isoNow()}][Hooks] PushHooks to ${type} ${name} ${hooks.length} \n`); + hooks = await this.hookRepository.listHooksByTypeAndName( + type, + name, + hooks[hooks.length - 1].id + ); + await this.taskService.appendTaskLog( + task, + `[${isoNow()}][Hooks] PushHooks to ${type} ${name} ${hooks.length} \n` + ); } } private async createTriggerTasks(hooks: Array, hookEvent: HookEvent) { - await pMap(hooks, async hook => { - const triggerHookTask = Task.createTriggerHookTask(hookEvent, hook.hookId); - await this.taskService.createTask(triggerHookTask, true); - }, { concurrency: 5 }); + await pMap( + hooks, + async hook => { + const triggerHookTask = Task.createTriggerHookTask( + hookEvent, + hook.hookId + ); + await this.taskService.createTask(triggerHookTask, true); + }, + { concurrency: 5 } + ); } } diff --git a/app/core/service/EventCorkerAdvice.ts b/app/core/service/EventCorkerAdvice.ts index e6098af5..8b02a8b7 100644 --- a/app/core/service/EventCorkerAdvice.ts +++ b/app/core/service/EventCorkerAdvice.ts @@ -1,5 +1,7 @@ -import { ContextEventBus, Inject } from '@eggjs/tegg'; -import { Advice, IAdvice } from '@eggjs/tegg/aop'; +import type { ContextEventBus } from '@eggjs/tegg'; +import { Inject } from '@eggjs/tegg'; +import type { IAdvice } from '@eggjs/tegg/aop'; +import { Advice } from '@eggjs/tegg/aop'; @Advice() export class EventCorkAdvice implements IAdvice { diff --git a/app/core/service/FixNoPaddingVersionService.ts b/app/core/service/FixNoPaddingVersionService.ts index 4add282d..380fd4e1 100644 --- a/app/core/service/FixNoPaddingVersionService.ts +++ b/app/core/service/FixNoPaddingVersionService.ts @@ -1,7 +1,7 @@ import { SingletonProto, AccessLevel, Inject } from '@eggjs/tegg'; -import { EggLogger } from 'egg'; +import type { EggLogger } from 'egg'; import pMap from 'p-map'; -import { PackageVersionRepository } from '../../repository/PackageVersionRepository.js'; +import type { PackageVersionRepository } from '../../repository/PackageVersionRepository.js'; import { PaddingSemVer } from '../entity/PaddingSemVer.js'; @SingletonProto({ @@ -17,17 +17,30 @@ export class FixNoPaddingVersionService { async fixPaddingVersion(id?: number): Promise { // eslint-disable-next-line no-constant-condition while (true) { - const packageVersions = await this.packageVersionRepository.findHaveNotPaddingVersion(id); + const packageVersions = + await this.packageVersionRepository.findHaveNotPaddingVersion(id); if (packageVersions.length === 0) { break; } - id = packageVersions[packageVersions.length - 1].id as unknown as number + 1; - this.logger.info('[FixNoPaddingVersionService] fix padding version ids %j', packageVersions.map(t => t.id)); + id = + (packageVersions[packageVersions.length - 1].id as unknown as number) + + 1; + this.logger.info( + '[FixNoPaddingVersionService] fix padding version ids %j', + packageVersions.map(t => t.id) + ); - await pMap(packageVersions, async packageVersion => { - const paddingSemver = new PaddingSemVer(packageVersion.version); - await this.packageVersionRepository.fixPaddingVersion(packageVersion.packageVersionId, paddingSemver); - }, { concurrency: 30 }); + await pMap( + packageVersions, + async packageVersion => { + const paddingSemver = new PaddingSemVer(packageVersion.version); + await this.packageVersionRepository.fixPaddingVersion( + packageVersion.packageVersionId, + paddingSemver + ); + }, + { concurrency: 30 } + ); } } } diff --git a/app/core/service/HookManageService.ts b/app/core/service/HookManageService.ts index 4d49c0f7..03651f66 100644 --- a/app/core/service/HookManageService.ts +++ b/app/core/service/HookManageService.ts @@ -1,12 +1,9 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; -import { - ForbiddenError, - NotFoundError, -} from 'egg-errors'; -import { EggAppConfig } from 'egg'; -import { HookRepository } from '../../repository/HookRepository.js'; +import { ForbiddenError, NotFoundError } from 'egg-errors'; +import type { EggAppConfig } from 'egg'; +import type { HookRepository } from '../../repository/HookRepository.js'; import { Hook } from '../entity/Hook.js'; -import { HookType } from '../../common/enum/Hook.js'; +import type { HookType } from '../../common/enum/Hook.js'; export interface CreateHookCommand { type: HookType; @@ -59,7 +56,9 @@ export class HookManageService { throw new NotFoundError(`hook ${cmd.hookId} not found`); } if (hook.ownerId !== cmd.operatorId) { - throw new ForbiddenError(`hook ${cmd.hookId} not belong to ${cmd.operatorId}`); + throw new ForbiddenError( + `hook ${cmd.hookId} not belong to ${cmd.operatorId}` + ); } hook.endpoint = cmd.endpoint; hook.secret = cmd.secret; @@ -73,7 +72,9 @@ export class HookManageService { throw new NotFoundError(`hook ${cmd.hookId} not found`); } if (hook.ownerId !== cmd.operatorId) { - throw new ForbiddenError(`hook ${cmd.hookId} not belong to ${cmd.operatorId}`); + throw new ForbiddenError( + `hook ${cmd.hookId} not belong to ${cmd.operatorId}` + ); } await this.hookRepository.removeHook(cmd.hookId); return hook; diff --git a/app/core/service/HookTriggerService.ts b/app/core/service/HookTriggerService.ts index 503f8e40..2fc84654 100644 --- a/app/core/service/HookTriggerService.ts +++ b/app/core/service/HookTriggerService.ts @@ -1,15 +1,15 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; -import { EggContextHttpClient } from 'egg'; -import { TriggerHookTask } from '../entity/Task.js'; -import { HookEvent } from '../entity/HookEvent.js'; -import { HookRepository } from '../../repository/HookRepository.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; -import { DistRepository } from '../../repository/DistRepository.js'; -import { UserRepository } from '../../repository/UserRepository.js'; -import { Hook } from '../entity/Hook.js'; +import type { EggContextHttpClient } from 'egg'; +import type { TriggerHookTask } from '../entity/Task.js'; +import type { HookEvent } from '../entity/HookEvent.js'; +import type { HookRepository } from '../../repository/HookRepository.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { DistRepository } from '../../repository/DistRepository.js'; +import type { UserRepository } from '../../repository/UserRepository.js'; +import type { Hook } from '../entity/Hook.js'; import { isoNow } from '../../common/LogUtil.js'; import { TaskState } from '../../common/enum/Task.js'; -import { TaskService } from './TaskService.js'; +import type { TaskService } from './TaskService.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; @SingletonProto({ @@ -38,24 +38,40 @@ export class HookTriggerService { const { hookId, hookEvent } = task.data; const hook = await this.hookRepository.findHookById(hookId); if (!hook) { - await this.taskService.finishTask(task, TaskState.Success, `[${isoNow()}][TriggerHooks] hook ${hookId} not exits`); + await this.taskService.finishTask( + task, + TaskState.Success, + `[${isoNow()}][TriggerHooks] hook ${hookId} not exits` + ); return; } try { const payload = await this.createTriggerPayload(task, hookEvent, hook); if (!payload) { - await this.taskService.finishTask(task, TaskState.Success, `[${isoNow()}][TriggerHooks] generate payload failed \n`); + await this.taskService.finishTask( + task, + TaskState.Success, + `[${isoNow()}][TriggerHooks] generate payload failed \n` + ); return; } const status = await this.doExecuteTrigger(hook, payload); hook.latestTaskId = task.taskId; task.data.responseStatus = status; await this.hookRepository.saveHook(hook); - await this.taskService.finishTask(task, TaskState.Success, `[${isoNow()}][TriggerHooks] trigger hook succeed ${status} \n`); + await this.taskService.finishTask( + task, + TaskState.Success, + `[${isoNow()}][TriggerHooks] trigger hook succeed ${status} \n` + ); } catch (e) { e.message = 'trigger hook failed: ' + e.message; task.error = e.message; - await this.taskService.finishTask(task, TaskState.Fail, `[${isoNow()}][TriggerHooks] ${e.stack} \n`); + await this.taskService.finishTask( + task, + TaskState.Fail, + `[${isoNow()}][TriggerHooks] ${e.stack} \n` + ); return; } } @@ -82,19 +98,33 @@ export class HookTriggerService { throw new Error(`hook response with ${res.status}`); } - async createTriggerPayload(task: TriggerHookTask, hookEvent: HookEvent, hook: Hook): Promise { - const [ scope, name ] = getScopeAndName(hookEvent.fullname); + async createTriggerPayload( + task: TriggerHookTask, + hookEvent: HookEvent, + hook: Hook + ): Promise { + const [scope, name] = getScopeAndName(hookEvent.fullname); const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) { - await this.taskService.finishTask(task, TaskState.Success, `[${isoNow()}][TriggerHooks] can not found pkg for ${hookEvent.fullname} \n`); + await this.taskService.finishTask( + task, + TaskState.Success, + `[${isoNow()}][TriggerHooks] can not found pkg for ${hookEvent.fullname} \n` + ); return; } const user = await this.userRepository.findUserByUserId(hook.ownerId); if (!user) { - await this.taskService.finishTask(task, TaskState.Success, `[${isoNow()}][TriggerHooks] can not found user for ${hook.ownerId} \n`); + await this.taskService.finishTask( + task, + TaskState.Success, + `[${isoNow()}][TriggerHooks] can not found user for ${hook.ownerId} \n` + ); return; } - const manifest = await this.distRepository.readDistBytesToJSON(pkg!.manifestsDist!); + const manifest = await this.distRepository.readDistBytesToJSON( + pkg!.manifestsDist! + ); return { event: hookEvent.event, name: pkg.fullname, diff --git a/app/core/service/PackageManagerService.ts b/app/core/service/PackageManagerService.ts index 7b25df77..c9a82e1d 100644 --- a/app/core/service/PackageManagerService.ts +++ b/app/core/service/PackageManagerService.ts @@ -1,13 +1,9 @@ import { stat, readFile } from 'node:fs/promises'; import { strict as assert } from 'node:assert'; -import { - AccessLevel, - SingletonProto, - EventBus, - Inject, -} from '@eggjs/tegg'; +import type { EventBus } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { BadRequestError, ForbiddenError, NotFoundError } from 'egg-errors'; -import { RequireAtLeastOne } from 'type-fest'; +import type { RequireAtLeastOne } from 'type-fest'; import npa from 'npm-package-arg'; import semver from 'semver'; import pMap from 'p-map'; @@ -20,23 +16,23 @@ import { hasShrinkWrapInTgz, } from '../../common/PackageUtil.js'; import { AbstractService } from '../../common/AbstractService.js'; -import { +import type { AbbreviatedPackageJSONType, AbbreviatedPackageManifestType, PackageJSONType, PackageManifestType, PackageRepository, } from '../../repository/PackageRepository.js'; -import { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; -import { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; -import { DistRepository } from '../../repository/DistRepository.js'; +import type { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; +import type { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; +import type { DistRepository } from '../../repository/DistRepository.js'; import { isDuplicateKeyError } from '../../repository/util/ErrorUtil.js'; import { Package } from '../entity/Package.js'; import { PackageVersion } from '../entity/PackageVersion.js'; import { PackageVersionBlock } from '../entity/PackageVersionBlock.js'; import { PackageTag } from '../entity/PackageTag.js'; -import { User } from '../entity/User.js'; -import { Dist } from '../entity/Dist.js'; +import type { User } from '../entity/User.js'; +import type { Dist } from '../entity/Dist.js'; import { PACKAGE_UNPUBLISHED, PACKAGE_BLOCKED, @@ -50,11 +46,11 @@ import { PACKAGE_TAG_REMOVED, PACKAGE_META_CHANGED, } from '../event/index.js'; -import { BugVersionService } from './BugVersionService.js'; -import { BugVersion } from '../entity/BugVersion.js'; -import { RegistryManagerService } from './RegistryManagerService.js'; -import { Registry } from '../entity/Registry.js'; -import { PackageVersionService } from './PackageVersionService.js'; +import type { BugVersionService } from './BugVersionService.js'; +import type { BugVersion } from '../entity/BugVersion.js'; +import type { RegistryManagerService } from './RegistryManagerService.js'; +import type { Registry } from '../entity/Registry.js'; +import type { PackageVersionService } from './PackageVersionService.js'; export interface PublishPackageCmd { // maintainer: Maintainer; @@ -67,12 +63,15 @@ export interface PublishPackageCmd { registryId?: string; readme: string; // require content or localFile field - dist: RequireAtLeastOne<{ - // package controller will use content field - content?: Uint8Array; - // sync worker will use localFile field - localFile?: string; - }, 'content' | 'localFile'>; + dist: RequireAtLeastOne< + { + // package controller will use content field + content?: Uint8Array; + // sync worker will use localFile field + localFile?: string; + }, + 'content' | 'localFile' + >; tags?: string[]; isPrivate: boolean; // only use on sync package @@ -142,11 +141,19 @@ export class PackageManagerService extends AbstractService { } await this.packageRepository.savePackage(pkg); // create maintainer - await this.packageRepository.savePackageMaintainer(pkg.packageId, publisher.userId); + await this.packageRepository.savePackageMaintainer( + pkg.packageId, + publisher.userId + ); - let pkgVersion = await this.packageRepository.findPackageVersion(pkg.packageId, cmd.version); + let pkgVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + cmd.version + ); if (pkgVersion) { - throw new ForbiddenError(`Can't modify pre-existing version: ${pkg.fullname}@${pkgVersion.version}`); + throw new ForbiddenError( + `Can't modify pre-existing version: ${pkg.fullname}@${pkgVersion.version}` + ); } // make sure cmd.packageJson.readme is deleted @@ -164,7 +171,9 @@ export class PackageManagerService extends AbstractService { cmd.packageJson.publish_time = publishTime.getTime(); } if (cmd.packageJson._hasShrinkwrap === undefined) { - cmd.packageJson._hasShrinkwrap = await hasShrinkWrapInTgz(cmd.dist.content || cmd.dist.localFile!); + cmd.packageJson._hasShrinkwrap = await hasShrinkWrapInTgz( + cmd.dist.content || cmd.dist.localFile! + ); } // set _npmUser field to cmd.packageJson @@ -181,7 +190,9 @@ export class PackageManagerService extends AbstractService { } // https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object - const hasInstallScript = detectInstallScript(cmd.packageJson) ? true : undefined; + const hasInstallScript = detectInstallScript(cmd.packageJson) + ? true + : undefined; let tarDistIntegrity: any; let tarDistSize = 0; if (cmd.dist.content) { @@ -207,7 +218,12 @@ export class PackageManagerService extends AbstractService { cmd.packageJson.dist = { ...cmd.packageJson.dist, - tarball: formatTarball(this.config.cnpmcore.registry, pkg.scope, pkg.name, cmd.version), + tarball: formatTarball( + this.config.cnpmcore.registry, + pkg.scope, + pkg.name, + cmd.version + ), size: tarDistSize, shasum: tarDistIntegrity.shasum, integrity: tarDistIntegrity.integrity, @@ -243,7 +259,8 @@ export class PackageManagerService extends AbstractService { _source_registry_name: cmd.packageJson._source_registry_name, } as AbbreviatedPackageJSONType); const abbreviatedDistBytes = Buffer.from(abbreviated); - const abbreviatedDistIntegrity = await calculateIntegrity(abbreviatedDistBytes); + const abbreviatedDistIntegrity = + await calculateIntegrity(abbreviatedDistBytes); const readmeDistBytes = Buffer.from(cmd.readme); const readmeDistIntegrity = await calculateIntegrity(readmeDistBytes); const manifestDistBytes = Buffer.from(JSON.stringify(cmd.packageJson)); @@ -271,7 +288,10 @@ export class PackageManagerService extends AbstractService { tarDist, }); await Promise.all([ - this.distRepository.saveDist(pkgVersion.abbreviatedDist, abbreviatedDistBytes), + this.distRepository.saveDist( + pkgVersion.abbreviatedDist, + abbreviatedDistBytes + ), this.distRepository.saveDist(pkgVersion.manifestDist, manifestDistBytes), this.distRepository.saveDist(pkgVersion.readmeDist, readmeDistBytes), ]); @@ -279,27 +299,39 @@ export class PackageManagerService extends AbstractService { await this.packageRepository.createPackageVersion(pkgVersion); } catch (e) { if (isDuplicateKeyError(e)) { - throw new ForbiddenError(`Can't modify pre-existing version: ${pkg.fullname}@${cmd.version}`); + throw new ForbiddenError( + `Can't modify pre-existing version: ${pkg.fullname}@${cmd.version}` + ); } throw e; } if (cmd.skipRefreshPackageManifests !== true) { - await this.refreshPackageChangeVersionsToDists(pkg, [ pkgVersion.version ]); + await this.refreshPackageChangeVersionsToDists(pkg, [pkgVersion.version]); } if (cmd.tags) { for (const tag of cmd.tags) { await this.savePackageTag(pkg, tag, cmd.version, true); - this.eventBus.emit(PACKAGE_VERSION_ADDED, pkg.fullname, pkgVersion.version, tag); + this.eventBus.emit( + PACKAGE_VERSION_ADDED, + pkg.fullname, + pkgVersion.version, + tag + ); } } else { - this.eventBus.emit(PACKAGE_VERSION_ADDED, pkg.fullname, pkgVersion.version, undefined); + this.eventBus.emit( + PACKAGE_VERSION_ADDED, + pkg.fullname, + pkgVersion.version, + undefined + ); } return pkgVersion; } async blockPackageByFullname(name: string, reason: string) { - const [ scope, pkgName ] = getScopeAndName(name); + const [scope, pkgName] = getScopeAndName(name); const pkg = await this.packageRepository.findPackage(scope, pkgName); if (!pkg) { throw new NotFoundError(`Package name(${name}) not found`); @@ -308,7 +340,9 @@ export class PackageManagerService extends AbstractService { } async blockPackage(pkg: Package, reason: string) { - let block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId); + let block = await this.packageVersionBlockRepository.findPackageBlock( + pkg.packageId + ); if (block) { block.reason = reason; } else { @@ -320,24 +354,37 @@ export class PackageManagerService extends AbstractService { } await this.packageVersionBlockRepository.savePackageVersionBlock(block); if (pkg.manifestsDist && pkg.abbreviatedsDist) { - const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist); + const fullManifests = + await this.distRepository.readDistBytesToJSON( + pkg.manifestsDist + ); if (fullManifests) { fullManifests.block = reason; } - const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist); + const abbreviatedManifests = + await this.distRepository.readDistBytesToJSON( + pkg.abbreviatedsDist + ); if (abbreviatedManifests) { abbreviatedManifests.block = reason; } - await this._updatePackageManifestsToDists(pkg, fullManifests || null, abbreviatedManifests || null); + await this._updatePackageManifestsToDists( + pkg, + fullManifests || null, + abbreviatedManifests || null + ); this.eventBus.emit(PACKAGE_BLOCKED, pkg.fullname); - this.logger.info('[packageManagerService.blockPackage:success] packageId: %s, reason: %j', - pkg.packageId, reason); + this.logger.info( + '[packageManagerService.blockPackage:success] packageId: %s, reason: %j', + pkg.packageId, + reason + ); } return block; } async unblockPackageByFullname(name: string) { - const [ scope, pkgName ] = getScopeAndName(name); + const [scope, pkgName] = getScopeAndName(name); const pkg = await this.packageRepository.findPackage(scope, pkgName); if (!pkg) { throw new NotFoundError(`Package name(${name}) not found`); @@ -346,28 +393,49 @@ export class PackageManagerService extends AbstractService { } async unblockPackage(pkg: Package) { - const block = await this.packageVersionBlockRepository.findPackageVersionBlock(pkg.packageId, '*'); + const block = + await this.packageVersionBlockRepository.findPackageVersionBlock( + pkg.packageId, + '*' + ); if (block) { - await this.packageVersionBlockRepository.removePackageVersionBlock(block.packageVersionBlockId); + await this.packageVersionBlockRepository.removePackageVersionBlock( + block.packageVersionBlockId + ); } if (pkg.manifestsDist && pkg.abbreviatedsDist) { - const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist); + const fullManifests = + await this.distRepository.readDistBytesToJSON( + pkg.manifestsDist + ); if (fullManifests) { fullManifests.block = undefined; } - const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist); + const abbreviatedManifests = + await this.distRepository.readDistBytesToJSON( + pkg.abbreviatedsDist + ); if (abbreviatedManifests) { abbreviatedManifests.block = undefined; } - await this._updatePackageManifestsToDists(pkg, fullManifests || null, abbreviatedManifests || null); + await this._updatePackageManifestsToDists( + pkg, + fullManifests || null, + abbreviatedManifests || null + ); this.eventBus.emit(PACKAGE_UNBLOCKED, pkg.fullname); - this.logger.info('[packageManagerService.unblockPackage:success] packageId: %s', - pkg.packageId); + this.logger.info( + '[packageManagerService.unblockPackage:success] packageId: %s', + pkg.packageId + ); } } async replacePackageMaintainersAndDist(pkg: Package, maintainers: User[]) { - await this.packageRepository.replacePackageMaintainers(pkg.packageId, maintainers.map(m => m.userId)); + await this.packageRepository.replacePackageMaintainers( + pkg.packageId, + maintainers.map(m => m.userId) + ); await this.refreshPackageMaintainersToDists(pkg); this.eventBus.emit(PACKAGE_MAINTAINER_CHANGED, pkg.fullname, maintainers); } @@ -375,7 +443,10 @@ export class PackageManagerService extends AbstractService { async savePackageMaintainers(pkg: Package, maintainers: User[]) { let hasNewRecord = false; for (const maintainer of maintainers) { - const newRecord = await this.packageRepository.savePackageMaintainer(pkg.packageId, maintainer.userId); + const newRecord = await this.packageRepository.savePackageMaintainer( + pkg.packageId, + maintainer.userId + ); if (newRecord) { hasNewRecord = true; } @@ -386,34 +457,67 @@ export class PackageManagerService extends AbstractService { } async removePackageMaintainer(pkg: Package, maintainer: User) { - await this.packageRepository.removePackageMaintainer(pkg.packageId, maintainer.userId); - this.eventBus.emit(PACKAGE_MAINTAINER_REMOVED, pkg.fullname, maintainer.name); + await this.packageRepository.removePackageMaintainer( + pkg.packageId, + maintainer.userId + ); + this.eventBus.emit( + PACKAGE_MAINTAINER_REMOVED, + pkg.fullname, + maintainer.name + ); } async refreshPackageMaintainersToDists(pkg: Package) { - await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'maintainers'); + await this._refreshPackageManifestRootAttributeOnlyToDists( + pkg, + 'maintainers' + ); } async refreshPackageDistTagsToDists(pkg: Package) { - await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags'); + await this._refreshPackageManifestRootAttributeOnlyToDists( + pkg, + 'dist-tags' + ); } async listPackageFullManifests(scope: string, name: string, isSync = false) { - return await this._listPackageFullOrAbbreviatedManifests(scope, name, true, isSync); + return await this._listPackageFullOrAbbreviatedManifests( + scope, + name, + true, + isSync + ); } - async listPackageAbbreviatedManifests(scope: string, name: string, isSync = false) { - return await this._listPackageFullOrAbbreviatedManifests(scope, name, false, isSync); + async listPackageAbbreviatedManifests( + scope: string, + name: string, + isSync = false + ) { + return await this._listPackageFullOrAbbreviatedManifests( + scope, + name, + false, + isSync + ); } - async showPackageVersionByVersionOrTag(scope: string, name: string, spec: string): Promise<{ - blockReason?: string, - pkg?: Package, - packageVersion?: PackageVersion | null, + async showPackageVersionByVersionOrTag( + scope: string, + name: string, + spec: string + ): Promise<{ + blockReason?: string; + pkg?: Package; + packageVersion?: PackageVersion | null; }> { const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) return {}; - const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId); + const block = await this.packageVersionBlockRepository.findPackageBlock( + pkg.packageId + ); if (block) { return { blockReason: block.reason, pkg }; } @@ -423,20 +527,36 @@ export class PackageManagerService extends AbstractService { if (!version) { return {}; } - const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version); + const packageVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + version + ); return { packageVersion, pkg }; } - async showPackageVersionManifest(scope: string, name: string, spec: string, isSync = false, isFullManifests = false) { + async showPackageVersionManifest( + scope: string, + name: string, + spec: string, + isSync = false, + isFullManifests = false + ) { const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) return {}; - const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId); + const block = await this.packageVersionBlockRepository.findPackageBlock( + pkg.packageId + ); if (block) { return { blockReason: block.reason, pkg }; } const fullname = getFullname(scope, name); const result = npa(`${fullname}@${spec}`); - const manifest = await this.packageVersionService.readManifest(pkg.packageId, result, isFullManifests, !isSync); + const manifest = await this.packageVersionService.readManifest( + pkg.packageId, + result, + isFullManifests, + !isSync + ); return { manifest, blockReason: null, pkg }; } @@ -446,7 +566,10 @@ export class PackageManagerService extends AbstractService { public plusPackageVersionCounter(fullname: string, version: string) { // set counter + 1, schedule will store them into database - const counters: Record> = PackageManagerService.downloadCounters; + const counters: Record< + string, + Record + > = PackageManagerService.downloadCounters; if (!counters[fullname]) counters[fullname] = {}; counters[fullname][version] = (counters[fullname][version] || 0) + 1; // Total @@ -465,12 +588,18 @@ export class PackageManagerService extends AbstractService { // will be call by schedule/SavePackageVersionDownloadCounter.ts async savePackageVersionCounters() { // { [fullname]: { [version]: number } } - const counters: Record> = PackageManagerService.downloadCounters; + const counters: Record< + string, + Record + > = PackageManagerService.downloadCounters; const fullnames = Object.keys(counters); if (fullnames.length === 0) return; PackageManagerService.downloadCounters = {}; - this.logger.info('[packageManagerService.savePackageVersionCounters:saving] %d fullnames', fullnames.length); + this.logger.info( + '[packageManagerService.savePackageVersionCounters:saving] %d fullnames', + fullnames.length + ); let total = 0; for (const fullname in counters) { @@ -482,36 +611,59 @@ export class PackageManagerService extends AbstractService { packageId = fullname.replace(SCOPE_TOTAL_PREFIX, ''); } else { // find packageId from fullname - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); packageId = await this.packageRepository.findPackageId(scope, name); } if (!packageId) continue; for (const version in versions) { const counter = versions[version]; - await this.packageVersionDownloadRepository.plus(packageId, version, counter); + await this.packageVersionDownloadRepository.plus( + packageId, + version, + counter + ); total += counter; } } - this.logger.info('[packageManagerService.savePackageVersionCounters:saved] %d total', total); + this.logger.info( + '[packageManagerService.savePackageVersionCounters:saved] %d total', + total + ); } - public async saveDeprecatedVersions(pkg: Package, deprecatedList: { version: string; deprecated: string }[]) { + public async saveDeprecatedVersions( + pkg: Package, + deprecatedList: { version: string; deprecated: string }[] + ) { const updateVersions: string[] = []; for (const { version, deprecated } of deprecatedList) { - const pkgVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version); + const pkgVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + version + ); if (!pkgVersion) continue; const message = deprecated === '' ? undefined : deprecated; - await this._mergeManifestDist(pkgVersion.manifestDist, { deprecated: message }); - await this._mergeManifestDist(pkgVersion.abbreviatedDist, { deprecated: message }); + await this._mergeManifestDist(pkgVersion.manifestDist, { + deprecated: message, + }); + await this._mergeManifestDist(pkgVersion.abbreviatedDist, { + deprecated: message, + }); await this.packageRepository.savePackageVersion(pkgVersion); updateVersions.push(version); } await this.refreshPackageChangeVersionsToDists(pkg, updateVersions); - this.eventBus.emit(PACKAGE_META_CHANGED, pkg.fullname, { deprecateds: deprecatedList }); + this.eventBus.emit(PACKAGE_META_CHANGED, pkg.fullname, { + deprecateds: deprecatedList, + }); } - public async savePackageVersionManifest(pkgVersion: PackageVersion, mergeManifest: object, mergeAbbreviated: object) { + public async savePackageVersionManifest( + pkgVersion: PackageVersion, + mergeManifest: object, + mergeAbbreviated: object + ) { await this._mergeManifestDist(pkgVersion.manifestDist, mergeManifest); await this._mergeManifestDist(pkgVersion.abbreviatedDist, mergeAbbreviated); } @@ -519,20 +671,33 @@ export class PackageManagerService extends AbstractService { /** * save package version readme */ - public async savePackageVersionReadme(pkgVersion: PackageVersion, readmeFile: string) { + public async savePackageVersionReadme( + pkgVersion: PackageVersion, + readmeFile: string + ) { await this.distRepository.saveDist(pkgVersion.readmeDist, readmeFile); - this.logger.info('[PackageManagerService.savePackageVersionReadme] save packageVersionId:%s readme:%s to dist:%s', - pkgVersion.packageVersionId, readmeFile, pkgVersion.readmeDist.distId); + this.logger.info( + '[PackageManagerService.savePackageVersionReadme] save packageVersionId:%s readme:%s to dist:%s', + pkgVersion.packageVersionId, + readmeFile, + pkgVersion.readmeDist.distId + ); } public async savePackageReadme(pkg: Package, readmeFile: string) { if (!pkg.manifestsDist) return; - const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist); + const fullManifests = + await this.distRepository.readDistBytesToJSON( + pkg.manifestsDist + ); if (!fullManifests) return; fullManifests.readme = await readFile(readmeFile, 'utf-8'); await this._updatePackageManifestsToDists(pkg, fullManifests, null); - this.logger.info('[PackageManagerService.savePackageReadme] save packageId:%s readme, size: %s', - pkg.packageId, fullManifests.readme.length); + this.logger.info( + '[PackageManagerService.savePackageReadme] save packageId:%s readme, size: %s', + pkg.packageId, + fullManifests.readme.length + ); } private async _removePackageVersionAndDist(pkgVersion: PackageVersion) { @@ -548,10 +713,14 @@ export class PackageManagerService extends AbstractService { } public async unpublishPackage(pkg: Package) { - const pkgVersions = await this.packageRepository.listPackageVersions(pkg.packageId); + const pkgVersions = await this.packageRepository.listPackageVersions( + pkg.packageId + ); // already unpublished if (pkgVersions.length === 0) { - this.logger.info(`[packageManagerService.unpublishPackage:skip] ${pkg.packageId} already unpublished`); + this.logger.info( + `[packageManagerService.unpublishPackage:skip] ${pkg.packageId} already unpublished` + ); return; } for (const pkgVersion of pkgVersions) { @@ -570,25 +739,46 @@ export class PackageManagerService extends AbstractService { // https://github.com/cnpm/cnpmjs.org/blob/ad622d55e384743b48e79bb6aec574a7f354ee9f/controllers/sync_module_worker.js#L828 'dist-tags': {}, }; - await this._mergeManifestDist(pkg.manifestsDist!, undefined, unpublishedInfo); - await this._mergeManifestDist(pkg.abbreviatedsDist!, undefined, unpublishedInfo); + await this._mergeManifestDist( + pkg.manifestsDist!, + undefined, + unpublishedInfo + ); + await this._mergeManifestDist( + pkg.abbreviatedsDist!, + undefined, + unpublishedInfo + ); this.eventBus.emit(PACKAGE_UNPUBLISHED, pkg.fullname); } - public async removePackageVersion(pkg: Package, pkgVersion: PackageVersion, skipRefreshPackageManifests = false) { - const currentVersions = await this.packageRepository.listPackageVersionNames(pkg.packageId); + public async removePackageVersion( + pkg: Package, + pkgVersion: PackageVersion, + skipRefreshPackageManifests = false + ) { + const currentVersions = + await this.packageRepository.listPackageVersionNames(pkg.packageId); // only one version, unpublish the package - if (currentVersions.length === 1 && currentVersions[0] === pkgVersion.version) { + if ( + currentVersions.length === 1 && + currentVersions[0] === pkgVersion.version + ) { await this.unpublishPackage(pkg); return; } // remove version & update tags await this._removePackageVersionAndDist(pkgVersion); - const versions = await this.packageRepository.listPackageVersionNames(pkg.packageId); + const versions = await this.packageRepository.listPackageVersionNames( + pkg.packageId + ); if (versions.length > 0) { let updateTag: string | undefined; // make sure latest tag exists - const latestTag = await this.packageRepository.findPackageTag(pkg.packageId, 'latest'); + const latestTag = await this.packageRepository.findPackageTag( + pkg.packageId, + 'latest' + ); if (latestTag?.version === pkgVersion.version) { // change latest version // https://github.com/npm/libnpmpublish/blob/main/unpublish.js#L62 @@ -599,15 +789,30 @@ export class PackageManagerService extends AbstractService { } } if (skipRefreshPackageManifests !== true) { - await this.refreshPackageChangeVersionsToDists(pkg, undefined, [ pkgVersion.version ]); - this.eventBus.emit(PACKAGE_VERSION_REMOVED, pkg.fullname, pkgVersion.version, updateTag); + await this.refreshPackageChangeVersionsToDists(pkg, undefined, [ + pkgVersion.version, + ]); + this.eventBus.emit( + PACKAGE_VERSION_REMOVED, + pkg.fullname, + pkgVersion.version, + updateTag + ); } return; } } - public async savePackageTag(pkg: Package, tag: string, version: string, skipEvent = false) { - let tagEntity = await this.packageRepository.findPackageTag(pkg.packageId, tag); + public async savePackageTag( + pkg: Package, + tag: string, + version: string, + skipEvent = false + ) { + let tagEntity = await this.packageRepository.findPackageTag( + pkg.packageId, + tag + ); if (!tagEntity) { tagEntity = PackageTag.create({ packageId: pkg.packageId, @@ -615,7 +820,10 @@ export class PackageManagerService extends AbstractService { version, }); await this.packageRepository.savePackageTag(tagEntity); - await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags'); + await this._refreshPackageManifestRootAttributeOnlyToDists( + pkg, + 'dist-tags' + ); if (!skipEvent) { this.eventBus.emit(PACKAGE_TAG_ADDED, pkg.fullname, tagEntity.tag); } @@ -627,7 +835,10 @@ export class PackageManagerService extends AbstractService { } tagEntity.version = version; await this.packageRepository.savePackageTag(tagEntity); - await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags'); + await this._refreshPackageManifestRootAttributeOnlyToDists( + pkg, + 'dist-tags' + ); if (!skipEvent) { this.eventBus.emit(PACKAGE_TAG_CHANGED, pkg.fullname, tagEntity.tag); } @@ -635,20 +846,36 @@ export class PackageManagerService extends AbstractService { } public async removePackageTag(pkg: Package, tag: string) { - const tagEntity = await this.packageRepository.findPackageTag(pkg.packageId, tag); + const tagEntity = await this.packageRepository.findPackageTag( + pkg.packageId, + tag + ); if (!tagEntity) return false; await this.packageRepository.removePackageTag(tagEntity); - await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags'); + await this._refreshPackageManifestRootAttributeOnlyToDists( + pkg, + 'dist-tags' + ); this.eventBus.emit(PACKAGE_TAG_REMOVED, pkg.fullname, tagEntity.tag); return true; } - public async refreshPackageChangeVersionsToDists(pkg: Package, updateVersions?: string[], removeVersions?: string[]) { + public async refreshPackageChangeVersionsToDists( + pkg: Package, + updateVersions?: string[], + removeVersions?: string[] + ) { if (!pkg.manifestsDist?.distId || !pkg.abbreviatedsDist?.distId) { return await this._refreshPackageManifestsToDists(pkg); } - const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist); - const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist); + const fullManifests = + await this.distRepository.readDistBytesToJSON( + pkg.manifestsDist + ); + const abbreviatedManifests = + await this.distRepository.readDistBytesToJSON( + pkg.abbreviatedsDist + ); if (!fullManifests?.versions || !abbreviatedManifests?.versions) { // is unpublished, refresh all again return await this._refreshPackageManifestsToDists(pkg); @@ -656,19 +883,30 @@ export class PackageManagerService extends AbstractService { if (updateVersions) { for (const version of updateVersions) { - const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version); + const packageVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + version + ); if (packageVersion) { - const manifest = await this.distRepository.readDistBytesToJSON(packageVersion.manifestDist); + const manifest = + await this.distRepository.readDistBytesToJSON( + packageVersion.manifestDist + ); if (!manifest) continue; if ('readme' in manifest) { delete manifest.readme; } fullManifests.versions[packageVersion.version] = manifest; - fullManifests.time[packageVersion.version] = packageVersion.publishTime; + fullManifests.time[packageVersion.version] = + packageVersion.publishTime; - const abbreviatedManifest = await this.distRepository.readDistBytesToJSON(packageVersion.abbreviatedDist); + const abbreviatedManifest = + await this.distRepository.readDistBytesToJSON( + packageVersion.abbreviatedDist + ); if (abbreviatedManifest) { - abbreviatedManifests.versions[packageVersion.version] = abbreviatedManifest; + abbreviatedManifests.versions[packageVersion.version] = + abbreviatedManifest; } } } @@ -682,15 +920,25 @@ export class PackageManagerService extends AbstractService { } // update dist-tags - await this._setPackageDistTagsAndLatestInfos(pkg, fullManifests, abbreviatedManifests); + await this._setPackageDistTagsAndLatestInfos( + pkg, + fullManifests, + abbreviatedManifests + ); // store to nfs dist - await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests); + await this._updatePackageManifestsToDists( + pkg, + fullManifests, + abbreviatedManifests + ); } async getSourceRegistry(pkg: Package): Promise { let registry: Registry | null; if (pkg.registryId) { - registry = await this.registryManagerService.findByRegistryId(pkg.registryId); + registry = await this.registryManagerService.findByRegistryId( + pkg.registryId + ); } else { registry = await this.registryManagerService.ensureDefaultRegistry(); } @@ -708,60 +956,103 @@ export class PackageManagerService extends AbstractService { // refresh package full manifests and abbreviated manifests to NFS private async _refreshPackageManifestsToDists(pkg: Package) { - const [ - fullManifests, - abbreviatedManifests, - ] = await Promise.all([ + const [fullManifests, abbreviatedManifests] = await Promise.all([ this._listPackageFullManifests(pkg), this._listPackageAbbreviatedManifests(pkg), ]); - await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests); + await this._updatePackageManifestsToDists( + pkg, + fullManifests, + abbreviatedManifests + ); } // only refresh root attributes only, e.g.: dist-tags, maintainers - private async _refreshPackageManifestRootAttributeOnlyToDists(pkg: Package, refreshAttr: 'dist-tags' | 'maintainers') { + private async _refreshPackageManifestRootAttributeOnlyToDists( + pkg: Package, + refreshAttr: 'dist-tags' | 'maintainers' + ) { if (refreshAttr === 'maintainers') { - const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist!); + const fullManifests = + await this.distRepository.readDistBytesToJSON( + pkg.manifestsDist! + ); const maintainers = await this._listPackageMaintainers(pkg); if (fullManifests) { fullManifests.maintainers = maintainers; await this._updatePackageManifestsToDists(pkg, fullManifests, null); } } else if (refreshAttr === 'dist-tags') { - const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist!); + const fullManifests = + await this.distRepository.readDistBytesToJSON( + pkg.manifestsDist! + ); if (fullManifests) { - const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist!); + const abbreviatedManifests = + await this.distRepository.readDistBytesToJSON( + pkg.abbreviatedsDist! + ); if (abbreviatedManifests) { - await this._setPackageDistTagsAndLatestInfos(pkg, fullManifests, abbreviatedManifests); + await this._setPackageDistTagsAndLatestInfos( + pkg, + fullManifests, + abbreviatedManifests + ); } - await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests || null); + await this._updatePackageManifestsToDists( + pkg, + fullManifests, + abbreviatedManifests || null + ); } } } - private _mergeLatestManifestFields(fullManifests: PackageManifestType, latestManifest: PackageJSONType | null) { + private _mergeLatestManifestFields( + fullManifests: PackageManifestType, + latestManifest: PackageJSONType | null + ) { if (!latestManifest) return; // https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#full-metadata-format const fieldsFromLatestManifest = [ - 'author', 'bugs', 'contributors', 'description', 'homepage', 'keywords', 'license', - 'readmeFilename', 'repository', + 'author', + 'bugs', + 'contributors', + 'description', + 'homepage', + 'keywords', + 'license', + 'readmeFilename', + 'repository', ] as const; // the latest version metas for (const field of fieldsFromLatestManifest) { if (latestManifest[field]) { - (fullManifests as Record)[field] = latestManifest[field]; + (fullManifests as Record)[field] = + latestManifest[field]; } } } - - private async _setPackageDistTagsAndLatestInfos(pkg: Package, fullManifests: PackageManifestType, abbreviatedManifests: AbbreviatedPackageManifestType) { + private async _setPackageDistTagsAndLatestInfos( + pkg: Package, + fullManifests: PackageManifestType, + abbreviatedManifests: AbbreviatedPackageManifestType + ) { const distTags = await this._listPackageDistTags(pkg); if (distTags.latest) { - const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, distTags.latest); + const packageVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + distTags.latest + ); if (packageVersion) { - fullManifests.readme = await this.distRepository.readDistBytesToString(packageVersion.readmeDist); - const latestManifest = await this.distRepository.readDistBytesToJSON(packageVersion.manifestDist); + fullManifests.readme = await this.distRepository.readDistBytesToString( + packageVersion.readmeDist + ); + const latestManifest = + await this.distRepository.readDistBytesToJSON( + packageVersion.manifestDist + ); this._mergeLatestManifestFields(fullManifests, latestManifest || null); } } @@ -769,8 +1060,15 @@ export class PackageManagerService extends AbstractService { abbreviatedManifests['dist-tags'] = distTags; } - private async _mergeManifestDist(manifestDist: Dist, mergeData?: any, replaceData?: any) { - let manifest = await this.distRepository.readDistBytesToJSON(manifestDist); + private async _mergeManifestDist( + manifestDist: Dist, + mergeData?: any, + replaceData?: any + ) { + let manifest = + await this.distRepository.readDistBytesToJSON( + manifestDist + ); if (mergeData && manifest) { Object.assign(manifest, mergeData); } @@ -785,13 +1083,19 @@ export class PackageManagerService extends AbstractService { await this.distRepository.saveDist(manifestDist, manifestBytes); } - private async _updatePackageManifestsToDists(pkg: Package, fullManifests: PackageManifestType | null, abbreviatedManifests: AbbreviatedPackageManifestType | null): Promise { + private async _updatePackageManifestsToDists( + pkg: Package, + fullManifests: PackageManifestType | null, + abbreviatedManifests: AbbreviatedPackageManifestType | null + ): Promise { const modified = new Date(); if (fullManifests) { fullManifests.time.modified = modified; // same to dist const fullManifestsDistBytes = Buffer.from(JSON.stringify(fullManifests)); - const fullManifestsDistIntegrity = await calculateIntegrity(fullManifestsDistBytes); + const fullManifestsDistIntegrity = await calculateIntegrity( + fullManifestsDistBytes + ); if (pkg.manifestsDist?.distId) { pkg.manifestsDist.size = fullManifestsDistBytes.length; pkg.manifestsDist.shasum = fullManifestsDistIntegrity.shasum; @@ -803,17 +1107,25 @@ export class PackageManagerService extends AbstractService { integrity: fullManifestsDistIntegrity.integrity, }); } - await this.distRepository.saveDist(pkg.manifestsDist, fullManifestsDistBytes); + await this.distRepository.saveDist( + pkg.manifestsDist, + fullManifestsDistBytes + ); await this.packageRepository.savePackageDist(pkg, true); } if (abbreviatedManifests) { abbreviatedManifests.modified = modified; - const abbreviatedManifestsDistBytes = Buffer.from(JSON.stringify(abbreviatedManifests)); - const abbreviatedManifestsDistIntegrity = await calculateIntegrity(abbreviatedManifestsDistBytes); + const abbreviatedManifestsDistBytes = Buffer.from( + JSON.stringify(abbreviatedManifests) + ); + const abbreviatedManifestsDistIntegrity = await calculateIntegrity( + abbreviatedManifestsDistBytes + ); if (pkg.abbreviatedsDist?.distId) { pkg.abbreviatedsDist.size = abbreviatedManifestsDistBytes.length; pkg.abbreviatedsDist.shasum = abbreviatedManifestsDistIntegrity.shasum; - pkg.abbreviatedsDist.integrity = abbreviatedManifestsDistIntegrity.integrity; + pkg.abbreviatedsDist.integrity = + abbreviatedManifestsDistIntegrity.integrity; } else { pkg.abbreviatedsDist = pkg.createAbbreviatedManifests({ size: abbreviatedManifestsDistBytes.length, @@ -821,19 +1133,26 @@ export class PackageManagerService extends AbstractService { integrity: abbreviatedManifestsDistIntegrity.integrity, }); } - await this.distRepository.saveDist(pkg.abbreviatedsDist, abbreviatedManifestsDistBytes); + await this.distRepository.saveDist( + pkg.abbreviatedsDist, + abbreviatedManifestsDistBytes + ); await this.packageRepository.savePackageDist(pkg, false); } } - private async _listPackageFullOrAbbreviatedManifests(scope: string, name: string, isFullManifests: boolean, isSync: boolean) { + private async _listPackageFullOrAbbreviatedManifests< + T extends PackageManifestType | AbbreviatedPackageManifestType, + >(scope: string, name: string, isFullManifests: boolean, isSync: boolean) { let etag = ''; let blockReason = ''; const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) return { etag, data: null, blockReason }; const registry = await this.getSourceRegistry(pkg); - const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId); + const block = await this.packageVersionBlockRepository.findPackageBlock( + pkg.packageId + ); if (block) { blockReason = block.reason; } @@ -851,7 +1170,11 @@ export class PackageManagerService extends AbstractService { etag = `"${dist.shasum}"`; const data = (await this.distRepository.readDistBytesToJSON(dist)) as T; if (bugVersion) { - await this.bugVersionService.fixPackageBugVersions(bugVersion, fullname, data.versions); + await this.bugVersionService.fixPackageBugVersions( + bugVersion, + fullname, + data.versions + ); } // set _source_registry_name in full manifestDist if (registry) { @@ -865,17 +1188,29 @@ export class PackageManagerService extends AbstractService { } // read from database - const fullManifests = isFullManifests ? await this._listPackageFullManifests(pkg) : null; - const abbreviatedManifests = isFullManifests ? null : await this._listPackageAbbreviatedManifests(pkg); + const fullManifests = isFullManifests + ? await this._listPackageFullManifests(pkg) + : null; + const abbreviatedManifests = isFullManifests + ? null + : await this._listPackageAbbreviatedManifests(pkg); if (!fullManifests && !abbreviatedManifests) { // not exists return { etag, data: null, blockReason }; } - await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests); + await this._updatePackageManifestsToDists( + pkg, + fullManifests, + abbreviatedManifests + ); const manifests = (fullManifests || abbreviatedManifests)! as T; /* c8 ignore next 5 */ if (bugVersion) { - await this.bugVersionService.fixPackageBugVersions(bugVersion, fullname, (manifests as any).versions); + await this.bugVersionService.fixPackageBugVersions( + bugVersion, + fullname, + (manifests as any).versions + ); const distBytes = Buffer.from(JSON.stringify(manifests)); const distIntegrity = await calculateIntegrity(distBytes); etag = `"${distIntegrity.shasum}"`; @@ -887,13 +1222,22 @@ export class PackageManagerService extends AbstractService { } private async _listPackageMaintainers(pkg: Package) { - const users = await this.packageRepository.listPackageMaintainers(pkg.packageId); - return users.map(({ displayName, email }) => ({ name: displayName, email })); + const users = await this.packageRepository.listPackageMaintainers( + pkg.packageId + ); + return users.map(({ displayName, email }) => ({ + name: displayName, + email, + })); } - private async _listPackageFullManifests(pkg: Package): Promise { + private async _listPackageFullManifests( + pkg: Package + ): Promise { // read all verions from db - const packageVersions = await this.packageRepository.listPackageVersions(pkg.packageId); + const packageVersions = await this.packageRepository.listPackageVersions( + pkg.packageId + ); if (packageVersions.length === 0) return null; const distTags = await this._listPackageDistTags(pkg); @@ -945,7 +1289,10 @@ export class PackageManagerService extends AbstractService { let latestPackageVersion = packageVersions[0]; // https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#package-metadata for (const packageVersion of packageVersions) { - const manifest = await this.distRepository.readDistBytesToJSON(packageVersion.manifestDist); + const manifest = + await this.distRepository.readDistBytesToJSON( + packageVersion.manifestDist + ); if (!manifest) continue; /* c8 ignore next 3 */ if ('readme' in manifest) { @@ -959,7 +1306,9 @@ export class PackageManagerService extends AbstractService { data.time[packageVersion.version] = packageVersion.publishTime; } // the latest version readme - data.readme = await this.distRepository.readDistBytesToString(latestPackageVersion.readmeDist); + data.readme = await this.distRepository.readDistBytesToString( + latestPackageVersion.readmeDist + ); if (!latestManifest) { latestManifest = data.versions[latestPackageVersion.version]; } @@ -967,9 +1316,13 @@ export class PackageManagerService extends AbstractService { return data; } - private async _listPackageAbbreviatedManifests(pkg: Package): Promise { + private async _listPackageAbbreviatedManifests( + pkg: Package + ): Promise { // read all verions from db - const packageVersions = await this.packageRepository.listPackageVersions(pkg.packageId); + const packageVersions = await this.packageRepository.listPackageVersions( + pkg.packageId + ); if (packageVersions.length === 0) return null; const distTags = await this._listPackageDistTags(pkg); @@ -983,7 +1336,10 @@ export class PackageManagerService extends AbstractService { }; for (const packageVersion of packageVersions) { - const manifest = await this.distRepository.readDistBytesToJSON(packageVersion.abbreviatedDist); + const manifest = + await this.distRepository.readDistBytesToJSON( + packageVersion.abbreviatedDist + ); if (manifest) { data.versions[packageVersion.version] = manifest; } @@ -995,20 +1351,26 @@ export class PackageManagerService extends AbstractService { // 只校验 dependencies // devDependencies、optionalDependencies、peerDependencies 不会影响依赖安装 不在这里进行校验 const { dependencies } = pkgJSON; - await pMap(Object.entries(dependencies || {}), async ([ fullname, spec ]) => { - try { - const specResult = npa(`${fullname}@${spec}`); - // 对于 git、alias、file 等类型的依赖,不进行版本校验 - if (![ 'range', 'tag', 'version' ].includes(specResult.type)) { - return; + await pMap( + Object.entries(dependencies || {}), + async ([fullname, spec]) => { + try { + const specResult = npa(`${fullname}@${spec}`); + // 对于 git、alias、file 等类型的依赖,不进行版本校验 + if (!['range', 'tag', 'version'].includes(specResult.type)) { + return; + } + const pkgVersion = await this.packageVersionService.getVersion( + npa(`${fullname}@${spec}`) + ); + assert(pkgVersion); + } catch { + throw new BadRequestError(`deps ${fullname}@${spec} not found`); } - const pkgVersion = await this.packageVersionService.getVersion(npa(`${fullname}@${spec}`)); - assert(pkgVersion); - } catch { - throw new BadRequestError(`deps ${fullname}@${spec} not found`); + }, + { + concurrency: 12, } - }, { - concurrency: 12, - }); + ); } } diff --git a/app/core/service/PackageSearchService.ts b/app/core/service/PackageSearchService.ts index fc389490..0a73f7a4 100644 --- a/app/core/service/PackageSearchService.ts +++ b/app/core/service/PackageSearchService.ts @@ -1,14 +1,18 @@ import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; -import { estypes, errors } from '@elastic/elasticsearch'; +import type { estypes } from '@elastic/elasticsearch'; +import { errors } from '@elastic/elasticsearch'; import dayjs from 'dayjs'; import { AbstractService } from '../../common/AbstractService.js'; import { formatAuthor, getScopeAndName } from '../../common/PackageUtil.js'; -import { PackageManagerService } from './PackageManagerService.js'; -import { SearchManifestType, SearchMappingType, SearchRepository } from '../../repository/SearchRepository.js'; -import { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; -import { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; - +import type { PackageManagerService } from './PackageManagerService.js'; +import type { + SearchManifestType, + SearchMappingType, + SearchRepository, +} from '../../repository/SearchRepository.js'; +import type { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -26,23 +30,39 @@ export class PackageSearchService extends AbstractService { protected packageVersionBlockRepository: PackageVersionBlockRepository; async syncPackage(fullname: string, isSync = true) { - const [ scope, name ] = getScopeAndName(fullname); - const fullManifests = await this.packageManagerService.listPackageFullManifests(scope, name, isSync); + const [scope, name] = getScopeAndName(fullname); + const fullManifests = + await this.packageManagerService.listPackageFullManifests( + scope, + name, + isSync + ); if (!fullManifests.data) { - this.logger.warn('[PackageSearchService.syncPackage] save package:%s not found', fullname); + this.logger.warn( + '[PackageSearchService.syncPackage] save package:%s not found', + fullname + ); return; } const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) { - this.logger.warn('[PackageSearchService.syncPackage] findPackage:%s not found', fullname); + this.logger.warn( + '[PackageSearchService.syncPackage] findPackage:%s not found', + fullname + ); return; } - const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId); + const block = await this.packageVersionBlockRepository.findPackageBlock( + pkg.packageId + ); if (block) { - this.logger.warn('[PackageSearchService.syncPackage] package:%s is blocked, try to remove es', fullname); + this.logger.warn( + '[PackageSearchService.syncPackage] package:%s is blocked, try to remove es', + fullname + ); await this.removePackage(fullname); return; } @@ -51,7 +71,11 @@ export class PackageSearchService extends AbstractService { const startDate = dayjs().subtract(1, 'year'); const endDate = dayjs(); - const entities = await this.packageVersionDownloadRepository.query(pkg.packageId, startDate.toDate(), endDate.toDate()); + const entities = await this.packageVersionDownloadRepository.query( + pkg.packageId, + startDate.toDate(), + endDate.toDate() + ); let downloadsAll = 0; for (const entity of entities) { for (let i = 1; i <= 31; i++) { @@ -76,7 +100,10 @@ export class PackageSearchService extends AbstractService { keywords: manifest.keywords || [], versions: Object.keys(manifest.versions), description: manifest.description, - license: typeof manifest.license === 'object' ? manifest.license?.type : manifest.license, + license: + typeof manifest.license === 'object' + ? manifest.license?.type + : manifest.license, maintainers: manifest.maintainers, author: formatAuthor(manifest.author), 'dist-tags': manifest['dist-tags'], @@ -112,7 +139,11 @@ export class PackageSearchService extends AbstractService { return await this.searchRepository.upsertPackage(document); } - async searchPackage(text: string, from: number, size: number): Promise<{ objects: (SearchManifestType | undefined)[], total: number }> { + async searchPackage( + text: string, + from: number, + size: number + ): Promise<{ objects: (SearchManifestType | undefined)[]; total: number }> { const matchQueries = this._buildMatchQueries(text); const scriptScore = this._buildScriptScore({ text, @@ -143,7 +174,10 @@ export class PackageSearchService extends AbstractService { // 从 https://github.com/npm/cli/pull/7407 (npm cli v10.6.0) 开始,npm cli 使用 publisher 字段(以前使用 maintainers 字段) // 从现有数据来看,_npmUser 字段和 publisher 字段是等价的 // 为了兼容老版本,不删除 _npmUser 字段 - if (!item._source?.package.publisher && item._source?.package._npmUser) { + if ( + !item._source?.package.publisher && + item._source?.package._npmUser + ) { item._source.package.publisher = { username: item._source.package._npmUser.name, email: item._source.package._npmUser.email, @@ -162,7 +196,10 @@ export class PackageSearchService extends AbstractService { } catch (error) { // if the package does not exist, returns success if (error instanceof errors.ResponseError && error?.statusCode === 404) { - this.logger.warn('[PackageSearchService.removePackage] remove package:%s not found', fullname); + this.logger.warn( + '[PackageSearchService.removePackage] remove package:%s not found', + fullname + ); return fullname; } throw error; @@ -241,7 +278,10 @@ export class PackageSearchService extends AbstractService { ]; } - private _buildScriptScore(params: { text: string | undefined, scoreEffect: number }) { + private _buildScriptScore(params: { + text: string | undefined; + scoreEffect: number; + }) { // keep search simple, only download(popularity) const downloads = 'doc["downloads.all"].value'; const source = `doc["package.name.raw"].value.equals(params.text) ? 100000 + ${downloads} : _score * Math.pow(${downloads}, params.scoreEffect)`; diff --git a/app/core/service/PackageSyncerService.ts b/app/core/service/PackageSyncerService.ts index 2f25749a..0fc9bcce 100644 --- a/app/core/service/PackageSyncerService.ts +++ b/app/core/service/PackageSyncerService.ts @@ -1,53 +1,62 @@ import os from 'node:os'; import { setTimeout } from 'node:timers/promises'; import { rm } from 'node:fs/promises'; -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { Pointcut } from '@eggjs/tegg/aop'; -import { EggHttpClient } from 'egg'; +import type { EggHttpClient } from 'egg'; import { isEqual, isEmpty } from 'lodash-es'; import semver from 'semver'; import { BadRequestError } from 'egg-errors'; -import { NPMRegistry, RegistryResponse } from '../../common/adapter/NPMRegistry.js'; -import { detectInstallScript, getScopeAndName } from '../../common/PackageUtil.js'; +import type { + NPMRegistry, + RegistryResponse, +} from '../../common/adapter/NPMRegistry.js'; +import { + detectInstallScript, + getScopeAndName, +} from '../../common/PackageUtil.js'; import { downloadToTempfile } from '../../common/FileUtil.js'; import { TaskState, TaskType } from '../../common/enum/Task.js'; import { AbstractService } from '../../common/AbstractService.js'; -import { TaskRepository } from '../../repository/TaskRepository.js'; -import { PackageJSONType, PackageManifestType, PackageRepository } from '../../repository/PackageRepository.js'; -import { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; -import { UserRepository } from '../../repository/UserRepository.js'; -import { Task, SyncPackageTaskOptions, CreateSyncPackageTask } from '../entity/Task.js'; -import { Package } from '../entity/Package.js'; -import { UserService } from './UserService.js'; -import { TaskService } from './TaskService.js'; -import { PackageManagerService } from './PackageManagerService.js'; -import { CacheService } from './CacheService.js'; -import { User } from '../entity/User.js'; -import { RegistryManagerService } from './RegistryManagerService.js'; -import { Registry } from '../entity/Registry.js'; -import { ScopeManagerService } from './ScopeManagerService.js'; +import type { TaskRepository } from '../../repository/TaskRepository.js'; +import type { + PackageJSONType, + PackageManifestType, + PackageRepository, +} from '../../repository/PackageRepository.js'; +import type { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; +import type { UserRepository } from '../../repository/UserRepository.js'; +import type { + SyncPackageTaskOptions, + CreateSyncPackageTask, +} from '../entity/Task.js'; +import { Task } from '../entity/Task.js'; +import type { Package } from '../entity/Package.js'; +import type { UserService } from './UserService.js'; +import type { TaskService } from './TaskService.js'; +import type { PackageManagerService } from './PackageManagerService.js'; +import type { CacheService } from './CacheService.js'; +import type { User } from '../entity/User.js'; +import type { RegistryManagerService } from './RegistryManagerService.js'; +import type { Registry } from '../entity/Registry.js'; +import type { ScopeManagerService } from './ScopeManagerService.js'; import { EventCorkAdvice } from './EventCorkerAdvice.js'; import { PresetRegistryName, SyncDeleteMode } from '../../common/constants.js'; type syncDeletePkgOptions = { - task: Task, - pkg: Package | null, - logUrl: string, - url: string, - logs: string[], - data: any, + task: Task; + pkg: Package | null; + logUrl: string; + url: string; + logs: string[]; + data: any; }; function isoNow() { return new Date().toISOString(); } -export class RegistryNotMatchError extends BadRequestError { -} +export class RegistryNotMatchError extends BadRequestError {} @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -79,15 +88,20 @@ export class PackageSyncerService extends AbstractService { private readonly scopeManagerService: ScopeManagerService; public async createTask(fullname: string, options?: SyncPackageTaskOptions) { - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const pkg = await this.packageRepository.findPackage(scope, name); // sync task request registry is not same as package registry if (pkg && pkg.registryId && options?.registryId) { if (pkg.registryId !== options.registryId) { - throw new RegistryNotMatchError(`package ${fullname} is not in registry ${options.registryId}`); + throw new RegistryNotMatchError( + `package ${fullname} is not in registry ${options.registryId}` + ); } } - return await this.taskService.createTask(Task.createSyncPackage(fullname, options), true); + return await this.taskService.createTask( + Task.createSyncPackage(fullname, options), + true + ); } public async findTask(taskId: string) { @@ -99,12 +113,18 @@ export class PackageSyncerService extends AbstractService { } public async findExecuteTask() { - return await this.taskService.findExecuteTask(TaskType.SyncPackage) as CreateSyncPackageTask; + return (await this.taskService.findExecuteTask( + TaskType.SyncPackage + )) as CreateSyncPackageTask; } public get allowSyncDownloadData() { const config = this.config.cnpmcore; - if (config.enableSyncDownloadData && config.syncDownloadDataSourceRegistry && config.syncDownloadDataMaxDate) { + if ( + config.enableSyncDownloadData && + config.syncDownloadDataSourceRegistry && + config.syncDownloadDataMaxDate + ) { return true; } return false; @@ -119,19 +139,32 @@ export class PackageSyncerService extends AbstractService { const start = '2011-01-01'; const end = this.config.cnpmcore.syncDownloadDataMaxDate; const registry = this.config.cnpmcore.syncDownloadDataSourceRegistry; - const remoteAuthToken = await this.registryManagerService.getAuthTokenByRegistryHost(registry); + const remoteAuthToken = + await this.registryManagerService.getAuthTokenByRegistryHost(registry); const logs: string[] = []; let downloads: { day: string; downloads: number }[]; - logs.push(`[${isoNow()}][DownloadData] 🚧🚧🚧🚧🚧 Syncing "${fullname}" download data "${start}:${end}" on ${registry} 🚧🚧🚧🚧🚧`); + logs.push( + `[${isoNow()}][DownloadData] 🚧🚧🚧🚧🚧 Syncing "${fullname}" download data "${start}:${end}" on ${registry} 🚧🚧🚧🚧🚧` + ); const failEnd = '❌❌❌❌❌ 🚮 give up 🚮 ❌❌❌❌❌'; try { - const { data, status, res } = await this.npmRegistry.getDownloadRanges(registry, fullname, start, end, { remoteAuthToken }); + const { data, status, res } = await this.npmRegistry.getDownloadRanges( + registry, + fullname, + start, + end, + { remoteAuthToken } + ); downloads = data.downloads || []; - logs.push(`[${isoNow()}][DownloadData] 🚧 HTTP [${status}] timing: ${JSON.stringify(res.timing)}, downloads: ${downloads.length}`); + logs.push( + `[${isoNow()}][DownloadData] 🚧 HTTP [${status}] timing: ${JSON.stringify(res.timing)}, downloads: ${downloads.length}` + ); } catch (err: any) { const status = err.status || 'unknow'; - logs.push(`[${isoNow()}][DownloadData] ❌ Get download data error: ${err}, status: ${status}`); + logs.push( + `[${isoNow()}][DownloadData] ❌ Get download data error: ${err}, status: ${status}` + ); logs.push(`[${isoNow()}][DownloadData] ${failEnd}`); await this.taskService.appendTaskLog(task, logs.join('\n')); return; @@ -143,39 +176,57 @@ export class PackageSyncerService extends AbstractService { // "downloads": 45 // }, const day = item.day; - const [ year, month, date ] = day.split('-'); + const [year, month, date] = day.split('-'); const yearMonth = parseInt(`${year}${month}`); if (!datas.has(yearMonth)) { datas.set(yearMonth, []); } const counters = datas.get(yearMonth); - counters!.push([ date, item.downloads ]); + counters!.push([date, item.downloads]); } - for (const [ yearMonth, counters ] of datas.entries()) { - await this.packageVersionDownloadRepository.saveSyncDataByMonth(pkg.packageId, yearMonth, counters); - logs.push(`[${isoNow()}][DownloadData] 🟢 ${yearMonth}: ${counters.length} days`); + for (const [yearMonth, counters] of datas.entries()) { + await this.packageVersionDownloadRepository.saveSyncDataByMonth( + pkg.packageId, + yearMonth, + counters + ); + logs.push( + `[${isoNow()}][DownloadData] 🟢 ${yearMonth}: ${counters.length} days` + ); } - logs.push(`[${isoNow()}][DownloadData] 🟢🟢🟢🟢🟢 ${registry}/${fullname} 🟢🟢🟢🟢🟢`); + logs.push( + `[${isoNow()}][DownloadData] 🟢🟢🟢🟢🟢 ${registry}/${fullname} 🟢🟢🟢🟢🟢` + ); await this.taskService.appendTaskLog(task, logs.join('\n')); } private async syncUpstream(task: Task) { const registry = this.npmRegistry.registry; const fullname = task.targetName; - const remoteAuthToken = await this.registryManagerService.getAuthTokenByRegistryHost(registry); + const remoteAuthToken = + await this.registryManagerService.getAuthTokenByRegistryHost(registry); let logs: string[] = []; let logId = ''; - logs.push(`[${isoNow()}][UP] 🚧🚧🚧🚧🚧 Waiting sync "${fullname}" task on ${registry} 🚧🚧🚧🚧🚧`); + logs.push( + `[${isoNow()}][UP] 🚧🚧🚧🚧🚧 Waiting sync "${fullname}" task on ${registry} 🚧🚧🚧🚧🚧` + ); const failEnd = `❌❌❌❌❌ Sync ${registry}/${fullname} 🚮 give up 🚮 ❌❌❌❌❌`; try { - const { data, status, res } = await this.npmRegistry.createSyncTask(fullname, { remoteAuthToken }); - logs.push(`[${isoNow()}][UP] 🚧 HTTP [${status}] timing: ${JSON.stringify(res.timing)}, data: ${JSON.stringify(data)}`); + const { data, status, res } = await this.npmRegistry.createSyncTask( + fullname, + { remoteAuthToken } + ); + logs.push( + `[${isoNow()}][UP] 🚧 HTTP [${status}] timing: ${JSON.stringify(res.timing)}, data: ${JSON.stringify(data)}` + ); logId = data.logId; } catch (err: any) { const status = err.status || 'unknow'; // 可能会抛出 AggregateError 异常 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError - logs.push(`[${isoNow()}][UP] ❌ Sync ${fullname} fail, create sync task error: ${err}, status: ${status} ${err instanceof AggregateError ? err.errors : ''}`); + logs.push( + `[${isoNow()}][UP] ❌ Sync ${fullname} fail, create sync task error: ${err}, status: ${status} ${err instanceof AggregateError ? err.errors : ''}` + ); logs.push(`[${isoNow()}][UP] ${failEnd}`); await this.taskService.appendTaskLog(task, logs.join('\n')); return; @@ -193,33 +244,47 @@ export class PackageSyncerService extends AbstractService { let useTime = Date.now() - startTime; while (useTime < maxTimeout) { // sleep 1s ~ 6s in random - const delay = process.env.NODE_ENV === 'test' ? 100 : 1000 + Math.random() * 5000; + const delay = + process.env.NODE_ENV === 'test' ? 100 : 1000 + Math.random() * 5000; await setTimeout(delay); try { - const { data, status, url } = await this.npmRegistry.getSyncTask(fullname, logId, offset, { remoteAuthToken }); + const { data, status, url } = await this.npmRegistry.getSyncTask( + fullname, + logId, + offset, + { remoteAuthToken } + ); useTime = Date.now() - startTime; if (!logUrl) { logUrl = url; } - const log = data && data.log || ''; + const log = (data && data.log) || ''; offset += log.length; if (data && data.syncDone) { - logs.push(`[${isoNow()}][UP] 🎉 Sync ${fullname} success [${useTime}ms], log: ${logUrl}, offset: ${offset}`); + logs.push( + `[${isoNow()}][UP] 🎉 Sync ${fullname} success [${useTime}ms], log: ${logUrl}, offset: ${offset}` + ); logs.push(`[${isoNow()}][UP] 🔗 ${registry}/${fullname}`); await this.taskService.appendTaskLog(task, logs.join('\n')); return; } - logs.push(`[${isoNow()}][UP] 🚧 HTTP [${status}] [${useTime}ms], offset: ${offset}`); + logs.push( + `[${isoNow()}][UP] 🚧 HTTP [${status}] [${useTime}ms], offset: ${offset}` + ); await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; } catch (err: any) { useTime = Date.now() - startTime; const status = err.status || 'unknow'; - logs.push(`[${isoNow()}][UP] 🚧 HTTP [${status}] [${useTime}ms] error: ${err}`); + logs.push( + `[${isoNow()}][UP] 🚧 HTTP [${status}] [${useTime}ms] error: ${err}` + ); } } // timeout - logs.push(`[${isoNow()}][UP] ❌ Sync ${fullname} fail, timeout, log: ${logUrl}, offset: ${offset}`); + logs.push( + `[${isoNow()}][UP] ❌ Sync ${fullname} fail, timeout, log: ${logUrl}, offset: ${offset}` + ); logs.push(`[${isoNow()}][UP] ${failEnd}`); await this.taskService.appendTaskLog(task, logs.join('\n')); } @@ -263,8 +328,10 @@ export class PackageSyncerService extends AbstractService { // "_attachments": {} // } let isSecurityHolder = true; - for (const versionInfo of Object.entries<{ _npmUser?: { name: string } }>(data.versions || {})) { - const [ v, info ] = versionInfo; + for (const versionInfo of Object.entries<{ _npmUser?: { name: string } }>( + data.versions || {} + )) { + const [v, info] = versionInfo; // >=0.0.1-security <0.0.2-0 const isSecurityVersion = semver.satisfies(v, '^0.0.1-security'); const isNpmUser = info?._npmUser?.name === 'npm'; @@ -285,11 +352,20 @@ export class PackageSyncerService extends AbstractService { // - ignore: 不做任何处理,直接结束任务 // - delete: 删除包数据,包括 manifest 存储 // - block: 软删除 将包标记为 block,用户无法直接使用 - private async syncDeletePkg({ task, pkg, logUrl, url, logs, data }: syncDeletePkgOptions) { + private async syncDeletePkg({ + task, + pkg, + logUrl, + url, + logs, + data, + }: syncDeletePkgOptions) { const fullname = task.targetName; const failEnd = `❌❌❌❌❌ ${url || fullname} ❌❌❌❌❌`; const syncDeleteMode: SyncDeleteMode = this.config.cnpmcore.syncDeleteMode; - logs.push(`[${isoNow()}] 🟢 Package "${fullname}" was removed in remote registry, response data: ${JSON.stringify(data)}, config.syncDeleteMode = ${syncDeleteMode}`); + logs.push( + `[${isoNow()}] 🟢 Package "${fullname}" was removed in remote registry, response data: ${JSON.stringify(data)}, config.syncDeleteMode = ${syncDeleteMode}` + ); // pkg not exists in local registry if (!pkg) { @@ -297,31 +373,46 @@ export class PackageSyncerService extends AbstractService { logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`); logs.push(`[${isoNow()}] ${failEnd}`); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); - this.logger.info('[PackageSyncerService.executeTask:fail-404] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-404] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); return; } if (syncDeleteMode === SyncDeleteMode.ignore) { // ignore deleted package - logs.push(`[${isoNow()}] 🟢 Skip remove since config.syncDeleteMode = ignore`); + logs.push( + `[${isoNow()}] 🟢 Skip remove since config.syncDeleteMode = ignore` + ); } else if (syncDeleteMode === SyncDeleteMode.block) { // block deleted package - await this.packageManagerService.blockPackage(pkg, 'Removed in remote registry'); - logs.push(`[${isoNow()}] 🟢 Block the package since config.syncDeleteMode = block`); + await this.packageManagerService.blockPackage( + pkg, + 'Removed in remote registry' + ); + logs.push( + `[${isoNow()}] 🟢 Block the package since config.syncDeleteMode = block` + ); } else if (syncDeleteMode === SyncDeleteMode.delete) { // delete package await this.packageManagerService.unpublishPackage(pkg); - logs.push(`[${isoNow()}] 🟢 Delete the package since config.syncDeleteMode = delete`); + logs.push( + `[${isoNow()}] 🟢 Delete the package since config.syncDeleteMode = delete` + ); } // update log logs.push(`[${isoNow()}] 📝 Log URL: ${logUrl}`); logs.push(`[${isoNow()}] 🔗 ${url}`); await this.taskService.finishTask(task, TaskState.Success, logs.join('\n')); - this.logger.info('[PackageSyncerService.executeTask:remove-package] taskId: %s, targetName: %s', - task.taskId, task.targetName); - + this.logger.info( + '[PackageSyncerService.executeTask:remove-package] taskId: %s, targetName: %s', + task.taskId, + task.targetName + ); } // 初始化对应的 Registry @@ -329,8 +420,13 @@ export class PackageSyncerService extends AbstractService { // 1. 其次从 task.data.registryId (创建单包同步任务时传入) // 2. 接着根据 scope 进行计算 (作为子包依赖同步时候,无 registryId) // 3. 最后返回 default registryId (可能 default registry 也不存在) - public async initSpecRegistry(task: Task, pkg: Package | null = null, scope?: string): Promise { - const registryId = pkg?.registryId || (task.data as SyncPackageTaskOptions).registryId; + public async initSpecRegistry( + task: Task, + pkg: Package | null = null, + scope?: string + ): Promise { + const registryId = + pkg?.registryId || (task.data as SyncPackageTaskOptions).registryId; let targetHost: string = this.config.cnpmcore.sourceRegistry; let registry: Registry | null = null; @@ -341,7 +437,9 @@ export class PackageSyncerService extends AbstractService { } else if (scope) { const scopeModel = await this.scopeManagerService.findByName(scope); if (scopeModel?.registryId) { - registry = await this.registryManagerService.findByRegistryId(scopeModel?.registryId); + registry = await this.registryManagerService.findByRegistryId( + scopeModel?.registryId + ); } } @@ -367,8 +465,14 @@ export class PackageSyncerService extends AbstractService { @Pointcut(EventCorkAdvice) public async executeTask(task: Task) { const fullname = task.targetName; - const [ scope, name ] = getScopeAndName(fullname); - const { tips, skipDependencies: originSkipDependencies, syncDownloadData, forceSyncHistory, specificVersions } = task.data as SyncPackageTaskOptions; + const [scope, name] = getScopeAndName(fullname); + const { + tips, + skipDependencies: originSkipDependencies, + syncDownloadData, + forceSyncHistory, + specificVersions, + } = task.data as SyncPackageTaskOptions; let pkg = await this.packageRepository.findPackage(scope, name); const registry = await this.initSpecRegistry(task, pkg, scope); const registryHost = this.npmRegistry.registry; @@ -378,34 +482,69 @@ export class PackageSyncerService extends AbstractService { logs.push(`[${isoNow()}] 👉👉👉👉👉 Tips: ${tips} 👈👈👈👈👈`); } - const taskQueueLength = await this.taskService.getTaskQueueLength(task.type); + const taskQueueLength = await this.taskService.getTaskQueueLength( + task.type + ); const taskQueueHighWaterSize = this.config.cnpmcore.taskQueueHighWaterSize; const taskQueueInHighWaterState = taskQueueLength >= taskQueueHighWaterSize; - const skipDependencies = taskQueueInHighWaterState ? true : !!originSkipDependencies; - const syncUpstream = !!(!taskQueueInHighWaterState && this.config.cnpmcore.sourceRegistryIsCNpm && this.config.cnpmcore.syncUpstreamFirst && registry.name === PresetRegistryName.default); + const skipDependencies = taskQueueInHighWaterState + ? true + : !!originSkipDependencies; + const syncUpstream = !!( + !taskQueueInHighWaterState && + this.config.cnpmcore.sourceRegistryIsCNpm && + this.config.cnpmcore.syncUpstreamFirst && + registry.name === PresetRegistryName.default + ); const logUrl = `${this.config.cnpmcore.registry}/-/package/${fullname}/syncs/${task.taskId}/log`; - this.logger.info('[PackageSyncerService.executeTask:start] taskId: %s, targetName: %s, attempts: %s, taskQueue: %s/%s, syncUpstream: %s, log: %s', - task.taskId, task.targetName, task.attempts, taskQueueLength, taskQueueHighWaterSize, syncUpstream, logUrl); - logs.push(`[${isoNow()}] 🚧🚧🚧🚧🚧 Syncing from ${registryHost}/${fullname}, skipDependencies: ${skipDependencies}, syncUpstream: ${syncUpstream}, syncDownloadData: ${!!syncDownloadData}, forceSyncHistory: ${!!forceSyncHistory} attempts: ${task.attempts}, worker: "${os.hostname()}/${process.pid}", taskQueue: ${taskQueueLength}/${taskQueueHighWaterSize} 🚧🚧🚧🚧🚧`); + this.logger.info( + '[PackageSyncerService.executeTask:start] taskId: %s, targetName: %s, attempts: %s, taskQueue: %s/%s, syncUpstream: %s, log: %s', + task.taskId, + task.targetName, + task.attempts, + taskQueueLength, + taskQueueHighWaterSize, + syncUpstream, + logUrl + ); + logs.push( + `[${isoNow()}] 🚧🚧🚧🚧🚧 Syncing from ${registryHost}/${fullname}, skipDependencies: ${skipDependencies}, syncUpstream: ${syncUpstream}, syncDownloadData: ${!!syncDownloadData}, forceSyncHistory: ${!!forceSyncHistory} attempts: ${task.attempts}, worker: "${os.hostname()}/${process.pid}", taskQueue: ${taskQueueLength}/${taskQueueHighWaterSize} 🚧🚧🚧🚧🚧` + ); if (specificVersions) { - logs.push(`[${isoNow()}] 👉 syncing specific versions: ${specificVersions.join(' | ')} 👈`); + logs.push( + `[${isoNow()}] 👉 syncing specific versions: ${specificVersions.join(' | ')} 👈` + ); } logs.push(`[${isoNow()}] 🚧 log: ${logUrl}`); if (registry?.name === PresetRegistryName.self) { - logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} has been published to the self registry, skip sync ❌❌❌❌❌`); + logs.push( + `[${isoNow()}] ❌❌❌❌❌ ${fullname} has been published to the self registry, skip sync ❌❌❌❌❌` + ); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); - this.logger.info('[PackageSyncerService.executeTask:fail] taskId: %s, targetName: %s, invalid registryId', - task.taskId, task.targetName); + this.logger.info( + '[PackageSyncerService.executeTask:fail] taskId: %s, targetName: %s, invalid registryId', + task.taskId, + task.targetName + ); return; } if (pkg && pkg?.registryId !== registry?.registryId) { if (pkg.registryId) { - logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} registry is ${pkg.registryId} not belong to ${registry?.registryId}, skip sync ❌❌❌❌❌`); - await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); - this.logger.info('[PackageSyncerService.executeTask:fail] taskId: %s, targetName: %s, invalid registryId', - task.taskId, task.targetName); + logs.push( + `[${isoNow()}] ❌❌❌❌❌ ${fullname} registry is ${pkg.registryId} not belong to ${registry?.registryId}, skip sync ❌❌❌❌❌` + ); + await this.taskService.finishTask( + task, + TaskState.Fail, + logs.join('\n') + ); + this.logger.info( + '[PackageSyncerService.executeTask:fail] taskId: %s, targetName: %s, invalid registryId', + task.taskId, + task.targetName + ); return; } // 多同步源之前没有 registryId @@ -418,10 +557,19 @@ export class PackageSyncerService extends AbstractService { if (syncDownloadData && pkg) { await this.syncDownloadData(task, pkg); logs.push(`[${isoNow()}] 🟢 log: ${logUrl}`); - logs.push(`[${isoNow()}] 🟢🟢🟢🟢🟢 Sync "${fullname}" download data success 🟢🟢🟢🟢🟢`); - await this.taskService.finishTask(task, TaskState.Success, logs.join('\n')); - this.logger.info('[PackageSyncerService.executeTask:success] taskId: %s, targetName: %s', - task.taskId, task.targetName); + logs.push( + `[${isoNow()}] 🟢🟢🟢🟢🟢 Sync "${fullname}" download data success 🟢🟢🟢🟢🟢` + ); + await this.taskService.finishTask( + task, + TaskState.Success, + logs.join('\n') + ); + this.logger.info( + '[PackageSyncerService.executeTask:success] taskId: %s, targetName: %s', + task.taskId, + task.targetName + ); return; } @@ -437,21 +585,33 @@ export class PackageSyncerService extends AbstractService { logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`); logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); - this.logger.info('[PackageSyncerService.executeTask:fail-block-list] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-block-list] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); return; } let registryFetchResult: RegistryResponse; try { - registryFetchResult = await this.npmRegistry.getFullManifests(fullname, { remoteAuthToken }); + registryFetchResult = await this.npmRegistry.getFullManifests(fullname, { + remoteAuthToken, + }); } catch (err: any) { const status = err.status || 'unknown'; task.error = `request manifests error: ${err}, status: ${status}`; - logs.push(`[${isoNow()}] ❌ Synced ${fullname} fail, ${task.error}, log: ${logUrl}`); + logs.push( + `[${isoNow()}] ❌ Synced ${fullname} fail, ${task.error}, log: ${logUrl}` + ); logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`); - this.logger.info('[PackageSyncerService.executeTask:fail-request-error] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-request-error] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); await this.taskService.retryTask(task, logs.join('\n')); return; } @@ -463,11 +623,19 @@ export class PackageSyncerService extends AbstractService { // registry will response status 522 and data will be null // > TypeError: Cannot read properties of null (reading 'readme') task.error = `request manifests response error, status: ${status}, data: ${JSON.stringify(data)}`; - logs.push(`[${isoNow()}] ❌ response headers: ${JSON.stringify(headers)}`); - logs.push(`[${isoNow()}] ❌ Synced ${fullname} fail, ${task.error}, log: ${logUrl}`); + logs.push( + `[${isoNow()}] ❌ response headers: ${JSON.stringify(headers)}` + ); + logs.push( + `[${isoNow()}] ❌ Synced ${fullname} fail, ${task.error}, log: ${logUrl}` + ); logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`); - this.logger.info('[PackageSyncerService.executeTask:fail-request-error] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-request-error] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); await this.taskService.retryTask(task, logs.join('\n')); return; } @@ -477,10 +645,16 @@ export class PackageSyncerService extends AbstractService { // https://github.com/node-modules/detect-port/issues/57 task.error = `Package not found, status 404, data: ${JSON.stringify(data)}`; logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`); - logs.push(`[${isoNow()}] ❌ Synced ${fullname} fail, ${task.error}, log: ${logUrl}`); + logs.push( + `[${isoNow()}] ❌ Synced ${fullname} fail, ${task.error}, log: ${logUrl}` + ); logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`); - this.logger.info('[PackageSyncerService.executeTask:fail-request-error] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-request-error] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); return; } @@ -496,7 +670,9 @@ export class PackageSyncerService extends AbstractService { const timeMap = data.time || {}; const failEnd = `❌❌❌❌❌ ${url || fullname} ❌❌❌❌❌`; const contentLength = headers['content-length'] || '-'; - logs.push(`[${isoNow()}] HTTP [${status}] content-length: ${contentLength}, timing: ${JSON.stringify(res.timing)}`); + logs.push( + `[${isoNow()}] HTTP [${status}] content-length: ${contentLength}, timing: ${JSON.stringify(res.timing)}` + ); if (this.isRemovedInRemote(registryFetchResult)) { await this.syncDeletePkg({ task, pkg, logs, logUrl, url, data }); @@ -508,7 +684,9 @@ export class PackageSyncerService extends AbstractService { // show latest information if (distTags.latest) { - logs.push(`[${isoNow()}] 📖 ${fullname} latest version: ${distTags.latest ?? '-'}, published time: ${JSON.stringify(timeMap[distTags.latest])}`); + logs.push( + `[${isoNow()}] 📖 ${fullname} latest version: ${distTags.latest ?? '-'}, published time: ${JSON.stringify(timeMap[distTags.latest])}` + ); } // 1. save maintainers @@ -517,7 +695,8 @@ export class PackageSyncerService extends AbstractService { // { name: 'jasonlaster11', email: 'jason.laster.11@gmail.com' } // ], let maintainers = data.maintainers; - const maintainersMap: Record = {}; + const maintainersMap: Record = + {}; const users: User[] = []; let changedUserCount = 0; if (!Array.isArray(maintainers) || maintainers.length === 0) { @@ -526,28 +705,47 @@ export class PackageSyncerService extends AbstractService { // security holding package will not contains maintainers, auto set npm and npm@npmjs.com to maintainer // "description": "security holding package", // "repository": "npm/security-holder" - if (data.description === 'security holding package' || data.repository === 'npm/security-holder') { - maintainers = data.maintainers = [{ name: 'npm', email: 'npm@npmjs.com' }]; + if ( + data.description === 'security holding package' || + data.repository === 'npm/security-holder' + ) { + maintainers = data.maintainers = [ + { name: 'npm', email: 'npm@npmjs.com' }, + ]; } else { // try to use latest tag version's maintainers instead - const latestPackageVersion = distTags.latest && versionMap[distTags.latest]; - if (latestPackageVersion && Array.isArray(latestPackageVersion.maintainers)) { + const latestPackageVersion = + distTags.latest && versionMap[distTags.latest]; + if ( + latestPackageVersion && + Array.isArray(latestPackageVersion.maintainers) + ) { maintainers = latestPackageVersion.maintainers; - logs.push(`[${isoNow()}] 📖 Use the latest version(${latestPackageVersion.version}) maintainers instead`); + logs.push( + `[${isoNow()}] 📖 Use the latest version(${latestPackageVersion.version}) maintainers instead` + ); } } } if (Array.isArray(maintainers) && maintainers.length > 0) { - logs.push(`[${isoNow()}] 🚧 Syncing maintainers: ${JSON.stringify(maintainers)}`); + logs.push( + `[${isoNow()}] 🚧 Syncing maintainers: ${JSON.stringify(maintainers)}` + ); for (const maintainer of maintainers) { if (maintainer.name && maintainer.email) { maintainersMap[maintainer.name] = maintainer; - const { changed, user } = await this.userService.saveUser(registry?.userPrefix, maintainer.name, maintainer.email); + const { changed, user } = await this.userService.saveUser( + registry?.userPrefix, + maintainer.name, + maintainer.email + ); users.push(user); if (changed) { changedUserCount++; - logs.push(`[${isoNow()}] 🟢 [${changedUserCount}] Synced ${maintainer.name} => ${user.name}(${user.userId})`); + logs.push( + `[${isoNow()}] 🟢 [${changedUserCount}] Synced ${maintainer.name} => ${user.name}(${user.userId})` + ); } } } @@ -575,50 +773,80 @@ export class PackageSyncerService extends AbstractService { task.error = `invalid maintainers: ${JSON.stringify(maintainers)}`; logs.push(`[${isoNow()}] ❌ ${task.error}, log: ${logUrl}`); logs.push(`[${isoNow()}] ${failEnd}`); - this.logger.info('[PackageSyncerService.executeTask:fail-invalid-maintainers] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-invalid-maintainers] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); return; } let lastErrorMessage = ''; const dependenciesSet = new Set(); - const { data: existsData } = await this.packageManagerService.listPackageFullManifests(scope, name); - const { data: abbreviatedManifests } = await this.packageManagerService.listPackageAbbreviatedManifests(scope, name); + const { data: existsData } = + await this.packageManagerService.listPackageFullManifests(scope, name); + const { data: abbreviatedManifests } = + await this.packageManagerService.listPackageAbbreviatedManifests( + scope, + name + ); const existsVersionMap = existsData?.versions ?? {}; const existsVersionCount = Object.keys(existsVersionMap).length; const abbreviatedVersionMap = abbreviatedManifests?.versions ?? {}; // 2. save versions - if (specificVersions && !this.config.cnpmcore.strictSyncSpecivicVersion && !specificVersions.includes(distTags.latest)) { - logs.push(`[${isoNow()}] 📦 Add latest tag version "${fullname}: ${distTags.latest}"`); + if ( + specificVersions && + !this.config.cnpmcore.strictSyncSpecivicVersion && + !specificVersions.includes(distTags.latest) + ) { + logs.push( + `[${isoNow()}] 📦 Add latest tag version "${fullname}: ${distTags.latest}"` + ); specificVersions.push(distTags.latest); } - const versions = specificVersions ? - Object.values(versionMap).filter(verItem => specificVersions.includes(verItem.version)) : - Object.values(versionMap); + const versions = specificVersions + ? Object.values(versionMap).filter(verItem => + specificVersions.includes(verItem.version) + ) + : Object.values(versionMap); // 全量同步时跳过排序 - const sortedAvailableVersions = specificVersions ? - versions.map(item => item.version).sort(semver.rcompare) : []; + const sortedAvailableVersions = specificVersions + ? versions.map(item => item.version).sort(semver.rcompare) + : []; // 在strictSyncSpecivicVersion模式下(不同步latest)且所有传入的version均不可用 if (specificVersions && sortedAvailableVersions.length === 0) { logs.push(`[${isoNow()}] ❌ `); task.error = 'There is no available specific versions, stop task.'; logs.push(`[${isoNow()}] ${task.error}, log: ${logUrl}`); logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname} ❌❌❌❌❌`); - this.logger.info('[PackageSyncerService.executeTask:fail-empty-list] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-empty-list] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); return; } if (specificVersions) { // specific versions may not in manifest. - const notAvailableVersionList = specificVersions.filter(i => !sortedAvailableVersions.includes(i)); - logs.push(`[${isoNow()}] 🚧 Syncing specific versions: ${sortedAvailableVersions.join(' | ')}`); + const notAvailableVersionList = specificVersions.filter( + i => !sortedAvailableVersions.includes(i) + ); + logs.push( + `[${isoNow()}] 🚧 Syncing specific versions: ${sortedAvailableVersions.join(' | ')}` + ); if (notAvailableVersionList.length > 0) { - logs.push(`🚧 Some specific versions are not available: 👉 ${notAvailableVersionList.join(' | ')} 👈`); + logs.push( + `🚧 Some specific versions are not available: 👉 ${notAvailableVersionList.join(' | ')} 👈` + ); } } else { - logs.push(`[${isoNow()}] 🚧 Syncing versions ${existsVersionCount} => ${versions.length}`); + logs.push( + `[${isoNow()}] 🚧 Syncing versions ${existsVersionCount} => ${versions.length}` + ); } const updateVersions: string[] = []; @@ -627,23 +855,37 @@ export class PackageSyncerService extends AbstractService { for (const item of versions) { const version: string = item.version; if (!version) continue; - let existsItem: typeof existsVersionMap[string] | undefined = existsVersionMap[version]; - let existsAbbreviatedItem: typeof abbreviatedVersionMap[string] | undefined = abbreviatedVersionMap[version]; + let existsItem: (typeof existsVersionMap)[string] | undefined = + existsVersionMap[version]; + let existsAbbreviatedItem: + | (typeof abbreviatedVersionMap)[string] + | undefined = abbreviatedVersionMap[version]; const shouldDeleteReadme = !!(existsItem && 'readme' in existsItem); if (pkg) { if (existsItem) { // check item on AbbreviatedManifests if (!existsAbbreviatedItem) { updateVersions.push(version); - logs.push(`[${isoNow()}] 🐛 Remote version ${version} not exists on local abbreviated manifests, need to refresh`); + logs.push( + `[${isoNow()}] 🐛 Remote version ${version} not exists on local abbreviated manifests, need to refresh` + ); } } if (existsItem && forceSyncHistory === true) { - const pkgVer = await this.packageRepository.findPackageVersion(pkg.packageId, version); + const pkgVer = await this.packageRepository.findPackageVersion( + pkg.packageId, + version + ); if (pkgVer) { - logs.push(`[${isoNow()}] 🚧 [${syncIndex}] Remove version ${version} for force sync history`); - await this.packageManagerService.removePackageVersion(pkg, pkgVer, true); + logs.push( + `[${isoNow()}] 🚧 [${syncIndex}] Remove version ${version} for force sync history` + ); + await this.packageManagerService.removePackageVersion( + pkg, + pkgVer, + true + ); existsItem = undefined; existsAbbreviatedItem = undefined; existsVersionMap[version] = undefined; @@ -658,12 +900,19 @@ export class PackageSyncerService extends AbstractService { // need libc field https://github.com/cnpm/cnpmcore/issues/187 // fix _npmUser field since https://github.com/cnpm/cnpmcore/issues/553 const metaDataKeys = [ - 'peerDependenciesMeta', 'os', 'cpu', 'libc', 'workspaces', 'hasInstallScript', - 'deprecated', '_npmUser', 'funding', + 'peerDependenciesMeta', + 'os', + 'cpu', + 'libc', + 'workspaces', + 'hasInstallScript', + 'deprecated', + '_npmUser', + 'funding', // https://github.com/cnpm/cnpmcore/issues/689 'acceptDependencies', ]; - const ignoreInAbbreviated = [ '_npmUser' ]; + const ignoreInAbbreviated = new Set(['_npmUser']); const diffMeta: Partial = {}; for (const key of metaDataKeys) { let remoteItemValue = item[key]; @@ -675,7 +924,14 @@ export class PackageSyncerService extends AbstractService { } if (!isEqual(remoteItemValue, existsItem[key])) { diffMeta[key] = remoteItemValue; - } else if (!ignoreInAbbreviated.includes(key) && existsAbbreviatedItem && !isEqual(remoteItemValue, (existsAbbreviatedItem as Record)[key])) { + } else if ( + !ignoreInAbbreviated.has(key) && + existsAbbreviatedItem && + !isEqual( + remoteItemValue, + (existsAbbreviatedItem as Record)[key] + ) + ) { // should diff exists abbreviated item too diffMeta[key] = remoteItemValue; } @@ -685,7 +941,7 @@ export class PackageSyncerService extends AbstractService { diffMeta.readme = undefined; } if (!isEmpty(diffMeta)) { - differentMetas.push([ existsItem, diffMeta ]); + differentMetas.push([existsItem, diffMeta]); } continue; } @@ -699,29 +955,46 @@ export class PackageSyncerService extends AbstractService { const tarball = dist && dist.tarball; if (!tarball) { lastErrorMessage = `missing tarball, dist: ${JSON.stringify(dist)}`; - logs.push(`[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} fail, ${lastErrorMessage}`); + logs.push( + `[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} fail, ${lastErrorMessage}` + ); await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; continue; } const publishTimeISO = timeMap[version]; - const publishTime = publishTimeISO ? new Date(publishTimeISO) : new Date(); + const publishTime = publishTimeISO + ? new Date(publishTimeISO) + : new Date(); const delay = Date.now() - publishTime.getTime(); - logs.push(`[${isoNow()}] 🚧 [${syncIndex}] Syncing version ${version}, delay: ${delay}ms [${publishTimeISO}], tarball: ${tarball}`); + logs.push( + `[${isoNow()}] 🚧 [${syncIndex}] Syncing version ${version}, delay: ${delay}ms [${publishTimeISO}], tarball: ${tarball}` + ); let localFile: string; try { - const { tmpfile, headers, timing } = - await downloadToTempfile(this.httpclient, this.config.dataDir, tarball, { remoteAuthToken }); + const { tmpfile, headers, timing } = await downloadToTempfile( + this.httpclient, + this.config.dataDir, + tarball, + { remoteAuthToken } + ); localFile = tmpfile; - logs.push(`[${isoNow()}] 🚧 [${syncIndex}] HTTP content-length: ${headers['content-length']}, timing: ${JSON.stringify(timing)} => ${localFile}`); + logs.push( + `[${isoNow()}] 🚧 [${syncIndex}] HTTP content-length: ${headers['content-length']}, timing: ${JSON.stringify(timing)} => ${localFile}` + ); } catch (err: any) { - if (err.name === 'DownloadNotFoundError' || err.name === 'DownloadStatusInvalidError') { + if ( + err.name === 'DownloadNotFoundError' || + err.name === 'DownloadStatusInvalidError' + ) { this.logger.warn('Download tarball %s error: %s', tarball, err); } else { this.logger.error('Download tarball %s error: %s', tarball, err); } lastErrorMessage = `download tarball error: ${err}`; - logs.push(`[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} fail, ${lastErrorMessage}`); + logs.push( + `[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} fail, ${lastErrorMessage}` + ); await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; continue; @@ -747,25 +1020,40 @@ export class PackageSyncerService extends AbstractService { }; try { // 当 version 记录已经存在时,还需要校验一下 pkg.manifests 是否存在 - const publisher = users.find(user => user.displayName === item._npmUser?.name) || users[0]; - const pkgVersion = await this.packageManagerService.publish(publishCmd, publisher); + const publisher = + users.find(user => user.displayName === item._npmUser?.name) || + users[0]; + const pkgVersion = await this.packageManagerService.publish( + publishCmd, + publisher + ); updateVersions.push(pkgVersion.version); - logs.push(`[${isoNow()}] 🎉 [${syncIndex}] Synced version ${version} success, packageVersionId: ${pkgVersion.packageVersionId}, db id: ${pkgVersion.id}`); + logs.push( + `[${isoNow()}] 🎉 [${syncIndex}] Synced version ${version} success, packageVersionId: ${pkgVersion.packageVersionId}, db id: ${pkgVersion.id}` + ); } catch (err: any) { if (err.name === 'ForbiddenError') { - logs.push(`[${isoNow()}] 🐛 [${syncIndex}] Synced version ${version} already exists, skip publish, try to set in local manifest`); + logs.push( + `[${isoNow()}] 🐛 [${syncIndex}] Synced version ${version} already exists, skip publish, try to set in local manifest` + ); // 如果 pkg.manifests 不存在,需要补充一下 updateVersions.push(version); } else { err.taskId = task.taskId; this.logger.error(err); lastErrorMessage = `publish error: ${err}`; - logs.push(`[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} error, ${lastErrorMessage}`); + logs.push( + `[${isoNow()}] ❌ [${syncIndex}] Synced version ${version} error, ${lastErrorMessage}` + ); if (err.name === 'BadRequestError') { // 由于当前版本的依赖不满足,尝试重试 // 默认会在当前队列最后重试 - this.logger.info('[PackageSyncerService.executeTask:fail-validate-deps] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[PackageSyncerService.executeTask:fail-validate-deps] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); await this.taskService.retryTask(task, logs.join('\n')); return; } @@ -779,7 +1067,8 @@ export class PackageSyncerService extends AbstractService { for (const dependencyName in dependencies) { dependenciesSet.add(dependencyName); } - const optionalDependencies: Record = item.optionalDependencies || {}; + const optionalDependencies: Record = + item.optionalDependencies || {}; for (const dependencyName in optionalDependencies) { dependenciesSet.add(dependencyName); } @@ -791,26 +1080,40 @@ export class PackageSyncerService extends AbstractService { } if (!pkg || !pkg.id) { // sync all versions fail in the first time - logs.push(`[${isoNow()}] ❌ All versions sync fail, package not exists, log: ${logUrl}`); + logs.push( + `[${isoNow()}] ❌ All versions sync fail, package not exists, log: ${logUrl}` + ); logs.push(`[${isoNow()}] ${failEnd}`); task.error = lastErrorMessage; - this.logger.info('[PackageSyncerService.executeTask:fail] taskId: %s, targetName: %s, package not exists', - task.taskId, task.targetName); + this.logger.info( + '[PackageSyncerService.executeTask:fail] taskId: %s, targetName: %s, package not exists', + task.taskId, + task.targetName + ); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); return; } // 2.1 save differentMetas - for (const [ existsItem, diffMeta ] of differentMetas) { - const pkgVersion = await this.packageRepository.findPackageVersion(pkg.packageId, existsItem.version); + for (const [existsItem, diffMeta] of differentMetas) { + const pkgVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + existsItem.version + ); if (pkgVersion) { - await this.packageManagerService.savePackageVersionManifest(pkgVersion, diffMeta, diffMeta); + await this.packageManagerService.savePackageVersionManifest( + pkgVersion, + diffMeta, + diffMeta + ); updateVersions.push(pkgVersion.version); let diffMetaInfo = JSON.stringify(diffMeta); if ('readme' in diffMeta) { diffMetaInfo += ', delete exists readme'; } - logs.push(`[${isoNow()}] 🟢 Synced version ${existsItem.version} success, different meta: ${diffMetaInfo}`); + logs.push( + `[${isoNow()}] 🟢 Synced version ${existsItem.version} success, different meta: ${diffMetaInfo}` + ); } } @@ -818,22 +1121,37 @@ export class PackageSyncerService extends AbstractService { // 2.3 find out remove versions for (const existsVersion in existsVersionMap) { if (!(existsVersion in versionMap)) { - const pkgVersion = await this.packageRepository.findPackageVersion(pkg.packageId, existsVersion); + const pkgVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + existsVersion + ); if (pkgVersion) { - await this.packageManagerService.removePackageVersion(pkg, pkgVersion, true); - logs.push(`[${isoNow()}] 🟢 Removed version ${existsVersion} success`); + await this.packageManagerService.removePackageVersion( + pkg, + pkgVersion, + true + ); + logs.push( + `[${isoNow()}] 🟢 Removed version ${existsVersion} success` + ); } removeVersions.push(existsVersion); } } - logs.push(`[${isoNow()}] 🟢 Synced updated ${updateVersions.length} versions, removed ${removeVersions.length} versions`); + logs.push( + `[${isoNow()}] 🟢 Synced updated ${updateVersions.length} versions, removed ${removeVersions.length} versions` + ); if (updateVersions.length > 0 || removeVersions.length > 0) { logs.push(`[${isoNow()}] 🚧 Refreshing manifests to dists ......`); const start = Date.now(); await this.taskService.appendTaskLog(task, logs.join('\n')); logs = []; - await this.packageManagerService.refreshPackageChangeVersionsToDists(pkg, updateVersions, removeVersions); + await this.packageManagerService.refreshPackageChangeVersionsToDists( + pkg, + updateVersions, + removeVersions + ); logs.push(`[${isoNow()}] 🟢 Refresh use ${Date.now() - start}ms`); } @@ -841,35 +1159,48 @@ export class PackageSyncerService extends AbstractService { // "dist-tags": { // "latest": "0.0.7" // }, - const changedTags: { tag: string, version?: string, action: string }[] = []; - const existsDistTags = existsData && existsData['dist-tags'] || {}; + const changedTags: { tag: string; version?: string; action: string }[] = []; + const existsDistTags = (existsData && existsData['dist-tags']) || {}; let shouldRefreshDistTags = false; for (const tag in distTags) { const version = distTags[tag]; const utf8mb3Regex = /[\u0020-\uD7FF\uE000-\uFFFD]/; if (!utf8mb3Regex.test(tag)) { - logs.push(`[${isoNow()}] 🚧 invalid tag(${tag}: ${version}), tag name is out of utf8mb3, skip`); + logs.push( + `[${isoNow()}] 🚧 invalid tag(${tag}: ${version}), tag name is out of utf8mb3, skip` + ); continue; } // 新 tag 指向的版本既不在存量数据里,也不在本次同步版本列表里 // 例如 latest 对应的 version 写入失败跳过 if (!existsVersionMap[version] && !updateVersions.includes(version)) { - logs.push(`[${isoNow()}] 🚧 invalid tag(${tag}: ${version}), version is not exists, skip`); + logs.push( + `[${isoNow()}] 🚧 invalid tag(${tag}: ${version}), version is not exists, skip` + ); continue; } - const changed = await this.packageManagerService.savePackageTag(pkg, tag, version); + const changed = await this.packageManagerService.savePackageTag( + pkg, + tag, + version + ); if (changed) { changedTags.push({ action: 'change', tag, version }); shouldRefreshDistTags = false; } else if (version !== existsDistTags[tag]) { shouldRefreshDistTags = true; - logs.push(`[${isoNow()}] 🚧 Remote tag(${tag}: ${version}) not exists in local dist-tags`); + logs.push( + `[${isoNow()}] 🚧 Remote tag(${tag}: ${version}) not exists in local dist-tags` + ); } } // 3.1 find out remove tags for (const tag in existsDistTags) { if (!(tag in distTags)) { - const changed = await this.packageManagerService.removePackageTag(pkg, tag); + const changed = await this.packageManagerService.removePackageTag( + pkg, + tag + ); if (changed) { changedTags.push({ action: 'remove', tag }); shouldRefreshDistTags = false; @@ -880,20 +1211,38 @@ export class PackageSyncerService extends AbstractService { // 在同步 sepcific version 时如果没有同步 latestTag 的版本会出现 latestTag 丢失或指向版本不正确的情况 if (specificVersions && this.config.cnpmcore.strictSyncSpecivicVersion) { // 不允许自动同步 latest 版本,从已同步版本中选出 latest - let latestStableVersion = semver.maxSatisfying(sortedAvailableVersions, '*'); + let latestStableVersion = semver.maxSatisfying( + sortedAvailableVersions, + '*' + ); // 所有版本都不是稳定版本则指向非稳定版本保证 latest 存在 if (!latestStableVersion) { latestStableVersion = sortedAvailableVersions[0]; } - if (!existsDistTags.latest || semver.rcompare(existsDistTags.latest, latestStableVersion) === 1) { - logs.push(`[${isoNow()}] 🚧 patch latest tag from specific versions 🚧`); - changedTags.push({ action: 'change', tag: 'latest', version: latestStableVersion }); - await this.packageManagerService.savePackageTag(pkg, 'latest', latestStableVersion); + if ( + !existsDistTags.latest || + semver.rcompare(existsDistTags.latest, latestStableVersion) === 1 + ) { + logs.push( + `[${isoNow()}] 🚧 patch latest tag from specific versions 🚧` + ); + changedTags.push({ + action: 'change', + tag: 'latest', + version: latestStableVersion, + }); + await this.packageManagerService.savePackageTag( + pkg, + 'latest', + latestStableVersion + ); } } if (changedTags.length > 0) { - logs.push(`[${isoNow()}] 🟢 Synced ${changedTags.length} tags: ${JSON.stringify(changedTags)}`); + logs.push( + `[${isoNow()}] 🟢 Synced ${changedTags.length} tags: ${JSON.stringify(changedTags)}` + ); } if (shouldRefreshDistTags) { await this.packageManagerService.refreshPackageDistTagsToDists(pkg); @@ -904,11 +1253,13 @@ export class PackageSyncerService extends AbstractService { await this.packageManagerService.savePackageMaintainers(pkg, users); // 4.1 find out remove maintainers const removedMaintainers: unknown[] = []; - const existsMaintainers = existsData && existsData.maintainers || []; + const existsMaintainers = (existsData && existsData.maintainers) || []; for (const maintainer of existsMaintainers) { const { name } = maintainer; if (!(name in maintainersMap)) { - const user = await this.userRepository.findUserByName(`${registry?.userPrefix || 'npm:'}${name}`); + const user = await this.userRepository.findUserByName( + `${registry?.userPrefix || 'npm:'}${name}` + ); if (user) { await this.packageManagerService.removePackageMaintainer(pkg, user); removedMaintainers.push(maintainer); @@ -916,23 +1267,35 @@ export class PackageSyncerService extends AbstractService { } } if (removedMaintainers.length > 0) { - logs.push(`[${isoNow()}] 🟢 Removed ${removedMaintainers.length} maintainers: ${JSON.stringify(removedMaintainers)}`); + logs.push( + `[${isoNow()}] 🟢 Removed ${removedMaintainers.length} maintainers: ${JSON.stringify(removedMaintainers)}` + ); } // 4.2 update package maintainers in dist // The event is initialized in the repository and distributed after uncork. // maintainers' information is updated in bulk to ensure consistency. if (!isEqual(maintainers, existsMaintainers)) { - logs.push(`[${isoNow()}] 🚧 Syncing maintainers to package manifest, from: ${JSON.stringify(maintainers)} to: ${JSON.stringify(existsMaintainers)}`); + logs.push( + `[${isoNow()}] 🚧 Syncing maintainers to package manifest, from: ${JSON.stringify(maintainers)} to: ${JSON.stringify(existsMaintainers)}` + ); await this.packageManagerService.refreshPackageMaintainersToDists(pkg); - logs.push(`[${isoNow()}] 🟢 Syncing maintainers to package manifest done`); + logs.push( + `[${isoNow()}] 🟢 Syncing maintainers to package manifest done` + ); } // 5. add deps sync task for (const dependencyName of dependenciesSet) { - const existsTask = await this.taskRepository.findTaskByTargetName(dependencyName, TaskType.SyncPackage, TaskState.Waiting); + const existsTask = await this.taskRepository.findTaskByTargetName( + dependencyName, + TaskType.SyncPackage, + TaskState.Waiting + ); if (existsTask) { - logs.push(`[${isoNow()}] 📖 Has dependency "${dependencyName}" sync task: ${existsTask.taskId}, db id: ${existsTask.id}`); + logs.push( + `[${isoNow()}] 📖 Has dependency "${dependencyName}" sync task: ${existsTask.taskId}, db id: ${existsTask.id}` + ); continue; } const tips = `Sync cause by "${fullname}" dependencies, parent task: ${task.taskId}`; @@ -941,7 +1304,9 @@ export class PackageSyncerService extends AbstractService { authorIp: task.authorIp, tips, }); - logs.push(`[${isoNow()}] 📦 Add dependency "${dependencyName}" sync task: ${dependencyTask.taskId}, db id: ${dependencyTask.id}`); + logs.push( + `[${isoNow()}] 📦 Add dependency "${dependencyName}" sync task: ${dependencyTask.taskId}, db id: ${dependencyTask.id}` + ); } if (syncDownloadData) { @@ -954,8 +1319,11 @@ export class PackageSyncerService extends AbstractService { logs.push(`[${isoNow()}] 📝 Log URL: ${logUrl}`); logs.push(`[${isoNow()}] 🔗 ${url}`); task.error = lastErrorMessage; - this.logger.info('[PackageSyncerService.executeTask:success] taskId: %s, targetName: %s', - task.taskId, task.targetName); + this.logger.info( + '[PackageSyncerService.executeTask:success] taskId: %s, targetName: %s', + task.taskId, + task.targetName + ); await this.taskService.finishTask(task, TaskState.Success, logs.join('\n')); } } diff --git a/app/core/service/PackageVersionFileService.ts b/app/core/service/PackageVersionFileService.ts index d766f817..b60f4df5 100644 --- a/app/core/service/PackageVersionFileService.ts +++ b/app/core/service/PackageVersionFileService.ts @@ -3,31 +3,22 @@ import { join, dirname, basename } from 'node:path'; import { randomUUID } from 'node:crypto'; // @ts-expect-error type error import tar from '@fengmk2/tar'; -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { ConflictError, ForbiddenError } from 'egg-errors'; import semver from 'semver'; import { AbstractService } from '../../common/AbstractService.js'; -import { - calculateIntegrity, - getFullname, -} from '../../common/PackageUtil.js'; +import { calculateIntegrity, getFullname } from '../../common/PackageUtil.js'; import { createTempDir, mimeLookup } from '../../common/FileUtil.js'; -import { - PackageRepository, -} from '../../repository/PackageRepository.js'; -import { PackageVersionFileRepository } from '../../repository/PackageVersionFileRepository.js'; -import { PackageVersionRepository } from '../../repository/PackageVersionRepository.js'; -import { DistRepository } from '../../repository/DistRepository.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { PackageVersionFileRepository } from '../../repository/PackageVersionFileRepository.js'; +import type { PackageVersionRepository } from '../../repository/PackageVersionRepository.js'; +import type { DistRepository } from '../../repository/DistRepository.js'; import { isDuplicateKeyError } from '../../repository/util/ErrorUtil.js'; import { PackageVersionFile } from '../entity/PackageVersionFile.js'; -import { PackageVersion } from '../entity/PackageVersion.js'; -import { Package } from '../entity/Package.js'; -import { PackageManagerService } from './PackageManagerService.js'; -import { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; +import type { PackageVersion } from '../entity/PackageVersion.js'; +import type { Package } from '../entity/Package.js'; +import type { PackageManagerService } from './PackageManagerService.js'; +import type { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; const unpkgWhiteListUrl = 'https://github.com/cnpm/unpkg-white-list'; const CHECK_TIMEOUT = process.env.NODE_ENV === 'test' ? 1 : 60000; @@ -51,34 +42,55 @@ export class PackageVersionFileService extends AbstractService { #unpkgWhiteListCheckTime: number = 0; #unpkgWhiteListCurrentVersion: string = ''; - #unpkgWhiteListAllowPackages: Record = {}; + #unpkgWhiteListAllowPackages: Record< + string, + { + version: string; + } + > = {}; #unpkgWhiteListAllowScopes: string[] = []; async listPackageVersionFiles(pkgVersion: PackageVersion, directory: string) { await this.#ensurePackageVersionFilesSync(pkgVersion); - return await this.packageVersionFileRepository.listPackageVersionFiles(pkgVersion.packageVersionId, directory); + return await this.packageVersionFileRepository.listPackageVersionFiles( + pkgVersion.packageVersionId, + directory + ); } async showPackageVersionFile(pkgVersion: PackageVersion, path: string) { await this.#ensurePackageVersionFilesSync(pkgVersion); const { directory, name } = this.#getDirectoryAndName(path); return await this.packageVersionFileRepository.findPackageVersionFile( - pkgVersion.packageVersionId, directory, name); + pkgVersion.packageVersionId, + directory, + name + ); } async #ensurePackageVersionFilesSync(pkgVersion: PackageVersion) { - const hasFiles = await this.packageVersionFileRepository.hasPackageVersionFiles(pkgVersion.packageVersionId); + const hasFiles = + await this.packageVersionFileRepository.hasPackageVersionFiles( + pkgVersion.packageVersionId + ); if (!hasFiles) { const lockName = `${pkgVersion.packageVersionId}:syncFiles`; - const lockRes = await this.cacheAdapter.usingLock(lockName, 60, async () => { - await this.syncPackageVersionFiles(pkgVersion); - }); + const lockRes = await this.cacheAdapter.usingLock( + lockName, + 60, + async () => { + await this.syncPackageVersionFiles(pkgVersion); + } + ); // lock fail if (!lockRes) { - this.logger.warn('[package:version:syncPackageVersionFiles] check lock:%s fail', lockName); - throw new ConflictError('Package version file sync is currently in progress. Please try again later.'); + this.logger.warn( + '[package:version:syncPackageVersionFiles] check lock:%s fail', + lockName + ); + throw new ConflictError( + 'Package version file sync is currently in progress. Please try again later.' + ); } } } @@ -92,27 +104,42 @@ export class PackageVersionFileService extends AbstractService { this.#unpkgWhiteListCheckTime = Date.now(); const whiteListScope = ''; const whiteListPackageName = 'unpkg-white-list'; - const whiteListPackageVersion = await this.packageVersionRepository.findVersionByTag( - whiteListScope, whiteListPackageName, 'latest'); + const whiteListPackageVersion = + await this.packageVersionRepository.findVersionByTag( + whiteListScope, + whiteListPackageName, + 'latest' + ); if (!whiteListPackageVersion) return; // same version, skip update for performance if (this.#unpkgWhiteListCurrentVersion === whiteListPackageVersion) return; // update the new version white list - const { manifest } = await this.packageManagerService.showPackageVersionManifest( - whiteListScope, whiteListPackageName, whiteListPackageVersion, false, true); + const { manifest } = + await this.packageManagerService.showPackageVersionManifest( + whiteListScope, + whiteListPackageName, + whiteListPackageVersion, + false, + true + ); if (!manifest) return; this.#unpkgWhiteListCurrentVersion = manifest.version; - this.#unpkgWhiteListAllowPackages = manifest.allowPackages ?? {} as any; - this.#unpkgWhiteListAllowScopes = manifest.allowScopes ?? [] as any; - this.logger.info('[PackageVersionFileService.updateUnpkgWhiteList] version:%s, total %s packages, %s scopes', + this.#unpkgWhiteListAllowPackages = manifest.allowPackages ?? ({} as any); + this.#unpkgWhiteListAllowScopes = manifest.allowScopes ?? ([] as any); + this.logger.info( + '[PackageVersionFileService.updateUnpkgWhiteList] version:%s, total %s packages, %s scopes', whiteListPackageVersion, Object.keys(this.#unpkgWhiteListAllowPackages).length, - this.#unpkgWhiteListAllowScopes.length, + this.#unpkgWhiteListAllowScopes.length ); } - async checkPackageVersionInUnpkgWhiteList(pkgScope: string, pkgName: string, pkgVersion: string) { + async checkPackageVersionInUnpkgWhiteList( + pkgScope: string, + pkgName: string, + pkgVersion: string + ) { if (!this.config.cnpmcore.enableSyncUnpkgFilesWhiteList) return; await this.#updateUnpkgWhiteList(); @@ -123,14 +150,22 @@ export class PackageVersionFileService extends AbstractService { const fullname = getFullname(pkgScope, pkgName); const pkgConfig = this.#unpkgWhiteListAllowPackages[fullname]; if (!pkgConfig?.version) { - throw new ForbiddenError(`"${fullname}" is not allow to unpkg files, see ${unpkgWhiteListUrl}`); + throw new ForbiddenError( + `"${fullname}" is not allow to unpkg files, see ${unpkgWhiteListUrl}` + ); } // satisfies 默认不会包含 prerelease 版本 // https://docs.npmjs.com/about-semantic-versioning#using-semantic-versioning-to-specify-update-types-your-package-can-accept // [x, *] 代表任意版本,这里统一通过 semver 来判断 - if (!semver.satisfies(pkgVersion, pkgConfig.version, { includePrerelease: true })) { - throw new ForbiddenError(`"${fullname}@${pkgVersion}" not satisfies "${pkgConfig.version}" to unpkg files, see ${unpkgWhiteListUrl}`); + if ( + !semver.satisfies(pkgVersion, pkgConfig.version, { + includePrerelease: true, + }) + ) { + throw new ForbiddenError( + `"${fullname}@${pkgVersion}" not satisfies "${pkgConfig.version}" to unpkg files, see ${unpkgWhiteListUrl}` + ); } } @@ -141,10 +176,21 @@ export class PackageVersionFileService extends AbstractService { const tarFile = `${tmpdir}.tgz`; const readmeFilenames: string[] = []; try { - this.logger.info('[PackageVersionFileService.syncPackageReadme:download-start] dist:%s(path:%s, size:%s) => tarFile:%s', - latestPkgVersion.tarDist.distId, latestPkgVersion.tarDist.path, latestPkgVersion.tarDist.size, tarFile); - await this.distRepository.downloadDistToFile(latestPkgVersion.tarDist, tarFile); - this.logger.info('[PackageVersionFileService.syncPackageReadme:extract-start] tmpdir:%s', tmpdir); + this.logger.info( + '[PackageVersionFileService.syncPackageReadme:download-start] dist:%s(path:%s, size:%s) => tarFile:%s', + latestPkgVersion.tarDist.distId, + latestPkgVersion.tarDist.path, + latestPkgVersion.tarDist.size, + tarFile + ); + await this.distRepository.downloadDistToFile( + latestPkgVersion.tarDist, + tarFile + ); + this.logger.info( + '[PackageVersionFileService.syncPackageReadme:extract-start] tmpdir:%s', + tmpdir + ); await tar.extract({ file: tarFile, cwd: tmpdir, @@ -163,8 +209,13 @@ export class PackageVersionFileService extends AbstractService { await this.packageManagerService.savePackageReadme(pkg, readmeFile); } } catch (err) { - this.logger.warn('[PackageVersionFileService.syncPackageReadme:error] packageVersionId: %s, readmeFilenames: %j, tmpdir: %s, error: %s', - latestPkgVersion.packageVersionId, readmeFilenames, tmpdir, err); + this.logger.warn( + '[PackageVersionFileService.syncPackageReadme:error] packageVersionId: %s, readmeFilenames: %j, tmpdir: %s, error: %s', + latestPkgVersion.packageVersionId, + readmeFilenames, + tmpdir, + err + ); // ignore TAR_BAD_ARCHIVE error if (err.code === 'TAR_BAD_ARCHIVE') return; throw err; @@ -173,8 +224,11 @@ export class PackageVersionFileService extends AbstractService { await fs.rm(tarFile, { force: true }); await fs.rm(tmpdir, { recursive: true, force: true }); } catch (err) { - this.logger.warn('[PackageVersionFileService.syncPackageReadme:warn] remove tmpdir: %s, error: %s', - tmpdir, err); + this.logger.warn( + '[PackageVersionFileService.syncPackageReadme:warn] remove tmpdir: %s, error: %s', + tmpdir, + err + ); } } } @@ -185,11 +239,17 @@ export class PackageVersionFileService extends AbstractService { if (!this.config.cnpmcore.enableUnpkg) return files; if (!this.config.cnpmcore.enableSyncUnpkgFiles) return files; - const pkg = await this.packageRepository.findPackageByPackageId(pkgVersion.packageId); + const pkg = await this.packageRepository.findPackageByPackageId( + pkgVersion.packageId + ); if (!pkg) return files; // check unpkg white list - await this.checkPackageVersionInUnpkgWhiteList(pkg.scope, pkg.name, pkgVersion.version); + await this.checkPackageVersionInUnpkgWhiteList( + pkg.scope, + pkg.name, + pkgVersion.version + ); const dirname = `unpkg_${pkg.fullname.replace('/', '_')}@${pkgVersion.version}_${randomUUID()}`; const tmpdir = await createTempDir(this.config.dataDir, dirname); @@ -197,10 +257,18 @@ export class PackageVersionFileService extends AbstractService { const paths: string[] = []; const readmeFilenames: string[] = []; try { - this.logger.info('[PackageVersionFileService.syncPackageVersionFiles:download-start] dist:%s(path:%s, size:%s) => tarFile:%s', - pkgVersion.tarDist.distId, pkgVersion.tarDist.path, pkgVersion.tarDist.size, tarFile); + this.logger.info( + '[PackageVersionFileService.syncPackageVersionFiles:download-start] dist:%s(path:%s, size:%s) => tarFile:%s', + pkgVersion.tarDist.distId, + pkgVersion.tarDist.path, + pkgVersion.tarDist.size, + tarFile + ); await this.distRepository.downloadDistToFile(pkgVersion.tarDist, tarFile); - this.logger.info('[PackageVersionFileService.syncPackageVersionFiles:extract-start] tmpdir:%s', tmpdir); + this.logger.info( + '[PackageVersionFileService.syncPackageVersionFiles:extract-start] tmpdir:%s', + tmpdir + ); await tar.extract({ file: tarFile, cwd: tmpdir, @@ -216,20 +284,38 @@ export class PackageVersionFileService extends AbstractService { }); for (const path of paths) { const localFile = join(tmpdir, path); - const file = await this.#savePackageVersionFile(pkg, pkgVersion, path, localFile); + const file = await this.#savePackageVersionFile( + pkg, + pkgVersion, + path, + localFile + ); files.push(file); } - this.logger.info('[PackageVersionFileService.syncPackageVersionFiles:success] packageVersionId: %s, %d paths, %d files, tmpdir: %s', - pkgVersion.packageVersionId, paths.length, files.length, tmpdir); + this.logger.info( + '[PackageVersionFileService.syncPackageVersionFiles:success] packageVersionId: %s, %d paths, %d files, tmpdir: %s', + pkgVersion.packageVersionId, + paths.length, + files.length, + tmpdir + ); if (readmeFilenames.length > 0) { const readmeFilename = this.#preferMarkdownReadme(readmeFilenames); const readmeFile = join(tmpdir, readmeFilename); - await this.packageManagerService.savePackageVersionReadme(pkgVersion, readmeFile); + await this.packageManagerService.savePackageVersionReadme( + pkgVersion, + readmeFile + ); } return files; } catch (err) { - this.logger.warn('[PackageVersionFileService.syncPackageVersionFiles:error] packageVersionId: %s, %d paths, tmpdir: %s, error: %s', - pkgVersion.packageVersionId, paths.length, tmpdir, err); + this.logger.warn( + '[PackageVersionFileService.syncPackageVersionFiles:error] packageVersionId: %s, %d paths, tmpdir: %s, error: %s', + pkgVersion.packageVersionId, + paths.length, + tmpdir, + err + ); // ignore TAR_BAD_ARCHIVE error if (err.code === 'TAR_BAD_ARCHIVE') return files; throw err; @@ -238,16 +324,27 @@ export class PackageVersionFileService extends AbstractService { await fs.rm(tarFile, { force: true }); await fs.rm(tmpdir, { recursive: true, force: true }); } catch (err) { - this.logger.warn('[PackageVersionFileService.syncPackageVersionFiles:warn] remove tmpdir: %s, error: %s', - tmpdir, err); + this.logger.warn( + '[PackageVersionFileService.syncPackageVersionFiles:warn] remove tmpdir: %s, error: %s', + tmpdir, + err + ); } } } - async #savePackageVersionFile(pkg: Package, pkgVersion: PackageVersion, path: string, localFile: string) { + async #savePackageVersionFile( + pkg: Package, + pkgVersion: PackageVersion, + path: string, + localFile: string + ) { const { directory, name } = this.#getDirectoryAndName(path); let file = await this.packageVersionFileRepository.findPackageVersionFile( - pkgVersion.packageVersionId, directory, name); + pkgVersion.packageVersionId, + directory, + name + ); if (file) return file; const stat = await fs.stat(localFile); const distIntegrity = await calculateIntegrity(localFile); @@ -270,8 +367,12 @@ export class PackageVersionFileService extends AbstractService { }); try { await this.packageVersionFileRepository.createPackageVersionFile(file); - this.logger.info('[PackageVersionFileService.#savePackageVersionFile:success] fileId: %s, size: %s, path: %s', - file.packageVersionFileId, dist.size, file.path); + this.logger.info( + '[PackageVersionFileService.#savePackageVersionFile:success] fileId: %s, size: %s, path: %s', + file.packageVersionFileId, + dist.size, + file.path + ); } catch (err) { // ignore Duplicate entry if (isDuplicateKeyError(err)) { @@ -302,7 +403,7 @@ export class PackageVersionFileService extends AbstractService { #matchReadmeFilename(filename: string) { // support README,README.* // https://github.com/npm/read-package-json/blob/main/lib/read-json.js#L280 - return (/^README(\.\w{1,20}|$)/i.test(filename)); + return /^README(\.\w{1,20}|$)/i.test(filename); } // https://github.com/npm/read-package-json/blob/main/lib/read-json.js#L280 diff --git a/app/core/service/PackageVersionService.ts b/app/core/service/PackageVersionService.ts index 86a56e00..7fc8a84f 100644 --- a/app/core/service/PackageVersionService.ts +++ b/app/core/service/PackageVersionService.ts @@ -1,14 +1,15 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import semver, { Range } from 'semver'; -import { Result, AliasResult } from 'npm-package-arg'; -import { PackageVersionRepository } from '../../repository/PackageVersionRepository.js'; +import type { Result, AliasResult } from 'npm-package-arg'; +import type { PackageVersionRepository } from '../../repository/PackageVersionRepository.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; import { SqlRange } from '../entity/SqlRange.js'; -import { BugVersionService } from './BugVersionService.js'; -import { type PackageJSONType, PackageRepository } from '../../repository/PackageRepository.js'; -import { DistRepository } from '../../repository/DistRepository.js'; -import { BugVersionAdvice } from '../entity/BugVersion.js'; -import { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; +import type { BugVersionService } from './BugVersionService.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import { type PackageJSONType } from '../../repository/PackageRepository.js'; +import type { DistRepository } from '../../repository/DistRepository.js'; +import type { BugVersionAdvice } from '../entity/BugVersion.js'; +import type { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -29,16 +30,23 @@ export class PackageVersionService { @Inject() private readonly distRepository: DistRepository; - async readManifest(pkgId: string, spec: Result, isFullManifests: boolean, withBugVersion = true): Promise { + async readManifest( + pkgId: string, + spec: Result, + isFullManifests: boolean, + withBugVersion = true + ): Promise { const realSpec = this.findRealSpec(spec); let version = await this.getVersion(realSpec, false); if (!version) { return undefined; } - let bugVersionAdvice: { - advice: BugVersionAdvice, - version: string, - } | undefined; + let bugVersionAdvice: + | { + advice: BugVersionAdvice; + version: string; + } + | undefined; if (withBugVersion) { const bugVersion = await this.bugVersionService.getBugVersion(); if (bugVersion) { @@ -54,9 +62,15 @@ export class PackageVersionService { } let manifest; if (isFullManifests) { - manifest = await this.distRepository.findPackageVersionManifest(pkgId, version); + manifest = await this.distRepository.findPackageVersionManifest( + pkgId, + version + ); } else { - manifest = await this.distRepository.findPackageAbbreviatedManifest(pkgId, version); + manifest = await this.distRepository.findPackageAbbreviatedManifest( + pkgId, + version + ); } if (manifest && bugVersionAdvice) { manifest.deprecated = `[WARNING] Use ${bugVersionAdvice.advice.version} instead of ${bugVersionAdvice.version}, reason: ${bugVersionAdvice.advice.reason}`; @@ -82,12 +96,19 @@ export class PackageVersionService { return realSpec; } - async getVersion(spec: Result, withBugVersion = true): Promise { + async getVersion( + spec: Result, + withBugVersion = true + ): Promise { let version: string | undefined | null; - const [ scope, name ] = getScopeAndName(spec.name!); + const [scope, name] = getScopeAndName(spec.name!); // 优先通过 tag 来进行判断 if (spec.type === 'tag') { - version = await this.packageVersionRepository.findVersionByTag(scope, name, spec.fetchSpec!); + version = await this.packageVersionRepository.findVersionByTag( + scope, + name, + spec.fetchSpec! + ); } else if (spec.type === 'version') { // 1.0.0 // '=1.0.0' => '1.0.0' @@ -97,17 +118,31 @@ export class PackageVersionService { // a@1.1 情况下,1.1 会解析为 range,如果有对应的 distTag 时会失效 // 这里需要进行兼容 // 仅当 spec 不为 version 时才查询,减少请求次数 - const versionMatchTag = await this.packageVersionRepository.findVersionByTag(scope, name, spec.fetchSpec!); + const versionMatchTag = + await this.packageVersionRepository.findVersionByTag( + scope, + name, + spec.fetchSpec! + ); if (versionMatchTag) { version = versionMatchTag; } else { const range = new Range(spec.fetchSpec!); const paddingSemVer = new SqlRange(range); if (paddingSemVer.containPreRelease) { - const versions = await this.packageVersionRepository.findSatisfyVersionsWithPrerelease(scope, name, paddingSemVer); + const versions = + await this.packageVersionRepository.findSatisfyVersionsWithPrerelease( + scope, + name, + paddingSemVer + ); version = semver.maxSatisfying(versions, range); } else { - version = await this.packageVersionRepository.findMaxSatisfyVersion(scope, name, paddingSemVer); + version = await this.packageVersionRepository.findMaxSatisfyVersion( + scope, + name, + paddingSemVer + ); } } } @@ -124,7 +159,7 @@ export class PackageVersionService { } async findBlockInfo(fullname: string) { - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const packageId = await this.packageRepository.findPackageId(scope, name); if (!packageId) { return null; diff --git a/app/core/service/ProxyCacheService.ts b/app/core/service/ProxyCacheService.ts index 1bb40f85..c23ae0f3 100644 --- a/app/core/service/ProxyCacheService.ts +++ b/app/core/service/ProxyCacheService.ts @@ -1,20 +1,32 @@ -import { EggHttpClient, HttpClientRequestOptions, HttpClientResponse, Context } from 'egg'; +import type { + EggHttpClient, + HttpClientRequestOptions, + HttpClientResponse, + Context, +} from 'egg'; import { ForbiddenError } from 'egg-errors'; import { SingletonProto, AccessLevel, Inject } from '@eggjs/tegg'; -import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; +import type { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; import { valid as semverValid } from 'semver'; import { AbstractService } from '../../common/AbstractService.js'; -import { TaskService } from './TaskService.js'; -import { CacheService } from './CacheService.js'; -import { RegistryManagerService } from './RegistryManagerService.js'; -import { NPMRegistry } from '../../common/adapter/NPMRegistry.js'; -import { NFSAdapter } from '../../common/adapter/NFSAdapter.js'; +import type { TaskService } from './TaskService.js'; +import type { CacheService } from './CacheService.js'; +import type { RegistryManagerService } from './RegistryManagerService.js'; +import type { NPMRegistry } from '../../common/adapter/NPMRegistry.js'; +import type { NFSAdapter } from '../../common/adapter/NFSAdapter.js'; import { ProxyCache } from '../entity/ProxyCache.js'; -import { Task, UpdateProxyCacheTaskOptions, CreateUpdateProxyCacheTask } from '../entity/Task.js'; -import { ProxyCacheRepository } from '../../repository/ProxyCacheRepository.js'; +import type { + UpdateProxyCacheTaskOptions, + CreateUpdateProxyCacheTask, +} from '../entity/Task.js'; +import { Task } from '../entity/Task.js'; +import type { ProxyCacheRepository } from '../../repository/ProxyCacheRepository.js'; import { TaskType, TaskState } from '../../common/enum/Task.js'; import { calculateIntegrity } from '../../common/PackageUtil.js'; -import { ABBREVIATED_META_TYPE, PROXY_CACHE_DIR_NAME } from '../../common/constants.js'; +import { + ABBREVIATED_META_TYPE, + PROXY_CACHE_DIR_NAME, +} from '../../common/constants.js'; import { DIST_NAMES, isPkgManifest } from '../entity/Package.js'; import type { AbbreviatedPackageManifestType, @@ -27,9 +39,13 @@ function isoNow() { return new Date().toISOString(); } -type GetSourceManifestAndCacheReturnType = T extends DIST_NAMES.ABBREVIATED | DIST_NAMES.MANIFEST ? AbbreviatedPackageJSONType | PackageJSONType : - T extends DIST_NAMES.FULL_MANIFESTS | DIST_NAMES.ABBREVIATED_MANIFESTS ? AbbreviatedPackageManifestType|PackageManifestType : never; - +type GetSourceManifestAndCacheReturnType = T extends + | DIST_NAMES.ABBREVIATED + | DIST_NAMES.MANIFEST + ? AbbreviatedPackageJSONType | PackageJSONType + : T extends DIST_NAMES.FULL_MANIFESTS | DIST_NAMES.ABBREVIATED_MANIFESTS + ? AbbreviatedPackageManifestType | PackageManifestType + : never; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -50,33 +66,54 @@ export class ProxyCacheService extends AbstractService { @Inject() private readonly cacheService: CacheService; @Inject() - private readonly backgroundTaskHelper:BackgroundTaskHelper; + private readonly backgroundTaskHelper: BackgroundTaskHelper; - async getPackageVersionTarResponse(fullname: string, ctx: Context): Promise { + async getPackageVersionTarResponse( + fullname: string, + ctx: Context + ): Promise { if (this.config.cnpmcore.syncPackageBlockList.includes(fullname)) { - throw new ForbiddenError(`stop proxy by block list: ${JSON.stringify(this.config.cnpmcore.syncPackageBlockList)}`); + throw new ForbiddenError( + `stop proxy by block list: ${JSON.stringify(this.config.cnpmcore.syncPackageBlockList)}` + ); } return await this.getProxyResponse(ctx); } - async getPackageManifest(fullname: string, fileType: DIST_NAMES.FULL_MANIFESTS| DIST_NAMES.ABBREVIATED_MANIFESTS): Promise { + async getPackageManifest( + fullname: string, + fileType: DIST_NAMES.FULL_MANIFESTS | DIST_NAMES.ABBREVIATED_MANIFESTS + ): Promise { const isFullManifests = fileType === DIST_NAMES.FULL_MANIFESTS; - const cachedStoreKey = (await this.proxyCacheRepository.findProxyCache(fullname, fileType))?.filePath; + const cachedStoreKey = ( + await this.proxyCacheRepository.findProxyCache(fullname, fileType) + )?.filePath; if (cachedStoreKey) { try { const nfsBytes = await this.nfsAdapter.getBytes(cachedStoreKey); - if (!nfsBytes) throw new Error('not found proxy cache, try again later.'); + if (!nfsBytes) + throw new Error('not found proxy cache, try again later.'); const nfsBuffer = Buffer.from(nfsBytes); const { shasum: etag } = await calculateIntegrity(nfsBytes); - await this.cacheService.savePackageEtagAndManifests(fullname, isFullManifests, etag, nfsBuffer); + await this.cacheService.savePackageEtagAndManifests( + fullname, + isFullManifests, + etag, + nfsBuffer + ); const nfsString = nfsBuffer.toString(); const nfsPkgManifest = JSON.parse(nfsString); - return nfsPkgManifest as AbbreviatedPackageManifestType|PackageManifestType; + return nfsPkgManifest as + | AbbreviatedPackageManifestType + | PackageManifestType; } catch (error) { /* c8 ignore next 6 */ - if (error.message.includes('not found proxy cache') || error.message.includes('Unexpected token : in JSON at')) { + if ( + error.message.includes('not found proxy cache') || + error.message.includes('Unexpected token : in JSON at') + ) { await this.nfsAdapter.remove(cachedStoreKey); await this.proxyCacheRepository.removeProxyCache(fullname, fileType); } @@ -84,7 +121,10 @@ export class ProxyCacheService extends AbstractService { } } - const manifest = await this.getRewrittenManifest(fullname, fileType); + const manifest = await this.getRewrittenManifest( + fullname, + fileType + ); this.backgroundTaskHelper.run(async () => { await this.storeRewrittenManifest(manifest, fullname, fileType); const cachedFiles = ProxyCache.create({ fullname, fileType }); @@ -94,32 +134,55 @@ export class ProxyCacheService extends AbstractService { } // used by GET /:fullname/:versionOrTag - async getPackageVersionManifest(fullname: string, fileType: DIST_NAMES.ABBREVIATED | DIST_NAMES.MANIFEST, versionOrTag: string): Promise { + async getPackageVersionManifest( + fullname: string, + fileType: DIST_NAMES.ABBREVIATED | DIST_NAMES.MANIFEST, + versionOrTag: string + ): Promise { let version; if (semverValid(versionOrTag)) { version = versionOrTag; } else { - const pkgManifest = await this.getPackageManifest(fullname, DIST_NAMES.ABBREVIATED_MANIFESTS); + const pkgManifest = await this.getPackageManifest( + fullname, + DIST_NAMES.ABBREVIATED_MANIFESTS + ); const distTags = pkgManifest['dist-tags'] || {}; version = distTags[versionOrTag] ? distTags[versionOrTag] : versionOrTag; } - const cachedStoreKey = (await this.proxyCacheRepository.findProxyCache(fullname, fileType, version))?.filePath; + const cachedStoreKey = ( + await this.proxyCacheRepository.findProxyCache( + fullname, + fileType, + version + ) + )?.filePath; if (cachedStoreKey) { try { const nfsBytes = await this.nfsAdapter.getBytes(cachedStoreKey); - if (!nfsBytes) throw new Error('not found proxy cache, try again later.'); + if (!nfsBytes) + throw new Error('not found proxy cache, try again later.'); const nfsString = Buffer.from(nfsBytes!).toString(); - return JSON.parse(nfsString) as PackageJSONType | AbbreviatedPackageJSONType; + return JSON.parse(nfsString) as + | PackageJSONType + | AbbreviatedPackageJSONType; } catch (error) { /* c8 ignore next 6 */ - if (error.message.includes('not found proxy cache') || error.message.includes('Unexpected token : in JSON at')) { + if ( + error.message.includes('not found proxy cache') || + error.message.includes('Unexpected token : in JSON at') + ) { await this.nfsAdapter.remove(cachedStoreKey); await this.proxyCacheRepository.removeProxyCache(fullname, fileType); } throw error; } } - const manifest = await this.getRewrittenManifest(fullname, fileType, versionOrTag); + const manifest = await this.getRewrittenManifest( + fullname, + fileType, + versionOrTag + ); this.backgroundTaskHelper.run(async () => { await this.storeRewrittenManifest(manifest, fullname, fileType); const cachedFiles = ProxyCache.create({ fullname, fileType, version }); @@ -128,28 +191,46 @@ export class ProxyCacheService extends AbstractService { return manifest; } - async removeProxyCache(fullname: string, fileType: DIST_NAMES, version?: string) { + async removeProxyCache( + fullname: string, + fileType: DIST_NAMES, + version?: string + ) { const storeKey = isPkgManifest(fileType) ? `/${PROXY_CACHE_DIR_NAME}/${fullname}/${fileType}` : `/${PROXY_CACHE_DIR_NAME}/${fullname}/${version}/${fileType}`; await this.nfsAdapter.remove(storeKey); - await this.proxyCacheRepository.removeProxyCache(fullname, fileType, version); + await this.proxyCacheRepository.removeProxyCache( + fullname, + fileType, + version + ); } - replaceTarballUrl(manifest: GetSourceManifestAndCacheReturnType, fileType: T) { + replaceTarballUrl( + manifest: GetSourceManifestAndCacheReturnType, + fileType: T + ) { const { sourceRegistry, registry } = this.config.cnpmcore; if (isPkgManifest(fileType)) { // pkg manifest - const versionMap = (manifest as AbbreviatedPackageManifestType|PackageManifestType)?.versions; + const versionMap = ( + manifest as AbbreviatedPackageManifestType | PackageManifestType + )?.versions; for (const key in versionMap) { const versionItem = versionMap[key]; if (versionItem?.dist?.tarball) { - versionItem.dist.tarball = versionItem.dist.tarball.replace(sourceRegistry, registry); + versionItem.dist.tarball = versionItem.dist.tarball.replace( + sourceRegistry, + registry + ); } } } else { // pkg version manifest - const distItem = (manifest as AbbreviatedPackageJSONType | PackageJSONType).dist; + const distItem = ( + manifest as AbbreviatedPackageJSONType | PackageJSONType + ).dist; if (distItem?.tarball) { distItem.tarball = distItem.tarball.replace(sourceRegistry, registry); } @@ -157,8 +238,14 @@ export class ProxyCacheService extends AbstractService { return manifest; } - async createTask(targetName: string, options: UpdateProxyCacheTaskOptions): Promise { - return await this.taskService.createTask(Task.createUpdateProxyCache(targetName, options), false) as CreateUpdateProxyCacheTask; + async createTask( + targetName: string, + options: UpdateProxyCacheTaskOptions + ): Promise { + return (await this.taskService.createTask( + Task.createUpdateProxyCache(targetName, options), + false + )) as CreateUpdateProxyCacheTask; } async findExecuteTask() { @@ -170,58 +257,115 @@ export class ProxyCacheService extends AbstractService { const fullname = (task as CreateUpdateProxyCacheTask).data.fullname; const { fileType, version } = (task as CreateUpdateProxyCacheTask).data; let cachedManifest; - logs.push(`[${isoNow()}] 🚧🚧🚧🚧🚧 Start update "${fullname}-${fileType}" 🚧🚧🚧🚧🚧`); + logs.push( + `[${isoNow()}] 🚧🚧🚧🚧🚧 Start update "${fullname}-${fileType}" 🚧🚧🚧🚧🚧` + ); try { - const cachedFiles = await this.proxyCacheRepository.findProxyCache(fullname, fileType); - if (!cachedFiles) throw new Error('task params error, can not found record in repo.'); - cachedManifest = await this.getRewrittenManifest(fullname, fileType); + const cachedFiles = await this.proxyCacheRepository.findProxyCache( + fullname, + fileType + ); + if (!cachedFiles) + throw new Error('task params error, can not found record in repo.'); + cachedManifest = await this.getRewrittenManifest( + fullname, + fileType + ); await this.storeRewrittenManifest(cachedManifest, fullname, fileType); ProxyCache.update(cachedFiles); await this.proxyCacheRepository.saveProxyCache(cachedFiles); } catch (error) { task.error = error; logs.push(`[${isoNow()}] ❌ ${task.error}`); - logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname}-${fileType} ${version ?? ''} ❌❌❌❌❌`); + logs.push( + `[${isoNow()}] ❌❌❌❌❌ ${fullname}-${fileType} ${version ?? ''} ❌❌❌❌❌` + ); await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n')); - this.logger.info('[ProxyCacheService.executeTask:fail] taskId: %s, targetName: %s, %s', - task.taskId, task.targetName, task.error); + this.logger.info( + '[ProxyCacheService.executeTask:fail] taskId: %s, targetName: %s, %s', + task.taskId, + task.targetName, + task.error + ); return; } logs.push(`[${isoNow()}] 🟢 Update Success.`); const isFullManifests = fileType === DIST_NAMES.FULL_MANIFESTS; - const cachedKey = await this.cacheService.getPackageEtag(fullname, isFullManifests); + const cachedKey = await this.cacheService.getPackageEtag( + fullname, + isFullManifests + ); if (cachedKey) { const cacheBytes = Buffer.from(JSON.stringify(cachedManifest)); const { shasum: etag } = await calculateIntegrity(cacheBytes); - await this.cacheService.savePackageEtagAndManifests(fullname, isFullManifests, etag, cacheBytes); + await this.cacheService.savePackageEtagAndManifests( + fullname, + isFullManifests, + etag, + cacheBytes + ); logs.push(`[${isoNow()}] 🟢 Update Cache Success.`); } await this.taskService.finishTask(task, TaskState.Success, logs.join('\n')); } // only used by schedule task - private async getRewrittenManifest(fullname:string, fileType: T, versionOrTag?:string): Promise> { + private async getRewrittenManifest( + fullname: string, + fileType: T, + versionOrTag?: string + ): Promise> { let responseResult; const USER_AGENT = 'npm_service.cnpmjs.org/cnpmcore'; switch (fileType) { case DIST_NAMES.FULL_MANIFESTS: { const url = `/${encodeURIComponent(fullname)}?t=${Date.now()}&cache=0`; - responseResult = await this.getProxyResponse({ url, headers: { accept: 'application/json', 'user-agent': USER_AGENT } }, { dataType: 'json' }); + responseResult = await this.getProxyResponse( + { + url, + headers: { accept: 'application/json', 'user-agent': USER_AGENT }, + }, + { dataType: 'json' } + ); break; } case DIST_NAMES.ABBREVIATED_MANIFESTS: { const url = `/${encodeURIComponent(fullname)}?t=${Date.now()}&cache=0`; - responseResult = await this.getProxyResponse({ url, headers: { accept: ABBREVIATED_META_TYPE, 'user-agent': USER_AGENT } }, { dataType: 'json' }); + responseResult = await this.getProxyResponse( + { + url, + headers: { + accept: ABBREVIATED_META_TYPE, + 'user-agent': USER_AGENT, + }, + }, + { dataType: 'json' } + ); break; } case DIST_NAMES.MANIFEST: { const url = `/${encodeURIComponent(fullname)}/${encodeURIComponent(versionOrTag!)}`; - responseResult = await this.getProxyResponse({ url, headers: { accept: 'application/json', 'user-agent': USER_AGENT } }, { dataType: 'json' }); + responseResult = await this.getProxyResponse( + { + url, + headers: { accept: 'application/json', 'user-agent': USER_AGENT }, + }, + { dataType: 'json' } + ); break; } case DIST_NAMES.ABBREVIATED: { const url = `/${encodeURIComponent(fullname)}/${encodeURIComponent(versionOrTag!)}`; - responseResult = await this.getProxyResponse({ url, headers: { accept: ABBREVIATED_META_TYPE, 'user-agent': USER_AGENT } }, { dataType: 'json' }); + responseResult = await this.getProxyResponse( + { + url, + headers: { + accept: ABBREVIATED_META_TYPE, + 'user-agent': USER_AGENT, + }, + }, + { dataType: 'json' } + ); break; } default: @@ -233,7 +377,11 @@ export class ProxyCacheService extends AbstractService { return manifest; } - private async storeRewrittenManifest(manifest: any, fullname: string, fileType: DIST_NAMES) { + private async storeRewrittenManifest( + manifest: any, + fullname: string, + fileType: DIST_NAMES + ) { let storeKey: string; if (isPkgManifest(fileType)) { storeKey = `/${PROXY_CACHE_DIR_NAME}/${fullname}/${fileType}`; @@ -245,14 +393,19 @@ export class ProxyCacheService extends AbstractService { await this.nfsAdapter.uploadBytes(storeKey, nfsBytes); } - async getProxyResponse(ctx: Partial, options?: HttpClientRequestOptions): Promise { + async getProxyResponse( + ctx: Partial, + options?: HttpClientRequestOptions + ): Promise { const registry = this.npmRegistry.registry; - const remoteAuthToken = await this.registryManagerService.getAuthTokenByRegistryHost(registry); - const authorization = this.npmRegistry.genAuthorizationHeader(remoteAuthToken); + const remoteAuthToken = + await this.registryManagerService.getAuthTokenByRegistryHost(registry); + const authorization = + this.npmRegistry.genAuthorizationHeader(remoteAuthToken); const url = `${this.npmRegistry.registry}${ctx.url}`; - const res = await this.httpclient.request(url, { + const res = (await this.httpclient.request(url, { timing: true, followRedirect: true, // once redirection is also count as a retry @@ -268,8 +421,12 @@ export class ProxyCacheService extends AbstractService { 'x-forwarded-for': ctx?.ip, via: `1.1, ${this.config.cnpmcore.registry}`, }, - }) as HttpClientResponse; - this.logger.info('[ProxyCacheService:getProxyStreamResponse] %s, status: %s', url, res.status); + })) as HttpClientResponse; + this.logger.info( + '[ProxyCacheService:getProxyStreamResponse] %s, status: %s', + url, + res.status + ); return res; } } diff --git a/app/core/service/RegistryManagerService.ts b/app/core/service/RegistryManagerService.ts index 7442c1c1..71c379ac 100644 --- a/app/core/service/RegistryManagerService.ts +++ b/app/core/service/RegistryManagerService.ts @@ -1,23 +1,30 @@ -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { E400, NotFoundError } from 'egg-errors'; -import { RegistryRepository } from '../../repository/RegistryRepository.js'; +import type { RegistryRepository } from '../../repository/RegistryRepository.js'; import { AbstractService } from '../../common/AbstractService.js'; import { Registry } from '../entity/Registry.js'; -import { PageOptions, PageResult } from '../util/EntityUtil.js'; -import { ScopeManagerService } from './ScopeManagerService.js'; -import { TaskService } from './TaskService.js'; +import type { PageOptions, PageResult } from '../util/EntityUtil.js'; +import type { ScopeManagerService } from './ScopeManagerService.js'; +import type { TaskService } from './TaskService.js'; import { Task } from '../entity/Task.js'; -import { ChangesStreamMode, PresetRegistryName } from '../../common/constants.js'; +import { + ChangesStreamMode, + PresetRegistryName, +} from '../../common/constants.js'; import { RegistryType } from '../../common/enum/Registry.js'; -export interface CreateRegistryCmd extends Pick { +export interface CreateRegistryCmd + extends Pick< + Registry, + 'changeStream' | 'host' | 'userPrefix' | 'type' | 'name' | 'authToken' + > { operatorId?: string; } -export interface UpdateRegistryCmd extends Pick { +export interface UpdateRegistryCmd + extends Pick< + Registry, + 'changeStream' | 'host' | 'type' | 'name' | 'authToken' + > { operatorId?: string; } export interface RemoveRegistryCmd extends Pick { @@ -43,26 +50,50 @@ export class RegistryManagerService extends AbstractService { async createSyncChangesStream(startSyncCmd: StartSyncCmd): Promise { const { registryId, operatorId = '-', since } = startSyncCmd; - this.logger.info('[RegistryManagerService.startSyncChangesStream:prepare] operatorId: %s, registryId: %s, since: %s', operatorId, registryId, since); - const registry = await this.registryRepository.findRegistryByRegistryId(registryId); + this.logger.info( + '[RegistryManagerService.startSyncChangesStream:prepare] operatorId: %s, registryId: %s, since: %s', + operatorId, + registryId, + since + ); + const registry = + await this.registryRepository.findRegistryByRegistryId(registryId); if (!registry) { throw new NotFoundError(`registry ${registryId} not found`); } // 防止和 GLOBAL_WORKER 冲突,只能有一个默认的全局 registry - const scopesCount = await this.scopeManagerService.countByRegistryId(registryId); + const scopesCount = + await this.scopeManagerService.countByRegistryId(registryId); if (scopesCount === 0) { - throw new E400(`registry ${registryId} has no scopes, please create scopes first`); + throw new E400( + `registry ${registryId} has no scopes, please create scopes first` + ); } // 启动 changeStream const targetName = `${registry.name.toUpperCase()}_WORKER`; - await this.taskService.createTask(Task.createChangesStream(targetName, registryId, since), false); + await this.taskService.createTask( + Task.createChangesStream(targetName, registryId, since), + false + ); } async createRegistry(createCmd: CreateRegistryCmd): Promise { - const { name, changeStream = '', host, userPrefix = '', type, operatorId = '-', authToken } = createCmd; - this.logger.info('[RegistryManagerService.createRegistry:prepare] operatorId: %s, createCmd: %j', operatorId, createCmd); + const { + name, + changeStream = '', + host, + userPrefix = '', + type, + operatorId = '-', + authToken, + } = createCmd; + this.logger.info( + '[RegistryManagerService.createRegistry:prepare] operatorId: %s, createCmd: %j', + operatorId, + createCmd + ); const registry = Registry.create({ name, changeStream, @@ -78,9 +109,21 @@ export class RegistryManagerService extends AbstractService { // 更新部分 registry 信息 // 不允许 userPrefix 字段变更 async updateRegistry(registryId: string, updateCmd: UpdateRegistryCmd) { - const { name, changeStream, host, type, operatorId = '-', authToken } = updateCmd; - this.logger.info('[RegistryManagerService.updateRegistry:prepare] operatorId: %s, updateCmd: %j', operatorId, updateCmd); - const registry = await this.registryRepository.findRegistryByRegistryId(registryId); + const { + name, + changeStream, + host, + type, + operatorId = '-', + authToken, + } = updateCmd; + this.logger.info( + '[RegistryManagerService.updateRegistry:prepare] operatorId: %s, updateCmd: %j', + operatorId, + updateCmd + ); + const registry = + await this.registryRepository.findRegistryByRegistryId(registryId); if (!registry) { throw new NotFoundError(`registry ${registryId} not found`); } @@ -108,7 +151,9 @@ export class RegistryManagerService extends AbstractService { } async findByRegistryHost(host?: string): Promise { - return host ? await this.registryRepository.findRegistryByRegistryHost(host) : null; + return host + ? await this.registryRepository.findRegistryByRegistryHost(host) + : null; } // 删除 Registry 方法 @@ -116,13 +161,22 @@ export class RegistryManagerService extends AbstractService { // 同时删除对应的 scope 数据 async remove(removeCmd: RemoveRegistryCmd): Promise { const { registryId, operatorId = '-' } = removeCmd; - this.logger.info('[RegistryManagerService.remove:prepare] operatorId: %s, registryId: %s', operatorId, registryId); + this.logger.info( + '[RegistryManagerService.remove:prepare] operatorId: %s, registryId: %s', + operatorId, + registryId + ); await this.registryRepository.removeRegistry(registryId); - await this.scopeManagerService.removeByRegistryId({ registryId, operatorId }); + await this.scopeManagerService.removeByRegistryId({ + registryId, + operatorId, + }); } async ensureSelfRegistry(): Promise { - const existRegistry = await this.registryRepository.findRegistry(PresetRegistryName.self); + const existRegistry = await this.registryRepository.findRegistry( + PresetRegistryName.self + ); if (existRegistry) { return existRegistry; } @@ -138,18 +192,26 @@ export class RegistryManagerService extends AbstractService { }); return newRegistry; - } async ensureDefaultRegistry(): Promise { - const existRegistry = await this.registryRepository.findRegistry(PresetRegistryName.default); + const existRegistry = await this.registryRepository.findRegistry( + PresetRegistryName.default + ); if (existRegistry) { return existRegistry; } // 从配置文件默认生成 - const { changesStreamRegistryMode, changesStreamRegistry: changesStreamHost, sourceRegistry: host } = this.config.cnpmcore; - const type = changesStreamRegistryMode === ChangesStreamMode.json ? RegistryType.Cnpmcore : RegistryType.Npm; + const { + changesStreamRegistryMode, + changesStreamRegistry: changesStreamHost, + sourceRegistry: host, + } = this.config.cnpmcore; + const type = + changesStreamRegistryMode === ChangesStreamMode.json + ? RegistryType.Cnpmcore + : RegistryType.Npm; const registry = await this.createRegistry({ name: PresetRegistryName.default, type, @@ -159,15 +221,13 @@ export class RegistryManagerService extends AbstractService { }); return registry; - } - async getAuthTokenByRegistryHost(host: string): Promise { + async getAuthTokenByRegistryHost(host: string): Promise { const registry = await this.findByRegistryHost(host); if (!registry) { return undefined; } return registry.authToken; } - } diff --git a/app/core/service/ScopeManagerService.ts b/app/core/service/ScopeManagerService.ts index 87c6da09..df944496 100644 --- a/app/core/service/ScopeManagerService.ts +++ b/app/core/service/ScopeManagerService.ts @@ -1,17 +1,14 @@ -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; -import { ScopeRepository } from '../../repository/ScopeRepository.js'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; +import type { ScopeRepository } from '../../repository/ScopeRepository.js'; import { AbstractService } from '../../common/AbstractService.js'; import { Scope } from '../entity/Scope.js'; -import { PageOptions, PageResult } from '../util/EntityUtil.js'; +import type { PageOptions, PageResult } from '../util/EntityUtil.js'; export interface CreateScopeCmd extends Pick { operatorId?: string; } -export interface UpdateRegistryCmd extends Pick { +export interface UpdateRegistryCmd + extends Pick { operatorId?: string; } @@ -43,7 +40,11 @@ export class ScopeManagerService extends AbstractService { async createScope(createCmd: CreateScopeCmd): Promise { const { name, registryId, operatorId } = createCmd; - this.logger.info('[ScopeManagerService.CreateScope:prepare] operatorId: %s, createCmd: %s', operatorId, createCmd); + this.logger.info( + '[ScopeManagerService.CreateScope:prepare] operatorId: %s, createCmd: %s', + operatorId, + createCmd + ); const scope = Scope.create({ name, registryId, @@ -56,19 +57,32 @@ export class ScopeManagerService extends AbstractService { return await this.scopeRepository.listScopes(page); } - async listScopesByRegistryId(registryId: string, page: PageOptions): Promise> { + async listScopesByRegistryId( + registryId: string, + page: PageOptions + ): Promise> { return await this.scopeRepository.listScopesByRegistryId(registryId, page); } - async removeByRegistryId(removeCmd: RemoveScopeByRegistryIdCmd): Promise { + async removeByRegistryId( + removeCmd: RemoveScopeByRegistryIdCmd + ): Promise { const { registryId, operatorId } = removeCmd; - this.logger.info('[ScopeManagerService.remove:prepare] operatorId: %s, registryId: %s', operatorId, registryId); + this.logger.info( + '[ScopeManagerService.remove:prepare] operatorId: %s, registryId: %s', + operatorId, + registryId + ); return await this.scopeRepository.removeScopeByRegistryId(registryId); } async remove(removeCmd: RemoveScopeCmd): Promise { const { scopeId, operatorId } = removeCmd; - this.logger.info('[ScopeManagerService.remove:prepare] operatorId: %s, scopeId: %s', operatorId, scopeId); + this.logger.info( + '[ScopeManagerService.remove:prepare] operatorId: %s, scopeId: %s', + operatorId, + scopeId + ); return await this.scopeRepository.removeScope(scopeId); } } diff --git a/app/core/service/TaskService.ts b/app/core/service/TaskService.ts index 9d3e1f65..df406b01 100644 --- a/app/core/service/TaskService.ts +++ b/app/core/service/TaskService.ts @@ -1,14 +1,11 @@ -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; -import { NFSAdapter } from '../../common/adapter/NFSAdapter.js'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; +import type { NFSAdapter } from '../../common/adapter/NFSAdapter.js'; import { TaskState, TaskType } from '../../common/enum/Task.js'; import { AbstractService } from '../../common/AbstractService.js'; -import { TaskRepository } from '../../repository/TaskRepository.js'; -import { Task, CreateSyncPackageTaskData } from '../entity/Task.js'; -import { QueueAdapter } from '../../common/typing.js'; +import type { TaskRepository } from '../../repository/TaskRepository.js'; +import type { CreateSyncPackageTaskData } from '../entity/Task.js'; +import { Task } from '../entity/Task.js'; +import type { QueueAdapter } from '../../common/typing.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -26,7 +23,10 @@ export class TaskService extends AbstractService { } public async createTask(task: Task, addTaskQueueOnExists: boolean) { - const existsTask = await this.taskRepository.findTaskByTargetName(task.targetName, task.type); + const existsTask = await this.taskRepository.findTaskByTargetName( + task.targetName, + task.type + ); // 只在包同步场景下做任务合并,其余场景通过 bizId 来进行任务幂等 if (existsTask && Task.needMergeWhenWaiting(task.type)) { @@ -35,15 +35,23 @@ export class TaskService extends AbstractService { if (existsTask.state === TaskState.Waiting) { if (task.type === TaskType.SyncPackage) { // 如果是specificVersions的任务则可能可以和存量任务进行合并 - const specificVersions = (task as Task).data?.specificVersions; - const existsTaskSpecificVersions = (existsTask as Task).data?.specificVersions; + const specificVersions = (task as Task) + .data?.specificVersions; + const existsTaskSpecificVersions = ( + existsTask as Task + ).data?.specificVersions; if (existsTaskSpecificVersions) { if (specificVersions) { // 存量的任务和新增任务都是同步指定版本的任务,合并两者版本至存量任务 - await this.taskRepository.updateSpecificVersionsOfWaitingTask(existsTask, specificVersions); + await this.taskRepository.updateSpecificVersionsOfWaitingTask( + existsTask, + specificVersions + ); } else { // 新增任务是全量同步任务,移除存量任务中的指定版本使其成为全量同步任务 - await this.taskRepository.updateSpecificVersionsOfWaitingTask(existsTask); + await this.taskRepository.updateSpecificVersionsOfWaitingTask( + existsTask + ); } } // 存量任务是全量同步任务,直接提高任务优先级 @@ -54,8 +62,13 @@ export class TaskService extends AbstractService { if (queueLength < this.config.cnpmcore.taskQueueHighWaterSize) { // make sure waiting task in queue await this.queueAdapter.push(task.type, existsTask.taskId); - this.logger.info('[TaskService.createTask:exists-to-queue] taskType: %s, targetName: %s, taskId: %s, queue size: %s', - task.type, task.targetName, task.taskId, queueLength); + this.logger.info( + '[TaskService.createTask:exists-to-queue] taskType: %s, targetName: %s, taskId: %s, queue size: %s', + task.type, + task.targetName, + task.taskId, + queueLength + ); } } } @@ -64,8 +77,13 @@ export class TaskService extends AbstractService { await this.taskRepository.saveTask(task); await this.queueAdapter.push(task.type, task.taskId); const queueLength = await this.getTaskQueueLength(task.type); - this.logger.info('[TaskService.createTask:new] taskType: %s, targetName: %s, taskId: %s, queue size: %s', - task.type, task.targetName, task.taskId, queueLength); + this.logger.info( + '[TaskService.createTask:new] taskType: %s, targetName: %s, taskId: %s, queue size: %s', + task.type, + task.targetName, + task.taskId, + queueLength + ); return task; } @@ -77,8 +95,13 @@ export class TaskService extends AbstractService { await this.taskRepository.saveTask(task); await this.queueAdapter.push(task.type, task.taskId); const queueLength = await this.getTaskQueueLength(task.type); - this.logger.info('[TaskService.retryTask:save] taskType: %s, targetName: %s, taskId: %s, queue size: %s', - task.type, task.targetName, task.taskId, queueLength); + this.logger.info( + '[TaskService.retryTask:save] taskType: %s, targetName: %s, taskId: %s, queue size: %s', + task.type, + task.targetName, + task.taskId, + queueLength + ); } public async findTask(taskId: string) { @@ -108,7 +131,10 @@ export class TaskService extends AbstractService { } const condition = task.start(); - const saveSucceed = await this.taskRepository.idempotentSaveTask(task, condition); + const saveSucceed = await this.taskRepository.idempotentSaveTask( + task, + condition + ); if (!saveSucceed) { taskId = await this.queueAdapter.pop(taskType); continue; @@ -121,7 +147,10 @@ export class TaskService extends AbstractService { public async retryExecuteTimeoutTasks() { // try processing timeout tasks in 10 mins - const tasks = await this.taskRepository.findTimeoutTasks(TaskState.Processing, 60000 * 10); + const tasks = await this.taskRepository.findTimeoutTasks( + TaskState.Processing, + 60000 * 10 + ); for (const task of tasks) { try { // ignore ChangesStream task, it won't timeout @@ -129,7 +158,11 @@ export class TaskService extends AbstractService { await this.finishTask(task, TaskState.Timeout); this.logger.warn( '[TaskService.retryExecuteTimeoutTasks:timeout] taskType: %s, targetName: %s, taskId: %s, attempts %s set to fail', - task.type, task.targetName, task.taskId, task.attempts); + task.type, + task.targetName, + task.taskId, + task.attempts + ); continue; } if (task.attempts >= 1) { @@ -139,26 +172,44 @@ export class TaskService extends AbstractService { await this.retryTask(task); this.logger.info( '[TaskService.retryExecuteTimeoutTasks:retry] taskType: %s, targetName: %s, taskId: %s, attempts %s will retry again', - task.type, task.targetName, task.taskId, task.attempts); + task.type, + task.targetName, + task.taskId, + task.attempts + ); } catch (e) { this.logger.error( '[TaskService.retryExecuteTimeoutTasks:error] processing task, taskType: %s, targetName: %s, taskId: %s, attempts %s will retry again', - task.type, task.targetName, task.taskId, task.attempts); + task.type, + task.targetName, + task.taskId, + task.attempts + ); this.logger.error(e); } } // try waiting timeout tasks in 30 mins - const waitingTasks = await this.taskRepository.findTimeoutTasks(TaskState.Waiting, 60000 * 30); + const waitingTasks = await this.taskRepository.findTimeoutTasks( + TaskState.Waiting, + 60000 * 30 + ); for (const task of waitingTasks) { try { await this.retryTask(task); this.logger.warn( '[TaskService.retryExecuteTimeoutTasks:retryWaiting] taskType: %s, targetName: %s, taskId: %s waiting too long', - task.type, task.targetName, task.taskId); + task.type, + task.targetName, + task.taskId + ); } catch (e) { this.logger.error( '[TaskService.retryExecuteTimeoutTasks:error] waiting task, taskType: %s, targetName: %s, taskId: %s, attempts %s will retry again', - task.type, task.targetName, task.taskId, task.attempts); + task.type, + task.targetName, + task.taskId, + task.attempts + ); this.logger.error(e); } } @@ -173,7 +224,11 @@ export class TaskService extends AbstractService { await this.taskRepository.saveTask(task); } - public async finishTask(task: Task, taskState: TaskState, appendLog?: string) { + public async finishTask( + task: Task, + taskState: TaskState, + appendLog?: string + ) { if (appendLog) { await this.appendLogToNFS(task, appendLog); } @@ -189,7 +244,7 @@ export class TaskService extends AbstractService { task.logStorePosition, { 'Content-Type': 'text/plain; charset=utf-8', - }, + } ); if (nextPosition) { task.logStorePosition = nextPosition; @@ -197,11 +252,14 @@ export class TaskService extends AbstractService { } catch (err: any) { // [PositionNotEqualToLengthError]: Position is not equal to file length, status: 409 // [ObjectNotAppendableError]: The object is not appendable - if (err.code === 'PositionNotEqualToLength' || err.code === 'ObjectNotAppendable') { + if ( + err.code === 'PositionNotEqualToLength' || + err.code === 'ObjectNotAppendable' + ) { // override exists log file await this.nfsAdapter.uploadBytes( task.logPath, - Buffer.from(appendLog + '\n'), + Buffer.from(appendLog + '\n') ); return; } diff --git a/app/core/service/TokenService.ts b/app/core/service/TokenService.ts index a08f6dd2..9167875f 100644 --- a/app/core/service/TokenService.ts +++ b/app/core/service/TokenService.ts @@ -1,20 +1,17 @@ import dayjs from 'dayjs'; -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { isEmpty } from 'lodash-es'; import { ForbiddenError, UnauthorizedError } from 'egg-errors'; import { AbstractService } from '../../common/AbstractService.js'; -import { Token, isGranularToken } from '../entity/Token.js'; -import { TokenPackage as TokenPackageModel } from '../../../app/repository/model/TokenPackage.js'; -import { Package as PackageModel } from '../../../app/repository/model/Package.js'; +import type { Token } from '../entity/Token.js'; +import { isGranularToken } from '../entity/Token.js'; +import type { TokenPackage as TokenPackageModel } from '../../../app/repository/model/TokenPackage.js'; +import type { Package as PackageModel } from '../../../app/repository/model/Package.js'; import { ModelConvertor } from '../../../app/repository/util/ModelConvertor.js'; import { Package as PackageEntity } from '../entity/Package.js'; import { getScopeAndName } from '../../../app/common/PackageUtil.js'; import { sha512 } from '../../../app/common/UserUtil.js'; -import { UserRepository } from '../../../app/repository/UserRepository.js'; +import type { UserRepository } from '../../../app/repository/UserRepository.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -30,8 +27,12 @@ export class TokenService extends AbstractService { public async listTokenPackages(token: Token) { if (isGranularToken(token)) { const models = await this.TokenPackage.find({ tokenId: token.tokenId }); - const packages = await this.Package.find({ packageId: models.map(m => m.packageId) }); - return packages.map(pkg => ModelConvertor.convertModelToEntity(pkg, PackageEntity)); + const packages = await this.Package.find({ + packageId: models.map(m => m.packageId), + }); + return packages.map(pkg => + ModelConvertor.convertModelToEntity(pkg, PackageEntity) + ); } return null; } @@ -48,7 +49,7 @@ export class TokenService extends AbstractService { public async checkGranularTokenAccess(token: Token, fullname: string) { // check for scope whitelist - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); // check for packages whitelist const allowedPackages = await this.listTokenPackages(token); @@ -57,7 +58,9 @@ export class TokenService extends AbstractService { return true; } - const existPkgConfig = allowedPackages?.find(pkg => pkg.scope === scope && pkg.name === name); + const existPkgConfig = allowedPackages?.find( + pkg => pkg.scope === scope && pkg.name === name + ); if (existPkgConfig) { return true; } @@ -68,7 +71,6 @@ export class TokenService extends AbstractService { } throw new ForbiddenError(`can't access package "${fullname}"`); - } async getUserAndToken(authorization: string) { @@ -77,8 +79,8 @@ export class TokenService extends AbstractService { if (!matchs) return null; const tokenValue = matchs[1]; const tokenKey = sha512(tokenValue); - const authorizedUserAndToken = await this.userRepository.findUserAndTokenByTokenKey(tokenKey); + const authorizedUserAndToken = + await this.userRepository.findUserAndTokenByTokenKey(tokenKey); return authorizedUserAndToken; } - } diff --git a/app/core/service/UserService.ts b/app/core/service/UserService.ts index 8f34ec16..4234d05f 100644 --- a/app/core/service/UserService.ts +++ b/app/core/service/UserService.ts @@ -1,22 +1,24 @@ import crypto from 'node:crypto'; -import { - AccessLevel, - SingletonProto, - Inject, -} from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { NotFoundError, ForbiddenError } from 'egg-errors'; -import { UserRepository } from '../../repository/UserRepository.js'; +import type { UserRepository } from '../../repository/UserRepository.js'; import { User as UserEntity } from '../entity/User.js'; -import { Token as TokenEntity, TokenType } from '../entity/Token.js'; +import type { TokenType } from '../entity/Token.js'; +import { Token as TokenEntity } from '../entity/Token.js'; import { WebauthnCredential as WebauthnCredentialEntity } from '../entity/WebauthnCredential.js'; import { LoginResultCode } from '../../common/enum/User.js'; -import { integrity, checkIntegrity, randomToken, sha512 } from '../../common/UserUtil.js'; +import { + integrity, + checkIntegrity, + randomToken, + sha512, +} from '../../common/UserUtil.js'; import { AbstractService } from '../../common/AbstractService.js'; -import { RegistryManagerService } from './RegistryManagerService.js'; +import type { RegistryManagerService } from './RegistryManagerService.js'; import { getPrefixedName } from '../../common/PackageUtil.js'; -import { Registry } from '../entity/Registry.js'; +import type { Registry } from '../entity/Registry.js'; -type Optional = Omit < T, K > & Partial ; +type Optional = Omit & Partial; type CreateUser = { name: string; @@ -77,19 +79,29 @@ export class UserService extends AbstractService { } const selfRegistry = await this.registryManagerService.ensureSelfRegistry(); - const selfUser = await this.findUserByName(getPrefixedName(selfRegistry.userPrefix, name)); + const selfUser = await this.findUserByName( + getPrefixedName(selfRegistry.userPrefix, name) + ); if (selfUser) { return selfUser; } - const defaultRegistry = await this.registryManagerService.ensureDefaultRegistry(); - const defaultUser = await this.findUserByName(getPrefixedName(defaultRegistry.userPrefix, name)); + const defaultRegistry = + await this.registryManagerService.ensureDefaultRegistry(); + const defaultUser = await this.findUserByName( + getPrefixedName(defaultRegistry.userPrefix, name) + ); return defaultUser; } - async findInRegistry(registry:Registry, name: string): Promise { - return await this.findUserByName(getPrefixedName(registry.userPrefix, name)); + async findInRegistry( + registry: Registry, + name: string + ): Promise { + return await this.findUserByName( + getPrefixedName(registry.userPrefix, name) + ); } async findUserByName(name: string): Promise { @@ -106,7 +118,12 @@ export class UserService extends AbstractService { return { code: LoginResultCode.Success, user, token }; } - async findOrCreateUser({ name, email, ip, password = crypto.randomUUID() }: Optional) { + async findOrCreateUser({ + name, + email, + ip, + password = crypto.randomUUID(), + }: Optional) { let user = await this.userRepository.findUserByName(name); if (!user) { const createRes = await this.create({ @@ -144,7 +161,11 @@ export class UserService extends AbstractService { return { user: userEntity, token }; } - async saveUser(userPrefix = 'npm:', name: string, email: string): Promise<{ changed: boolean, user: UserEntity }> { + async saveUser( + userPrefix = 'npm:', + name: string, + email: string + ): Promise<{ changed: boolean; user: UserEntity }> { const storeName = name.startsWith('name:') ? name : `${userPrefix}${name}`; let user = await this.userRepository.findUserByName(storeName); if (!user) { @@ -189,26 +210,41 @@ export class UserService extends AbstractService { } async removeToken(userId: string, tokenKeyOrTokenValue: string) { - let token = await this.userRepository.findTokenByTokenKey(tokenKeyOrTokenValue); + let token = + await this.userRepository.findTokenByTokenKey(tokenKeyOrTokenValue); if (!token) { // tokenKeyOrTokenValue is token value, sha512 and find again - token = await this.userRepository.findTokenByTokenKey(sha512(tokenKeyOrTokenValue)); + token = await this.userRepository.findTokenByTokenKey( + sha512(tokenKeyOrTokenValue) + ); } if (!token) { throw new NotFoundError(`Token "${tokenKeyOrTokenValue}" not exists`); } if (token.userId !== userId) { - throw new ForbiddenError(`Not authorized to remove token "${tokenKeyOrTokenValue}"`); + throw new ForbiddenError( + `Not authorized to remove token "${tokenKeyOrTokenValue}"` + ); } await this.userRepository.removeToken(token.tokenId); } - async findWebauthnCredential(userId: string, browserType: string | undefined | null) { - const credential = await this.userRepository.findCredentialByUserIdAndBrowserType(userId, browserType || null); + async findWebauthnCredential( + userId: string, + browserType: string | undefined | null + ) { + const credential = + await this.userRepository.findCredentialByUserIdAndBrowserType( + userId, + browserType || null + ); return credential; } - async createWebauthnCredential(userId: string | undefined, options: CreateWebauthnCredentialOptions) { + async createWebauthnCredential( + userId: string | undefined, + options: CreateWebauthnCredentialOptions + ) { const credentialEntity = WebauthnCredentialEntity.create({ userId: userId as string, credentialId: options.credentialId, @@ -220,10 +256,13 @@ export class UserService extends AbstractService { } async removeWebauthnCredential(userId?: string, browserType?: string) { - const credential = await this.userRepository.findCredentialByUserIdAndBrowserType(userId, browserType || null); + const credential = + await this.userRepository.findCredentialByUserIdAndBrowserType( + userId, + browserType || null + ); if (credential) { await this.userRepository.removeCredential(credential.wancId); } } - } diff --git a/app/core/util/EntityUtil.ts b/app/core/util/EntityUtil.ts index 2bfc2cb1..2639e0a0 100644 --- a/app/core/util/EntityUtil.ts +++ b/app/core/util/EntityUtil.ts @@ -1,11 +1,14 @@ import ObjectID from 'bson-objectid'; import { E400 } from 'egg-errors'; -import { EntityData } from '../entity/Entity.js'; +import type { EntityData } from '../entity/Entity.js'; type PartialBy = Omit & Partial>; -export type EasyData = PartialBy; +export type EasyData = PartialBy< + T, + 'createdAt' | 'updatedAt' | Id +>; const MAX_PAGE_SIZE = 100 as const; export interface PageOptions { @@ -14,7 +17,7 @@ export interface PageOptions { } export interface PageResult { count: number; - data: Array + data: Array; } export interface PageLimitOptions { offset: number; @@ -22,7 +25,10 @@ export interface PageLimitOptions { } export class EntityUtil { - static defaultData(data: EasyData, id: Id): T { + static defaultData( + data: EasyData, + id: Id + ): T { Reflect.set(data, id, EntityUtil.createId()); data.createdAt = data.createdAt || new Date(); data.updatedAt = data.updatedAt || new Date(); diff --git a/app/infra/AuthAdapter.ts b/app/infra/AuthAdapter.ts index b99ffe1e..2c87c011 100644 --- a/app/infra/AuthAdapter.ts +++ b/app/infra/AuthAdapter.ts @@ -1,13 +1,13 @@ -import { - AccessLevel, - EggContext, - Inject, - SingletonProto, -} from '@eggjs/tegg'; -import { Redis } from 'ioredis'; +import type { EggContext } from '@eggjs/tegg'; +import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; +import type { Redis } from 'ioredis'; import { randomUUID } from 'node:crypto'; -import { AuthClient, AuthUrlResult, userResult } from '../common/typing.js'; +import type { + AuthClient, + AuthUrlResult, + userResult, +} from '../common/typing.js'; const ONE_DAY = 3600 * 24; @@ -50,5 +50,4 @@ export class AuthAdapter implements AuthClient { } return null; } - } diff --git a/app/infra/NFSClientAdapter.ts b/app/infra/NFSClientAdapter.ts index cb9d310c..4211c41a 100644 --- a/app/infra/NFSClientAdapter.ts +++ b/app/infra/NFSClientAdapter.ts @@ -1,14 +1,20 @@ -import { Readable } from 'node:stream'; +import type { Readable } from 'node:stream'; import { AccessLevel, LifecycleInit, Inject, SingletonProto, } from '@eggjs/tegg'; -import { EggAppConfig, EggLogger } from 'egg'; +import type { EggAppConfig, EggLogger } from 'egg'; import FSClient from 'fs-cnpm'; -import { AppendResult, NFSClient, UploadOptions, UploadResult, DownloadOptions } from '../common/typing.js'; +import type { + AppendResult, + NFSClient, + UploadOptions, + UploadResult, + DownloadOptions, +} from '../common/typing.js'; @SingletonProto({ name: 'nfsClient', @@ -36,11 +42,16 @@ export class NFSClientAdapter implements NFSClient { this._client = this.config.nfs.client; } else { if (this.config.env === 'prod') { - throw new Error('[NFSAdapter] Can\'t use local fs NFS on production env'); + throw new Error( + "[NFSAdapter] Can't use local fs NFS on production env" + ); } // try to use fs-cnpm, don't use it on production env - this.logger.warn('[NFSAdapter] Don\'t use local fs NFS on production env, store on %s', this.config.nfs.dir); + this.logger.warn( + "[NFSAdapter] Don't use local fs NFS on production env, store on %s", + this.config.nfs.dir + ); this._client = new FSClient({ dir: this.config.nfs.dir }); } @@ -49,7 +60,10 @@ export class NFSClientAdapter implements NFSClient { } } - async appendBytes(bytes: Uint8Array, options: UploadOptions): Promise { + async appendBytes( + bytes: Uint8Array, + options: UploadOptions + ): Promise { if (this._client.appendBytes) { return await this._client.appendBytes(bytes, options); } @@ -68,14 +82,20 @@ export class NFSClientAdapter implements NFSClient { return await this._client.remove(key); } - async upload(filePath: string, options: UploadOptions): Promise { + async upload( + filePath: string, + options: UploadOptions + ): Promise { if (this.config.nfs.removeBeforeUpload) { await this.remove(options.key); } return await this._client.upload(filePath, options); } - async uploadBytes(bytes: Uint8Array, options: UploadOptions): Promise { + async uploadBytes( + bytes: Uint8Array, + options: UploadOptions + ): Promise { if (this.config.nfs.removeBeforeUpload) { await this.remove(options.key); } @@ -85,7 +105,11 @@ export class NFSClientAdapter implements NFSClient { return await this._client.uploadBuffer(bytes, options); } - async download(key: string, filePath: string, options: DownloadOptions): Promise { + async download( + key: string, + filePath: string, + options: DownloadOptions + ): Promise { return await this._client.download(key, filePath, options); } } diff --git a/app/infra/QueueAdapter.ts b/app/infra/QueueAdapter.ts index 4b617570..87f7f0d7 100644 --- a/app/infra/QueueAdapter.ts +++ b/app/infra/QueueAdapter.ts @@ -1,11 +1,7 @@ -import { - AccessLevel, - Inject, - SingletonProto, -} from '@eggjs/tegg'; -import { Redis } from 'ioredis'; +import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; +import type { Redis } from 'ioredis'; -import { QueueAdapter } from '../common/typing.js'; +import type { QueueAdapter } from '../common/typing.js'; /** * Use sort set to keep queue in order and keep same value only insert once @@ -32,12 +28,16 @@ export class RedisQueueAdapter implements QueueAdapter { */ async push(key: string, item: T): Promise { const score = await this.redis.incr(this.getQueueScoreName(key)); - const res = await this.redis.zadd(this.getQueueName(key), score, JSON.stringify(item)); + const res = await this.redis.zadd( + this.getQueueName(key), + score, + JSON.stringify(item) + ); return res !== 0; } async pop(key: string) { - const [ json ] = await this.redis.zpopmin(this.getQueueName(key)); + const [json] = await this.redis.zpopmin(this.getQueueName(key)); if (!json) return null; return JSON.parse(json) as T; } diff --git a/app/infra/SearchAdapter.ts b/app/infra/SearchAdapter.ts index 80244576..110c50ab 100644 --- a/app/infra/SearchAdapter.ts +++ b/app/infra/SearchAdapter.ts @@ -1,12 +1,11 @@ -import { - AccessLevel, - Inject, - SingletonProto, -} from '@eggjs/tegg'; -import { EggAppConfig } from 'egg'; -import { Client as ElasticsearchClient, estypes } from '@elastic/elasticsearch'; +import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; +import type { EggAppConfig } from 'egg'; +import type { + Client as ElasticsearchClient, + estypes, +} from '@elastic/elasticsearch'; -import { SearchAdapter } from '../common/typing.js'; +import type { SearchAdapter } from '../common/typing.js'; /** * Use elasticsearch to search the huge npm packages. @@ -23,7 +22,9 @@ export class ESSearchAdapter implements SearchAdapter { private readonly elasticsearch: ElasticsearchClient; // 由 elasticsearch 插件引入 async search(query: any): Promise> { - const { cnpmcore: { elasticsearchIndex: index } } = this.config; + const { + cnpmcore: { elasticsearchIndex: index }, + } = this.config; const result = await this.elasticsearch.search({ index, ...query, @@ -32,7 +33,9 @@ export class ESSearchAdapter implements SearchAdapter { } async upsert(id: string, document: T): Promise { - const { cnpmcore: { elasticsearchIndex: index } } = this.config; + const { + cnpmcore: { elasticsearchIndex: index }, + } = this.config; const res = await this.elasticsearch.index({ id, index, @@ -42,7 +45,9 @@ export class ESSearchAdapter implements SearchAdapter { } async delete(id: string): Promise { - const { cnpmcore: { elasticsearchIndex: index } } = this.config; + const { + cnpmcore: { elasticsearchIndex: index }, + } = this.config; const res = await this.elasticsearch.delete({ index, id, diff --git a/app/port/UserRoleManager.ts b/app/port/UserRoleManager.ts index 073904e0..ad9373c0 100644 --- a/app/port/UserRoleManager.ts +++ b/app/port/UserRoleManager.ts @@ -1,19 +1,15 @@ -import { - AccessLevel, - Inject, - EggContext, - ContextProto, -} from '@eggjs/tegg'; -import { EggAppConfig, EggLogger } from 'egg'; +import type { EggContext } from '@eggjs/tegg'; +import { AccessLevel, Inject, ContextProto } from '@eggjs/tegg'; +import type { EggAppConfig, EggLogger } from 'egg'; import { UnauthorizedError, ForbiddenError } from 'egg-errors'; -import { PackageRepository } from '../repository/PackageRepository.js'; -import { Package as PackageEntity } from '../core/entity/Package.js'; -import { User as UserEntity } from '../core/entity/User.js'; -import { Token as TokenEntity } from '../core/entity/Token.js'; +import type { PackageRepository } from '../repository/PackageRepository.js'; +import type { Package as PackageEntity } from '../core/entity/Package.js'; +import type { User as UserEntity } from '../core/entity/User.js'; +import type { Token as TokenEntity } from '../core/entity/Token.js'; import { getScopeAndName } from '../common/PackageUtil.js'; -import { RegistryManagerService } from '../core/service/RegistryManagerService.js'; -import { TokenService } from '../core/service/TokenService.js'; +import type { RegistryManagerService } from '../core/service/RegistryManagerService.js'; +import type { TokenService } from '../core/service/TokenService.js'; // https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-tokens-on-the-website export type TokenRole = 'read' | 'publish' | 'setting'; @@ -44,7 +40,6 @@ export class UserRoleManager { // 3. pkg scope is allowed to publish // use AbstractController#ensurePublishAccess ensure pkg exists; public async checkPublishAccess(ctx: EggContext, fullname: string) { - const user = await this.requiredAuthorizedUser(ctx, 'publish'); // 1. admin has all access @@ -59,7 +54,7 @@ export class UserRoleManager { await this.tokenService.checkGranularTokenAccess(token, fullname); // 3. has published in current registry - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const pkg = await this.packageRepository.findPackage(scope, name); const selfRegistry = await this.registryManagerService.ensureSelfRegistry(); const inSelfRegistry = pkg?.registryId === selfRegistry.registryId; @@ -108,7 +103,8 @@ export class UserRoleManager { this.handleAuthorized = true; const authorization = ctx.get('authorization'); if (!authorization) return null; - const authorizedUserAndToken = await this.tokenService.getUserAndToken(authorization); + const authorizedUserAndToken = + await this.tokenService.getUserAndToken(authorization); if (!authorizedUserAndToken) { return null; } @@ -130,9 +126,14 @@ export class UserRoleManager { } const { user, token } = authorizedUserAndToken; // only enable npm client and version check setting will go into this condition - if (this.config.cnpmcore.enableNpmClientAndVersionCheck && role === 'publish') { + if ( + this.config.cnpmcore.enableNpmClientAndVersionCheck && + role === 'publish' + ) { if (token.isReadonly) { - throw new ForbiddenError(`Read-only Token "${token.tokenMark}" can't publish`); + throw new ForbiddenError( + `Read-only Token "${token.tokenMark}" can't publish` + ); } // only support npm >= 7.0.0 allow publish action // user-agent: "npm/6.14.12 node/v10.24.1 darwin x64" @@ -147,22 +148,29 @@ export class UserRoleManager { } if (role === 'setting') { if (token.isReadonly) { - throw new ForbiddenError(`Read-only Token "${token.tokenMark}" can't setting`); + throw new ForbiddenError( + `Read-only Token "${token.tokenMark}" can't setting` + ); } if (token.isAutomation) { - throw new ForbiddenError(`Automation Token "${token.tokenMark}" can't setting`); + throw new ForbiddenError( + `Automation Token "${token.tokenMark}" can't setting` + ); } } return user; } public async requiredPackageMaintainer(pkg: PackageEntity, user: UserEntity) { - - const maintainers = await this.packageRepository.listPackageMaintainers(pkg.packageId); + const maintainers = await this.packageRepository.listPackageMaintainers( + pkg.packageId + ); const maintainer = maintainers.find(m => m.userId === user.userId); if (!maintainer) { const names = maintainers.map(m => m.name).join(', '); - throw new ForbiddenError(`"${user.name}" not authorized to modify ${pkg.fullname}, please contact maintainers: "${names}"`); + throw new ForbiddenError( + `"${user.name}" not authorized to modify ${pkg.fullname}, please contact maintainers: "${names}"` + ); } } @@ -173,10 +181,14 @@ export class UserRoleManager { } const allowScopes = user.scopes ?? cnpmcoreConfig.allowScopes; if (!scope) { - throw new ForbiddenError(`Package scope required, legal scopes: "${allowScopes.join(', ')}"`); + throw new ForbiddenError( + `Package scope required, legal scopes: "${allowScopes.join(', ')}"` + ); } if (!allowScopes.includes(scope)) { - throw new ForbiddenError(`Scope "${scope}" not match legal scopes: "${allowScopes.join(', ')}"`); + throw new ForbiddenError( + `Scope "${scope}" not match legal scopes: "${allowScopes.join(', ')}"` + ); } } diff --git a/app/port/config.ts b/app/port/config.ts index 0b82ffbe..ee8ff85b 100644 --- a/app/port/config.ts +++ b/app/port/config.ts @@ -1,39 +1,43 @@ -import { SyncDeleteMode, SyncMode, ChangesStreamMode } from '../common/constants.js'; -import { DATABASE_TYPE } from '../../config/database.js'; +import type { + SyncDeleteMode, + SyncMode, + ChangesStreamMode, +} from '../common/constants.js'; +import type { DATABASE_TYPE } from '../../config/database.js'; export { cnpmcoreConfig } from '../../config/config.default.js'; export type CnpmcoreConfig = { - name: string, + name: string; /** * enable hook or not */ - hookEnable: boolean, + hookEnable: boolean; /** * mac custom hooks count */ - hooksLimit: number, + hooksLimit: number; /** * upstream registry url */ - sourceRegistry: string, + sourceRegistry: string; /** * upstream registry is base on `cnpmcore` or not * if your upstream is official npm registry, please turn it off */ - sourceRegistryIsCNpm: boolean, + sourceRegistryIsCNpm: boolean; /** * sync upstream first */ - syncUpstreamFirst: boolean, + syncUpstreamFirst: boolean; /** * sync upstream timeout, default is 3mins */ - sourceRegistrySyncTimeout: number, + sourceRegistrySyncTimeout: number; /** * sync task high water size, default is 100 */ - taskQueueHighWaterSize: number, + taskQueueHighWaterSize: number; /** * sync mode * - none: don't sync npm package @@ -41,111 +45,111 @@ export type CnpmcoreConfig = { * - all: sync all npm packages * - exist: only sync exist packages, effected when `enableCheckRecentlyUpdated` or `enableChangesStream` is enabled */ - syncMode: SyncMode, - syncDeleteMode: SyncDeleteMode, - syncPackageWorkerMaxConcurrentTasks: number, - triggerHookWorkerMaxConcurrentTasks: number, - createTriggerHookWorkerMaxConcurrentTasks: number, + syncMode: SyncMode; + syncDeleteMode: SyncDeleteMode; + syncPackageWorkerMaxConcurrentTasks: number; + triggerHookWorkerMaxConcurrentTasks: number; + createTriggerHookWorkerMaxConcurrentTasks: number; /** * stop syncing these packages in future */ - syncPackageBlockList: string[], + syncPackageBlockList: string[]; /** * check recently from https://www.npmjs.com/browse/updated, if use set changesStreamRegistry to cnpmcore, * maybe you should disable it */ - enableCheckRecentlyUpdated: boolean, + enableCheckRecentlyUpdated: boolean; /** * mirror binary, default is false */ - enableSyncBinary: boolean, + enableSyncBinary: boolean; /** * sync binary source api, default is `${sourceRegistry}/-/binary` */ - syncBinaryFromAPISource: string, + syncBinaryFromAPISource: string; /** * enable sync downloads data from source registry https://github.com/cnpm/cnpmcore/issues/108 * all three parameters must be configured at the same time to take effect */ - enableSyncDownloadData: boolean, - syncDownloadDataSourceRegistry: string, + enableSyncDownloadData: boolean; + syncDownloadDataSourceRegistry: string; /** * should be YYYY-MM-DD format */ - syncDownloadDataMaxDate: string, + syncDownloadDataMaxDate: string; /** * @see https://github.com/npm/registry-follower-tutorial */ - enableChangesStream: boolean, - checkChangesStreamInterval: number, - changesStreamRegistry: string, + enableChangesStream: boolean; + checkChangesStreamInterval: number; + changesStreamRegistry: string; /** * handle _changes request mode, default is 'streaming', please set it to 'json' when on cnpmcore registry */ - changesStreamRegistryMode: ChangesStreamMode, + changesStreamRegistryMode: ChangesStreamMode; /** * registry url */ - registry: string, + registry: string; /** * https://docs.npmjs.com/cli/v6/using-npm/config#always-auth npm <= 6 * if `alwaysAuth=true`, all api request required access token */ - alwaysAuth: boolean, + alwaysAuth: boolean; /** * white scope list */ - allowScopes: string[], + allowScopes: string[]; /** * allow publish non-scope package, disable by default */ - allowPublishNonScopePackage: boolean, + allowPublishNonScopePackage: boolean; /** * Public registration is allowed, otherwise only admins can login */ - allowPublicRegistration: boolean, + allowPublicRegistration: boolean; /** * default system admins */ - admins: Record, + admins: Record; /** * use webauthn for login, https://webauthn.guide/ * only support platform authenticators, browser support: https://webauthn.me/browser-support */ - enableWebAuthn: boolean, + enableWebAuthn: boolean; /** * http response cache control header */ - enableCDN: boolean, + enableCDN: boolean; /** * if you are using CDN, can override it * it meaning cache 300s on CDN server and client side. */ - cdnCacheControlHeader: string, + cdnCacheControlHeader: string; /** * if you are using CDN, can set it to 'Accept, Accept-Encoding' */ - cdnVaryHeader: string, + cdnVaryHeader: string; /** * store full package version manifests data to database table(package_version_manifests), default is false */ - enableStoreFullPackageVersionManifestsToDatabase: boolean, + enableStoreFullPackageVersionManifestsToDatabase: boolean; /** * only support npm as client and npm >= 7.0.0 allow publish action */ - enableNpmClientAndVersionCheck: boolean, + enableNpmClientAndVersionCheck: boolean; /** * sync when package not found, only effect when syncMode = all/exist */ - syncNotFound: boolean, + syncNotFound: boolean; /** * redirect to source registry when package not found */ - redirectNotFound: boolean, + redirectNotFound: boolean; /** * enable unpkg features, https://github.com/cnpm/cnpmcore/issues/452 */ - enableUnpkg: boolean, + enableUnpkg: boolean; /** * enable sync unpkg files */ @@ -158,29 +162,29 @@ export type CnpmcoreConfig = { * enable this would make sync specific version task not append latest version into this task automatically,it would mark the local latest stable version as latest tag. * in most cases, you should set to false to keep the same behavior as source registry. */ - strictSyncSpecivicVersion: boolean, + strictSyncSpecivicVersion: boolean; /** - * enable elasticsearch - */ - enableElasticsearch: boolean, + * enable elasticsearch + */ + enableElasticsearch: boolean; /** - * elasticsearch index. if enableElasticsearch is true, you must set a index to write es doc. - */ - elasticsearchIndex: string, + * elasticsearch index. if enableElasticsearch is true, you must set a index to write es doc. + */ + elasticsearchIndex: string; /** * strictly enforces/validates manifest and tgz when publish, https://github.com/cnpm/cnpmcore/issues/542 */ - strictValidateTarballPkg?: boolean, + strictValidateTarballPkg?: boolean; /** * strictly enforces/validates dependencies version when publish or sync */ - strictValidatePackageDeps?: boolean, + strictValidatePackageDeps?: boolean; /** * database config */ database: { - type: DATABASE_TYPE | string, - }, + type: DATABASE_TYPE | string; + }; }; diff --git a/app/port/controller/AbstractController.ts b/app/port/controller/AbstractController.ts index 4b2205d9..304a39a2 100644 --- a/app/port/controller/AbstractController.ts +++ b/app/port/controller/AbstractController.ts @@ -1,27 +1,17 @@ -import { - NotFoundError, - UnavailableForLegalReasonsError, -} from 'egg-errors'; -import { - Inject, - EggContext, -} from '@eggjs/tegg'; -import { - EggLogger, - EggAppConfig, -} from 'egg'; +import { NotFoundError, UnavailableForLegalReasonsError } from 'egg-errors'; +import type { EggContext } from '@eggjs/tegg'; +import { Inject } from '@eggjs/tegg'; +import type { EggLogger, EggAppConfig } from 'egg'; import { MiddlewareController } from '../middleware/index.js'; -import { UserRoleManager } from '../UserRoleManager.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; -import { UserRepository } from '../../repository/UserRepository.js'; +import type { UserRoleManager } from '../UserRoleManager.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { UserRepository } from '../../repository/UserRepository.js'; import { getFullname, getScopeAndName } from '../../common/PackageUtil.js'; -import { Package as PackageEntity } from '../../core/entity/Package.js'; -import { PackageVersion as PackageVersionEntity } from '../../core/entity/PackageVersion.js'; -import { UserService } from '../../core/service/UserService.js'; -import { - VersionRule, -} from '../typebox.js'; +import type { Package as PackageEntity } from '../../core/entity/Package.js'; +import type { PackageVersion as PackageVersionEntity } from '../../core/entity/PackageVersion.js'; +import type { UserService } from '../../core/service/UserService.js'; +import { VersionRule } from '../typebox.js'; import { SyncMode } from '../../common/constants.js'; class PackageNotFoundError extends NotFoundError { @@ -62,11 +52,15 @@ export abstract class AbstractController extends MiddlewareController { return scope && this.config.cnpmcore.allowScopes.includes(scope); } - protected async ensurePublishAccess(ctx: EggContext, fullname: string, checkPkgExist = true) { + protected async ensurePublishAccess( + ctx: EggContext, + fullname: string, + checkPkgExist = true + ) { const user = await this.userRoleManager.checkPublishAccess(ctx, fullname); let pkg: PackageEntity | null = null; if (checkPkgExist) { - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) { throw this.createPackageNotFoundError(fullname, undefined); @@ -109,19 +103,28 @@ export abstract class AbstractController extends MiddlewareController { } protected createPackageNotFoundError(fullname: string, version?: string) { - const message = version ? `${fullname}@${version} not found` : `${fullname} not found`; + const message = version + ? `${fullname}@${version} not found` + : `${fullname} not found`; const err = new PackageNotFoundError(message); return err; } - protected createPackageNotFoundErrorWithRedirect(fullname: string, version?: string, allowSync = false) { + protected createPackageNotFoundErrorWithRedirect( + fullname: string, + version?: string, + allowSync = false + ) { // const err = new PackageNotFoundError(message); const err = this.createPackageNotFoundError(fullname, version); - const [ scope ] = getScopeAndName(fullname); + const [scope] = getScopeAndName(fullname); // dont sync private scope if (!this.isPrivateScope(scope)) { // syncMode = none/admin, redirect public package to source registry - if (!this.enableSync && this.config.cnpmcore.syncMode !== SyncMode.admin) { + if ( + !this.enableSync && + this.config.cnpmcore.syncMode !== SyncMode.admin + ) { if (this.redirectNotFound) { err.redirectToSourceRegistry = this.sourceRegistry; } @@ -142,35 +145,67 @@ export abstract class AbstractController extends MiddlewareController { return err; } - protected createPackageBlockError(reason: string, fullname: string, version?: string) { - const message = version ? `${fullname}@${version} was blocked` : `${fullname} was blocked`; + protected createPackageBlockError( + reason: string, + fullname: string, + version?: string + ) { + const message = version + ? `${fullname}@${version} was blocked` + : `${fullname} was blocked`; return new UnavailableForLegalReasonsError(`${message}, reason: ${reason}`); } - protected async getPackageEntityByFullname(fullname: string, allowSync?: boolean): Promise { - const [ scope, name ] = getScopeAndName(fullname); + protected async getPackageEntityByFullname( + fullname: string, + allowSync?: boolean + ): Promise { + const [scope, name] = getScopeAndName(fullname); return await this.getPackageEntity(scope, name, allowSync); } // try to get package entity, throw NotFoundError when package not exists - protected async getPackageEntity(scope: string, name: string, allowSync?:boolean): Promise { + protected async getPackageEntity( + scope: string, + name: string, + allowSync?: boolean + ): Promise { const packageEntity = await this.packageRepository.findPackage(scope, name); if (!packageEntity) { const fullname = getFullname(scope, name); - throw this.createPackageNotFoundErrorWithRedirect(fullname, undefined, allowSync); + throw this.createPackageNotFoundErrorWithRedirect( + fullname, + undefined, + allowSync + ); } return packageEntity; } - protected async getPackageVersionEntity(pkg: PackageEntity, version: string, allowSync?: boolean): Promise { - const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version); + protected async getPackageVersionEntity( + pkg: PackageEntity, + version: string, + allowSync?: boolean + ): Promise { + const packageVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + version + ); if (!packageVersion) { - throw this.createPackageNotFoundErrorWithRedirect(pkg.fullname, version, allowSync); + throw this.createPackageNotFoundErrorWithRedirect( + pkg.fullname, + version, + allowSync + ); } return packageVersion; } - protected getAndCheckVersionFromFilename(ctx: EggContext, fullname: string, filenameWithVersion: string) { + protected getAndCheckVersionFromFilename( + ctx: EggContext, + fullname: string, + filenameWithVersion: string + ) { const scopeAndName = getScopeAndName(fullname); const name = scopeAndName[1]; // @foo/bar/-/bar-1.0.0 == filename: bar ==> 1.0.0 diff --git a/app/port/controller/BinarySyncController.ts b/app/port/controller/BinarySyncController.ts index 54f576cb..3db8bde1 100644 --- a/app/port/controller/BinarySyncController.ts +++ b/app/port/controller/BinarySyncController.ts @@ -1,19 +1,20 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, Context, - EggContext, Inject, } from '@eggjs/tegg'; import path from 'node:path'; import { NotFoundError } from 'egg-errors'; import { AbstractController } from './AbstractController.js'; -import { BinarySyncerService } from '../../core/service/BinarySyncerService.js'; -import { Binary } from '../../core/entity/Binary.js'; -import binaries, { BinaryName } from '../../../config/binaries.js'; +import type { BinarySyncerService } from '../../core/service/BinarySyncerService.js'; +import type { Binary } from '../../core/entity/Binary.js'; +import type { BinaryName } from '../../../config/binaries.js'; +import binaries from '../../../config/binaries.js'; import { BinaryNameRule, BinarySubpathRule } from '../typebox.js'; @HTTPController() @@ -35,13 +36,15 @@ export class BinarySyncController extends AbstractController { method: HTTPMethodEnum.GET, }) async listBinaries() { - return Object.entries(binaries).map(([ binaryName, binaryConfig ]) => { + return Object.entries(binaries).map(([binaryName, binaryConfig]) => { return { name: `${binaryName}/`, category: `${binaryConfig.category}/`, description: binaryConfig.description, distUrl: binaryConfig.distUrl, - repoUrl: /^https?:\/\//.test(binaryConfig.repo) ? binaryConfig.repo : `https://github.com/${binaryConfig.repo}`, + repoUrl: /^https?:\/\//.test(binaryConfig.repo) + ? binaryConfig.repo + : `https://github.com/${binaryConfig.repo}`, type: 'dir', url: `${this.config.cnpmcore.registry}/-/binary/${binaryConfig.category}/`, }; @@ -52,7 +55,11 @@ export class BinarySyncController extends AbstractController { path: '/-/binary/:binaryName(@[^/]{1,220}/[^/]{1,220}|[^@/]{1,220})/:subpath(.*)', method: HTTPMethodEnum.GET, }) - async showBinary(@Context() ctx: EggContext, @HTTPParam() binaryName: BinaryName, @HTTPParam() subpath: string) { + async showBinary( + @Context() ctx: EggContext, + @HTTPParam() binaryName: BinaryName, + @HTTPParam() subpath: string + ) { // check binaryName valid try { ctx.tValidate(BinaryNameRule, binaryName); @@ -74,14 +81,22 @@ export class BinarySyncController extends AbstractController { const parent = parsed.dir === '/' ? '/' : `${parsed.dir}/`; const name = subpath.endsWith('/') ? `${parsed.base}/` : parsed.base; // 首先查询 binary === category 的情况 - let binary = await this.binarySyncerService.findBinary(binaryName, parent, name); + let binary = await this.binarySyncerService.findBinary( + binaryName, + parent, + name + ); if (!binary) { // 查询不到再去查询 mergeCategory 的情况 const category = binaries?.[binaryName]?.category; if (category) { // canvas/v2.6.1/canvas-v2.6.1-node-v57-linux-glibc-x64.tar.gz // -> node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz - binary = await this.binarySyncerService.findBinary(category, parent, name.replace(new RegExp(`^${binaryName}-`), `${category}-`)); + binary = await this.binarySyncerService.findBinary( + category, + parent, + name.replace(new RegExp(`^${binaryName}-`), `${category}-`) + ); } } @@ -110,7 +125,10 @@ export class BinarySyncController extends AbstractController { path: '/-/binary/:binaryName(@[^/]{1,220}/[^/]{1,220}|[^@/]{1,220})', method: HTTPMethodEnum.GET, }) - async showBinaryIndex(@Context() ctx: EggContext, @HTTPParam() binaryName: BinaryName) { + async showBinaryIndex( + @Context() ctx: EggContext, + @HTTPParam() binaryName: BinaryName + ) { // check binaryName valid try { ctx.tValidate(BinaryNameRule, binaryName); diff --git a/app/port/controller/ChangesStreamController.ts b/app/port/controller/ChangesStreamController.ts index e4729992..e2c2ce77 100644 --- a/app/port/controller/ChangesStreamController.ts +++ b/app/port/controller/ChangesStreamController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -5,12 +6,11 @@ import { HTTPQuery, Inject, Context, - EggContext, } from '@eggjs/tegg'; import { Type } from 'egg-typebox-validate/typebox'; import { AbstractController } from './AbstractController.js'; -import { ChangeRepository } from '../../repository/ChangeRepository.js'; +import type { ChangeRepository } from '../../repository/ChangeRepository.js'; const ChangeRule = Type.Object({ since: Type.Integer({ minimum: 0 }), @@ -37,10 +37,9 @@ export class ChangesStreamController extends AbstractController { seq: change.id, type: change.type, id: change.targetName, - changes: [ change.data ], + changes: [change.data], }; }); return { results }; } } - diff --git a/app/port/controller/DownloadController.ts b/app/port/controller/DownloadController.ts index f00c8c1c..9c5b0b90 100644 --- a/app/port/controller/DownloadController.ts +++ b/app/port/controller/DownloadController.ts @@ -8,9 +8,12 @@ import { import { UnprocessableEntityError, NotFoundError } from 'egg-errors'; import { AbstractController } from './AbstractController.js'; -import { FULLNAME_REG_STRING, getScopeAndName } from '../../common/PackageUtil.js'; +import { + FULLNAME_REG_STRING, + getScopeAndName, +} from '../../common/PackageUtil.js'; import dayjs from '../../common/dayjs.js'; -import { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; +import type { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; const DATE_FORMAT = 'YYYY-MM-DD'; @@ -23,17 +26,25 @@ export class DownloadController extends AbstractController { path: `/downloads/range/:range/:fullname(${FULLNAME_REG_STRING})`, method: HTTPMethodEnum.GET, }) - async showPackageDownloads(@HTTPParam() fullname: string, @HTTPParam() range: string) { - const [ startDate, endDate ] = this.checkAndGetRange(range); - const [ scope, name ] = getScopeAndName(fullname); + async showPackageDownloads( + @HTTPParam() fullname: string, + @HTTPParam() range: string + ) { + const [startDate, endDate] = this.checkAndGetRange(range); + const [scope, name] = getScopeAndName(fullname); const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) throw new NotFoundError(`${fullname} not found`); - const entities = await this.packageVersionDownloadRepository.query(pkg.packageId, startDate.toDate(), endDate.toDate()); + const entities = await this.packageVersionDownloadRepository.query( + pkg.packageId, + startDate.toDate(), + endDate.toDate() + ); const days: Record = {}; - const versions: Record = {}; + const versions: Record = {}; for (const entity of entities) { const yearMonth = String(entity.yearMonth); - const prefix = yearMonth.substring(0, 4) + '-' + yearMonth.substring(4, 6); + const prefix = + yearMonth.substring(0, 4) + '-' + yearMonth.substring(4, 6); for (let i = 1; i <= 31; i++) { const day = String(i).padStart(2, '0'); const field = `d${day}` as keyof typeof entity; @@ -64,13 +75,21 @@ export class DownloadController extends AbstractController { path: '/downloads/:scope/:range', method: HTTPMethodEnum.GET, }) - async showTotalDownloads(@HTTPParam() scope: string, @HTTPParam() range: string) { - const [ startDate, endDate ] = this.checkAndGetRange(range); - const entities = await this.packageVersionDownloadRepository.query(scope, startDate.toDate(), endDate.toDate()); + async showTotalDownloads( + @HTTPParam() scope: string, + @HTTPParam() range: string + ) { + const [startDate, endDate] = this.checkAndGetRange(range); + const entities = await this.packageVersionDownloadRepository.query( + scope, + startDate.toDate(), + endDate.toDate() + ); const days: Record = {}; for (const entity of entities) { const yearMonth = String(entity.yearMonth); - const prefix = yearMonth.substring(0, 4) + '-' + yearMonth.substring(4, 6); + const prefix = + yearMonth.substring(0, 4) + '-' + yearMonth.substring(4, 6); for (let i = 1; i <= 31; i++) { const day = String(i).padStart(2, '0'); const field = `d${day}` as keyof typeof entity; @@ -93,14 +112,18 @@ export class DownloadController extends AbstractController { private checkAndGetRange(range: string) { const matchs = /^(\d{4}-\d{2}-\d{2}):(\d{4}-\d{2}-\d{2})$/.exec(range); if (!matchs) { - throw new UnprocessableEntityError(`range(${range}) format invalid, must be "${DATE_FORMAT}:${DATE_FORMAT}" style`); + throw new UnprocessableEntityError( + `range(${range}) format invalid, must be "${DATE_FORMAT}:${DATE_FORMAT}" style` + ); } const start = matchs[1]; const end = matchs[2]; let startDate = dayjs(start, DATE_FORMAT, true); let endDate = dayjs(end, DATE_FORMAT, true); if (!startDate.isValid() || !endDate.isValid()) { - throw new UnprocessableEntityError(`range(${range}) format invalid, must be "${DATE_FORMAT}:${DATE_FORMAT}" style`); + throw new UnprocessableEntityError( + `range(${range}) format invalid, must be "${DATE_FORMAT}:${DATE_FORMAT}" style` + ); } if (endDate.isBefore(startDate)) { const tmp = startDate; @@ -111,8 +134,9 @@ export class DownloadController extends AbstractController { const maxDate = startDate.add(1, 'year'); if (endDate.isAfter(maxDate)) { throw new UnprocessableEntityError( - `range(${range}) beyond the processable range, max up to "${maxDate.format(DATE_FORMAT)}"`); + `range(${range}) beyond the processable range, max up to "${maxDate.format(DATE_FORMAT)}"` + ); } - return [ startDate, endDate ]; + return [startDate, endDate]; } } diff --git a/app/port/controller/HomeController.ts b/app/port/controller/HomeController.ts index 820378b3..f67b1a86 100644 --- a/app/port/controller/HomeController.ts +++ b/app/port/controller/HomeController.ts @@ -1,17 +1,21 @@ import { performance } from 'node:perf_hooks'; +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, HTTPMethodEnum, Context, - EggContext, Inject, } from '@eggjs/tegg'; import pkg from 'egg/package.json' with { type: 'json' }; import { AbstractController } from './AbstractController.js'; -import { CacheService, DownloadInfo, UpstreamRegistryInfo } from '../../core/service/CacheService.js'; -import { HomeService } from '../../core/service/HomeService.js'; +import type { + CacheService, + DownloadInfo, + UpstreamRegistryInfo, +} from '../../core/service/CacheService.js'; +import type { HomeService } from '../../core/service/HomeService.js'; const EggVersion = pkg.version; const startTime = new Date(); @@ -24,9 +28,9 @@ const startTime = new Date(); type SiteTotalData = LegacyInfo & SiteEnvInfo & TotalInfo; type LegacyInfo = { - source_registry: string, - changes_stream_registry: string, - sync_changes_steam: any, + source_registry: string; + changes_stream_registry: string; + sync_changes_steam: any; }; type SiteEnvInfo = { @@ -50,7 +54,6 @@ type TotalInfo = { upstream_registries?: UpstreamRegistryInfo[]; }; - @HTTPController() export class HomeController extends AbstractController { @Inject() @@ -124,5 +127,4 @@ export class HomeController extends AbstractController { async miscGet(@Context() ctx: EggContext) { await this.homeService.misc(ctx.path); } - } diff --git a/app/port/controller/HookController.ts b/app/port/controller/HookController.ts index fa6a85bf..a107e48d 100644 --- a/app/port/controller/HookController.ts +++ b/app/port/controller/HookController.ts @@ -1,6 +1,6 @@ +import type { EggContext } from '@eggjs/tegg'; import { Context, - EggContext, HTTPBody, HTTPController, HTTPMethod, @@ -9,11 +9,11 @@ import { Inject, } from '@eggjs/tegg'; -import { HookManageService } from '../../core/service/HookManageService.js'; -import { TaskService } from '../../core/service/TaskService.js'; -import { UserRoleManager } from '../UserRoleManager.js'; -import { HookType } from '../../common/enum/Hook.js'; -import { TriggerHookTask } from '../../core/entity/Task.js'; +import type { HookManageService } from '../../core/service/HookManageService.js'; +import type { TaskService } from '../../core/service/TaskService.js'; +import type { UserRoleManager } from '../UserRoleManager.js'; +import type { HookType } from '../../common/enum/Hook.js'; +import type { TriggerHookTask } from '../../core/entity/Task.js'; import { HookConvertor } from './convertor/HookConvertor.js'; import { CreateHookRequestRule, UpdateHookRequestRule } from '../typebox.js'; @@ -46,9 +46,15 @@ export class HookController { path: '/v1/hooks/hook', method: HTTPMethodEnum.POST, }) - async createHook(@Context() ctx: EggContext, @HTTPBody() req: CreateHookRequest) { + async createHook( + @Context() ctx: EggContext, + @HTTPBody() req: CreateHookRequest + ) { ctx.tValidate(CreateHookRequestRule, req); - const user = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); + const user = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); const hook = await this.hookManageService.createHook({ ownerId: user.userId, type: req.type as HookType, @@ -63,9 +69,16 @@ export class HookController { path: '/v1/hooks/hook/:id', method: HTTPMethodEnum.PUT, }) - async updateHook(@Context() ctx: EggContext, @HTTPParam() id: string, @HTTPBody() req: UpdateHookRequest) { + async updateHook( + @Context() ctx: EggContext, + @HTTPParam() id: string, + @HTTPBody() req: UpdateHookRequest + ) { ctx.tValidate(UpdateHookRequestRule, req); - const user = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); + const user = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); const hook = await this.hookManageService.updateHook({ operatorId: user.userId, hookId: id, @@ -74,7 +87,9 @@ export class HookController { }); let task: TriggerHookTask | null = null; if (hook.latestTaskId) { - task = await this.taskService.findTask(hook.latestTaskId) as TriggerHookTask; + task = (await this.taskService.findTask( + hook.latestTaskId + )) as TriggerHookTask; } return HookConvertor.convertToHookVo(hook, user, task); } @@ -84,14 +99,19 @@ export class HookController { method: HTTPMethodEnum.DELETE, }) async deleteHook(@Context() ctx: EggContext, @HTTPParam() id: string) { - const user = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); + const user = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); const hook = await this.hookManageService.deleteHook({ operatorId: user.userId, hookId: id, }); let task: TriggerHookTask | null = null; if (hook.latestTaskId) { - task = await this.taskService.findTask(hook.latestTaskId) as TriggerHookTask; + task = (await this.taskService.findTask( + hook.latestTaskId + )) as TriggerHookTask; } return HookConvertor.convertToDeleteHookVo(hook, user, task); } @@ -103,9 +123,13 @@ export class HookController { async listHooks(@Context() ctx: EggContext) { const user = await this.userRoleManager.requiredAuthorizedUser(ctx, 'read'); const hooks = await this.hookManageService.listHooksByOwnerId(user.userId); - const tasks = await this.taskService.findTasks(hooks.map(t => t.latestTaskId).filter((t): t is string => !!t)); + const tasks = await this.taskService.findTasks( + hooks.map(t => t.latestTaskId).filter((t): t is string => !!t) + ); const res = hooks.map(hook => { - const task = tasks.find(t => t.taskId === hook.latestTaskId) as TriggerHookTask; + const task = tasks.find( + t => t.taskId === hook.latestTaskId + ) as TriggerHookTask; return HookConvertor.convertToHookVo(hook, user, task); }); return { @@ -122,7 +146,9 @@ export class HookController { const hook = await this.hookManageService.getHookByOwnerId(id, user.userId); let task: TriggerHookTask | null = null; if (hook.latestTaskId) { - task = await this.taskService.findTask(hook.latestTaskId) as TriggerHookTask; + task = (await this.taskService.findTask( + hook.latestTaskId + )) as TriggerHookTask; } return HookConvertor.convertToHookVo(hook, user, task); } diff --git a/app/port/controller/PackageBlockController.ts b/app/port/controller/PackageBlockController.ts index 58989b0e..3e33629e 100644 --- a/app/port/controller/PackageBlockController.ts +++ b/app/port/controller/PackageBlockController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -5,7 +6,6 @@ import { HTTPParam, HTTPBody, Context, - EggContext, Inject, Middleware, } from '@eggjs/tegg'; @@ -13,9 +13,10 @@ import { ForbiddenError } from 'egg-errors'; import { AbstractController } from './AbstractController.js'; import { FULLNAME_REG_STRING } from '../../common/PackageUtil.js'; -import { PackageManagerService } from '../../core/service/PackageManagerService.js'; -import { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; -import { BlockPackageRule, BlockPackageType } from '../typebox.js'; +import type { PackageManagerService } from '../../core/service/PackageManagerService.js'; +import type { PackageVersionBlockRepository } from '../../repository/PackageVersionBlockRepository.js'; +import type { BlockPackageType } from '../typebox.js'; +import { BlockPackageRule } from '../typebox.js'; import { AdminAccess } from '../middleware/AdminAccess.js'; @HTTPController() @@ -31,19 +32,34 @@ export class PackageBlockController extends AbstractController { method: HTTPMethodEnum.PUT, }) @Middleware(AdminAccess) - async blockPackage(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPBody() data: BlockPackageType) { + async blockPackage( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPBody() data: BlockPackageType + ) { const params = { fullname, reason: data.reason }; ctx.tValidate(BlockPackageRule, params); - const packageEntity = await this.getPackageEntityByFullname(params.fullname); + const packageEntity = await this.getPackageEntityByFullname( + params.fullname + ); if (packageEntity.isPrivate) { - throw new ForbiddenError(`Can't block private package "${params.fullname}"`); + throw new ForbiddenError( + `Can't block private package "${params.fullname}"` + ); } - const authorized = await this.userRoleManager.getAuthorizedUserAndToken(ctx); - const block = await this.packageManagerService.blockPackage(packageEntity, - `${params.reason} (operator: ${authorized?.user.name}/${authorized?.user.userId})`); - ctx.logger.info('[PackageBlockController.blockPackage:success] fullname: %s, packageId: %s, packageVersionBlockId: %s', - fullname, packageEntity.packageId, block.packageVersionBlockId); + const authorized = + await this.userRoleManager.getAuthorizedUserAndToken(ctx); + const block = await this.packageManagerService.blockPackage( + packageEntity, + `${params.reason} (operator: ${authorized?.user.name}/${authorized?.user.userId})` + ); + ctx.logger.info( + '[PackageBlockController.blockPackage:success] fullname: %s, packageId: %s, packageVersionBlockId: %s', + fullname, + packageEntity.packageId, + block.packageVersionBlockId + ); ctx.status = 201; return { ok: true, @@ -58,15 +74,21 @@ export class PackageBlockController extends AbstractController { method: HTTPMethodEnum.DELETE, }) @Middleware(AdminAccess) - async unblockPackage(@Context() ctx: EggContext, @HTTPParam() fullname: string) { + async unblockPackage( + @Context() ctx: EggContext, + @HTTPParam() fullname: string + ) { const packageEntity = await this.getPackageEntityByFullname(fullname); if (packageEntity.isPrivate) { throw new ForbiddenError(`Can't unblock private package "${fullname}"`); } await this.packageManagerService.unblockPackage(packageEntity); - ctx.logger.info('[PackageBlockController.unblockPackage:success] fullname: %s, packageId: %s', - fullname, packageEntity.packageId); + ctx.logger.info( + '[PackageBlockController.unblockPackage:success] fullname: %s, packageId: %s', + fullname, + packageEntity.packageId + ); return { ok: true, }; @@ -79,7 +101,10 @@ export class PackageBlockController extends AbstractController { }) async listPackageBlocks(@HTTPParam() fullname: string) { const packageEntity = await this.getPackageEntityByFullname(fullname); - const blocks = await this.packageVersionBlockRepository.listPackageVersionBlocks(packageEntity.packageId); + const blocks = + await this.packageVersionBlockRepository.listPackageVersionBlocks( + packageEntity.packageId + ); return { data: blocks.map(block => { return { diff --git a/app/port/controller/PackageSyncController.ts b/app/port/controller/PackageSyncController.ts index 872a4816..c2df7499 100644 --- a/app/port/controller/PackageSyncController.ts +++ b/app/port/controller/PackageSyncController.ts @@ -1,3 +1,4 @@ +import type { EggContext, BackgroundTaskHelper } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -5,20 +6,22 @@ import { HTTPParam, HTTPBody, Context, - EggContext, Inject, HTTPQuery, - BackgroundTaskHelper, } from '@eggjs/tegg'; import { ForbiddenError, NotFoundError } from 'egg-errors'; import { AbstractController } from './AbstractController.js'; -import { FULLNAME_REG_STRING, getScopeAndName } from '../../common/PackageUtil.js'; -import { Task } from '../../core/entity/Task.js'; -import { PackageSyncerService } from '../../core/service/PackageSyncerService.js'; -import { RegistryManagerService } from '../../core/service/RegistryManagerService.js'; +import { + FULLNAME_REG_STRING, + getScopeAndName, +} from '../../common/PackageUtil.js'; +import type { Task } from '../../core/entity/Task.js'; +import type { PackageSyncerService } from '../../core/service/PackageSyncerService.js'; +import type { RegistryManagerService } from '../../core/service/RegistryManagerService.js'; import { TaskState } from '../../common/enum/Task.js'; -import { SyncPackageTaskRule, SyncPackageTaskType } from '../typebox.js'; +import type { SyncPackageTaskType } from '../typebox.js'; +import { SyncPackageTaskRule } from '../typebox.js'; import { SyncMode } from '../../common/constants.js'; @HTTPController() @@ -34,9 +37,15 @@ export class PackageSyncController extends AbstractController { private async _executeTaskAsync(task: Task) { const startTime = Date.now(); - this.logger.info('[PackageSyncController:executeTask:start] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', - task.taskId, task.targetName, task.attempts, task.data, task.updatedAt, - startTime - task.updatedAt.getTime()); + this.logger.info( + '[PackageSyncController:executeTask:start] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', + task.taskId, + task.targetName, + task.attempts, + task.data, + task.updatedAt, + startTime - task.updatedAt.getTime() + ); let result = 'success'; try { await this.packageSyncerService.executeTask(task); @@ -45,8 +54,13 @@ export class PackageSyncController extends AbstractController { this.logger.error(err); } finally { const use = Date.now() - startTime; - this.logger.info('[PackageSyncController:executeTask:%s] taskId: %s, targetName: %s, use %sms', - result, task.taskId, task.targetName, use); + this.logger.info( + '[PackageSyncController:executeTask:%s] taskId: %s, targetName: %s, use %sms', + result, + task.taskId, + task.targetName, + use + ); } } @@ -55,11 +69,17 @@ export class PackageSyncController extends AbstractController { path: `/-/package/:fullname(${FULLNAME_REG_STRING})/syncs`, method: HTTPMethodEnum.PUT, }) - async createSyncTask(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPBody() data: SyncPackageTaskType) { + async createSyncTask( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPBody() data: SyncPackageTaskType + ) { if (!this.enableSync) { throw new ForbiddenError('Not allow to sync package'); } - const tips = data.tips || `Sync cause by "${ctx.href}", parent traceId: ${ctx.tracer.traceId}`; + const tips = + data.tips || + `Sync cause by "${ctx.href}", parent traceId: ${ctx.tracer.traceId}`; const isAdmin = await this.userRoleManager.isAdmin(ctx); if (this.config.cnpmcore.syncMode === SyncMode.admin && !isAdmin) { @@ -77,23 +97,39 @@ export class PackageSyncController extends AbstractController { specificVersions: data.specificVersions, }; ctx.tValidate(SyncPackageTaskRule, params); - const [ scope, name ] = getScopeAndName(params.fullname); + const [scope, name] = getScopeAndName(params.fullname); const packageEntity = await this.packageRepository.findPackage(scope, name); - const registry = await this.registryManagerService.findByRegistryName(data?.registryName); + const registry = await this.registryManagerService.findByRegistryName( + data?.registryName + ); if (!registry && data.registryName) { - throw new ForbiddenError(`Can't find target registry "${data.registryName}"`); + throw new ForbiddenError( + `Can't find target registry "${data.registryName}"` + ); } if (packageEntity?.isPrivate && !registry) { - throw new ForbiddenError(`Can't sync private package "${params.fullname}"`); + throw new ForbiddenError( + `Can't sync private package "${params.fullname}"` + ); } - if (params.syncDownloadData && !this.packageSyncerService.allowSyncDownloadData) { + if ( + params.syncDownloadData && + !this.packageSyncerService.allowSyncDownloadData + ) { throw new ForbiddenError('Not allow to sync package download data'); } - if (registry && packageEntity?.registryId && packageEntity.registryId !== registry.registryId) { - throw new ForbiddenError(`The package is synced from ${packageEntity.registryId}`); + if ( + registry && + packageEntity?.registryId && + packageEntity.registryId !== registry.registryId + ) { + throw new ForbiddenError( + `The package is synced from ${packageEntity.registryId}` + ); } - const authorized = await this.userRoleManager.getAuthorizedUserAndToken(ctx); + const authorized = + await this.userRoleManager.getAuthorizedUserAndToken(ctx); const task = await this.packageSyncerService.createTask(params.fullname, { authorIp: ctx.ip, authorId: authorized?.user.userId, @@ -102,17 +138,23 @@ export class PackageSyncController extends AbstractController { syncDownloadData: params.syncDownloadData, forceSyncHistory: params.forceSyncHistory, registryId: registry?.registryId, - specificVersions: params.specificVersions && JSON.parse(params.specificVersions), + specificVersions: + params.specificVersions && JSON.parse(params.specificVersions), }); - ctx.logger.info('[PackageSyncController.createSyncTask:success] taskId: %s, fullname: %s', - task.taskId, fullname); + ctx.logger.info( + '[PackageSyncController.createSyncTask:success] taskId: %s, fullname: %s', + task.taskId, + fullname + ); if (data.force) { if (isAdmin) { // set background task timeout to 5min this.backgroundTaskHelper.timeout = 1000 * 60 * 5; this.backgroundTaskHelper.run(async () => { - ctx.logger.info('[PackageSyncController.createSyncTask:execute-immediately] taskId: %s', - task.taskId); + ctx.logger.info( + '[PackageSyncController.createSyncTask:execute-immediately] taskId: %s', + task.taskId + ); // execute task in background await this._executeTaskAsync(task); }); @@ -133,9 +175,15 @@ export class PackageSyncController extends AbstractController { path: `/-/package/:fullname(${FULLNAME_REG_STRING})/syncs/:taskId`, method: HTTPMethodEnum.GET, }) - async showSyncTask(@HTTPParam() fullname: string, @HTTPParam() taskId: string) { + async showSyncTask( + @HTTPParam() fullname: string, + @HTTPParam() taskId: string + ) { const task = await this.packageSyncerService.findTask(taskId); - if (!task) throw new NotFoundError(`Package "${fullname}" sync task "${taskId}" not found`); + if (!task) + throw new NotFoundError( + `Package "${fullname}" sync task "${taskId}" not found` + ); let logUrl: string | undefined; if (task.state !== TaskState.Waiting) { logUrl = `${this.config.cnpmcore.registry}/-/package/${fullname}/syncs/${taskId}/log`; @@ -157,13 +205,26 @@ export class PackageSyncController extends AbstractController { path: `/-/package/:fullname(${FULLNAME_REG_STRING})/syncs/:taskId/log`, method: HTTPMethodEnum.GET, }) - async showSyncTaskLog(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() taskId: string) { + async showSyncTaskLog( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() taskId: string + ) { const task = await this.packageSyncerService.findTask(taskId); - if (!task) throw new NotFoundError(`Package "${fullname}" sync task "${taskId}" not found`); - if (task.state === TaskState.Waiting) throw new NotFoundError(`Package "${fullname}" sync task "${taskId}" log not found`); + if (!task) + throw new NotFoundError( + `Package "${fullname}" sync task "${taskId}" not found` + ); + if (task.state === TaskState.Waiting) + throw new NotFoundError( + `Package "${fullname}" sync task "${taskId}" log not found` + ); const logUrlOrStream = await this.packageSyncerService.findTaskLog(task); - if (!logUrlOrStream) throw new NotFoundError(`Package "${fullname}" sync task "${taskId}" log not found`); + if (!logUrlOrStream) + throw new NotFoundError( + `Package "${fullname}" sync task "${taskId}" log not found` + ); if (typeof logUrlOrStream === 'string') { ctx.redirect(logUrlOrStream); return; @@ -179,7 +240,11 @@ export class PackageSyncController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/sync`, method: HTTPMethodEnum.PUT, }) - async deprecatedCreateSyncTask(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPQuery() nodeps: string) { + async deprecatedCreateSyncTask( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPQuery() nodeps: string + ) { const options: SyncPackageTaskType = { fullname, tips: `Sync cause by "${ctx.href}", parent traceId: ${ctx.tracer.traceId}`, @@ -201,9 +266,13 @@ export class PackageSyncController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/sync/log/:taskId`, method: HTTPMethodEnum.GET, }) - async deprecatedShowSyncTask(@HTTPParam() fullname: string, @HTTPParam() taskId: string) { + async deprecatedShowSyncTask( + @HTTPParam() fullname: string, + @HTTPParam() taskId: string + ) { const task = await this.showSyncTask(fullname, taskId); - const syncDone = task.state !== TaskState.Waiting && task.state !== TaskState.Processing; + const syncDone = + task.state !== TaskState.Waiting && task.state !== TaskState.Processing; const stateMessage = syncDone ? '[done]' : '[processing]'; // https://github.com/cnpm/cnpm/blob/cadd3cd54c22b1a157810a43ab10febdb2410ca6/bin/cnpm-sync#L82 const log = `[${new Date().toISOString()}] ${stateMessage} Sync ${fullname} data: ${JSON.stringify(task)}`; diff --git a/app/port/controller/PackageTagController.ts b/app/port/controller/PackageTagController.ts index e30746a0..959807fa 100644 --- a/app/port/controller/PackageTagController.ts +++ b/app/port/controller/PackageTagController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -5,14 +6,13 @@ import { HTTPParam, HTTPBody, Context, - EggContext, Inject, } from '@eggjs/tegg'; import { ForbiddenError } from 'egg-errors'; import { AbstractController } from './AbstractController.js'; import { FULLNAME_REG_STRING } from '../../common/PackageUtil.js'; -import { PackageManagerService } from '../../core/service/PackageManagerService.js'; +import type { PackageManagerService } from '../../core/service/PackageManagerService.js'; import { TagRule, TagWithVersionRule } from '../typebox.js'; @HTTPController() @@ -29,7 +29,9 @@ export class PackageTagController extends AbstractController { }) async showTags(@HTTPParam() fullname: string) { const packageEntity = await this.getPackageEntityByFullname(fullname); - const tagEntities = await this.packageRepository.listPackageTags(packageEntity.packageId); + const tagEntities = await this.packageRepository.listPackageTags( + packageEntity.packageId + ); const tags: Record = {}; for (const entity of tagEntities) { tags[entity.tag] = entity.version; @@ -44,13 +46,25 @@ export class PackageTagController extends AbstractController { path: `/-/package/:fullname(${FULLNAME_REG_STRING})/dist-tags/:tag`, method: HTTPMethodEnum.PUT, }) - async saveTag(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() tag: string, @HTTPBody() version: string) { + async saveTag( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() tag: string, + @HTTPBody() version: string + ) { const data = { tag, version }; ctx.tValidate(TagWithVersionRule, data); const ensureRes = await this.ensurePublishAccess(ctx, fullname, true); const pkg = ensureRes.pkg!; - const packageVersion = await this.getPackageVersionEntity(pkg, data.version); - await this.packageManagerService.savePackageTag(pkg, data.tag, packageVersion.version); + const packageVersion = await this.getPackageVersionEntity( + pkg, + data.version + ); + await this.packageManagerService.savePackageTag( + pkg, + data.tag, + packageVersion.version + ); return { ok: true }; } @@ -60,7 +74,11 @@ export class PackageTagController extends AbstractController { path: `/-/package/:fullname(${FULLNAME_REG_STRING})/dist-tags/:tag`, method: HTTPMethodEnum.DELETE, }) - async removeTag(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() tag: string) { + async removeTag( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() tag: string + ) { const data = { tag }; ctx.tValidate(TagRule, data); if (tag === 'latest') { diff --git a/app/port/controller/PackageVersionFileController.ts b/app/port/controller/PackageVersionFileController.ts index d69be553..2c14ad78 100644 --- a/app/port/controller/PackageVersionFileController.ts +++ b/app/port/controller/PackageVersionFileController.ts @@ -1,4 +1,5 @@ import { join } from 'node:path'; +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -7,34 +8,36 @@ import { HTTPQuery, Inject, Context, - EggContext, Middleware, } from '@eggjs/tegg'; import { NotFoundError } from 'egg-errors'; import { AbstractController } from './AbstractController.js'; import { AdminAccess } from '../middleware/AdminAccess.js'; -import { getScopeAndName, FULLNAME_REG_STRING } from '../../common/PackageUtil.js'; -import { PackageVersionFileService } from '../../core/service/PackageVersionFileService.js'; -import { PackageManagerService } from '../../core/service/PackageManagerService.js'; -import { PackageVersionFile } from '../../core/entity/PackageVersionFile.js'; -import { PackageVersion } from '../../core/entity/PackageVersion.js'; -import { DistRepository } from '../../repository/DistRepository.js'; +import { + getScopeAndName, + FULLNAME_REG_STRING, +} from '../../common/PackageUtil.js'; +import type { PackageVersionFileService } from '../../core/service/PackageVersionFileService.js'; +import type { PackageManagerService } from '../../core/service/PackageManagerService.js'; +import type { PackageVersionFile } from '../../core/entity/PackageVersionFile.js'; +import type { PackageVersion } from '../../core/entity/PackageVersion.js'; +import type { DistRepository } from '../../repository/DistRepository.js'; import { Spec } from '../typebox.js'; type FileItem = { - path: string, - type: 'file', - contentType: string, + path: string; + type: 'file'; + contentType: string; integrity: string; - lastModified: Date, - size: number, + lastModified: Date; + size: number; }; type DirectoryItem = { - path: string, - type: 'directory', - files: (DirectoryItem | FileItem)[], + path: string; + type: 'directory'; + files: (DirectoryItem | FileItem)[]; }; function formatFileItem(file: PackageVersionFile): FileItem { @@ -72,16 +75,27 @@ export class PackageVersionFileController extends AbstractController { method: HTTPMethodEnum.PUT, }) @Middleware(AdminAccess) - async sync(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() versionSpec: string) { + async sync( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() versionSpec: string + ) { ctx.tValidate(Spec, `${fullname}@${versionSpec}`); this.#requireUnpkgEnable(); - const [ scope, name ] = getScopeAndName(fullname); - const { packageVersion } = await this.packageManagerService.showPackageVersionByVersionOrTag( - scope, name, versionSpec); + const [scope, name] = getScopeAndName(fullname); + const { packageVersion } = + await this.packageManagerService.showPackageVersionByVersionOrTag( + scope, + name, + versionSpec + ); if (!packageVersion) { throw new NotFoundError(`${fullname}@${versionSpec} not found`); } - const files = await this.packageVersionFileService.syncPackageVersionFiles(packageVersion); + const files = + await this.packageVersionFileService.syncPackageVersionFiles( + packageVersion + ); return files.map(file => formatFileItem(file)); } @@ -92,15 +106,23 @@ export class PackageVersionFileController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/:versionSpec/files`, method: HTTPMethodEnum.GET, }) - async listFiles(@Context() ctx: EggContext, - @HTTPParam() fullname: string, - @HTTPParam() versionSpec: string, - @HTTPQuery() meta: string) { + async listFiles( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() versionSpec: string, + @HTTPQuery() meta: string + ) { this.#requireUnpkgEnable(); ctx.tValidate(Spec, `${fullname}@${versionSpec}`); ctx.vary(this.config.cnpmcore.cdnVaryHeader); - const [ scope, name ] = getScopeAndName(fullname); - const packageVersion = await this.#getPackageVersion(ctx, fullname, scope, name, versionSpec); + const [scope, name] = getScopeAndName(fullname); + const packageVersion = await this.#getPackageVersion( + ctx, + fullname, + scope, + name, + versionSpec + ); ctx.set('cache-control', META_CACHE_CONTROL); const hasMeta = typeof meta === 'string' || ctx.path.endsWith('/files/'); // meta request @@ -111,7 +133,14 @@ export class PackageVersionFileController extends AbstractController { } return files; } - const { manifest } = await this.packageManagerService.showPackageVersionManifest(scope, name, versionSpec, false, true); + const { manifest } = + await this.packageManagerService.showPackageVersionManifest( + scope, + name, + versionSpec, + false, + true + ); // GET /foo/1.0.0/files => /foo/1.0.0/files/{main} // ignore empty entry exp: @types/node@20.2.5/ const indexFile = manifest?.main || 'index.js'; @@ -124,40 +153,62 @@ export class PackageVersionFileController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/:versionSpec/files/:path(.+)`, method: HTTPMethodEnum.GET, }) - async raw(@Context() ctx: EggContext, - @HTTPParam() fullname: string, - @HTTPParam() versionSpec: string, - @HTTPParam() path: string, - @HTTPQuery() meta: string) { + async raw( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() versionSpec: string, + @HTTPParam() path: string, + @HTTPQuery() meta: string + ) { this.#requireUnpkgEnable(); ctx.tValidate(Spec, `${fullname}@${versionSpec}`); ctx.vary(this.config.cnpmcore.cdnVaryHeader); - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); path = `/${path}`; - const packageVersion = await this.#getPackageVersion(ctx, fullname, scope, name, versionSpec); + const packageVersion = await this.#getPackageVersion( + ctx, + fullname, + scope, + name, + versionSpec + ); if (path.endsWith('/')) { const directory = path.substring(0, path.length - 1); const files = await this.#listFilesByDirectory(packageVersion, directory); if (!files) { - throw new NotFoundError(`${fullname}@${versionSpec}/files${directory} not found`); + throw new NotFoundError( + `${fullname}@${versionSpec}/files${directory} not found` + ); } ctx.set('cache-control', META_CACHE_CONTROL); return files; } - await this.packageVersionFileService.checkPackageVersionInUnpkgWhiteList(scope, name, packageVersion.version); - const file = await this.packageVersionFileService.showPackageVersionFile(packageVersion, path); + await this.packageVersionFileService.checkPackageVersionInUnpkgWhiteList( + scope, + name, + packageVersion.version + ); + const file = await this.packageVersionFileService.showPackageVersionFile( + packageVersion, + path + ); const hasMeta = typeof meta === 'string'; if (!file) { - const possibleFile = await this.#searchPossibleEntries(packageVersion, path); + const possibleFile = await this.#searchPossibleEntries( + packageVersion, + path + ); if (possibleFile) { const route = `/${fullname}/${versionSpec}/files${possibleFile.path}${hasMeta ? '?meta' : ''}`; ctx.redirect(route); return; } - throw new NotFoundError(`File ${fullname}@${versionSpec}${path} not found`); + throw new NotFoundError( + `File ${fullname}@${versionSpec}${path} not found` + ); } if (hasMeta) { @@ -178,13 +229,21 @@ export class PackageVersionFileController extends AbstractController { * 2. if given path is directory and has `index.js` file, redirect to it. e.g. using `lib` alias to access `lib/index.js` or `lib/index.json` * @param {PackageVersion} packageVersion packageVersion * @param {String} path filepath - * @return {Promise} return packageVersionFile or null + * @returns {Promise} return packageVersionFile or null */ async #searchPossibleEntries(packageVersion: PackageVersion, path: string) { - const possiblePath = [ `${path}.js`, `${path}.json`, `${path}/index.js`, `${path}/index.json` ]; + const possiblePath = [ + `${path}.js`, + `${path}.json`, + `${path}/index.js`, + `${path}/index.json`, + ]; for (const pathItem of possiblePath) { - const file = await this.packageVersionFileService.showPackageVersionFile(packageVersion, pathItem); + const file = await this.packageVersionFileService.showPackageVersionFile( + packageVersion, + pathItem + ); if (file) { return file; @@ -192,9 +251,19 @@ export class PackageVersionFileController extends AbstractController { } } - async #getPackageVersion(ctx: EggContext, fullname: string, scope: string, name: string, versionSpec: string) { - const { blockReason, packageVersion } = await this.packageManagerService.showPackageVersionByVersionOrTag( - scope, name, versionSpec); + async #getPackageVersion( + ctx: EggContext, + fullname: string, + scope: string, + name: string, + versionSpec: string + ) { + const { blockReason, packageVersion } = + await this.packageManagerService.showPackageVersionByVersionOrTag( + scope, + name, + versionSpec + ); if (blockReason) { this.setCDNHeaders(ctx); throw this.createPackageBlockError(blockReason, fullname, versionSpec); @@ -204,15 +273,28 @@ export class PackageVersionFileController extends AbstractController { } if (packageVersion.version !== versionSpec) { ctx.set('cache-control', META_CACHE_CONTROL); - let location = ctx.url.replace(`/${fullname}/${versionSpec}/files`, `/${fullname}/${packageVersion.version}/files`); - location = location.replace(`/${fullname}/${encodeURIComponent(versionSpec)}/files`, `/${fullname}/${packageVersion.version}/files`); + let location = ctx.url.replace( + `/${fullname}/${versionSpec}/files`, + `/${fullname}/${packageVersion.version}/files` + ); + location = location.replace( + `/${fullname}/${encodeURIComponent(versionSpec)}/files`, + `/${fullname}/${packageVersion.version}/files` + ); throw this.createControllerRedirectError(location); } return packageVersion; } - async #listFilesByDirectory(packageVersion: PackageVersion, directory: string) { - const { files, directories } = await this.packageVersionFileService.listPackageVersionFiles(packageVersion, directory); + async #listFilesByDirectory( + packageVersion: PackageVersion, + directory: string + ) { + const { files, directories } = + await this.packageVersionFileService.listPackageVersionFiles( + packageVersion, + directory + ); if (files.length === 0 && directories.length === 0) return null; const info: DirectoryItem = { diff --git a/app/port/controller/ProxyCacheController.ts b/app/port/controller/ProxyCacheController.ts index c9330d4c..7dd69e8b 100644 --- a/app/port/controller/ProxyCacheController.ts +++ b/app/port/controller/ProxyCacheController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -6,20 +7,22 @@ import { HTTPQuery, HTTPParam, Context, - EggContext, } from '@eggjs/tegg'; -import { ForbiddenError, NotFoundError, UnauthorizedError, NotImplementedError } from 'egg-errors'; +import { + ForbiddenError, + NotFoundError, + UnauthorizedError, + NotImplementedError, +} from 'egg-errors'; import { AbstractController } from './AbstractController.js'; -import { ProxyCacheRepository } from '../../repository/ProxyCacheRepository.js'; -import { Static } from 'egg-typebox-validate/typebox'; -import { QueryPageOptions } from '../typebox.js'; +import type { ProxyCacheRepository } from '../../repository/ProxyCacheRepository.js'; +import type { Static } from 'egg-typebox-validate/typebox'; +import type { QueryPageOptions } from '../typebox.js'; import { FULLNAME_REG_STRING } from '../../common/PackageUtil.js'; -import { - ProxyCacheService, -} from '../../core/service/ProxyCacheService.js'; +import type { ProxyCacheService } from '../../core/service/ProxyCacheService.js'; import { SyncMode } from '../../common/constants.js'; -import { CacheService } from '../../core/service/CacheService.js'; +import type { CacheService } from '../../core/service/CacheService.js'; import { isPkgManifest } from '../../core/entity/Package.js'; @HTTPController() @@ -37,7 +40,7 @@ export class ProxyCacheController extends AbstractController { }) async listProxyCache( @HTTPQuery() pageSize: Static['pageSize'], - @HTTPQuery() pageIndex: Static['pageIndex'], + @HTTPQuery() pageIndex: Static['pageIndex'] ) { if (this.config.cnpmcore.syncMode !== SyncMode.proxy) { throw new ForbiddenError('proxy mode is not enabled'); @@ -52,15 +55,21 @@ export class ProxyCacheController extends AbstractController { method: HTTPMethodEnum.GET, path: `/-/proxy-cache/:fullname(${FULLNAME_REG_STRING})`, }) - async showProxyCaches(@HTTPQuery() pageSize: Static['pageSize'], - @HTTPQuery() pageIndex: Static['pageIndex'], @HTTPParam() fullname: string) { + async showProxyCaches( + @HTTPQuery() pageSize: Static['pageSize'], + @HTTPQuery() pageIndex: Static['pageIndex'], + @HTTPParam() fullname: string + ) { if (this.config.cnpmcore.syncMode !== SyncMode.proxy) { throw new ForbiddenError('proxy mode is not enabled'); } - return await this.proxyCacheRepository.listCachedFiles({ - pageSize, - pageIndex, - }, fullname); + return await this.proxyCacheRepository.listCachedFiles( + { + pageSize, + pageIndex, + }, + fullname + ); } @HTTPMethod({ @@ -72,9 +81,8 @@ export class ProxyCacheController extends AbstractController { throw new ForbiddenError('proxy mode is not enabled'); } - const refreshList = await this.proxyCacheRepository.findProxyCaches( - fullname, - ); + const refreshList = + await this.proxyCacheRepository.findProxyCaches(fullname); if (refreshList.length === 0) { throw new NotFoundError(); } @@ -88,7 +96,7 @@ export class ProxyCacheController extends AbstractController { { fullname: item.fullname, fileType: item.fileType, - }, + } ); return task; }); @@ -108,15 +116,18 @@ export class ProxyCacheController extends AbstractController { throw new ForbiddenError('proxy mode is not enabled'); } - const proxyCachesList = await this.proxyCacheRepository.findProxyCaches( - fullname, - ); + const proxyCachesList = + await this.proxyCacheRepository.findProxyCaches(fullname); if (proxyCachesList.length === 0) { throw new NotFoundError(); } await this.cacheService.removeCache(fullname); const removingList = proxyCachesList.map(item => { - return this.proxyCacheService.removeProxyCache(item.fullname, item.fileType, item.version); + return this.proxyCacheService.removeProxyCache( + item.fullname, + item.fileType, + item.version + ); }); await Promise.all(removingList); return { diff --git a/app/port/controller/RegistryController.ts b/app/port/controller/RegistryController.ts index 630321d6..905f9457 100644 --- a/app/port/controller/RegistryController.ts +++ b/app/port/controller/RegistryController.ts @@ -1,6 +1,6 @@ +import type { EggContext } from '@eggjs/tegg'; import { Context, - EggContext, HTTPBody, HTTPController, HTTPMethod, @@ -11,13 +11,21 @@ import { Middleware, } from '@eggjs/tegg'; import { NotFoundError } from 'egg-errors'; -import { Static } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; import { AbstractController } from './AbstractController.js'; -import { RegistryManagerService, UpdateRegistryCmd } from '../../core/service/RegistryManagerService.js'; +import type { + RegistryManagerService, + UpdateRegistryCmd, +} from '../../core/service/RegistryManagerService.js'; import { AdminAccess } from '../middleware/AdminAccess.js'; -import { ScopeManagerService } from '../../core/service/ScopeManagerService.js'; -import { RegistryCreateOptions, QueryPageOptions, RegistryCreateSyncOptions, RegistryUpdateOptions } from '../typebox.js'; +import type { ScopeManagerService } from '../../core/service/ScopeManagerService.js'; +import type { QueryPageOptions } from '../typebox.js'; +import { + RegistryCreateOptions, + RegistryCreateSyncOptions, + RegistryUpdateOptions, +} from '../typebox.js'; @HTTPController() export class RegistryController extends AbstractController { @@ -30,8 +38,14 @@ export class RegistryController extends AbstractController { path: '/-/registry', method: HTTPMethodEnum.GET, }) - async listRegistries(@HTTPQuery() pageSize: Static['pageSize'], @HTTPQuery() pageIndex: Static['pageIndex']) { - const registries = await this.registryManagerService.listRegistries({ pageSize, pageIndex }); + async listRegistries( + @HTTPQuery() pageSize: Static['pageSize'], + @HTTPQuery() pageIndex: Static['pageIndex'] + ) { + const registries = await this.registryManagerService.listRegistries({ + pageSize, + pageIndex, + }); return registries; } @@ -51,12 +65,19 @@ export class RegistryController extends AbstractController { path: '/-/registry/:id/scopes', method: HTTPMethodEnum.GET, }) - async showRegistryScopes(@HTTPParam() id: string, @HTTPQuery() pageSize: Static['pageSize'], @HTTPQuery() pageIndex: Static['pageIndex']) { + async showRegistryScopes( + @HTTPParam() id: string, + @HTTPQuery() pageSize: Static['pageSize'], + @HTTPQuery() pageIndex: Static['pageIndex'] + ) { const registry = await this.registryManagerService.findByRegistryId(id); if (!registry) { throw new NotFoundError('registry not found'); } - const scopes = await this.scopeManagerService.listScopesByRegistryId(id, { pageIndex, pageSize }); + const scopes = await this.scopeManagerService.listScopesByRegistryId(id, { + pageIndex, + pageSize, + }); return scopes; } @@ -65,10 +86,23 @@ export class RegistryController extends AbstractController { method: HTTPMethodEnum.POST, }) @Middleware(AdminAccess) - async createRegistry(@Context() ctx: EggContext, @HTTPBody() registryOptions: Static) { + async createRegistry( + @Context() ctx: EggContext, + @HTTPBody() registryOptions: Static + ) { ctx.tValidate(RegistryCreateOptions, registryOptions); - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); - const { name, changeStream, host, userPrefix = '', type, authToken } = registryOptions; + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); + const { + name, + changeStream, + host, + userPrefix = '', + type, + authToken, + } = registryOptions; await this.registryManagerService.createRegistry({ name, changeStream, @@ -86,15 +120,26 @@ export class RegistryController extends AbstractController { method: HTTPMethodEnum.POST, }) @Middleware(AdminAccess) - async createRegistrySyncTask(@Context() ctx: EggContext, @HTTPParam() id: string, @HTTPBody() registryOptions: Static) { + async createRegistrySyncTask( + @Context() ctx: EggContext, + @HTTPParam() id: string, + @HTTPBody() registryOptions: Static + ) { ctx.tValidate(RegistryCreateSyncOptions, registryOptions); const { since } = registryOptions; const registry = await this.registryManagerService.findByRegistryId(id); if (!registry) { throw new NotFoundError('registry not found'); } - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); - await this.registryManagerService.createSyncChangesStream({ registryId: registry.registryId, since, operatorId: authorizedUser.userId }); + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); + await this.registryManagerService.createSyncChangesStream({ + registryId: registry.registryId, + since, + operatorId: authorizedUser.userId, + }); return { ok: true }; } @@ -104,8 +149,14 @@ export class RegistryController extends AbstractController { }) @Middleware(AdminAccess) async removeRegistry(@Context() ctx: EggContext, @HTTPParam() id: string) { - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); - await this.registryManagerService.remove({ registryId: id, operatorId: authorizedUser.userId }); + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); + await this.registryManagerService.remove({ + registryId: id, + operatorId: authorizedUser.userId, + }); return { ok: true }; } @@ -114,7 +165,11 @@ export class RegistryController extends AbstractController { method: HTTPMethodEnum.PATCH, }) @Middleware(AdminAccess) - async updateRegistry(@Context() ctx: EggContext, @HTTPParam() id: string, @HTTPBody() updateRegistryOptions: Partial) { + async updateRegistry( + @Context() ctx: EggContext, + @HTTPParam() id: string, + @HTTPBody() updateRegistryOptions: Partial + ) { ctx.tValidate(RegistryUpdateOptions, updateRegistryOptions); const registry = await this.registryManagerService.findByRegistryId(id); if (!registry) { @@ -129,7 +184,10 @@ export class RegistryController extends AbstractController { authToken, ...updateRegistryOptions, }; - await this.registryManagerService.updateRegistry(registry.registryId, _updateRegistryOptions); + await this.registryManagerService.updateRegistry( + registry.registryId, + _updateRegistryOptions + ); } return { ok: true }; } diff --git a/app/port/controller/ScopeController.ts b/app/port/controller/ScopeController.ts index 316c7547..457feecd 100644 --- a/app/port/controller/ScopeController.ts +++ b/app/port/controller/ScopeController.ts @@ -1,6 +1,6 @@ +import type { EggContext } from '@eggjs/tegg'; import { Context, - EggContext, HTTPBody, HTTPController, HTTPMethod, @@ -10,15 +10,14 @@ import { Middleware, } from '@eggjs/tegg'; import { E400 } from 'egg-errors'; -import { Static } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; import { AbstractController } from './AbstractController.js'; import { AdminAccess } from '../middleware/AdminAccess.js'; -import { ScopeManagerService } from '../../core/service/ScopeManagerService.js'; -import { RegistryManagerService } from '../../core/service/RegistryManagerService.js'; +import type { ScopeManagerService } from '../../core/service/ScopeManagerService.js'; +import type { RegistryManagerService } from '../../core/service/RegistryManagerService.js'; import { ScopeCreateOptions } from '../typebox.js'; - @HTTPController() export class ScopeController extends AbstractController { @Inject() @@ -32,12 +31,19 @@ export class ScopeController extends AbstractController { method: HTTPMethodEnum.POST, }) @Middleware(AdminAccess) - async createScope(@Context() ctx: EggContext, @HTTPBody() scopeOptions: Static) { - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); + async createScope( + @Context() ctx: EggContext, + @HTTPBody() scopeOptions: Static + ) { + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); ctx.tValidate(ScopeCreateOptions, scopeOptions); const { name, registryId } = scopeOptions; - const registry = await this.registryManagerService.findByRegistryId(registryId); + const registry = + await this.registryManagerService.findByRegistryId(registryId); if (!registry) { throw new E400(`registry ${registryId} not found`); } @@ -56,9 +62,14 @@ export class ScopeController extends AbstractController { }) @Middleware(AdminAccess) async removeScope(@Context() ctx: EggContext, @HTTPParam() id: string) { - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); - await this.scopeManagerService.remove({ scopeId: id, operatorId: authorizedUser.userId }); + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); + await this.scopeManagerService.remove({ + scopeId: id, + operatorId: authorizedUser.userId, + }); return { ok: true }; } - } diff --git a/app/port/controller/TokenController.ts b/app/port/controller/TokenController.ts index f6a0394d..9061dc7f 100644 --- a/app/port/controller/TokenController.ts +++ b/app/port/controller/TokenController.ts @@ -1,4 +1,5 @@ import { ForbiddenError, UnauthorizedError } from 'egg-errors'; +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -6,12 +7,12 @@ import { HTTPBody, HTTPParam, Context, - EggContext, Inject, } from '@eggjs/tegg'; -import { Static, Type } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; +import { Type } from 'egg-typebox-validate/typebox'; -import { AuthAdapter } from '../../infra/AuthAdapter.js'; +import type { AuthAdapter } from '../../infra/AuthAdapter.js'; import { AbstractController } from './AbstractController.js'; import { TokenType, isGranularToken } from '../../core/entity/Token.js'; @@ -23,18 +24,26 @@ const TokenOptionsRule = Type.Object({ readonly: Type.Optional(Type.Boolean()), automation: Type.Optional(Type.Boolean()), // only allow 10 ip for now - cidr_whitelist: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 10 })), + cidr_whitelist: Type.Optional( + Type.Array(Type.String({ maxLength: 100 }), { maxItems: 10 }) + ), }); type TokenOptions = Static; const GranularTokenOptionsRule = Type.Object({ automation: Type.Optional(Type.Boolean()), readonly: Type.Optional(Type.Boolean()), - cidr_whitelist: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 10 })), + cidr_whitelist: Type.Optional( + Type.Array(Type.String({ maxLength: 100 }), { maxItems: 10 }) + ), name: Type.String({ maxLength: 255 }), description: Type.Optional(Type.String({ maxLength: 255 })), - allowedScopes: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 50 })), - allowedPackages: Type.Optional(Type.Array(Type.String({ maxLength: 100 }), { maxItems: 50 })), + allowedScopes: Type.Optional( + Type.Array(Type.String({ maxLength: 100 }), { maxItems: 50 }) + ), + allowedPackages: Type.Optional( + Type.Array(Type.String({ maxLength: 100 }), { maxItems: 50 }) + ), expires: Type.Number({ minimum: 1, maximum: 365 }), }); type GranularTokenOptions = Static; @@ -48,11 +57,19 @@ export class TokenController extends AbstractController { path: '/-/npm/v1/tokens', method: HTTPMethodEnum.POST, }) - async createToken(@Context() ctx: EggContext, @HTTPBody() tokenOptions: TokenOptions) { - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); + async createToken( + @Context() ctx: EggContext, + @HTTPBody() tokenOptions: TokenOptions + ) { + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); ctx.tValidate(TokenOptionsRule, tokenOptions); - if (!this.userService.checkPassword(authorizedUser, tokenOptions.password)) { + if ( + !this.userService.checkPassword(authorizedUser, tokenOptions.password) + ) { throw new UnauthorizedError('Invalid password'); } @@ -78,7 +95,10 @@ export class TokenController extends AbstractController { method: HTTPMethodEnum.DELETE, }) async removeToken(@Context() ctx: EggContext, @HTTPParam() tokenKey: string) { - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); await this.userService.removeToken(authorizedUser.userId, tokenKey); return { ok: true }; } @@ -98,7 +118,10 @@ export class TokenController extends AbstractController { // host: 'localhost:7001', // connection: 'keep-alive' // } - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'setting'); + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'setting' + ); const tokens = await this.userRepository.listTokens(authorizedUser.userId); // { // "objects": [ @@ -115,7 +138,8 @@ export class TokenController extends AbstractController { // "total": 2, // "urls": {} // } - const objects = tokens.filter(token => !isGranularToken(token)) + const objects = tokens + .filter(token => !isGranularToken(token)) .map(token => { return { token: token.tokenMark, @@ -137,7 +161,11 @@ export class TokenController extends AbstractController { if (!userRes?.name || !userRes?.email) { throw new ForbiddenError('need login first'); } - const user = await this.userService.findOrCreateUser({ name: userRes.name, email: userRes.email, ip }); + const user = await this.userService.findOrCreateUser({ + name: userRes.name, + email: userRes.email, + ip, + }); return user; } @@ -151,12 +179,24 @@ export class TokenController extends AbstractController { // 1. Need to submit token name and expires // 2. Optional to submit description, allowScopes, allowPackages information // 3. Need to implement ensureCurrentUser method in AuthAdapter, or pass in this.user - async createGranularToken(@Context() ctx: EggContext, @HTTPBody() tokenOptions: GranularTokenOptions) { + async createGranularToken( + @Context() ctx: EggContext, + @HTTPBody() tokenOptions: GranularTokenOptions + ) { ctx.tValidate(GranularTokenOptionsRule, tokenOptions); const user = await this.ensureWebUser(ctx.ip); // 生成 Token - const { name, description, allowedPackages, allowedScopes, cidr_whitelist, automation, readonly, expires } = tokenOptions; + const { + name, + description, + allowedPackages, + allowedScopes, + cidr_whitelist, + automation, + readonly, + expires, + } = tokenOptions; const token = await this.userService.createToken(user.userId, { name, type: TokenType.granular, @@ -193,7 +233,15 @@ export class TokenController extends AbstractController { const granularTokens = tokens.filter(token => isGranularToken(token)); const objects = granularTokens.map(token => { - const { name, description, expiredAt, allowedPackages, allowedScopes, lastUsedAt, type } = token; + const { + name, + description, + expiredAt, + allowedPackages, + allowedScopes, + lastUsedAt, + type, + } = token; return { name, description, diff --git a/app/port/controller/UserController.ts b/app/port/controller/UserController.ts index bdf48ac0..7e9c2ceb 100644 --- a/app/port/controller/UserController.ts +++ b/app/port/controller/UserController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -5,7 +6,6 @@ import { HTTPParam, HTTPBody, Context, - EggContext, } from '@eggjs/tegg'; import { UnprocessableEntityError, @@ -13,7 +13,8 @@ import { UnauthorizedError, ForbiddenError, } from 'egg-errors'; -import { Static, Type } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; +import { Type } from 'egg-typebox-validate/typebox'; import { AbstractController } from './AbstractController.js'; import { LoginResultCode } from '../../common/enum/User.js'; @@ -57,7 +58,11 @@ export class UserController extends AbstractController { path: '/-/user/org.couchdb.user::username', method: HTTPMethodEnum.PUT, }) - async loginOrCreateUser(@Context() ctx: EggContext, @HTTPParam() username: string, @HTTPBody() user: User) { + async loginOrCreateUser( + @Context() ctx: EggContext, + @HTTPParam() username: string, + @HTTPBody() user: User + ) { // headers: { // 'user-agent': 'npm/8.1.2 node/v16.13.1 darwin arm64 workspaces/false', // 'npm-command': 'adduser', @@ -71,7 +76,9 @@ export class UserController extends AbstractController { // console.log(username, user, ctx.headers, ctx.href); ctx.tValidate(UserRule, user); if (username !== user.name) { - throw new UnprocessableEntityError(`username(${username}) not match user.name(${user.name})`); + throw new UnprocessableEntityError( + `username(${username}) not match user.name(${user.name})` + ); } if (this.config.cnpmcore.allowPublicRegistration === false) { if (!this.config.cnpmcore.admins[user.name]) { @@ -126,12 +133,16 @@ export class UserController extends AbstractController { method: HTTPMethodEnum.DELETE, }) async logout(@Context() ctx: EggContext, @HTTPParam() token: string) { - const authorizedUserAndToken = await this.userRoleManager.getAuthorizedUserAndToken(ctx); + const authorizedUserAndToken = + await this.userRoleManager.getAuthorizedUserAndToken(ctx); if (!authorizedUserAndToken) return { ok: false }; if (authorizedUserAndToken.token.tokenKey !== sha512(token)) { throw new UnprocessableEntityError('invalid token'); } - await this.userService.removeToken(authorizedUserAndToken.user.userId, token); + await this.userService.removeToken( + authorizedUserAndToken.user.userId, + token + ); return { ok: true }; } @@ -145,7 +156,8 @@ export class UserController extends AbstractController { if (!user) { throw new NotFoundError(`User "${username}" not found`); } - const authorized = await this.userRoleManager.getAuthorizedUserAndToken(ctx); + const authorized = + await this.userRoleManager.getAuthorizedUserAndToken(ctx); return { _id: `org.couchdb.user:${user.displayName}`, name: user.displayName, @@ -160,11 +172,20 @@ export class UserController extends AbstractController { }) async whoami(@Context() ctx: EggContext) { await this.userRoleManager.requiredAuthorizedUser(ctx, 'read'); - const authorizedRes = await this.userRoleManager.getAuthorizedUserAndToken(ctx); + const authorizedRes = + await this.userRoleManager.getAuthorizedUserAndToken(ctx); const { token, user } = authorizedRes!; if (isGranularToken(token)) { - const { name, description, expiredAt, allowedPackages, allowedScopes, lastUsedAt, type } = token; + const { + name, + description, + expiredAt, + allowedPackages, + allowedScopes, + lastUsedAt, + type, + } = token; return { username: user.displayName, name, @@ -186,7 +207,6 @@ export class UserController extends AbstractController { return { username: user.displayName, }; - } // https://github.com/cnpm/cnpmcore/issues/64 @@ -204,7 +224,10 @@ export class UserController extends AbstractController { method: HTTPMethodEnum.GET, }) async showProfile(@Context() ctx: EggContext) { - const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'read'); + const authorizedUser = await this.userRoleManager.requiredAuthorizedUser( + ctx, + 'read' + ); return { // "tfa": { // "pending": false, diff --git a/app/port/controller/admin/PaddingVersionController.ts b/app/port/controller/admin/PaddingVersionController.ts index cfcf304f..32a44af8 100644 --- a/app/port/controller/admin/PaddingVersionController.ts +++ b/app/port/controller/admin/PaddingVersionController.ts @@ -1,13 +1,15 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, HTTPMethodEnum, Inject, - HTTPQuery, Context, EggContext, + HTTPQuery, + Context, } from '@eggjs/tegg'; import { AbstractController } from '../AbstractController.js'; -import { FixNoPaddingVersionService } from '../../../core/service/FixNoPaddingVersionService.js'; +import type { FixNoPaddingVersionService } from '../../../core/service/FixNoPaddingVersionService.js'; @HTTPController() export class PaddingVersionController extends AbstractController { @@ -18,7 +20,10 @@ export class PaddingVersionController extends AbstractController { method: HTTPMethodEnum.PUT, path: '/-/admin/npm/fixPaddingVersion', }) - async fixNoPaddingVersion(@Context() ctx: EggContext, @HTTPQuery() id: string) { + async fixNoPaddingVersion( + @Context() ctx: EggContext, + @HTTPQuery() id: string + ) { const isAdmin = await this.userRoleManager.isAdmin(ctx); if (!isAdmin) { return { diff --git a/app/port/controller/convertor/HookConvertor.ts b/app/port/controller/convertor/HookConvertor.ts index bb43ddfb..d4df9803 100644 --- a/app/port/controller/convertor/HookConvertor.ts +++ b/app/port/controller/convertor/HookConvertor.ts @@ -1,7 +1,7 @@ -import { Hook } from '../../../core/entity/Hook.js'; -import { TriggerHookTask } from '../../../core/entity/Task.js'; -import { User } from '../../../core/entity/User.js'; -import { HookType } from '../../../common/enum/Hook.js'; +import type { Hook } from '../../../core/entity/Hook.js'; +import type { TriggerHookTask } from '../../../core/entity/Task.js'; +import type { User } from '../../../core/entity/User.js'; +import type { HookType } from '../../../common/enum/Hook.js'; export interface HookVo { id: string; @@ -12,10 +12,10 @@ export interface HookVo { type: HookType; created: Date; updated: Date; - delivered: boolean, - last_delivery: Date | null, - response_code: number, - status: 'active', + delivered: boolean; + last_delivery: Date | null; + response_code: number; + status: 'active'; } export interface DeleteHookVo { @@ -27,15 +27,19 @@ export interface DeleteHookVo { type: HookType; created: Date; updated: Date; - delivered: boolean, - last_delivery: Date | null, - response_code: number, - status: 'active', - deleted: boolean, + delivered: boolean; + last_delivery: Date | null; + response_code: number; + status: 'active'; + deleted: boolean; } export class HookConvertor { - static convertToHookVo(hook: Hook, user: User, task?: TriggerHookTask | null | undefined): HookVo { + static convertToHookVo( + hook: Hook, + user: User, + task?: TriggerHookTask | null | undefined + ): HookVo { return { id: hook.hookId, username: user.name, @@ -52,7 +56,11 @@ export class HookConvertor { }; } - static convertToDeleteHookVo(hook: Hook, user: User, task?: TriggerHookTask | null): DeleteHookVo { + static convertToDeleteHookVo( + hook: Hook, + user: User, + task?: TriggerHookTask | null + ): DeleteHookVo { const vo = HookConvertor.convertToHookVo(hook, user, task); return Object.assign(vo, { deleted: true, diff --git a/app/port/controller/package/DownloadPackageVersionTar.ts b/app/port/controller/package/DownloadPackageVersionTar.ts index 31eb376c..f912b5e5 100644 --- a/app/port/controller/package/DownloadPackageVersionTar.ts +++ b/app/port/controller/package/DownloadPackageVersionTar.ts @@ -1,7 +1,6 @@ import { PassThrough } from 'node:stream'; -import { - NotFoundError, -} from 'egg-errors'; +import { NotFoundError } from 'egg-errors'; +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -9,17 +8,19 @@ import { HTTPParam, Inject, Context, - EggContext, } from '@eggjs/tegg'; import { AbstractController } from '../AbstractController.js'; -import { FULLNAME_REG_STRING, getScopeAndName } from '../../../common/PackageUtil.js'; +import { + FULLNAME_REG_STRING, + getScopeAndName, +} from '../../../common/PackageUtil.js'; import { SyncMode } from '../../../common/constants.js'; -import { NFSAdapter } from '../../../common/adapter/NFSAdapter.js'; -import { PackageManagerService } from '../../../core/service/PackageManagerService.js'; -import { ProxyCacheService } from '../../../core/service/ProxyCacheService.js'; -import { PackageSyncerService } from '../../../core/service/PackageSyncerService.js'; -import { RegistryManagerService } from '../../../core/service/RegistryManagerService.js'; +import type { NFSAdapter } from '../../../common/adapter/NFSAdapter.js'; +import type { PackageManagerService } from '../../../core/service/PackageManagerService.js'; +import type { ProxyCacheService } from '../../../core/service/ProxyCacheService.js'; +import type { PackageSyncerService } from '../../../core/service/PackageSyncerService.js'; +import type { RegistryManagerService } from '../../../core/service/RegistryManagerService.js'; @HTTPController() export class DownloadPackageVersionTarController extends AbstractController { @@ -51,9 +52,17 @@ export class DownloadPackageVersionTarController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/-/:filenameWithVersion.tgz`, method: HTTPMethodEnum.GET, }) - async download(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() filenameWithVersion: string) { + async download( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() filenameWithVersion: string + ) { // tgz file storeKey: `/packages/${this.fullname}/${version}/${filename}` - const version = this.getAndCheckVersionFromFilename(ctx, fullname, filenameWithVersion); + const version = this.getAndCheckVersionFromFilename( + ctx, + fullname, + filenameWithVersion + ); const storeKey = `/packages/${fullname}/${version}/${filenameWithVersion}.tgz`; const downloadUrl = await this.nfsAdapter.getDownloadUrl(storeKey); @@ -70,7 +79,11 @@ export class DownloadPackageVersionTarController extends AbstractController { let packageVersion; try { pkg = await this.getPackageEntityByFullname(fullname, allowSync); - packageVersion = await this.getPackageVersionEntity(pkg, version, allowSync); + packageVersion = await this.getPackageVersionEntity( + pkg, + version, + allowSync + ); } catch (error) { if (this.config.cnpmcore.syncMode === SyncMode.proxy) { // proxy mode package version not found. @@ -91,9 +104,16 @@ export class DownloadPackageVersionTarController extends AbstractController { return; } // read from database - ctx.logger.info('[PackageController:downloadVersionTar] %s@%s, packageVersionId: %s', - pkg.fullname, version, packageVersion.packageVersionId); - const urlOrStream = await this.packageManagerService.downloadPackageVersionTar(packageVersion); + ctx.logger.info( + '[PackageController:downloadVersionTar] %s@%s, packageVersionId: %s', + pkg.fullname, + version, + packageVersion.packageVersionId + ); + const urlOrStream = + await this.packageManagerService.downloadPackageVersionTar( + packageVersion + ); if (!urlOrStream) { throw new NotFoundError(`"${filenameWithVersion}.tgz" not found`); } @@ -111,15 +131,24 @@ export class DownloadPackageVersionTarController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/download/:fullnameWithVersion+.tgz`, method: HTTPMethodEnum.GET, }) - async deprecatedDownload(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() fullnameWithVersion: string) { + async deprecatedDownload( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() fullnameWithVersion: string + ) { // /@emotion/utils/download/@emotion/utils-0.11.3.tgz // => /@emotion/utils/-/utils-0.11.3.tgz const filenameWithVersion = getScopeAndName(fullnameWithVersion)[1]; return await this.download(ctx, fullname, filenameWithVersion); } - private async getTgzProxyStream(ctx: EggContext, fullname: string, version: string) { - const { headers, status, res } = await this.proxyCacheService.getPackageVersionTarResponse(fullname, ctx); + private async getTgzProxyStream( + ctx: EggContext, + fullname: string, + version: string + ) { + const { headers, status, res } = + await this.proxyCacheService.getPackageVersionTarResponse(fullname, ctx); ctx.status = status; ctx.set(headers as Record); ctx.runInBackground(async () => { @@ -128,10 +157,13 @@ export class DownloadPackageVersionTarController extends AbstractController { authorId: `pid_${process.pid}`, tips: `Sync specific version in proxy mode cause by "${ctx.href}"`, skipDependencies: true, - specificVersions: [ version ], + specificVersions: [version], }); - ctx.logger.info('[DownloadPackageVersionTarController.createSyncTask:success] taskId: %s, fullname: %s', - task.taskId, fullname); + ctx.logger.info( + '[DownloadPackageVersionTarController.createSyncTask:success] taskId: %s, fullname: %s', + task.taskId, + fullname + ); }); return res; } @@ -152,7 +184,11 @@ export class DownloadPackageVersionTarController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/-/:scope/:filenameWithVersion.tgz`, method: HTTPMethodEnum.GET, }) - async downloadVerdaccioPathStyle(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() filenameWithVersion: string) { + async downloadVerdaccioPathStyle( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() filenameWithVersion: string + ) { return this.download(ctx, fullname, filenameWithVersion); } } diff --git a/app/port/controller/package/RemovePackageVersionController.ts b/app/port/controller/package/RemovePackageVersionController.ts index 2af9df55..384e4cfc 100644 --- a/app/port/controller/package/RemovePackageVersionController.ts +++ b/app/port/controller/package/RemovePackageVersionController.ts @@ -1,7 +1,5 @@ -import { - BadRequestError, - ForbiddenError, -} from 'egg-errors'; +import { BadRequestError, ForbiddenError } from 'egg-errors'; +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -9,14 +7,13 @@ import { HTTPParam, Inject, Context, - EggContext, } from '@eggjs/tegg'; import { AbstractController } from '../AbstractController.js'; import { FULLNAME_REG_STRING } from '../../../common/PackageUtil.js'; -import { PackageManagerService } from '../../../core/service/PackageManagerService.js'; -import { Package } from '../../../core/entity/Package.js'; -import { PackageVersion } from '../../../core/entity/PackageVersion.js'; +import type { PackageManagerService } from '../../../core/service/PackageManagerService.js'; +import type { Package } from '../../../core/entity/Package.js'; +import type { PackageVersion } from '../../../core/entity/PackageVersion.js'; @HTTPController() export class RemovePackageVersionController extends AbstractController { @@ -36,14 +33,22 @@ export class RemovePackageVersionController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/-/:filenameWithVersion.tgz/-rev/:rev`, method: HTTPMethodEnum.DELETE, }) - async removeByTarballUrl(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPParam() filenameWithVersion: string) { + async removeByTarballUrl( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPParam() filenameWithVersion: string + ) { const npmCommand = ctx.get('npm-command'); if (npmCommand !== 'unpublish') { throw new BadRequestError('Only allow "unpublish" npm-command'); } const ensureRes = await this.ensurePublishAccess(ctx, fullname, true); const pkg = ensureRes.pkg!; - const version = this.getAndCheckVersionFromFilename(ctx, fullname, filenameWithVersion); + const version = this.getAndCheckVersionFromFilename( + ctx, + fullname, + filenameWithVersion + ); const packageVersion = await this.getPackageVersionEntity(pkg, version); await this.#removePackageVersion(pkg, packageVersion); return { ok: true }; @@ -62,7 +67,10 @@ export class RemovePackageVersionController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/-rev/:rev`, method: HTTPMethodEnum.DELETE, }) - async removeByPkgUri(@Context() ctx: EggContext, @HTTPParam() fullname: string) { + async removeByPkgUri( + @Context() ctx: EggContext, + @HTTPParam() fullname: string + ) { const npmCommand = ctx.get('npm-command'); if (npmCommand !== 'unpublish') { throw new BadRequestError('Only allow "unpublish" npm-command'); @@ -70,16 +78,25 @@ export class RemovePackageVersionController extends AbstractController { const ensureRes = await this.ensurePublishAccess(ctx, fullname, true); const pkg = ensureRes.pkg!; // try to remove the latest version first - const packageTag = await this.packageRepository.findPackageTag(pkg.packageId, 'latest'); + const packageTag = await this.packageRepository.findPackageTag( + pkg.packageId, + 'latest' + ); let packageVersion: PackageVersion | null = null; if (packageTag) { - packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, packageTag.version); + packageVersion = await this.packageRepository.findPackageVersion( + pkg.packageId, + packageTag.version + ); } if (packageVersion) { await this.#removePackageVersion(pkg, packageVersion); } else { - this.logger.info('[PackageController:unpublishPackage] %s, packageId: %s', - pkg.fullname, pkg.packageId); + this.logger.info( + '[PackageController:unpublishPackage] %s, packageId: %s', + pkg.fullname, + pkg.packageId + ); await this.packageManagerService.unpublishPackage(pkg); } return { ok: true }; @@ -88,11 +105,20 @@ export class RemovePackageVersionController extends AbstractController { async #removePackageVersion(pkg: Package, packageVersion: PackageVersion) { // https://docs.npmjs.com/policies/unpublish // can unpublish anytime within the first 72 hours after publishing - if (pkg.isPrivate && Date.now() - packageVersion.publishTime.getTime() >= 3600000 * 72) { - throw new ForbiddenError(`${pkg.fullname}@${packageVersion.version} unpublish is not allowed after 72 hours of released`); + if ( + pkg.isPrivate && + Date.now() - packageVersion.publishTime.getTime() >= 3600000 * 72 + ) { + throw new ForbiddenError( + `${pkg.fullname}@${packageVersion.version} unpublish is not allowed after 72 hours of released` + ); } - this.logger.info('[PackageController:removeVersion] %s@%s, packageVersionId: %s', - pkg.fullname, packageVersion.version, packageVersion.packageVersionId); + this.logger.info( + '[PackageController:removeVersion] %s@%s, packageVersionId: %s', + pkg.fullname, + packageVersion.version, + packageVersion.packageVersionId + ); await this.packageManagerService.removePackageVersion(pkg, packageVersion); } } diff --git a/app/port/controller/package/SavePackageVersionController.ts b/app/port/controller/package/SavePackageVersionController.ts index 9c3e5b75..2dc0c7b8 100644 --- a/app/port/controller/package/SavePackageVersionController.ts +++ b/app/port/controller/package/SavePackageVersionController.ts @@ -1,10 +1,11 @@ -import { PackageJson, Simplify } from 'type-fest'; +import type { PackageJson, Simplify } from 'type-fest'; import { isEqual } from 'lodash-es'; import { UnprocessableEntityError, ForbiddenError, ConflictError, } from 'egg-errors'; +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -13,39 +14,56 @@ import { HTTPBody, Inject, Context, - EggContext, } from '@eggjs/tegg'; -import * as ssri from 'ssri'; +import { checkData, fromData } from 'ssri'; import validateNpmPackageName from 'validate-npm-package-name'; -import { Static, Type } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; +import { Type } from 'egg-typebox-validate/typebox'; import { AbstractController } from '../AbstractController.js'; -import { getScopeAndName, FULLNAME_REG_STRING, extractPackageJSON } from '../../../common/PackageUtil.js'; -import { PackageManagerService } from '../../../core/service/PackageManagerService.js'; -import { PackageVersion as PackageVersionEntity } from '../../../core/entity/PackageVersion.js'; +import { + getScopeAndName, + FULLNAME_REG_STRING, + extractPackageJSON, +} from '../../../common/PackageUtil.js'; +import type { PackageManagerService } from '../../../core/service/PackageManagerService.js'; +import type { PackageVersion as PackageVersionEntity } from '../../../core/entity/PackageVersion.js'; import { VersionRule, TagWithVersionRule, Name as NameType, Description as DescriptionType, } from '../../typebox.js'; -import { RegistryManagerService } from '../../../core/service/RegistryManagerService.js'; -import { PackageJSONType } from '../../../repository/PackageRepository.js'; -import { CacheAdapter } from '../../../common/adapter/CacheAdapter.js'; +import type { RegistryManagerService } from '../../../core/service/RegistryManagerService.js'; +import type { PackageJSONType } from '../../../repository/PackageRepository.js'; +import type { CacheAdapter } from '../../../common/adapter/CacheAdapter.js'; -const STRICT_CHECK_TARBALL_FIELDS: (keyof PackageJson)[] = [ 'name', 'version', 'scripts', 'dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies', 'license', 'licenses', 'bin' ]; +const STRICT_CHECK_TARBALL_FIELDS: (keyof PackageJson)[] = [ + 'name', + 'version', + 'scripts', + 'dependencies', + 'devDependencies', + 'peerDependencies', + 'optionalDependencies', + 'license', + 'licenses', + 'bin', +]; -type PackageVersion = Simplify; +type PackageVersion = Simplify< + PackageJson.PackageJsonStandard & { + name: 'string'; + version: 'string'; + deprecated?: 'string'; + readme?: 'string'; + dist?: { + shasum: string; + integrity: string; + [key: string]: string | number; + }; + } +>; const FullPackageRule = Type.Object({ name: NameType, @@ -54,18 +72,21 @@ const FullPackageRule = Type.Object({ _attachments: Type.Optional(Type.Any()), description: Type.Optional(DescriptionType), 'dist-tags': Type.Optional(Type.Record(Type.String(), Type.String())), - readme: Type.Optional(Type.String({ transform: [ 'trim' ] })), + readme: Type.Optional(Type.String({ transform: ['trim'] })), }); // overwrite versions & _attachments -type FullPackage = Omit, 'versions' | '_attachments'> & -{ versions: { [key: string]: PackageVersion } } & -{ _attachments: { - [key: string]: { - content_type: string; - data: string; - length: number; +type FullPackage = Omit< + Static, + 'versions' | '_attachments' +> & { versions: { [key: string]: PackageVersion } } & { + _attachments: { + [key: string]: { + content_type: string; + data: string; + length: number; + }; }; -}}; +}; // base64 regex https://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data/475217#475217 const PACKAGE_ATTACH_DATA_RE = /^[A-Za-z0-9+/]{4}/; @@ -89,24 +110,36 @@ export class SavePackageVersionController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})`, method: HTTPMethodEnum.PUT, }) - async save(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPBody() pkg: FullPackage) { + async save( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPBody() pkg: FullPackage + ) { this.validateNpmCommand(ctx); ctx.tValidate(FullPackageRule, pkg); const { user } = await this.ensurePublishAccess(ctx, fullname, false); fullname = fullname.trim(); if (fullname !== pkg.name) { - throw new UnprocessableEntityError(`fullname(${fullname}) not match package.name(${pkg.name})`); + throw new UnprocessableEntityError( + `fullname(${fullname}) not match package.name(${pkg.name})` + ); } // Using https://github.com/npm/validate-npm-package-name to validate package name const validateResult = validateNpmPackageName(pkg.name); if (!validateResult.validForNewPackages) { // if pkg already exists, still allow to publish - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) { - const errors = (validateResult.errors || validateResult.warnings || []).join(', '); - throw new UnprocessableEntityError(`package.name invalid, errors: ${errors}`); + const errors = ( + validateResult.errors || + validateResult.warnings || + [] + ).join(', '); + throw new UnprocessableEntityError( + `package.name invalid, errors: ${errors}` + ); } } const versions = Object.values(pkg.versions); @@ -120,7 +153,9 @@ export class SavePackageVersionController extends AbstractController { if (!attachmentFilename) { // `deprecated: ''` meaning remove deprecated message - const isDeprecatedRequest = versions.some(version => 'deprecated' in version); + const isDeprecatedRequest = versions.some( + version => 'deprecated' in version + ); // handle deprecated request // PUT /:fullname?write=true // https://github.com/npm/cli/blob/latest/lib/commands/deprecate.js#L48 @@ -144,23 +179,30 @@ export class SavePackageVersionController extends AbstractController { throw new UnprocessableEntityError('dist-tags is empty'); } - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); // see @https://github.com/cnpm/cnpmcore/issues/574 // add default latest tag if (!pkg['dist-tags']!.latest) { const existsPkg = await this.packageRepository.findPackage(scope, name); - const existsLatestTag = existsPkg && await this.packageRepository.findPackageTag(existsPkg?.packageId, 'latest'); + const existsLatestTag = + existsPkg && + (await this.packageRepository.findPackageTag( + existsPkg?.packageId, + 'latest' + )); if (!existsPkg || !existsLatestTag) { this.logger.warn('[package:version:add] add default latest tag'); pkg['dist-tags']!.latest = pkg['dist-tags']![tagNames[0]]; - tagNames = [ ...tagNames, 'latest' ]; + tagNames = [...tagNames, 'latest']; } } const tagWithVersion = { tag: tagNames[0], version: distTags[tagNames[0]] }; ctx.tValidate(TagWithVersionRule, tagWithVersion); if (tagWithVersion.version !== packageVersion.version) { - throw new UnprocessableEntityError(`dist-tags version "${tagWithVersion.version}" not match package version "${packageVersion.version}"`); + throw new UnprocessableEntityError( + `dist-tags version "${tagWithVersion.version}" not match package version "${packageVersion.version}"` + ); } // check attachment data format and size @@ -168,11 +210,15 @@ export class SavePackageVersionController extends AbstractController { throw new UnprocessableEntityError('attachment.data format invalid'); } if (!PACKAGE_ATTACH_DATA_RE.test(attachment.data)) { - throw new UnprocessableEntityError('attachment.data string format invalid'); + throw new UnprocessableEntityError( + 'attachment.data string format invalid' + ); } const tarballBytes = Buffer.from(attachment.data, 'base64'); if (tarballBytes.length !== attachment.length) { - throw new UnprocessableEntityError(`attachment size ${attachment.length} not match download size ${tarballBytes.length}`); + throw new UnprocessableEntityError( + `attachment size ${attachment.length} not match download size ${tarballBytes.length}` + ); } // check integrity or shasum @@ -180,16 +226,19 @@ export class SavePackageVersionController extends AbstractController { // for content security reason // check integrity if (integrity) { - const algorithm = ssri.checkData(tarballBytes, integrity); + const algorithm = checkData(tarballBytes, integrity); if (!algorithm) { throw new UnprocessableEntityError('dist.integrity invalid'); } } else { - const integrityObj = ssri.fromData(tarballBytes, { - algorithms: [ 'sha1' ], + const integrityObj = fromData(tarballBytes, { + algorithms: ['sha1'], }); const shasum = integrityObj.sha1[0].hexDigest(); - if (packageVersion.dist?.shasum && packageVersion.dist.shasum !== shasum) { + if ( + packageVersion.dist?.shasum && + packageVersion.dist.shasum !== shasum + ) { // if integrity not exists, check shasum throw new UnprocessableEntityError('dist.shasum invalid'); } @@ -205,13 +254,15 @@ export class SavePackageVersionController extends AbstractController { return !isEqual(tarballPkg[key], versionManifest[targetKey]); }); if (diffKeys.length > 0) { - throw new UnprocessableEntityError(`${diffKeys} mismatch between tarball and manifest`); + throw new UnprocessableEntityError( + `${diffKeys} mismatch between tarball and manifest` + ); } } - // make sure readme is string - const readme = typeof packageVersion.readme === 'string' ? packageVersion.readme : ''; + const readme = + typeof packageVersion.readme === 'string' ? packageVersion.readme : ''; // remove readme packageVersion.readme = undefined; // make sure description is string @@ -223,32 +274,46 @@ export class SavePackageVersionController extends AbstractController { let packageVersionEntity: PackageVersionEntity | undefined; const lockName = `${pkg.name}:publish`; - const lockRes = await this.cacheAdapter.usingLock(`${pkg.name}:publish`, 60, async () => { - packageVersionEntity = await this.packageManagerService.publish({ - scope, - name, - version: packageVersion.version, - description: packageVersion.description as string, - packageJson: packageVersion as PackageJSONType, - readme, - dist: { - content: tarballBytes, - }, - tags: tagNames, - registryId: registry.registryId, - isPrivate: true, - }, user); - }); + const lockRes = await this.cacheAdapter.usingLock( + `${pkg.name}:publish`, + 60, + async () => { + packageVersionEntity = await this.packageManagerService.publish( + { + scope, + name, + version: packageVersion.version, + description: packageVersion.description as string, + packageJson: packageVersion as PackageJSONType, + readme, + dist: { + content: tarballBytes, + }, + tags: tagNames, + registryId: registry.registryId, + isPrivate: true, + }, + user + ); + } + ); // lock fail if (!lockRes) { this.logger.warn('[package:version:add] check lock:%s fail', lockName); - throw new ConflictError('Unable to create the publication lock, please try again later.'); + throw new ConflictError( + 'Unable to create the publication lock, please try again later.' + ); } - this.logger.info('[package:version:add] %s@%s, packageVersionId: %s, tag: %s, userId: %s', - packageVersion.name, packageVersion.version, packageVersionEntity?.packageVersionId, - tagWithVersion.tag, user?.userId); + this.logger.info( + '[package:version:add] %s@%s, packageVersionId: %s, tag: %s, userId: %s', + packageVersion.name, + packageVersion.version, + packageVersionEntity?.packageVersionId, + tagWithVersion.tag, + user?.userId + ); ctx.status = 201; return { ok: true, @@ -257,11 +322,17 @@ export class SavePackageVersionController extends AbstractController { } // https://github.com/cnpm/cnpmjs.org/issues/415 - private async saveDeprecatedVersions(fullname: string, versions: PackageVersion[]) { + private async saveDeprecatedVersions( + fullname: string, + versions: PackageVersion[] + ) { const pkg = await this.getPackageEntityByFullname(fullname); - await this.packageManagerService.saveDeprecatedVersions(pkg, versions.map(v => { - return { version: v.version, deprecated: v.deprecated! }; - })); + await this.packageManagerService.saveDeprecatedVersions( + pkg, + versions.map(v => { + return { version: v.version, deprecated: v.deprecated! }; + }) + ); return { ok: true }; } diff --git a/app/port/controller/package/SearchPackageController.ts b/app/port/controller/package/SearchPackageController.ts index bee766b0..3fd5f236 100644 --- a/app/port/controller/package/SearchPackageController.ts +++ b/app/port/controller/package/SearchPackageController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -7,14 +8,13 @@ import { Inject, Middleware, Context, - EggContext, } from '@eggjs/tegg'; -import { Static } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; import { E451 } from 'egg-errors'; import { AbstractController } from '../AbstractController.js'; -import { SearchQueryOptions } from '../../typebox.js'; -import { PackageSearchService } from '../../../core/service/PackageSearchService.js'; +import type { SearchQueryOptions } from '../../typebox.js'; +import type { PackageSearchService } from '../../../core/service/PackageSearchService.js'; import { FULLNAME_REG_STRING } from '../../../common/PackageUtil.js'; import { AdminAccess } from '../../middleware/AdminAccess.js'; @@ -32,12 +32,18 @@ export class SearchPackageController extends AbstractController { @Context() ctx: EggContext, @HTTPQuery() text: Static['text'], @HTTPQuery() from: Static['from'], - @HTTPQuery() size: Static['size'], + @HTTPQuery() size: Static['size'] ) { if (!this.config.cnpmcore.enableElasticsearch) { - throw new E451('search feature not enabled in `config.cnpmcore.enableElasticsearch`'); + throw new E451( + 'search feature not enabled in `config.cnpmcore.enableElasticsearch`' + ); } - const data = await this.packageSearchService.searchPackage(text, from, size); + const data = await this.packageSearchService.searchPackage( + text, + from, + size + ); this.setCDNHeaders(ctx); return data; } @@ -48,7 +54,9 @@ export class SearchPackageController extends AbstractController { }) async sync(@HTTPParam() fullname: string) { if (!this.config.cnpmcore.enableElasticsearch) { - throw new E451('search feature not enabled in `config.cnpmcore.enableElasticsearch`'); + throw new E451( + 'search feature not enabled in `config.cnpmcore.enableElasticsearch`' + ); } const name = await this.packageSearchService.syncPackage(fullname, true); return { package: name }; @@ -61,7 +69,9 @@ export class SearchPackageController extends AbstractController { @Middleware(AdminAccess) async delete(@HTTPParam() fullname: string) { if (!this.config.cnpmcore.enableElasticsearch) { - throw new E451('search feature not enabled in `config.cnpmcore.enableElasticsearch`'); + throw new E451( + 'search feature not enabled in `config.cnpmcore.enableElasticsearch`' + ); } const name = await this.packageSearchService.removePackage(fullname); return { package: name }; diff --git a/app/port/controller/package/ShowPackageController.ts b/app/port/controller/package/ShowPackageController.ts index 45f30dd4..b14bc86f 100644 --- a/app/port/controller/package/ShowPackageController.ts +++ b/app/port/controller/package/ShowPackageController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -5,16 +6,18 @@ import { HTTPParam, Inject, Context, - EggContext, } from '@eggjs/tegg'; import { AbstractController } from '../AbstractController.js'; -import { getScopeAndName, FULLNAME_REG_STRING } from '../../../common/PackageUtil.js'; +import { + getScopeAndName, + FULLNAME_REG_STRING, +} from '../../../common/PackageUtil.js'; import { isSyncWorkerRequest } from '../../../common/SyncUtil.js'; -import { PackageManagerService } from '../../../core/service/PackageManagerService.js'; -import { CacheService } from '../../../core/service/CacheService.js'; +import type { PackageManagerService } from '../../../core/service/PackageManagerService.js'; +import type { CacheService } from '../../../core/service/CacheService.js'; import { ABBREVIATED_META_TYPE, SyncMode } from '../../../common/constants.js'; -import { ProxyCacheService } from '../../../core/service/ProxyCacheService.js'; +import type { ProxyCacheService } from '../../../core/service/ProxyCacheService.js'; import { calculateIntegrity } from '../../../common/PackageUtil.js'; import { DIST_NAMES } from '../../../core/entity/Package.js'; @@ -34,14 +37,18 @@ export class ShowPackageController extends AbstractController { method: HTTPMethodEnum.GET, }) async show(@Context() ctx: EggContext, @HTTPParam() fullname: string) { - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const isSync = isSyncWorkerRequest(ctx); - const isFullManifests = ctx.accepts([ 'json', ABBREVIATED_META_TYPE ]) !== ABBREVIATED_META_TYPE; + const isFullManifests = + ctx.accepts(['json', ABBREVIATED_META_TYPE]) !== ABBREVIATED_META_TYPE; // handle cache // fallback to db when cache error try { - const cacheEtag = await this.cacheService.getPackageEtag(fullname, isFullManifests); + const cacheEtag = await this.cacheService.getPackageEtag( + fullname, + isFullManifests + ); if (!isSync && cacheEtag) { let requestEtag = ctx.request.get('if-none-match'); if (requestEtag.startsWith('W/')) { @@ -55,7 +62,10 @@ export class ShowPackageController extends AbstractController { return; } // get cache pkg data - const cacheBytes = await this.cacheService.getPackageManifests(fullname, isFullManifests); + const cacheBytes = await this.cacheService.getPackageManifests( + fullname, + isFullManifests + ); if (cacheBytes && cacheBytes.length > 0) { ctx.set('etag', `W/${cacheEtag}`); ctx.type = 'json'; @@ -65,16 +75,26 @@ export class ShowPackageController extends AbstractController { } } catch (e) { this.logger.error(e); - this.logger.error('[ShowPackageController.show:error] get cache error, ignore'); + this.logger.error( + '[ShowPackageController.show:error] get cache error, ignore' + ); } // handle cache miss - let result: { etag: string; data: any, blockReason: string }; + let result: { etag: string; data: any; blockReason: string }; if (this.config.cnpmcore.syncMode === SyncMode.proxy) { // proxy mode - const fileType = isFullManifests ? DIST_NAMES.FULL_MANIFESTS : DIST_NAMES.ABBREVIATED_MANIFESTS; - const { data: sourceManifest } = await this.proxyCacheService.getProxyResponse(ctx, { dataType: 'json' }); - const pkgManifest = this.proxyCacheService.replaceTarballUrl(sourceManifest, fileType); + const fileType = isFullManifests + ? DIST_NAMES.FULL_MANIFESTS + : DIST_NAMES.ABBREVIATED_MANIFESTS; + const { data: sourceManifest } = + await this.proxyCacheService.getProxyResponse(ctx, { + dataType: 'json', + }); + const pkgManifest = this.proxyCacheService.replaceTarballUrl( + sourceManifest, + fileType + ); const nfsBytes = Buffer.from(JSON.stringify(pkgManifest)); const { shasum: etag } = await calculateIntegrity(nfsBytes); @@ -82,9 +102,18 @@ export class ShowPackageController extends AbstractController { } else { // sync mode if (isFullManifests) { - result = await this.packageManagerService.listPackageFullManifests(scope, name, isSync); + result = await this.packageManagerService.listPackageFullManifests( + scope, + name, + isSync + ); } else { - result = await this.packageManagerService.listPackageAbbreviatedManifests(scope, name, isSync); + result = + await this.packageManagerService.listPackageAbbreviatedManifests( + scope, + name, + isSync + ); } } const { etag, data, blockReason } = result; @@ -92,7 +121,11 @@ export class ShowPackageController extends AbstractController { if (!etag) { const allowSync = this.getAllowSync(ctx); // don't set cdn header, no cdn cache for new package to sync as soon as possible - throw this.createPackageNotFoundErrorWithRedirect(fullname, undefined, allowSync); + throw this.createPackageNotFoundErrorWithRedirect( + fullname, + undefined, + allowSync + ); } if (blockReason) { this.setCDNHeaders(ctx); @@ -104,7 +137,12 @@ export class ShowPackageController extends AbstractController { // sync request response with no bug version fixed if (!isSync) { ctx.runInBackground(async () => { - await this.cacheService.savePackageEtagAndManifests(fullname, isFullManifests, etag, cacheBytes); + await this.cacheService.savePackageEtagAndManifests( + fullname, + isFullManifests, + etag, + cacheBytes + ); }); } diff --git a/app/port/controller/package/ShowPackageVersionController.ts b/app/port/controller/package/ShowPackageVersionController.ts index e86bf0db..91206e19 100644 --- a/app/port/controller/package/ShowPackageVersionController.ts +++ b/app/port/controller/package/ShowPackageVersionController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -5,7 +6,6 @@ import { HTTPParam, Inject, Context, - EggContext, } from '@eggjs/tegg'; import { NotFoundError } from 'egg-errors'; @@ -15,8 +15,8 @@ import { FULLNAME_REG_STRING, } from '../../../common/PackageUtil.js'; import { isSyncWorkerRequest } from '../../../common/SyncUtil.js'; -import { PackageManagerService } from '../../../core/service/PackageManagerService.js'; -import { ProxyCacheService } from '../../../core/service/ProxyCacheService.js'; +import type { PackageManagerService } from '../../../core/service/PackageManagerService.js'; +import type { ProxyCacheService } from '../../../core/service/ProxyCacheService.js'; import { Spec } from '../../../port/typebox.js'; import { ABBREVIATED_META_TYPE, SyncMode } from '../../../common/constants.js'; import { DIST_NAMES } from '../../../core/entity/Package.js'; @@ -36,14 +36,14 @@ export class ShowPackageVersionController extends AbstractController { async show( @Context() ctx: EggContext, @HTTPParam() fullname: string, - @HTTPParam() versionSpec: string, + @HTTPParam() versionSpec: string ) { // https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#full-metadata-format ctx.tValidate(Spec, `${fullname}@${versionSpec}`); - const [ scope, name ] = getScopeAndName(fullname); + const [scope, name] = getScopeAndName(fullname); const isSync = isSyncWorkerRequest(ctx); const isFullManifests = - ctx.accepts([ 'json', ABBREVIATED_META_TYPE ]) !== ABBREVIATED_META_TYPE; + ctx.accepts(['json', ABBREVIATED_META_TYPE]) !== ABBREVIATED_META_TYPE; const { blockReason, manifest, pkg } = await this.packageManagerService.showPackageVersionManifest( @@ -51,7 +51,7 @@ export class ShowPackageVersionController extends AbstractController { name, versionSpec, isSync, - isFullManifests, + isFullManifests ); const allowSync = this.getAllowSync(ctx); @@ -68,12 +68,16 @@ export class ShowPackageVersionController extends AbstractController { return await this.proxyCacheService.getPackageVersionManifest( fullname, fileType, - versionSpec, + versionSpec ); } if (!pkg) { - throw this.createPackageNotFoundErrorWithRedirect(fullname, undefined, allowSync); + throw this.createPackageNotFoundErrorWithRedirect( + fullname, + undefined, + allowSync + ); } if (!manifest) { throw new NotFoundError(`${fullname}@${versionSpec} not found`); diff --git a/app/port/controller/package/UpdatePackageController.ts b/app/port/controller/package/UpdatePackageController.ts index fd05be6c..9a01a747 100644 --- a/app/port/controller/package/UpdatePackageController.ts +++ b/app/port/controller/package/UpdatePackageController.ts @@ -1,7 +1,5 @@ -import { - UnprocessableEntityError, - BadRequestError, -} from 'egg-errors'; +import { UnprocessableEntityError, BadRequestError } from 'egg-errors'; +import type { EggContext } from '@eggjs/tegg'; import { HTTPController, HTTPMethod, @@ -10,20 +8,23 @@ import { HTTPBody, Inject, Context, - EggContext, } from '@eggjs/tegg'; -import { Static, Type } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; +import { Type } from 'egg-typebox-validate/typebox'; import { AbstractController } from '../AbstractController.js'; import { FULLNAME_REG_STRING } from '../../../common/PackageUtil.js'; -import { User as UserEntity } from '../../../core/entity/User.js'; -import { PackageManagerService } from '../../../core/service/PackageManagerService.js'; +import type { User as UserEntity } from '../../../core/entity/User.js'; +import type { PackageManagerService } from '../../../core/service/PackageManagerService.js'; const MaintainerDataRule = Type.Object({ - maintainers: Type.Array(Type.Object({ - name: Type.String({ minLength: 1, maxLength: 100 }), - email: Type.String({ format: 'email', maxLength: 400 }), - }), { minItems: 1 }), + maintainers: Type.Array( + Type.Object({ + name: Type.String({ minLength: 1, maxLength: 100 }), + email: Type.String({ format: 'email', maxLength: 400 }), + }), + { minItems: 1 } + ), }); type Maintainer = Static; @@ -38,7 +39,11 @@ export class UpdatePackageController extends AbstractController { path: `/:fullname(${FULLNAME_REG_STRING})/-rev/:rev`, method: HTTPMethodEnum.PUT, }) - async update(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPBody() data: Maintainer) { + async update( + @Context() ctx: EggContext, + @HTTPParam() fullname: string, + @HTTPBody() data: Maintainer + ) { if (this.isNpmCommandValid(ctx, 'unpublish')) { // ignore it return { ok: false }; @@ -46,7 +51,9 @@ export class UpdatePackageController extends AbstractController { // only support update maintainer if (!this.isNpmCommandValid(ctx, 'owner')) { const npmCommand = this.getNpmCommand(ctx); - throw new BadRequestError(`header: npm-command expected "owner", but got "${npmCommand}"`); + throw new BadRequestError( + `header: npm-command expected "owner", but got "${npmCommand}"` + ); } ctx.tValidate(MaintainerDataRule, data); const ensureRes = await this.ensurePublishAccess(ctx, fullname, true); @@ -55,17 +62,25 @@ export class UpdatePackageController extends AbstractController { // make sure all maintainers exists const users: UserEntity[] = []; for (const maintainer of data.maintainers) { - if (registry?.userPrefix && !maintainer.name.startsWith(registry.userPrefix)) { + if ( + registry?.userPrefix && + !maintainer.name.startsWith(registry.userPrefix) + ) { maintainer.name = `${registry?.userPrefix}${maintainer.name}`; } const user = await this.userRepository.findUserByName(maintainer.name); if (!user) { - throw new UnprocessableEntityError(`Maintainer "${maintainer.name}" not exists`); + throw new UnprocessableEntityError( + `Maintainer "${maintainer.name}" not exists` + ); } users.push(user); } - await this.packageManagerService.replacePackageMaintainersAndDist(pkg, users); + await this.packageManagerService.replacePackageMaintainersAndDist( + pkg, + users + ); return { ok: true }; } diff --git a/app/port/middleware/AdminAccess.ts b/app/port/middleware/AdminAccess.ts index 0758da37..e18aaded 100644 --- a/app/port/middleware/AdminAccess.ts +++ b/app/port/middleware/AdminAccess.ts @@ -1,4 +1,4 @@ -import { EggContext, Next } from '@eggjs/tegg'; +import type { EggContext, Next } from '@eggjs/tegg'; import { ForbiddenError } from 'egg-errors'; import { UserRoleManager } from '../UserRoleManager.js'; diff --git a/app/port/middleware/AlwaysAuth.ts b/app/port/middleware/AlwaysAuth.ts index 789db016..f42bfa45 100644 --- a/app/port/middleware/AlwaysAuth.ts +++ b/app/port/middleware/AlwaysAuth.ts @@ -1,11 +1,12 @@ -import { EggContext, Next } from '@eggjs/tegg'; +import type { EggContext, Next } from '@eggjs/tegg'; import { UserRoleManager } from '../UserRoleManager.js'; export async function AlwaysAuth(ctx: EggContext, next: Next) { if (ctx.app.config.cnpmcore.alwaysAuth) { // ignore login request: `PUT /-/user/org.couchdb.user::username` - const isLoginRequest = ctx.method === 'PUT' && ctx.path.startsWith('/-/user/org.couchdb.user:'); + const isLoginRequest = + ctx.method === 'PUT' && ctx.path.startsWith('/-/user/org.couchdb.user:'); if (!isLoginRequest) { const userRoleManager = await ctx.getEggObject(UserRoleManager); await userRoleManager.requiredAuthorizedUser(ctx, 'read'); diff --git a/app/port/middleware/ErrorHandler.ts b/app/port/middleware/ErrorHandler.ts index 39364028..996737f0 100644 --- a/app/port/middleware/ErrorHandler.ts +++ b/app/port/middleware/ErrorHandler.ts @@ -1,4 +1,4 @@ -import { EggContext, Next } from '@eggjs/tegg'; +import type { EggContext, Next } from '@eggjs/tegg'; import { PackageSyncerService } from '../../core/service/PackageSyncerService.js'; @@ -12,14 +12,21 @@ export async function ErrorHandler(ctx: EggContext, next: Next) { if (err.syncPackage) { // create sync task const syncPackage = err.syncPackage; - const packageSyncerService = await ctx.getEggObject(PackageSyncerService); - const task = await packageSyncerService.createTask(syncPackage.fullname, { - authorIp: ctx.ip, - authorId: ctx.userId as string, - tips: `Sync cause by "${syncPackage.fullname}" missing, request URL "${ctx.href}"`, - }); - ctx.logger.info('[middleware:ErrorHandler][syncPackage] create sync package "%s" task %s', - syncPackage.fullname, task.taskId); + const packageSyncerService = + await ctx.getEggObject(PackageSyncerService); + const task = await packageSyncerService.createTask( + syncPackage.fullname, + { + authorIp: ctx.ip, + authorId: ctx.userId as string, + tips: `Sync cause by "${syncPackage.fullname}" missing, request URL "${ctx.href}"`, + } + ); + ctx.logger.info( + '[middleware:ErrorHandler][syncPackage] create sync package "%s" task %s', + syncPackage.fullname, + task.taskId + ); } if (err.redirectToSourceRegistry) { // redirect to sourceRegistry @@ -32,14 +39,24 @@ export async function ErrorHandler(ctx: EggContext, next: Next) { } // http status, default is DEFAULT_SERVER_ERROR_STATUS - ctx.status = (typeof err.status === 'number' && err.status >= 200) ? err.status : DEFAULT_SERVER_ERROR_STATUS; + ctx.status = + typeof err.status === 'number' && err.status >= 200 + ? err.status + : DEFAULT_SERVER_ERROR_STATUS; // don't log NotImplementedError - if (ctx.status >= DEFAULT_SERVER_ERROR_STATUS && err.name !== 'NotImplementedError') { + if ( + ctx.status >= DEFAULT_SERVER_ERROR_STATUS && + err.name !== 'NotImplementedError' + ) { ctx.logger.error(err); } let message = err.message; // convert ctx.tValidate error - if (err.name === 'UnprocessableEntityError' && err.currentSchema && err.errors[0]?.message) { + if ( + err.name === 'UnprocessableEntityError' && + err.currentSchema && + err.errors[0]?.message + ) { // { // instancePath: '/password', // schemaPath: '#/properties/password/minLength', @@ -55,7 +72,9 @@ export async function ErrorHandler(ctx: EggContext, next: Next) { } // error body format https://github.com/npm/npm-registry-fetch/blob/main/errors.js#L45 ctx.body = { - error: err.code ? `[${String(err.code).toUpperCase()}] ${message}` : message, + error: err.code + ? `[${String(err.code).toUpperCase()}] ${message}` + : message, }; } } diff --git a/app/port/middleware/Tracing.ts b/app/port/middleware/Tracing.ts index f05bac11..8b68ecd8 100644 --- a/app/port/middleware/Tracing.ts +++ b/app/port/middleware/Tracing.ts @@ -1,4 +1,4 @@ -import { EggContext, Next } from '@eggjs/tegg'; +import type { EggContext, Next } from '@eggjs/tegg'; export async function Tracing(ctx: EggContext, next: Next) { // headers: { @@ -13,11 +13,13 @@ export async function Tracing(ctx: EggContext, next: Next) { // } ctx.set('request-id', ctx.tracer.traceId); if (ctx.method !== 'HEAD') { - ctx.logger.info('[Tracing] auth: %s, npm-command: %s, referer: %s, user-agent: %j', + ctx.logger.info( + '[Tracing] auth: %s, npm-command: %s, referer: %s, user-agent: %j', ctx.get('authorization') ? 1 : 0, ctx.get('npm-command') || '-', ctx.get('referer') || '-', - ctx.get('user-agent')); + ctx.get('user-agent') + ); } await next(); } diff --git a/app/port/schedule/ChangesStreamWorker.ts b/app/port/schedule/ChangesStreamWorker.ts index ef481084..37d1f77c 100644 --- a/app/port/schedule/ChangesStreamWorker.ts +++ b/app/port/schedule/ChangesStreamWorker.ts @@ -1,8 +1,9 @@ -import { EggAppConfig, EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { ChangesStreamService } from '../../core/service/ChangesStreamService.js'; +import type { ChangesStreamService } from '../../core/service/ChangesStreamService.js'; @Schedule({ type: ScheduleType.WORKER, @@ -21,7 +22,11 @@ export class ChangesStreamWorker { private readonly logger: EggLogger; async subscribe() { - if (this.config.cnpmcore.syncMode === 'none' || !this.config.cnpmcore.enableChangesStream) return; + if ( + this.config.cnpmcore.syncMode === 'none' || + !this.config.cnpmcore.enableChangesStream + ) + return; const task = await this.changesStreamService.findExecuteTask(); if (!task) return; this.logger.info('[ChangesStreamWorker:start] taskId: %s', task.taskId); diff --git a/app/port/schedule/CheckProxyCacheUpdateWorker.ts b/app/port/schedule/CheckProxyCacheUpdateWorker.ts index 43ea25cb..656f73d6 100644 --- a/app/port/schedule/CheckProxyCacheUpdateWorker.ts +++ b/app/port/schedule/CheckProxyCacheUpdateWorker.ts @@ -1,10 +1,11 @@ -import { EggAppConfig, EggLogger } from 'egg'; -import { CronParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { CronParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { ProxyCacheRepository } from '../../repository/ProxyCacheRepository.js'; +import type { ProxyCacheRepository } from '../../repository/ProxyCacheRepository.js'; import { SyncMode } from '../../common/constants.js'; -import { ProxyCacheService } from '../../core/service/ProxyCacheService.js'; +import type { ProxyCacheService } from '../../core/service/ProxyCacheService.js'; import { isPkgManifest } from '../../core/entity/Package.js'; @Schedule({ @@ -14,7 +15,6 @@ import { isPkgManifest } from '../../core/entity/Package.js'; }, }) export class CheckProxyCacheUpdateWorker { - @Inject() private readonly config: EggAppConfig; @@ -25,31 +25,45 @@ export class CheckProxyCacheUpdateWorker { private proxyCacheService: ProxyCacheService; @Inject() - private readonly proxyCacheRepository:ProxyCacheRepository; + private readonly proxyCacheRepository: ProxyCacheRepository; async subscribe() { if (this.config.cnpmcore.syncMode !== SyncMode.proxy) return; let pageIndex = 0; - let { data: list } = await this.proxyCacheRepository.listCachedFiles({ pageSize: 5, pageIndex }); + let { data: list } = await this.proxyCacheRepository.listCachedFiles({ + pageSize: 5, + pageIndex, + }); while (list.length !== 0) { for (const item of list) { try { if (isPkgManifest(item.fileType)) { // 仅manifests需要更新,指定版本的package.json文件发布后不会改变 - const task = await this.proxyCacheService.createTask(`${item.fullname}/${item.fileType}`, { - fullname: item.fullname, - fileType: item.fileType, - }); - this.logger.info('[CheckProxyCacheUpdateWorker.subscribe:createTask][%s] taskId: %s, targetName: %s', - pageIndex, task.taskId, task.targetName); + const task = await this.proxyCacheService.createTask( + `${item.fullname}/${item.fileType}`, + { + fullname: item.fullname, + fileType: item.fileType, + } + ); + this.logger.info( + '[CheckProxyCacheUpdateWorker.subscribe:createTask][%s] taskId: %s, targetName: %s', + pageIndex, + task.taskId, + task.targetName + ); } } catch (err) { this.logger.error(err); } } pageIndex++; - list = (await this.proxyCacheRepository.listCachedFiles({ pageSize: 5, pageIndex })).data; + list = ( + await this.proxyCacheRepository.listCachedFiles({ + pageSize: 5, + pageIndex, + }) + ).data; } - } } diff --git a/app/port/schedule/CheckRecentlyUpdatedPackages.ts b/app/port/schedule/CheckRecentlyUpdatedPackages.ts index 134d5a51..d3a7af28 100644 --- a/app/port/schedule/CheckRecentlyUpdatedPackages.ts +++ b/app/port/schedule/CheckRecentlyUpdatedPackages.ts @@ -1,9 +1,10 @@ -import { EggAppConfig, EggHttpClient, EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggHttpClient, EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { PackageSyncerService } from '../../core/service/PackageSyncerService.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; +import type { PackageSyncerService } from '../../core/service/PackageSyncerService.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; import { getScopeAndName } from '../../common/PackageUtil.js'; import { SyncMode } from '../../common/constants.js'; @@ -31,8 +32,16 @@ export class CheckRecentlyUpdatedPackages { private readonly httpclient: EggHttpClient; async subscribe() { - const notAllowUpdateModeList = [ SyncMode.none, SyncMode.admin, SyncMode.proxy ]; - if (notAllowUpdateModeList.includes(this.config.cnpmcore.syncMode) || !this.config.cnpmcore.enableCheckRecentlyUpdated) return; + const notAllowUpdateModeList = [ + SyncMode.none, + SyncMode.admin, + SyncMode.proxy, + ]; + if ( + notAllowUpdateModeList.includes(this.config.cnpmcore.syncMode) || + !this.config.cnpmcore.enableCheckRecentlyUpdated + ) + return; const pageSize = 36; const pageCount = this.config.env === 'unittest' ? 2 : 5; for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) { @@ -44,14 +53,23 @@ export class CheckRecentlyUpdatedPackages { followRedirect: true, timeout: 10000, }); - this.logger.info('[CheckRecentlyUpdatedPackages.subscribe][%s] request %s status: %s, data size: %s', - pageIndex, pageUrl, status, data.length); + this.logger.info( + '[CheckRecentlyUpdatedPackages.subscribe][%s] request %s status: %s, data size: %s', + pageIndex, + pageUrl, + status, + data.length + ); if (status === 200) { html = data.toString(); } } catch (err) { - this.logger.info('[CheckRecentlyUpdatedPackages.subscribe:error][%s] request %s error: %s', - pageIndex, pageUrl, err); + this.logger.info( + '[CheckRecentlyUpdatedPackages.subscribe:error][%s] request %s error: %s', + pageIndex, + pageUrl, + err + ); this.logger.error(err); continue; } @@ -63,13 +81,20 @@ export class CheckRecentlyUpdatedPackages { const data = JSON.parse(matchs[1]); const packages = data.context.packages || []; if (Array.isArray(packages)) { - this.logger.info('[CheckRecentlyUpdatedPackages.subscribe][%s] parse %d packages on %s', - pageIndex, packages.length, pageUrl); + this.logger.info( + '[CheckRecentlyUpdatedPackages.subscribe][%s] parse %d packages on %s', + pageIndex, + packages.length, + pageUrl + ); for (const pkg of packages) { // skip update when package does not exist if (this.config.cnpmcore.syncMode === 'exist') { - const [ scope, name ] = getScopeAndName(pkg.name); - const pkgId = await this.packageRepository.findPackageId(scope, name); + const [scope, name] = getScopeAndName(pkg.name); + const pkgId = await this.packageRepository.findPackageId( + scope, + name + ); if (!pkgId) { continue; } @@ -77,13 +102,21 @@ export class CheckRecentlyUpdatedPackages { const task = await this.packageSyncerService.createTask(pkg.name, { tips: `Sync cause by recently updated packages ${pageUrl}`, }); - this.logger.info('[CheckRecentlyUpdatedPackages.subscribe:createTask][%s] taskId: %s, targetName: %s', - pageIndex, task.taskId, task.targetName); + this.logger.info( + '[CheckRecentlyUpdatedPackages.subscribe:createTask][%s] taskId: %s, targetName: %s', + pageIndex, + task.taskId, + task.targetName + ); } } } catch (err) { - this.logger.info('[CheckRecentlyUpdatedPackages.subscribe:error][%s] parse %s context json error: %s', - pageIndex, pageUrl, err); + this.logger.info( + '[CheckRecentlyUpdatedPackages.subscribe:error][%s] parse %s context json error: %s', + pageIndex, + pageUrl, + err + ); this.logger.error(err); } } diff --git a/app/port/schedule/CleanTempDir.ts b/app/port/schedule/CleanTempDir.ts index 7cc4e0dd..77783501 100644 --- a/app/port/schedule/CleanTempDir.ts +++ b/app/port/schedule/CleanTempDir.ts @@ -1,8 +1,9 @@ import { rm, access } from 'node:fs/promises'; import path from 'node:path'; -import { EggAppConfig, EggLogger } from 'egg'; -import { CronParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { CronParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; import dayjs from '../../common/dayjs.js'; @@ -39,7 +40,11 @@ export class CleanTempDir { } catch { exists = false; } - this.logger.info('[CleanTempDir.subscribe] dir "%s" exists: %s', dir, exists); + this.logger.info( + '[CleanTempDir.subscribe] dir "%s" exists: %s', + dir, + exists + ); if (exists) { await rm(dir, { recursive: true, force: true }); this.logger.info('[CleanTempDir.subscribe] remove dir "%s"', dir); diff --git a/app/port/schedule/CreateSyncBinaryTask.ts b/app/port/schedule/CreateSyncBinaryTask.ts index d8b6aafe..3470e417 100644 --- a/app/port/schedule/CreateSyncBinaryTask.ts +++ b/app/port/schedule/CreateSyncBinaryTask.ts @@ -1,9 +1,11 @@ -import { EggAppConfig } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { BinarySyncerService } from '../../core/service/BinarySyncerService.js'; -import binaries, { BinaryName } from '../../../config/binaries.js'; +import type { BinarySyncerService } from '../../core/service/BinarySyncerService.js'; +import type { BinaryName } from '../../../config/binaries.js'; +import binaries from '../../../config/binaries.js'; @Schedule({ type: ScheduleType.WORKER, @@ -22,7 +24,7 @@ export class CreateSyncBinaryTask { async subscribe() { if (!this.config.cnpmcore.enableSyncBinary) return; - for (const [ binaryName, binary ] of Object.entries(binaries)) { + for (const [binaryName, binary] of Object.entries(binaries)) { if (this.config.env === 'unittest' && binaryName !== 'node') continue; if (binary.disable) continue; diff --git a/app/port/schedule/CreateTriggerHookWorker.ts b/app/port/schedule/CreateTriggerHookWorker.ts index 29607c63..b0ac119c 100644 --- a/app/port/schedule/CreateTriggerHookWorker.ts +++ b/app/port/schedule/CreateTriggerHookWorker.ts @@ -1,11 +1,12 @@ -import { EggAppConfig, EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { TaskService } from '../../core/service/TaskService.js'; +import type { TaskService } from '../../core/service/TaskService.js'; import { TaskType } from '../../common/enum/Task.js'; -import { CreateHookTask } from '../../core/entity/Task.js'; -import { CreateHookTriggerService } from '../../core/service/CreateHookTriggerService.js'; +import type { CreateHookTask } from '../../core/entity/Task.js'; +import type { CreateHookTriggerService } from '../../core/service/CreateHookTriggerService.js'; let executingCount = 0; @@ -30,30 +31,60 @@ export class CreateTriggerHookWorker { async subscribe() { if (!this.config.cnpmcore.hookEnable) return; - if (executingCount >= this.config.cnpmcore.createTriggerHookWorkerMaxConcurrentTasks) return; + if ( + executingCount >= + this.config.cnpmcore.createTriggerHookWorkerMaxConcurrentTasks + ) + return; executingCount++; try { - let task = await this.taskService.findExecuteTask(TaskType.CreateHook) as CreateHookTask; + let task = (await this.taskService.findExecuteTask( + TaskType.CreateHook + )) as CreateHookTask; while (task) { const startTime = Date.now(); - this.logger.info('[CreateTriggerHookWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', - executingCount, task.taskId, task.targetName, task.attempts, task.data, task.updatedAt, - startTime - task.updatedAt.getTime()); + this.logger.info( + '[CreateTriggerHookWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', + executingCount, + task.taskId, + task.targetName, + task.attempts, + task.data, + task.updatedAt, + startTime - task.updatedAt.getTime() + ); await this.createHookTriggerService.executeTask(task); const use = Date.now() - startTime; - this.logger.info('[CreateTriggerHookWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', - executingCount, task.taskId, task.targetName, use); - if (executingCount >= this.config.cnpmcore.createTriggerHookWorkerMaxConcurrentTasks) { - this.logger.info('[CreateTriggerHookWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', - executingCount, this.config.cnpmcore.createTriggerHookWorkerMaxConcurrentTasks); + this.logger.info( + '[CreateTriggerHookWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', + executingCount, + task.taskId, + task.targetName, + use + ); + if ( + executingCount >= + this.config.cnpmcore.createTriggerHookWorkerMaxConcurrentTasks + ) { + this.logger.info( + '[CreateTriggerHookWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', + executingCount, + this.config.cnpmcore.createTriggerHookWorkerMaxConcurrentTasks + ); break; } // try next task - task = await this.taskService.findExecuteTask(TaskType.CreateHook) as CreateHookTask; + task = (await this.taskService.findExecuteTask( + TaskType.CreateHook + )) as CreateHookTask; } } catch (err) { - this.logger.error('[TriggerHookWorker:subscribe:executeTask:error][%s] %s', executingCount, err); + this.logger.error( + '[TriggerHookWorker:subscribe:executeTask:error][%s] %s', + executingCount, + err + ); } finally { executingCount--; } diff --git a/app/port/schedule/SavePackageVersionDownloadCounter.ts b/app/port/schedule/SavePackageVersionDownloadCounter.ts index ca3c66ec..32779e29 100644 --- a/app/port/schedule/SavePackageVersionDownloadCounter.ts +++ b/app/port/schedule/SavePackageVersionDownloadCounter.ts @@ -1,7 +1,8 @@ -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { PackageManagerService } from '../../core/service/PackageManagerService.js'; +import type { PackageManagerService } from '../../core/service/PackageManagerService.js'; @Schedule({ type: ScheduleType.WORKER, diff --git a/app/port/schedule/SyncBinaryWorker.ts b/app/port/schedule/SyncBinaryWorker.ts index 2d58ff40..78a67422 100644 --- a/app/port/schedule/SyncBinaryWorker.ts +++ b/app/port/schedule/SyncBinaryWorker.ts @@ -1,8 +1,9 @@ -import { EggAppConfig, EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { BinarySyncerService } from '../../core/service/BinarySyncerService.js'; +import type { BinarySyncerService } from '../../core/service/BinarySyncerService.js'; import { isTimeoutError } from '../../common/ErrorUtil.js'; @Schedule({ @@ -28,15 +29,26 @@ export class SyncBinaryWorker { if (!task) return; const startTime = Date.now(); - this.logger.info('[SyncBinaryWorker:executeTask:start] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', - task.taskId, task.targetName, task.attempts, task.data, task.updatedAt, - startTime - task.updatedAt.getTime()); + this.logger.info( + '[SyncBinaryWorker:executeTask:start] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', + task.taskId, + task.targetName, + task.attempts, + task.data, + task.updatedAt, + startTime - task.updatedAt.getTime() + ); try { await this.binarySyncerService.executeTask(task); } catch (err) { const use = Date.now() - startTime; - this.logger.warn('[SyncBinaryWorker:executeTask:error] taskId: %s, targetName: %s, use %sms, error: %s', - task.taskId, task.targetName, use, err.message); + this.logger.warn( + '[SyncBinaryWorker:executeTask:error] taskId: %s, targetName: %s, use %sms, error: %s', + task.taskId, + task.targetName, + use, + err.message + ); if (isTimeoutError(err)) { this.logger.warn(err); } else { @@ -45,7 +57,11 @@ export class SyncBinaryWorker { return; } const use = Date.now() - startTime; - this.logger.info('[SyncBinaryWorker:executeTask:success] taskId: %s, targetName: %s, use %sms', - task.taskId, task.targetName, use); + this.logger.info( + '[SyncBinaryWorker:executeTask:success] taskId: %s, targetName: %s, use %sms', + task.taskId, + task.targetName, + use + ); } } diff --git a/app/port/schedule/SyncPackageWorker.ts b/app/port/schedule/SyncPackageWorker.ts index ee9c1194..dd40a50a 100644 --- a/app/port/schedule/SyncPackageWorker.ts +++ b/app/port/schedule/SyncPackageWorker.ts @@ -1,8 +1,9 @@ -import { EggAppConfig, EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { PackageSyncerService } from '../../core/service/PackageSyncerService.js'; +import type { PackageSyncerService } from '../../core/service/PackageSyncerService.js'; import { SyncMode } from '../../common/constants.js'; let executingCount = 0; @@ -25,30 +26,55 @@ export class SyncPackageWorker { async subscribe() { if (this.config.cnpmcore.syncMode === SyncMode.none) return; - if (executingCount >= this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks) return; + if ( + executingCount >= this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks + ) + return; executingCount++; try { let task = await this.packageSyncerService.findExecuteTask(); while (task) { const startTime = Date.now(); - this.logger.info('[SyncPackageWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', - executingCount, task.taskId, task.targetName, task.attempts, task.data, task.updatedAt, - startTime - task.updatedAt.getTime()); + this.logger.info( + '[SyncPackageWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', + executingCount, + task.taskId, + task.targetName, + task.attempts, + task.data, + task.updatedAt, + startTime - task.updatedAt.getTime() + ); await this.packageSyncerService.executeTask(task); const use = Date.now() - startTime; - this.logger.info('[SyncPackageWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', - executingCount, task.taskId, task.targetName, use); - if (executingCount >= this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks) { - this.logger.info('[SyncPackageWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', - executingCount, this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks); + this.logger.info( + '[SyncPackageWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', + executingCount, + task.taskId, + task.targetName, + use + ); + if ( + executingCount >= + this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks + ) { + this.logger.info( + '[SyncPackageWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', + executingCount, + this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks + ); break; } // try next task task = await this.packageSyncerService.findExecuteTask(); } } catch (err) { - this.logger.error('[SyncPackageWorker:subscribe:executeTask:error][%s] %s', executingCount, err); + this.logger.error( + '[SyncPackageWorker:subscribe:executeTask:error][%s] %s', + executingCount, + err + ); } finally { executingCount--; } diff --git a/app/port/schedule/SyncProxyCacheWorker.ts b/app/port/schedule/SyncProxyCacheWorker.ts index d7f8e40c..20aeba21 100644 --- a/app/port/schedule/SyncProxyCacheWorker.ts +++ b/app/port/schedule/SyncProxyCacheWorker.ts @@ -1,8 +1,9 @@ -import { EggAppConfig, EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { ProxyCacheService } from '../../core/service/ProxyCacheService.js'; +import type { ProxyCacheService } from '../../core/service/ProxyCacheService.js'; import { SyncMode } from '../../common/constants.js'; let executingCount = 0; @@ -25,30 +26,55 @@ export class SyncProxyCacheWorker { async subscribe() { if (this.config.cnpmcore.syncMode !== SyncMode.proxy) return; - if (executingCount >= this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks) return; + if ( + executingCount >= this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks + ) + return; executingCount++; try { let task = await this.proxyCacheService.findExecuteTask(); while (task) { const startTime = Date.now(); - this.logger.info('[SyncProxyCacheWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', - executingCount, task.taskId, task.targetName, task.attempts, task.data, task.updatedAt, - startTime - task.updatedAt.getTime()); + this.logger.info( + '[SyncProxyCacheWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', + executingCount, + task.taskId, + task.targetName, + task.attempts, + task.data, + task.updatedAt, + startTime - task.updatedAt.getTime() + ); await this.proxyCacheService.executeTask(task); const use = Date.now() - startTime; - this.logger.info('[SyncProxyCacheWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', - executingCount, task.taskId, task.targetName, use); - if (executingCount >= this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks) { - this.logger.info('[SyncProxyCacheWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', - executingCount, this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks); + this.logger.info( + '[SyncProxyCacheWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', + executingCount, + task.taskId, + task.targetName, + use + ); + if ( + executingCount >= + this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks + ) { + this.logger.info( + '[SyncProxyCacheWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', + executingCount, + this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks + ); break; } // try next task task = await this.proxyCacheService.findExecuteTask(); } } catch (err) { - this.logger.error('[SyncProxyCacheWorker:subscribe:executeTask:error][%s] %s', executingCount, err); + this.logger.error( + '[SyncProxyCacheWorker:subscribe:executeTask:error][%s] %s', + executingCount, + err + ); } finally { executingCount--; } diff --git a/app/port/schedule/TaskTimeoutHandler.ts b/app/port/schedule/TaskTimeoutHandler.ts index 05bbda6e..daa4cd84 100644 --- a/app/port/schedule/TaskTimeoutHandler.ts +++ b/app/port/schedule/TaskTimeoutHandler.ts @@ -1,18 +1,22 @@ -import { EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { TaskService } from '../../core/service/TaskService.js'; -import { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; +import type { TaskService } from '../../core/service/TaskService.js'; +import type { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; -@Schedule({ - type: ScheduleType.WORKER, - scheduleData: { - interval: 60000, +@Schedule( + { + type: ScheduleType.WORKER, + scheduleData: { + interval: 60000, + }, }, -}, { - immediate: process.env.NODE_ENV !== 'test', -}) + { + immediate: process.env.NODE_ENV !== 'test', + } +) export class TaskTimeoutHandler { @Inject() private readonly taskService: TaskService; @@ -26,7 +30,10 @@ export class TaskTimeoutHandler { async subscribe() { await this.cacheAdapter.usingLock('TaskTimeoutHandler', 60, async () => { const result = await this.taskService.retryExecuteTimeoutTasks(); - this.logger.info('[TaskTimeoutHandler:subscribe] retry execute timeout tasks: %j', result); + this.logger.info( + '[TaskTimeoutHandler:subscribe] retry execute timeout tasks: %j', + result + ); }); } } diff --git a/app/port/schedule/TriggerHookWorker.ts b/app/port/schedule/TriggerHookWorker.ts index 8eafea43..fadc5b60 100644 --- a/app/port/schedule/TriggerHookWorker.ts +++ b/app/port/schedule/TriggerHookWorker.ts @@ -1,11 +1,12 @@ -import { EggAppConfig, EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggAppConfig, EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { HookTriggerService } from '../../core/service/HookTriggerService.js'; -import { TaskService } from '../../core/service/TaskService.js'; +import type { HookTriggerService } from '../../core/service/HookTriggerService.js'; +import type { TaskService } from '../../core/service/TaskService.js'; import { TaskType } from '../../common/enum/Task.js'; -import { TriggerHookTask } from '../../core/entity/Task.js'; +import type { TriggerHookTask } from '../../core/entity/Task.js'; let executingCount = 0; @Schedule({ @@ -29,30 +30,59 @@ export class TriggerHookWorker { async subscribe() { if (!this.config.cnpmcore.hookEnable) return; - if (executingCount >= this.config.cnpmcore.triggerHookWorkerMaxConcurrentTasks) return; + if ( + executingCount >= this.config.cnpmcore.triggerHookWorkerMaxConcurrentTasks + ) + return; executingCount++; try { - let task = await this.taskService.findExecuteTask(TaskType.TriggerHook) as TriggerHookTask; + let task = (await this.taskService.findExecuteTask( + TaskType.TriggerHook + )) as TriggerHookTask; while (task) { const startTime = Date.now(); - this.logger.info('[TriggerHookWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', - executingCount, task.taskId, task.targetName, task.attempts, task.data, task.updatedAt, - startTime - task.updatedAt.getTime()); + this.logger.info( + '[TriggerHookWorker:subscribe:executeTask:start][%s] taskId: %s, targetName: %s, attempts: %s, params: %j, updatedAt: %s, delay %sms', + executingCount, + task.taskId, + task.targetName, + task.attempts, + task.data, + task.updatedAt, + startTime - task.updatedAt.getTime() + ); await this.hookTriggerService.executeTask(task); const use = Date.now() - startTime; - this.logger.info('[TriggerHookWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', - executingCount, task.taskId, task.targetName, use); - if (executingCount >= this.config.cnpmcore.triggerHookWorkerMaxConcurrentTasks) { - this.logger.info('[TriggerHookWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', - executingCount, this.config.cnpmcore.triggerHookWorkerMaxConcurrentTasks); + this.logger.info( + '[TriggerHookWorker:subscribe:executeTask:success][%s] taskId: %s, targetName: %s, use %sms', + executingCount, + task.taskId, + task.targetName, + use + ); + if ( + executingCount >= + this.config.cnpmcore.triggerHookWorkerMaxConcurrentTasks + ) { + this.logger.info( + '[TriggerHookWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s', + executingCount, + this.config.cnpmcore.triggerHookWorkerMaxConcurrentTasks + ); break; } // try next task - task = await this.taskService.findExecuteTask(TaskType.TriggerHook) as TriggerHookTask; + task = (await this.taskService.findExecuteTask( + TaskType.TriggerHook + )) as TriggerHookTask; } } catch (err) { - this.logger.error('[TriggerHookWorker:subscribe:executeTask:error][%s] %s', executingCount, err); + this.logger.error( + '[TriggerHookWorker:subscribe:executeTask:error][%s] %s', + executingCount, + err + ); } finally { executingCount--; } diff --git a/app/port/schedule/UpdateTotalData.ts b/app/port/schedule/UpdateTotalData.ts index 8e48b2cf..430370f3 100644 --- a/app/port/schedule/UpdateTotalData.ts +++ b/app/port/schedule/UpdateTotalData.ts @@ -1,27 +1,35 @@ -import { EggLogger } from 'egg'; -import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; +import type { EggLogger } from 'egg'; +import type { IntervalParams } from '@eggjs/tegg/schedule'; +import { Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; -import { ChangesStreamTaskData } from '../../core/entity/Task.js'; -import { RegistryManagerService } from '../../core/service/RegistryManagerService.js'; -import { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; -import { PackageRepository } from '../../repository/PackageRepository.js'; -import { TaskRepository } from '../../repository/TaskRepository.js'; -import { ChangeRepository } from '../../repository/ChangeRepository.js'; -import { CacheService, DownloadInfo, TotalData } from '../../core/service/CacheService.js'; +import type { ChangesStreamTaskData } from '../../core/entity/Task.js'; +import type { RegistryManagerService } from '../../core/service/RegistryManagerService.js'; +import type { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository.js'; +import type { PackageRepository } from '../../repository/PackageRepository.js'; +import type { TaskRepository } from '../../repository/TaskRepository.js'; +import type { ChangeRepository } from '../../repository/ChangeRepository.js'; +import type { + CacheService, + DownloadInfo, + TotalData, +} from '../../core/service/CacheService.js'; import { TaskType } from '../../common/enum/Task.js'; import { GLOBAL_WORKER } from '../../common/constants.js'; import dayjs from '../../common/dayjs.js'; -@Schedule({ - type: ScheduleType.WORKER, - scheduleData: { - interval: 60000, +@Schedule( + { + type: ScheduleType.WORKER, + scheduleData: { + interval: 60000, + }, }, -}, { - // immediate = false on unittest env - immediate: process.env.NODE_ENV !== 'test', -}) + { + // immediate = false on unittest env + immediate: process.env.NODE_ENV !== 'test', + } +) export class UpdateTotalData { @Inject() private readonly logger: EggLogger; @@ -59,23 +67,49 @@ export class UpdateTotalData { }; const today = dayjs(); const lastYearStartDay = today.subtract(1, 'year').startOf('year'); - const rows = await this.packageVersionDownloadRepository.query('total', lastYearStartDay.toDate(), today.toDate()); + const rows = await this.packageVersionDownloadRepository.query( + 'total', + lastYearStartDay.toDate(), + today.toDate() + ); if (rows.length > 0) { const todayInt = Number(today.format('YYYYMMDD')); const yesterdayInt = Number(today.subtract(1, 'day').format('YYYYMMDD')); - const samedayLastweekInt = Number(today.subtract(1, 'week').startOf('week').format('YYYYMMDD')); - const thisWeekStartDayInt = Number(today.startOf('week').format('YYYYMMDD')); + const samedayLastweekInt = Number( + today.subtract(1, 'week').startOf('week').format('YYYYMMDD') + ); + const thisWeekStartDayInt = Number( + today.startOf('week').format('YYYYMMDD') + ); const thisWeekEndDayInt = Number(today.endOf('week').format('YYYYMMDD')); - const thisMonthStartDayInt = Number(today.startOf('month').format('YYYYMMDD')); - const thisMonthEndDayInt = Number(today.endOf('month').format('YYYYMMDD')); - const thisYearStartDayInt = Number(today.startOf('year').format('YYYYMMDD')); + const thisMonthStartDayInt = Number( + today.startOf('month').format('YYYYMMDD') + ); + const thisMonthEndDayInt = Number( + today.endOf('month').format('YYYYMMDD') + ); + const thisYearStartDayInt = Number( + today.startOf('year').format('YYYYMMDD') + ); const thisYearEndDayInt = Number(today.endOf('year').format('YYYYMMDD')); - const lastWeekStartDayInt = Number(today.subtract(1, 'week').startOf('week').format('YYYYMMDD')); - const lastWeekEndDayInt = Number(today.subtract(1, 'week').endOf('week').format('YYYYMMDD')); - const lastMonthStartDayInt = Number(today.subtract(1, 'month').startOf('month').format('YYYYMMDD')); - const lastMonthEndDayInt = Number(today.subtract(1, 'month').endOf('month').format('YYYYMMDD')); - const lastYearStartDayInt = Number(today.subtract(1, 'year').startOf('year').format('YYYYMMDD')); - const lastYearEndDayInt = Number(today.subtract(1, 'year').endOf('year').format('YYYYMMDD')); + const lastWeekStartDayInt = Number( + today.subtract(1, 'week').startOf('week').format('YYYYMMDD') + ); + const lastWeekEndDayInt = Number( + today.subtract(1, 'week').endOf('week').format('YYYYMMDD') + ); + const lastMonthStartDayInt = Number( + today.subtract(1, 'month').startOf('month').format('YYYYMMDD') + ); + const lastMonthEndDayInt = Number( + today.subtract(1, 'month').endOf('month').format('YYYYMMDD') + ); + const lastYearStartDayInt = Number( + today.subtract(1, 'year').startOf('year').format('YYYYMMDD') + ); + const lastYearEndDayInt = Number( + today.subtract(1, 'year').endOf('year').format('YYYYMMDD') + ); for (const row of rows) { for (let i = 1; i <= 31; i++) { @@ -86,13 +120,20 @@ export class UpdateTotalData { const dayInt = row.yearMonth * 100 + i; if (dayInt === todayInt) download.today += counter; if (dayInt === yesterdayInt) download.yesterday += counter; - if (dayInt === samedayLastweekInt) download.samedayLastweek += counter; - if (dayInt >= thisWeekStartDayInt && dayInt <= thisWeekEndDayInt) download.thisweek += counter; - if (dayInt >= thisMonthStartDayInt && dayInt <= thisMonthEndDayInt) download.thismonth += counter; - if (dayInt >= thisYearStartDayInt && dayInt <= thisYearEndDayInt) download.thisyear += counter; - if (dayInt >= lastWeekStartDayInt && dayInt <= lastWeekEndDayInt) download.lastweek += counter; - if (dayInt >= lastMonthStartDayInt && dayInt <= lastMonthEndDayInt) download.lastmonth += counter; - if (dayInt >= lastYearStartDayInt && dayInt <= lastYearEndDayInt) download.lastyear += counter; + if (dayInt === samedayLastweekInt) + download.samedayLastweek += counter; + if (dayInt >= thisWeekStartDayInt && dayInt <= thisWeekEndDayInt) + download.thisweek += counter; + if (dayInt >= thisMonthStartDayInt && dayInt <= thisMonthEndDayInt) + download.thismonth += counter; + if (dayInt >= thisYearStartDayInt && dayInt <= thisYearEndDayInt) + download.thisyear += counter; + if (dayInt >= lastWeekStartDayInt && dayInt <= lastWeekEndDayInt) + download.lastweek += counter; + if (dayInt >= lastMonthStartDayInt && dayInt <= lastMonthEndDayInt) + download.lastmonth += counter; + if (dayInt >= lastYearStartDayInt && dayInt <= lastYearEndDayInt) + download.lastyear += counter; } } } @@ -107,18 +148,22 @@ export class UpdateTotalData { const totalData: TotalData = { ...packageTotal, download, - lastChangeId: lastChange && lastChange.id || 0, + lastChangeId: (lastChange && lastChange.id) || 0, cacheTime: new Date().toISOString(), changesStream: {} as unknown as ChangesStreamTaskData, upstreamRegistries: [], }; - const tasks = await this.taskRepository.findTasksByCondition({ type: TaskType.ChangesStream }); + const tasks = await this.taskRepository.findTasksByCondition({ + type: TaskType.ChangesStream, + }); for (const task of tasks) { // 全局 changesStream const data = task.data as ChangesStreamTaskData; // 补充录入 upstreamRegistries - const registry = await this.registryManagerService.findByRegistryId(data.registryId as string); + const registry = await this.registryManagerService.findByRegistryId( + data.registryId as string + ); if (registry) { totalData.upstreamRegistries.push({ ...data, diff --git a/app/port/typebox.ts b/app/port/typebox.ts index e41a7918..505caff9 100644 --- a/app/port/typebox.ts +++ b/app/port/typebox.ts @@ -1,31 +1,33 @@ -import { Type, Static } from 'egg-typebox-validate/typebox'; +import type { Static } from 'egg-typebox-validate/typebox'; +import { Type } from 'egg-typebox-validate/typebox'; import semver from 'semver'; import npa from 'npm-package-arg'; import { uniq } from 'lodash-es'; -import { Ajv } from 'egg-typebox-validate'; +import type { Ajv } from 'egg-typebox-validate'; import { RegistryType } from '../common/enum/Registry.js'; import { HookType } from '../common/enum/Hook.js'; -import binaryConfig, { BinaryName } from '../../config/binaries.js'; +import type { BinaryName } from '../../config/binaries.js'; +import binaryConfig from '../../config/binaries.js'; export const Name = Type.String({ - transform: [ 'trim' ], + transform: ['trim'], }); export const Url = Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 2048, }); export const Secret = Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 200, }); export const HookName = Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 428, }); @@ -34,7 +36,7 @@ export const HookTypeType = Type.Enum(HookType); export const BinaryNameRule = Type.String({ format: 'binary-name', - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 220, }); @@ -45,21 +47,21 @@ export const BinarySubpathRule = Type.RegEx(/^[ -~]{1,1024}$/); export const Tag = Type.String({ format: 'semver-tag', - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 214, }); // min: 0.0.0 export const Version = Type.String({ format: 'semver-version', - transform: [ 'trim' ], + transform: ['trim'], minLength: 5, maxLength: 256, }); export const VersionStringArray = Type.String({ format: 'unique-semver-version-array', - transform: [ 'trim' ], + transform: ['trim'], }); export const Spec = Type.String({ @@ -67,7 +69,10 @@ export const Spec = Type.String({ minLength: 1, }); -export const Description = Type.String({ maxLength: 10240, transform: [ 'trim' ] }); +export const Description = Type.String({ + maxLength: 10240, + transform: ['trim'], +}); export const TagRule = Type.Object({ tag: Tag, @@ -83,7 +88,7 @@ export const TagWithVersionRule = Type.Object({ export const SyncPackageTaskRule = Type.Object({ fullname: Name, tips: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], maxLength: 1024, }), skipDependencies: Type.Boolean(), @@ -101,7 +106,7 @@ export type SyncPackageTaskType = Static; export const BlockPackageRule = Type.Object({ fullname: Name, reason: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], maxLength: 10240, }), }); @@ -140,7 +145,7 @@ export function patchAjv(ajv: Ajv) { try { // do not support alias // exp: https://unpkg.com/good@npm:cnpmcore@3.17.1/dist/app.js - return [ 'tag', 'version', 'range' ].includes(npa(spec).type); + return ['tag', 'version', 'range'].includes(npa(spec).type); } catch { return false; } @@ -172,15 +177,19 @@ export function patchAjv(ajv: Ajv) { } export const QueryPageOptions = Type.Object({ - pageSize: Type.Optional(Type.Number({ - transform: [ 'trim' ], - minimum: 1, - maximum: 100, - })), - pageIndex: Type.Optional(Type.Number({ - transform: [ 'trim' ], - minimum: 0, - })), + pageSize: Type.Optional( + Type.Number({ + transform: ['trim'], + minimum: 1, + maximum: 100, + }) + ), + pageIndex: Type.Optional( + Type.Number({ + transform: ['trim'], + minimum: 0, + }) + ), }); export const RegistryCreateSyncOptions = Type.Object({ @@ -189,74 +198,76 @@ export const RegistryCreateSyncOptions = Type.Object({ export const RegistryCreateOptions = Type.Object({ name: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, }), host: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 4096, }), changeStream: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 4096, }), - userPrefix: Type.Optional(Type.String({ - transform: [ 'trim' ], - minLength: 1, - maxLength: 256, - })), + userPrefix: Type.Optional( + Type.String({ + transform: ['trim'], + minLength: 1, + maxLength: 256, + }) + ), type: Type.Enum(RegistryType), authToken: Type.Optional( Type.String({ - transform: [ 'trim' ], + transform: ['trim'], maxLength: 256, - }), + }) ), }); export const RegistryUpdateOptions = Type.Object({ name: Type.Optional( Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, - }), + }) ), host: Type.Optional( Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 4096, - }), + }) ), changeStream: Type.Optional( Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 4096, - }), + }) ), type: Type.Optional(Type.Enum(RegistryType)), authToken: Type.Optional( Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, - }), + }) ), }); export const ScopeCreateOptions = Type.Object({ name: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, }), registryId: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, }), @@ -264,17 +275,17 @@ export const ScopeCreateOptions = Type.Object({ export const ScopeUpdateOptions = Type.Object({ name: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, }), registryId: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, }), scopeId: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, }), @@ -282,17 +293,17 @@ export const ScopeUpdateOptions = Type.Object({ export const SearchQueryOptions = Type.Object({ from: Type.Number({ - transform: [ 'trim' ], + transform: ['trim'], minimum: 0, default: 0, }), size: Type.Number({ - transform: [ 'trim' ], + transform: ['trim'], minimum: 1, default: 20, }), text: Type.String({ - transform: [ 'trim' ], + transform: ['trim'], minLength: 1, maxLength: 256, }), diff --git a/app/port/webauth/WebauthController.ts b/app/port/webauth/WebauthController.ts index 034a0458..9dc90175 100644 --- a/app/port/webauth/WebauthController.ts +++ b/app/port/webauth/WebauthController.ts @@ -1,3 +1,4 @@ +import type { EggContext } from '@eggjs/tegg'; import { Inject, HTTPController, @@ -6,32 +7,34 @@ import { HTTPParam, HTTPBody, Context, - EggContext, HTTPQuery, } from '@eggjs/tegg'; -import { - EggLogger, - EggAppConfig, -} from 'egg'; -import { Static, Type } from 'egg-typebox-validate/typebox'; +import type { EggLogger, EggAppConfig } from 'egg'; +import type { Static } from 'egg-typebox-validate/typebox'; +import { Type } from 'egg-typebox-validate/typebox'; import { ForbiddenError, NotFoundError } from 'egg-errors'; import { createHash } from 'node:crypto'; import base64url from 'base64url'; +import type { + VerifyRegistrationResponseOpts, + VerifyAuthenticationResponseOpts, +} from '@simplewebauthn/server'; import { generateRegistrationOptions, verifyRegistrationResponse, generateAuthenticationOptions, verifyAuthenticationResponse, - VerifyRegistrationResponseOpts, - VerifyAuthenticationResponseOpts, } from '@simplewebauthn/server'; -import type { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/typescript-types'; +import type { + PublicKeyCredentialCreationOptionsJSON, + PublicKeyCredentialRequestOptionsJSON, +} from '@simplewebauthn/typescript-types'; import { LoginResultCode, WanStatusCode } from '../../common/enum/User.js'; -import { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; -import { UserService } from '../../core/service/UserService.js'; +import type { CacheAdapter } from '../../common/adapter/CacheAdapter.js'; +import type { UserService } from '../../core/service/UserService.js'; import { MiddlewareController } from '../middleware/index.js'; -import { AuthAdapter } from '../../infra/AuthAdapter.js'; +import type { AuthAdapter } from '../../infra/AuthAdapter.js'; import { genRSAKeys, decryptRSA } from '../../common/CryptoUtil.js'; import { getBrowserTypeForWebauthn } from '../../common/UserUtil.js'; @@ -55,7 +58,6 @@ type LoginImplementRequest = { wanCredentialRegiData: unknown; wanCredentialAuthData: unknown; needUnbindWan: boolean; - }; const UserRule = Type.Object({ @@ -86,7 +88,10 @@ export class WebauthController extends MiddlewareController { path: '/-/v1/login', method: HTTPMethodEnum.POST, }) - async login(@Context() ctx: EggContext, @HTTPBody() loginRequest: LoginRequest) { + async login( + @Context() ctx: EggContext, + @HTTPBody() loginRequest: LoginRequest + ) { ctx.tValidate(LoginRequestRule, loginRequest); return this.authAdapter.getAuthUrl(ctx); } @@ -95,7 +100,10 @@ export class WebauthController extends MiddlewareController { path: '/-/v1/login/request/session/:sessionId', method: HTTPMethodEnum.GET, }) - async loginRender(@Context() ctx: EggContext, @HTTPParam() sessionId: string) { + async loginRender( + @Context() ctx: EggContext, + @HTTPParam() sessionId: string + ) { ctx.tValidate(SessionRule, { sessionId }); ctx.type = 'html'; const sessionToken = await this.cacheAdapter.get(sessionId); @@ -116,17 +124,30 @@ export class WebauthController extends MiddlewareController { path: '/-/v1/login/request/session/:sessionId', method: HTTPMethodEnum.POST, }) - async loginImplement(@Context() ctx: EggContext, @HTTPParam() sessionId: string, @HTTPBody() loginImplementRequest: LoginImplementRequest) { + async loginImplement( + @Context() ctx: EggContext, + @HTTPParam() sessionId: string, + @HTTPBody() loginImplementRequest: LoginImplementRequest + ) { ctx.tValidate(SessionRule, { sessionId }); const sessionToken = await this.cacheAdapter.get(sessionId); if (typeof sessionToken !== 'string') { - return { ok: false, message: 'Session not found, please try again on your command line' }; + return { + ok: false, + message: 'Session not found, please try again on your command line', + }; } - const { accData, wanCredentialRegiData, wanCredentialAuthData, needUnbindWan } = loginImplementRequest; + const { + accData, + wanCredentialRegiData, + wanCredentialAuthData, + needUnbindWan, + } = loginImplementRequest; const { username, password = '' } = accData; const enableWebAuthn = this.config.cnpmcore.enableWebAuthn; - const isSupportWebAuthn = ctx.protocol === 'https' || ctx.hostname === 'localhost'; + const isSupportWebAuthn = + ctx.protocol === 'https' || ctx.hostname === 'localhost'; let token = ''; let user; @@ -137,23 +158,35 @@ export class WebauthController extends MiddlewareController { } } - const browserType = getBrowserTypeForWebauthn(ctx.headers['user-agent']) || undefined; - const expectedChallenge = (await this.cacheAdapter.get(`${sessionId}_challenge`)) || ''; + const browserType = + getBrowserTypeForWebauthn(ctx.headers['user-agent']) || undefined; + const expectedChallenge = + (await this.cacheAdapter.get(`${sessionId}_challenge`)) || ''; const expectedOrigin = this.config.cnpmcore.registry; const expectedRPID = new URL(expectedOrigin).hostname; // webauthn authentication if (enableWebAuthn && isSupportWebAuthn && wanCredentialAuthData) { user = await this.userService.findUserByName(username); if (!user) { - return { ok: false, message: 'Unauthorized, Please check your login name' }; + return { + ok: false, + message: 'Unauthorized, Please check your login name', + }; } - const credential = await this.userService.findWebauthnCredential(user.userId, browserType); + const credential = await this.userService.findWebauthnCredential( + user.userId, + browserType + ); if (!credential?.credentialId || !credential?.publicKey) { - return { ok: false, message: 'Unauthorized, Please check your login name' }; + return { + ok: false, + message: 'Unauthorized, Please check your login name', + }; } try { const verification = await verifyAuthenticationResponse({ - response: wanCredentialAuthData as VerifyAuthenticationResponseOpts['response'], + response: + wanCredentialAuthData as VerifyAuthenticationResponseOpts['response'], expectedChallenge, expectedOrigin, expectedRPID, @@ -167,11 +200,26 @@ export class WebauthController extends MiddlewareController { }); const { verified } = verification; if (!verified) { - return { ok: false, message: 'Invalid security arguments, please try again on your browser' }; + return { + ok: false, + message: + 'Invalid security arguments, please try again on your browser', + }; } } catch (err) { - this.logger.error('[WebauthController.loginImplement:verify-authentication-fail] expectedChallenge: %s, expectedOrigin: %s, expectedRPID: %s, wanCredentialAuthData: %j, error: %j', expectedChallenge, expectedOrigin, expectedRPID, wanCredentialAuthData, err); - return { ok: false, message: 'Authentication failed, please continue to sign in with your password' }; + this.logger.error( + '[WebauthController.loginImplement:verify-authentication-fail] expectedChallenge: %s, expectedOrigin: %s, expectedRPID: %s, wanCredentialAuthData: %j, error: %j', + expectedChallenge, + expectedOrigin, + expectedRPID, + wanCredentialAuthData, + err + ); + return { + ok: false, + message: + 'Authentication failed, please continue to sign in with your password', + }; } const createToken = await this.userService.createToken(user.userId); token = createToken.token!; @@ -183,7 +231,10 @@ export class WebauthController extends MiddlewareController { // check privateKey valid const privateKey = await this.cacheAdapter.get(`${sessionId}_privateKey`); if (!privateKey) { - return { ok: false, message: 'Invalid security arguments, please try again on your browser' }; + return { + ok: false, + message: 'Invalid security arguments, please try again on your browser', + }; } // check login name and password valid const realPassword = decryptRSA(privateKey, password); @@ -200,7 +251,10 @@ export class WebauthController extends MiddlewareController { const result = await this.userService.login(username, realPassword); // user exists and password not match if (result.code === LoginResultCode.Fail) { - return { ok: false, message: 'Please check your login name and password' }; + return { + ok: false, + message: 'Please check your login name and password', + }; } if (result.code === LoginResultCode.Success) { @@ -209,7 +263,10 @@ export class WebauthController extends MiddlewareController { user = result.user; // need unbind webauthn credential if (needUnbindWan) { - await this.userService.removeWebauthnCredential(user?.userId, browserType); + await this.userService.removeWebauthnCredential( + user?.userId, + browserType + ); } } else { // others: LoginResultCode.UserNotFound @@ -231,7 +288,8 @@ export class WebauthController extends MiddlewareController { if (enableWebAuthn && isSupportWebAuthn && wanCredentialRegiData) { try { const verification = await verifyRegistrationResponse({ - response: wanCredentialRegiData as VerifyRegistrationResponseOpts['response'], + response: + wanCredentialRegiData as VerifyRegistrationResponseOpts['response'], expectedChallenge, expectedOrigin, expectedRPID, @@ -240,9 +298,13 @@ export class WebauthController extends MiddlewareController { if (verified && registrationInfo) { const { credentialPublicKey, credentialID } = registrationInfo; // @ts-expect-error type error - const base64CredentialPublicKey = base64url.encode(Buffer.from(new Uint8Array(credentialPublicKey))); + const base64CredentialPublicKey = base64url.encode( + Buffer.from(new Uint8Array(credentialPublicKey)) + ); // @ts-expect-error type error - const base64CredentialID = base64url.encode(Buffer.from(new Uint8Array(credentialID))); + const base64CredentialID = base64url.encode( + Buffer.from(new Uint8Array(credentialID)) + ); this.userService.createWebauthnCredential(user?.userId, { credentialId: base64CredentialID, publicKey: base64CredentialPublicKey, @@ -250,7 +312,14 @@ export class WebauthController extends MiddlewareController { }); } } catch (err) { - this.logger.error('[WebauthController.loginImplement:verify-registration-fail] expectedChallenge: %s, expectedOrigin: %s, expectedRPID: %s, wanCredentialRegiData: %j, error: %j', expectedChallenge, expectedOrigin, expectedRPID, wanCredentialRegiData, err); + this.logger.error( + '[WebauthController.loginImplement:verify-registration-fail] expectedChallenge: %s, expectedOrigin: %s, expectedRPID: %s, wanCredentialRegiData: %j, error: %j', + expectedChallenge, + expectedOrigin, + expectedRPID, + wanCredentialRegiData, + err + ); } } @@ -261,20 +330,32 @@ export class WebauthController extends MiddlewareController { path: '/-/v1/login/request/prepare/:sessionId', method: HTTPMethodEnum.GET, }) - async loginPrepare(@Context() ctx: EggContext, @HTTPParam() sessionId: string, @HTTPQuery() name: string) { + async loginPrepare( + @Context() ctx: EggContext, + @HTTPParam() sessionId: string, + @HTTPQuery() name: string + ) { ctx.tValidate(SessionRule, { sessionId }); const sessionToken = await this.cacheAdapter.get(sessionId); if (typeof sessionToken !== 'string') { - return { ok: false, message: 'Session not found, please try again on your command line' }; + return { + ok: false, + message: 'Session not found, please try again on your command line', + }; } const browserType = getBrowserTypeForWebauthn(ctx.headers['user-agent']); const expectedRPID = new URL(this.config.cnpmcore.registry).hostname; const user = await this.userService.findUserByName(name); - const result: LoginPrepareResult = { wanStatus: WanStatusCode.UserNotFound }; + const result: LoginPrepareResult = { + wanStatus: WanStatusCode.UserNotFound, + }; let credential; if (user) { - credential = await this.userService.findWebauthnCredential(user.userId, browserType); + credential = await this.userService.findWebauthnCredential( + user.userId, + browserType + ); result.wanStatus = WanStatusCode.Unbound; } if (credential?.credentialId && credential?.publicKey) { @@ -282,17 +363,24 @@ export class WebauthController extends MiddlewareController { result.wanCredentialAuthOption = generateAuthenticationOptions({ timeout: 60000, rpID: expectedRPID, - allowCredentials: [{ - // @ts-expect-error type error - id: base64url.toBuffer(credential.credentialId), - type: 'public-key', - transports: [ 'internal' ], - }], + allowCredentials: [ + { + // @ts-expect-error type error + id: base64url.toBuffer(credential.credentialId), + type: 'public-key', + transports: ['internal'], + }, + ], }); - await this.cacheAdapter.set(`${sessionId}_challenge`, result.wanCredentialAuthOption.challenge); + await this.cacheAdapter.set( + `${sessionId}_challenge`, + result.wanCredentialAuthOption.challenge + ); } else { const encoder = new TextEncoder(); - const regUserIdBuffer = createHash('sha256').update(encoder.encode(name)).digest(); + const regUserIdBuffer = createHash('sha256') + .update(encoder.encode(name)) + .digest(); result.wanCredentialRegiOption = generateRegistrationOptions({ rpName: ctx.app.config.name, rpID: expectedRPID, @@ -306,7 +394,10 @@ export class WebauthController extends MiddlewareController { authenticatorAttachment: 'platform', }, }); - await this.cacheAdapter.set(`${sessionId}_challenge`, result.wanCredentialRegiOption.challenge); + await this.cacheAdapter.set( + `${sessionId}_challenge`, + result.wanCredentialRegiOption.challenge + ); } return result; } @@ -328,7 +419,11 @@ export class WebauthController extends MiddlewareController { throw new ForbiddenError('invalid user info'); } const { name, email } = userRes; - const { token } = await this.userService.ensureTokenByUser({ name, email, ip: ctx.ip }); + const { token } = await this.userService.ensureTokenByUser({ + name, + email, + ip: ctx.ip, + }); await this.cacheAdapter.set(sessionId, token!.token!); return { success: true }; diff --git a/app/repository/AbstractRepository.ts b/app/repository/AbstractRepository.ts index eaa61e08..c4e9abd5 100644 --- a/app/repository/AbstractRepository.ts +++ b/app/repository/AbstractRepository.ts @@ -1,9 +1,5 @@ -import { - Inject, -} from '@eggjs/tegg'; -import { - EggLogger, -} from 'egg'; +import { Inject } from '@eggjs/tegg'; +import type { EggLogger } from 'egg'; export abstract class AbstractRepository { @Inject() diff --git a/app/repository/ChangeRepository.ts b/app/repository/ChangeRepository.ts index dc92abb4..fc571a19 100644 --- a/app/repository/ChangeRepository.ts +++ b/app/repository/ChangeRepository.ts @@ -2,7 +2,7 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { ModelConvertor } from './util/ModelConvertor.js'; import type { Change as ChangeModel } from './model/Change.js'; -import { Change as ChangeEntity } from '../core/entity/Change.js'; +import type { Change as ChangeEntity } from '../core/entity/Change.js'; import { AbstractRepository } from './AbstractRepository.js'; @SingletonProto({ @@ -17,7 +17,9 @@ export class ChangeRepository extends AbstractRepository { } async query(since: number, limit: number): Promise> { - const models = await this.Change.find({ id: { $gte: since } }).order('id', 'asc').limit(limit); + const models = await this.Change.find({ id: { $gte: since } }) + .order('id', 'asc') + .limit(limit); return models.toObject() as ChangeEntity[]; } diff --git a/app/repository/DistRepository.ts b/app/repository/DistRepository.ts index 03c476de..1578661d 100644 --- a/app/repository/DistRepository.ts +++ b/app/repository/DistRepository.ts @@ -1,8 +1,11 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; -import { NFSAdapter } from '../common/adapter/NFSAdapter.js'; -import { PackageJSONType, PackageRepository } from './PackageRepository.js'; -import { Dist } from '../core/entity/Dist.js'; +import type { NFSAdapter } from '../common/adapter/NFSAdapter.js'; +import type { + PackageJSONType, + PackageRepository, +} from './PackageRepository.js'; +import type { Dist } from '../core/entity/Dist.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -14,10 +17,16 @@ export class DistRepository { @Inject() private readonly nfsAdapter: NFSAdapter; - async findPackageVersionManifest(packageId: string, version: string): Promise { - const packageVersion = await this.packageRepository.findPackageVersion(packageId, version); + async findPackageVersionManifest( + packageId: string, + version: string + ): Promise { + const packageVersion = await this.packageRepository.findPackageVersion( + packageId, + version + ); if (packageVersion) { - const [ packageVersionJson, readme ] = await Promise.all([ + const [packageVersionJson, readme] = await Promise.all([ this.readDistBytesToJSON(packageVersion.manifestDist), this.readDistBytesToString(packageVersion.readmeDist), ]); @@ -28,8 +37,14 @@ export class DistRepository { } } - async findPackageAbbreviatedManifest(packageId: string, version: string): Promise { - const packageVersion = await this.packageRepository.findPackageVersion(packageId, version); + async findPackageAbbreviatedManifest( + packageId: string, + version: string + ): Promise { + const packageVersion = await this.packageRepository.findPackageVersion( + packageId, + version + ); if (packageVersion) { return await this.readDistBytesToJSON(packageVersion.abbreviatedDist); } diff --git a/app/repository/HookRepository.ts b/app/repository/HookRepository.ts index 6afcd82b..b6318a90 100644 --- a/app/repository/HookRepository.ts +++ b/app/repository/HookRepository.ts @@ -3,7 +3,7 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { Hook } from '../core/entity/Hook.js'; import type { Hook as HookModel } from './model/Hook.js'; import { ModelConvertor } from './util/ModelConvertor.js'; -import { HookType } from '../common/enum/Hook.js'; +import type { HookType } from '../common/enum/Hook.js'; export interface UpdateHookCommand { hookId: string; @@ -42,12 +42,15 @@ export class HookRepository { * only endpoint and secret can be updated */ async updateHook(cmd: UpdateHookCommand) { - this.Hook.update({ - hookId: cmd.hookId, - }, { - endpoint: cmd.endpoint, - secret: cmd.secret, - }); + this.Hook.update( + { + hookId: cmd.hookId, + }, + { + endpoint: cmd.endpoint, + secret: cmd.secret, + } + ); } async listHooksByOwnerId(ownerId: string) { @@ -55,10 +58,16 @@ export class HookRepository { return hookRows.map(row => ModelConvertor.convertModelToEntity(row, Hook)); } - async listHooksByTypeAndName(type: HookType, name: string, since?: bigint): Promise> { + async listHooksByTypeAndName( + type: HookType, + name: string, + since?: bigint + ): Promise> { let hookRows: Array; if (typeof since !== 'undefined') { - hookRows = await this.Hook.find({ type, name, id: { $gt: since } }).limit(100); + hookRows = await this.Hook.find({ type, name, id: { $gt: since } }).limit( + 100 + ); } else { hookRows = await this.Hook.find({ type, name }).limit(100); } diff --git a/app/repository/PackageRepository.ts b/app/repository/PackageRepository.ts index 6c3f3895..58ef9466 100644 --- a/app/repository/PackageRepository.ts +++ b/app/repository/PackageRepository.ts @@ -1,14 +1,14 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; -import { Orm } from '@eggjs/tegg-orm-plugin'; -import { EggAppConfig } from 'egg'; +import type { Orm } from '@eggjs/tegg-orm-plugin'; +import type { EggAppConfig } from 'egg'; -import { Bone } from './util/leoric.js'; +import type { Bone } from './util/leoric.js'; import { Package as PackageModel } from './model/Package.js'; import { Package as PackageEntity } from '../core/entity/Package.js'; import { ModelConvertor } from './util/ModelConvertor.js'; import { PackageVersion as PackageVersionEntity } from '../core/entity/PackageVersion.js'; import { PackageVersion as PackageVersionModel } from './model/PackageVersion.js'; -import { PackageVersionManifest as PackageVersionManifestEntity } from '../core/entity/PackageVersionManifest.js'; +import type { PackageVersionManifest as PackageVersionManifestEntity } from '../core/entity/PackageVersionManifest.js'; import type { PackageVersionManifest as PackageVersionManifestModel } from './model/PackageVersionManifest.js'; import type { Dist as DistModel } from './model/Dist.js'; import { Dist as DistEntity } from '../core/entity/Dist.js'; @@ -18,7 +18,7 @@ import type { Maintainer as MaintainerModel } from './model/Maintainer.js'; import type { User as UserModel } from './model/User.js'; import { User as UserEntity } from '../core/entity/User.js'; import { AbstractRepository } from './AbstractRepository.js'; -import { BugVersionPackages } from '../core/entity/BugVersion.js'; +import type { BugVersionPackages } from '../core/entity/BugVersion.js'; import { DATABASE_TYPE } from '../../config/database.js'; export type PackageManifestType = Pick & { @@ -34,9 +34,13 @@ export type PackageManifestType = Pick & { }; } & CnpmcorePatchInfo; -export type AbbreviatedPackageJSONType = Pick & CnpmcorePatchInfo; +export type AbbreviatedPackageJSONType = Pick & + CnpmcorePatchInfo; -export type AbbreviatedPackageManifestType = Pick & { +export type AbbreviatedPackageManifestType = Pick< + PackageManifestType, + 'dist-tags' | 'name' +> & { modified: Date; versions: Record; time?: PackageManifestType['time']; @@ -59,9 +63,11 @@ export type PackageJSONType = CnpmcorePatchInfo & { maintainers?: ContributorType[] | string[]; files?: string[]; main?: string; - bin?: string | { - [key: string]: string; - }; + bin?: + | string + | { + [key: string]: string; + }; man?: string | string[]; directories?: DirectoriesType; repository?: RepositoryType; @@ -108,8 +114,19 @@ export type PackageJSONType = CnpmcorePatchInfo & { [key: string]: unknown; }; -type PackageJSONPickKey = 'name' | 'author' | 'bugs' | 'description' | 'homepage' | 'keywords' | -'license' | 'readme' | 'readmeFilename' | 'repository' | 'versions' | 'contributors'; +type PackageJSONPickKey = + | 'name' + | 'author' + | 'bugs' + | 'description' + | 'homepage' + | 'keywords' + | 'license' + | 'readme' + | 'readmeFilename' + | 'repository' + | 'versions' + | 'contributors'; export type CnpmcorePatchInfo = { _cnpmcore_publish_time?: Date; @@ -118,17 +135,37 @@ export type CnpmcorePatchInfo = { block?: string; }; -type AbbreviatedKey = 'name' | 'version' | 'deprecated' | 'dependencies' | 'optionalDependencies' | -'devDependencies' | 'bundleDependencies' | 'peerDependencies' | 'peerDependenciesMeta' | 'bin' | -'os' | 'cpu' | 'libc' | 'workspaces' | 'directories' | 'dist' | 'engines' | 'hasInstallScript' | -'publish_time' | 'block' | '_hasShrinkwrap' | 'acceptDependencies' | 'funding'; +type AbbreviatedKey = + | 'name' + | 'version' + | 'deprecated' + | 'dependencies' + | 'optionalDependencies' + | 'devDependencies' + | 'bundleDependencies' + | 'peerDependencies' + | 'peerDependenciesMeta' + | 'bin' + | 'os' + | 'cpu' + | 'libc' + | 'workspaces' + | 'directories' + | 'dist' + | 'engines' + | 'hasInstallScript' + | 'publish_time' + | 'block' + | '_hasShrinkwrap' + | 'acceptDependencies' + | 'funding'; type DistType = { - tarball: string, - size: number, - shasum: string, - integrity: string, - [key: string]: unknown, + tarball: string; + size: number; + shasum: string; + integrity: string; + [key: string]: unknown; }; export type AuthorType = { @@ -198,30 +235,49 @@ export class PackageRepository extends AbstractRepository { private readonly orm: Orm; async #convertPackageModelToEntity(model: PackageModel) { - const manifestsDistModel = model.manifestsDistId ? await this.Dist.findOne({ distId: model.manifestsDistId }) : null; - const abbreviatedsDistModel = model.abbreviatedsDistId ? await this.Dist.findOne({ distId: model.abbreviatedsDistId }) : null; + const manifestsDistModel = model.manifestsDistId + ? await this.Dist.findOne({ distId: model.manifestsDistId }) + : null; + const abbreviatedsDistModel = model.abbreviatedsDistId + ? await this.Dist.findOne({ distId: model.abbreviatedsDistId }) + : null; const data = { - manifestsDist: manifestsDistModel && ModelConvertor.convertModelToEntity(manifestsDistModel, DistEntity), - abbreviatedsDist: abbreviatedsDistModel && ModelConvertor.convertModelToEntity(abbreviatedsDistModel, DistEntity), + manifestsDist: + manifestsDistModel && + ModelConvertor.convertModelToEntity(manifestsDistModel, DistEntity), + abbreviatedsDist: + abbreviatedsDistModel && + ModelConvertor.convertModelToEntity(abbreviatedsDistModel, DistEntity), }; - const entity = ModelConvertor.convertModelToEntity(model, PackageEntity, data); + const entity = ModelConvertor.convertModelToEntity( + model, + PackageEntity, + data + ); return entity; } - async findPackage(scope: string, name: string): Promise { + async findPackage( + scope: string, + name: string + ): Promise { const model = await this.Package.findOne({ scope, name }); if (!model) return null; return await this.#convertPackageModelToEntity(model); } - async findPackageByPackageId(packageId: string): Promise { + async findPackageByPackageId( + packageId: string + ): Promise { const model = await this.Package.findOne({ packageId }); if (!model) return null; return await this.#convertPackageModelToEntity(model); } async findPackageId(scope: string, name: string) { - const model = await this.Package.findOne({ scope, name }).select('packageId'); + const model = await this.Package.findOne({ scope, name }).select( + 'packageId' + ); if (!model) return null; return model.packageId; } @@ -232,13 +288,25 @@ export class PackageRepository extends AbstractRepository { if (!model) return; await ModelConvertor.saveEntityToModel(pkgEntity, model); } else { - const model = await ModelConvertor.convertEntityToModel(pkgEntity, this.Package); - this.logger.info('[PackageRepository:savePackage:new] id: %s, packageId: %s', model.id, model.packageId); + const model = await ModelConvertor.convertEntityToModel( + pkgEntity, + this.Package + ); + this.logger.info( + '[PackageRepository:savePackage:new] id: %s, packageId: %s', + model.id, + model.packageId + ); } } - async savePackageDist(pkgEntity: PackageEntity, isFullManifests: boolean): Promise { - const dist = isFullManifests ? pkgEntity.manifestsDist : pkgEntity.abbreviatedsDist; + async savePackageDist( + pkgEntity: PackageEntity, + isFullManifests: boolean + ): Promise { + const dist = isFullManifests + ? pkgEntity.manifestsDist + : pkgEntity.abbreviatedsDist; if (!dist) return; if (dist.id) { const model = await this.Dist.findOne({ id: dist.id }); @@ -246,55 +314,94 @@ export class PackageRepository extends AbstractRepository { await ModelConvertor.saveEntityToModel(dist, model); } else { const model = await ModelConvertor.convertEntityToModel(dist, this.Dist); - this.logger.info('[PackageRepository:savePackageDist:new] id: %s, distId: %s, packageId: %s', - model.id, model.distId, pkgEntity.packageId); + this.logger.info( + '[PackageRepository:savePackageDist:new] id: %s, distId: %s, packageId: %s', + model.id, + model.distId, + pkgEntity.packageId + ); } await this.savePackage(pkgEntity); } - async removePackageDist(pkgEntity: PackageEntity, isFullManifests: boolean): Promise { - const dist = isFullManifests ? pkgEntity.manifestsDist : pkgEntity.abbreviatedsDist; + async removePackageDist( + pkgEntity: PackageEntity, + isFullManifests: boolean + ): Promise { + const dist = isFullManifests + ? pkgEntity.manifestsDist + : pkgEntity.abbreviatedsDist; if (!dist) return; const model = await this.Dist.findOne({ id: dist.id }); if (!model) return; await model.remove(); - this.logger.info('[PackageRepository:removePackageDist:remove] id: %s, distId: %s, packageId: %s', - model.id, model.distId, pkgEntity.packageId); + this.logger.info( + '[PackageRepository:removePackageDist:remove] id: %s, distId: %s, packageId: %s', + model.id, + model.distId, + pkgEntity.packageId + ); Reflect.set(dist, 'distId', null); await this.savePackage(pkgEntity); } // Package Maintainers // return true meaning create new record - async savePackageMaintainer(packageId: string, userId: string): Promise { + async savePackageMaintainer( + packageId: string, + userId: string + ): Promise { let model = await this.Maintainer.findOne({ packageId, userId }); if (!model) { model = await this.Maintainer.create({ packageId, userId }); - this.logger.info('[PackageRepository:addPackageMaintainer:new] id: %s, packageId: %s, userId: %s', - model.id, model.packageId, model.userId); + this.logger.info( + '[PackageRepository:addPackageMaintainer:new] id: %s, packageId: %s, userId: %s', + model.id, + model.packageId, + model.userId + ); return true; } } async listPackageMaintainers(packageId: string): Promise { const models = await this.Maintainer.find({ packageId }); - const userModels = await this.User.find({ userId: models.map(m => m.userId) }); - return userModels.map(user => ModelConvertor.convertModelToEntity(user, UserEntity)); + const userModels = await this.User.find({ + userId: models.map(m => m.userId), + }); + return userModels.map(user => + ModelConvertor.convertModelToEntity(user, UserEntity) + ); } - async replacePackageMaintainers(packageId: string, userIds: string[]): Promise { + async replacePackageMaintainers( + packageId: string, + userIds: string[] + ): Promise { await this.Maintainer.transaction(async ({ connection }) => { // delete exists // const removeCount = await this.Maintainer.remove({ packageId }, true, { transaction }); - const removeCount = await this.Maintainer.remove({ packageId }, true, { connection }); - this.logger.info('[PackageRepository:replacePackageMaintainers:remove] %d rows, packageId: %s', - removeCount, packageId); + const removeCount = await this.Maintainer.remove({ packageId }, true, { + connection, + }); + this.logger.info( + '[PackageRepository:replacePackageMaintainers:remove] %d rows, packageId: %s', + removeCount, + packageId + ); // add news for (const userId of userIds) { // const model = await this.Maintainer.create({ packageId, userId }, transaction); - const model = await this.Maintainer.create({ packageId, userId }, { connection }); - this.logger.info('[PackageRepository:replacePackageMaintainers:new] id: %s, packageId: %s, userId: %s', - model.id, model.packageId, model.userId); + const model = await this.Maintainer.create( + { packageId, userId }, + { connection } + ); + this.logger.info( + '[PackageRepository:replacePackageMaintainers:new] id: %s, packageId: %s, userId: %s', + model.id, + model.packageId, + model.userId + ); } }); } @@ -303,8 +410,12 @@ export class PackageRepository extends AbstractRepository { const model = await this.Maintainer.findOne({ packageId, userId }); if (model) { await model.remove(); - this.logger.info('[PackageRepository:removePackageMaintainer:remove] id: %s, packageId: %s, userId: %s', - model.id, model.packageId, model.userId); + this.logger.info( + '[PackageRepository:removePackageMaintainer:remove] id: %s, packageId: %s, userId: %s', + model.id, + model.packageId, + model.userId + ); return true; } return false; @@ -313,50 +424,96 @@ export class PackageRepository extends AbstractRepository { // TODO: support paging async listPackagesByUserId(userId: string): Promise { const models = await this.Maintainer.find({ userId }); - const packageModels = await this.Package.find({ packageId: models.map(m => m.packageId) }); - return packageModels.map(pkg => ModelConvertor.convertModelToEntity(pkg, PackageEntity)); + const packageModels = await this.Package.find({ + packageId: models.map(m => m.packageId), + }); + return packageModels.map(pkg => + ModelConvertor.convertModelToEntity(pkg, PackageEntity) + ); } async createPackageVersion(pkgVersionEntity: PackageVersionEntity) { await this.PackageVersion.transaction(async transaction => { await Promise.all([ // FIXME: transaction is not the options - ModelConvertor.convertEntityToModel(pkgVersionEntity, this.PackageVersion, transaction), - ModelConvertor.convertEntityToModel(pkgVersionEntity.manifestDist, this.Dist, transaction), - ModelConvertor.convertEntityToModel(pkgVersionEntity.tarDist, this.Dist, transaction), - ModelConvertor.convertEntityToModel(pkgVersionEntity.readmeDist, this.Dist, transaction), - ModelConvertor.convertEntityToModel(pkgVersionEntity.abbreviatedDist, this.Dist, transaction), + ModelConvertor.convertEntityToModel( + pkgVersionEntity, + this.PackageVersion, + transaction + ), + ModelConvertor.convertEntityToModel( + pkgVersionEntity.manifestDist, + this.Dist, + transaction + ), + ModelConvertor.convertEntityToModel( + pkgVersionEntity.tarDist, + this.Dist, + transaction + ), + ModelConvertor.convertEntityToModel( + pkgVersionEntity.readmeDist, + this.Dist, + transaction + ), + ModelConvertor.convertEntityToModel( + pkgVersionEntity.abbreviatedDist, + this.Dist, + transaction + ), ]); }); } async savePackageVersion(pkgVersionEntity: PackageVersionEntity) { // only abbreviatedDist and manifestDist allow to change, like `deprecated` message - let model = await this.Dist.findOne({ id: pkgVersionEntity.manifestDist.id }); + let model = await this.Dist.findOne({ + id: pkgVersionEntity.manifestDist.id, + }); if (model) { - await ModelConvertor.saveEntityToModel(pkgVersionEntity.manifestDist, model); + await ModelConvertor.saveEntityToModel( + pkgVersionEntity.manifestDist, + model + ); } - model = await this.Dist.findOne({ id: pkgVersionEntity.abbreviatedDist.id }); + model = await this.Dist.findOne({ + id: pkgVersionEntity.abbreviatedDist.id, + }); if (model) { - await ModelConvertor.saveEntityToModel(pkgVersionEntity.abbreviatedDist, model); + await ModelConvertor.saveEntityToModel( + pkgVersionEntity.abbreviatedDist, + model + ); } if (pkgVersionEntity.id) { - const model = await this.PackageVersion.findOne({ id: pkgVersionEntity.id }); + const model = await this.PackageVersion.findOne({ + id: pkgVersionEntity.id, + }); if (model) { await ModelConvertor.saveEntityToModel(pkgVersionEntity, model); } } } - async findPackageVersion(packageId: string, version: string): Promise { - const pkgVersionModel = await this.PackageVersion.findOne({ packageId, version }); + async findPackageVersion( + packageId: string, + version: string + ): Promise { + const pkgVersionModel = await this.PackageVersion.findOne({ + packageId, + version, + }); if (!pkgVersionModel) return null; return await this.fillPackageVersionEntityData(pkgVersionModel); } - async listPackageVersions(packageId: string): Promise { + async listPackageVersions( + packageId: string + ): Promise { // FIXME: read all versions will hit the memory limit - const models = await this.PackageVersion.find({ packageId }).order('id desc'); + const models = await this.PackageVersion.find({ packageId }).order( + 'id desc' + ); const entities: PackageVersionEntity[] = []; for (const model of models) { entities.push(await this.fillPackageVersionEntityData(model)); @@ -365,15 +522,20 @@ export class PackageRepository extends AbstractRepository { } async listPackageVersionNames(packageId: string): Promise { - const rows = await this.PackageVersion.find({ packageId }).select('version').order('id desc'); + const rows = await this.PackageVersion.find({ packageId }) + .select('version') + .order('id desc'); return rows.map(row => row.version); } // only for unittest now async removePackageVersions(packageId: string): Promise { const removeCount = await this.PackageVersion.remove({ packageId }); - this.logger.info('[PackageRepository:removePackageVersions:remove] %d rows, packageId: %s', - removeCount, packageId); + this.logger.info( + '[PackageRepository:removePackageVersions:remove] %d rows, packageId: %s', + removeCount, + packageId + ); } async removePackageVersion(pkgVersion: PackageVersionEntity): Promise { @@ -385,27 +547,48 @@ export class PackageRepository extends AbstractRepository { pkgVersion.tarDist.distId, ], }); - const removeCount = await this.PackageVersion.remove({ packageVersionId: pkgVersion.packageVersionId }); - this.logger.info('[PackageRepository:removePackageVersion:remove] %d dist rows, %d rows, packageVersionId: %s', - distRemoveCount, removeCount, pkgVersion.packageVersionId); + const removeCount = await this.PackageVersion.remove({ + packageVersionId: pkgVersion.packageVersionId, + }); + this.logger.info( + '[PackageRepository:removePackageVersion:remove] %d dist rows, %d rows, packageVersionId: %s', + distRemoveCount, + removeCount, + pkgVersion.packageVersionId + ); } - async savePackageVersionManifest(manifestEntity: PackageVersionManifestEntity): Promise { - let model = await this.PackageVersionManifest.findOne({ packageVersionId: manifestEntity.packageVersionId }); + async savePackageVersionManifest( + manifestEntity: PackageVersionManifestEntity + ): Promise { + let model = await this.PackageVersionManifest.findOne({ + packageVersionId: manifestEntity.packageVersionId, + }); if (model) { model.manifest = manifestEntity.manifest; await model.save(); } else { - model = await ModelConvertor.convertEntityToModel(manifestEntity, this.PackageVersionManifest); - this.logger.info('[PackageRepository:savePackageVersionManifest:new] id: %s, packageVersionId: %s', - model.id, model.packageVersionId); + model = await ModelConvertor.convertEntityToModel( + manifestEntity, + this.PackageVersionManifest + ); + this.logger.info( + '[PackageRepository:savePackageVersionManifest:new] id: %s, packageVersionId: %s', + model.id, + model.packageVersionId + ); } } async findPackageVersionManifest(packageVersionId: string) { - const model = await this.PackageVersionManifest.findOne({ packageVersionId }); + const model = await this.PackageVersionManifest.findOne({ + packageVersionId, + }); if (!model) return null; - return ModelConvertor.convertModelToEntity(model, this.PackageVersionManifest); + return ModelConvertor.convertModelToEntity( + model, + this.PackageVersionManifest + ); } private async getTotalCountByModel(model: typeof Bone): Promise { @@ -438,19 +621,24 @@ export class PackageRepository extends AbstractRepository { let lastPackageVersion = ''; if (lastPkg) { - lastPackage = lastPkg.scope ? `${lastPkg.scope}/${lastPkg.name}` : lastPkg.name; + lastPackage = lastPkg.scope + ? `${lastPkg.scope}/${lastPkg.name}` + : lastPkg.name; // FIXME: id will be out of range number // 可能存在 id 增长不连续的情况,通过 count 查询 packageCount = await this.getTotalCountByModel(PackageModel); } if (lastVersion) { - const pkg = await this.Package.findOne({ packageId: lastVersion.packageId }); + const pkg = await this.Package.findOne({ + packageId: lastVersion.packageId, + }); if (pkg) { const fullname = pkg.scope ? `${pkg.scope}/${pkg.name}` : pkg.name; lastPackageVersion = `${fullname}@${lastVersion.version}`; } - packageVersionCount = await this.getTotalCountByModel(PackageVersionModel); + packageVersionCount = + await this.getTotalCountByModel(PackageVersionModel); } return { packageCount, @@ -460,7 +648,9 @@ export class PackageRepository extends AbstractRepository { }; } - private async fillPackageVersionEntityData(model: PackageVersionModel): Promise { + private async fillPackageVersionEntityData( + model: PackageVersionModel + ): Promise { const [ tarDistModel, readmeDistModel, @@ -473,15 +663,30 @@ export class PackageRepository extends AbstractRepository { this.Dist.findOne({ distId: model.abbreviatedDistId }), ]); const data = { - tarDist: tarDistModel && ModelConvertor.convertModelToEntity(tarDistModel, DistEntity), - readmeDist: readmeDistModel && ModelConvertor.convertModelToEntity(readmeDistModel, DistEntity), - manifestDist: manifestDistModel && ModelConvertor.convertModelToEntity(manifestDistModel, DistEntity), - abbreviatedDist: abbreviatedDistModel && ModelConvertor.convertModelToEntity(abbreviatedDistModel, DistEntity), + tarDist: + tarDistModel && + ModelConvertor.convertModelToEntity(tarDistModel, DistEntity), + readmeDist: + readmeDistModel && + ModelConvertor.convertModelToEntity(readmeDistModel, DistEntity), + manifestDist: + manifestDistModel && + ModelConvertor.convertModelToEntity(manifestDistModel, DistEntity), + abbreviatedDist: + abbreviatedDistModel && + ModelConvertor.convertModelToEntity(abbreviatedDistModel, DistEntity), }; - return ModelConvertor.convertModelToEntity(model, PackageVersionEntity, data); + return ModelConvertor.convertModelToEntity( + model, + PackageVersionEntity, + data + ); } - async findPackageTag(packageId: string, tag: string): Promise { + async findPackageTag( + packageId: string, + tag: string + ): Promise { const model = await this.PackageTag.findOne({ packageId, tag }); if (!model) return null; const entity = ModelConvertor.convertModelToEntity(model, PackageTagEntity); @@ -494,9 +699,17 @@ export class PackageRepository extends AbstractRepository { if (!model) return; await ModelConvertor.saveEntityToModel(packageTagEntity, model); } else { - const model = await ModelConvertor.convertEntityToModel(packageTagEntity, this.PackageTag); - this.logger.info('[PackageRepository:savePackageTag:new] id: %s, packageTagId: %s, tags: %s => %s', - model.id, model.packageTagId, model.tag, model.version); + const model = await ModelConvertor.convertEntityToModel( + packageTagEntity, + this.PackageTag + ); + this.logger.info( + '[PackageRepository:savePackageTag:new] id: %s, packageTagId: %s, tags: %s => %s', + model.id, + model.packageTagId, + model.tag, + model.version + ); } } @@ -504,17 +717,22 @@ export class PackageRepository extends AbstractRepository { const model = await this.PackageTag.findOne({ id: packageTagEntity.id }); if (!model) return; await model.remove(); - this.logger.info('[PackageRepository:removePackageTag:remove] id: %s, packageTagId: %s, packageId: %s', - model.id, model.packageTagId, model.packageId); + this.logger.info( + '[PackageRepository:removePackageTag:remove] id: %s, packageTagId: %s, packageId: %s', + model.id, + model.packageTagId, + model.packageId + ); } async listPackageTags(packageId: string): Promise { const models = await this.PackageTag.find({ packageId }); const entities: PackageTagEntity[] = []; for (const model of models) { - entities.push(ModelConvertor.convertModelToEntity(model, PackageTagEntity)); + entities.push( + ModelConvertor.convertModelToEntity(model, PackageTagEntity) + ); } return entities; } - } diff --git a/app/repository/PackageVersionRepository.ts b/app/repository/PackageVersionRepository.ts index 3cceaec1..39098a75 100644 --- a/app/repository/PackageVersionRepository.ts +++ b/app/repository/PackageVersionRepository.ts @@ -1,12 +1,12 @@ import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; -import { PaddingSemVer } from '../core/entity/PaddingSemVer.js'; +import type { PaddingSemVer } from '../core/entity/PaddingSemVer.js'; import type { Package as PackageModel } from './model/Package.js'; import { PackageVersion } from '../core/entity/PackageVersion.js'; import type { PackageTag } from './model/PackageTag.js'; import { ModelConvertor } from './util/ModelConvertor.js'; import type { PackageVersion as PackageVersionModel } from './model/PackageVersion.js'; -import { SqlRange } from '../core/entity/SqlRange.js'; +import type { SqlRange } from '../core/entity/SqlRange.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -21,34 +21,47 @@ export class PackageVersionRepository { @Inject() private readonly PackageTag: typeof PackageTag; - async findHaveNotPaddingVersion(id?: number): Promise { if (!id) { - id = await this.PackageVersion.minimum('id') - .where('paddingVersion is null') as number; + id = (await this.PackageVersion.minimum('id').where( + 'paddingVersion is null' + )) as number; } if (!id) return []; - const versions = await this.PackageVersion.find({ id: { $gte: id } } as object) - .limit(1000); - const versionModels = versions.map(t => ModelConvertor.convertModelToEntity(t, PackageVersion)); + const versions = await this.PackageVersion.find({ + id: { $gte: id }, + } as object).limit(1000); + const versionModels = versions.map(t => + ModelConvertor.convertModelToEntity(t, PackageVersion) + ); return (versionModels as any).toObject(); } - async fixPaddingVersion(pkgVersionId: string, paddingSemver: PaddingSemVer): Promise { - await this.PackageVersion.update({ packageVersionId: pkgVersionId }, { - paddingVersion: paddingSemver.paddingVersion, - isPreRelease: paddingSemver.isPreRelease, - }); + async fixPaddingVersion( + pkgVersionId: string, + paddingSemver: PaddingSemVer + ): Promise { + await this.PackageVersion.update( + { packageVersionId: pkgVersionId }, + { + paddingVersion: paddingSemver.paddingVersion, + isPreRelease: paddingSemver.isPreRelease, + } + ); } - async findVersionByTag(scope: string, name: string, tag: string): Promise { - const tags = await this.PackageTag.select('version') + async findVersionByTag( + scope: string, + name: string, + tag: string + ): Promise { + const tags = (await this.PackageTag.select('version') .join(this.Package as any, 'packageTags.packageId = packages.packageId') .where({ scope, name, tag, - } as object) as { version: string }[]; + } as object)) as { version: string }[]; const tagModel = tags && tags[0]; return tagModel?.version; } @@ -56,29 +69,46 @@ export class PackageVersionRepository { /** * if sql version not contains prerelease, find the max version */ - async findMaxSatisfyVersion(scope: string, name: string, sqlRange: SqlRange): Promise { - const versions = await this.PackageVersion - .select('packageVersions.version') - .join(this.Package as any, 'packageVersions.packageId = packages.packageId') + async findMaxSatisfyVersion( + scope: string, + name: string, + sqlRange: SqlRange + ): Promise { + const versions = (await this.PackageVersion.select( + 'packageVersions.version' + ) + .join( + this.Package as any, + 'packageVersions.packageId = packages.packageId' + ) .where({ 'packages.scope': scope, 'packages.name': name, ...sqlRange.condition, } as object) - .order('packageVersions.paddingVersion', 'desc') as { version: string }[]; + .order('packageVersions.paddingVersion', 'desc')) as { + version: string; + }[]; return versions?.[0]?.version; } - async findSatisfyVersionsWithPrerelease(scope: string, name: string, sqlRange: SqlRange): Promise> { - const versions = await this.PackageVersion - .select('version') - .join(this.Package as any, 'packageVersions.packageId = packages.packageId') + async findSatisfyVersionsWithPrerelease( + scope: string, + name: string, + sqlRange: SqlRange + ): Promise> { + const versions = await this.PackageVersion.select('version') + .join( + this.Package as any, + 'packageVersions.packageId = packages.packageId' + ) .where({ scope, name, ...sqlRange.condition, } as object); - return (versions as any).toObject() + return (versions as any) + .toObject() .map((t: { version: string }) => t.version); } } diff --git a/app/repository/ProxyCacheRepository.ts b/app/repository/ProxyCacheRepository.ts index 2926ab68..a052824d 100644 --- a/app/repository/ProxyCacheRepository.ts +++ b/app/repository/ProxyCacheRepository.ts @@ -4,8 +4,9 @@ import { ModelConvertor } from './util/ModelConvertor.js'; import type { ProxyCache as ProxyModeCachedFilesModel } from './model/ProxyCache.js'; import { ProxyCache as ProxyCacheEntity } from '../core/entity/ProxyCache.js'; import { AbstractRepository } from './AbstractRepository.js'; -import { DIST_NAMES } from '../core/entity/Package.js'; -import { EntityUtil, PageOptions, PageResult } from '../core/util/EntityUtil.js'; +import type { DIST_NAMES } from '../core/entity/Package.js'; +import type { PageOptions, PageResult } from '../core/util/EntityUtil.js'; +import { EntityUtil } from '../core/util/EntityUtil.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) @@ -14,47 +15,78 @@ export class ProxyCacheRepository extends AbstractRepository { private readonly ProxyCache: typeof ProxyModeCachedFilesModel; async saveProxyCache(proxyCacheEntity: ProxyCacheEntity) { - let model = proxyCacheEntity.version ? - await this.ProxyCache.findOne({ fullname: proxyCacheEntity.fullname, version: proxyCacheEntity.version, fileType: proxyCacheEntity.fileType }) : - await this.ProxyCache.findOne({ fullname: proxyCacheEntity.fullname, fileType: proxyCacheEntity.fileType }); + let model = proxyCacheEntity.version + ? await this.ProxyCache.findOne({ + fullname: proxyCacheEntity.fullname, + version: proxyCacheEntity.version, + fileType: proxyCacheEntity.fileType, + }) + : await this.ProxyCache.findOne({ + fullname: proxyCacheEntity.fullname, + fileType: proxyCacheEntity.fileType, + }); if (model) { model.updatedAt = proxyCacheEntity.updatedAt; await model.save(); } else { try { - model = await ModelConvertor.convertEntityToModel(proxyCacheEntity, this.ProxyCache); + model = await ModelConvertor.convertEntityToModel( + proxyCacheEntity, + this.ProxyCache + ); } catch (e) { - e.message = '[ProxyCacheRepository] insert ProxyCache failed: ' + e.message; + e.message = + '[ProxyCacheRepository] insert ProxyCache failed: ' + e.message; throw e; } } return model; } - async findProxyCache(fullname: string, fileType: DIST_NAMES, version?: string): Promise { - const model = version ? await this.ProxyCache.findOne({ fullname, version, fileType }) : await this.ProxyCache.findOne({ fullname, fileType }); - if (model) return ModelConvertor.convertModelToEntity(model, ProxyCacheEntity); + async findProxyCache( + fullname: string, + fileType: DIST_NAMES, + version?: string + ): Promise { + const model = version + ? await this.ProxyCache.findOne({ fullname, version, fileType }) + : await this.ProxyCache.findOne({ fullname, fileType }); + if (model) + return ModelConvertor.convertModelToEntity(model, ProxyCacheEntity); return null; } // used by update & delete all cache async findProxyCaches(fullname: string, version?: string) { - const models = version ? await this.ProxyCache.find({ fullname, version }) : await this.ProxyCache.find({ fullname }); + const models = version + ? await this.ProxyCache.find({ fullname, version }) + : await this.ProxyCache.find({ fullname }); return models; } - async listCachedFiles(page: PageOptions, fullname?: string): Promise> { + async listCachedFiles( + page: PageOptions, + fullname?: string + ): Promise> { const { offset, limit } = EntityUtil.convertPageOptionsToLimitOption(page); - const count = fullname ? await this.ProxyCache.find({ fullname }).count() : await this.ProxyCache.find().count(); - const models = fullname ? await this.ProxyCache.find({ fullname }).offset(offset).limit(limit) : await this.ProxyCache.find().offset(offset).limit(limit); + const count = fullname + ? await this.ProxyCache.find({ fullname }).count() + : await this.ProxyCache.find().count(); + const models = fullname + ? await this.ProxyCache.find({ fullname }).offset(offset).limit(limit) + : await this.ProxyCache.find().offset(offset).limit(limit); return { count, - data: models.map(model => ModelConvertor.convertModelToEntity(model, ProxyCacheEntity)), + data: models.map(model => + ModelConvertor.convertModelToEntity(model, ProxyCacheEntity) + ), }; } async removeProxyCache(fullname: string, fileType: string, version?: string) { - version ? await this.ProxyCache.remove({ fullname, version, fileType }) : await this.ProxyCache.remove({ fullname, fileType }); + version + ? await this.ProxyCache.remove({ fullname, version, fileType }) + : await this.ProxyCache.remove({ fullname, fileType }); } async truncateProxyCache() { diff --git a/app/repository/RegistryRepository.ts b/app/repository/RegistryRepository.ts index 369be306..e0a8cc22 100644 --- a/app/repository/RegistryRepository.ts +++ b/app/repository/RegistryRepository.ts @@ -1,10 +1,12 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { ModelConvertor } from './util/ModelConvertor.js'; -import { Registry, Registry as RegistryEntity } from '../core/entity/Registry.js'; +import type { Registry } from '../core/entity/Registry.js'; +import { Registry as RegistryEntity } from '../core/entity/Registry.js'; import { AbstractRepository } from './AbstractRepository.js'; import type { Registry as RegistryModel } from './model/Registry.js'; -import { EntityUtil, PageOptions, PageResult } from '../core/util/EntityUtil.js'; +import type { PageOptions, PageResult } from '../core/util/EntityUtil.js'; +import { EntityUtil } from '../core/util/EntityUtil.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -19,7 +21,9 @@ export class RegistryRepository extends AbstractRepository { const models = await this.Registry.find().offset(offset).limit(limit); return { count, - data: models.map(model => ModelConvertor.convertModelToEntity(model, RegistryEntity)), + data: models.map(model => + ModelConvertor.convertModelToEntity(model, RegistryEntity) + ), }; } @@ -31,7 +35,9 @@ export class RegistryRepository extends AbstractRepository { return null; } - async findRegistryByRegistryId(registryId: string): Promise { + async findRegistryByRegistryId( + registryId: string + ): Promise { const model = await this.Registry.findOne({ registryId }); if (model) { return ModelConvertor.convertModelToEntity(model, RegistryEntity); @@ -39,7 +45,9 @@ export class RegistryRepository extends AbstractRepository { return null; } - async findRegistryByRegistryHost(host: string): Promise { + async findRegistryByRegistryHost( + host: string + ): Promise { const model = await this.Registry.findOne({ host }); if (model) { return ModelConvertor.convertModelToEntity(model, RegistryEntity); @@ -54,15 +62,19 @@ export class RegistryRepository extends AbstractRepository { await ModelConvertor.saveEntityToModel(registry, model); return model; } - const model = await ModelConvertor.convertEntityToModel(registry, this.Registry); - this.logger.info('[RegistryRepository:saveRegistry:new] id: %s, registryId: %s', - model.id, model.registryId); + const model = await ModelConvertor.convertEntityToModel( + registry, + this.Registry + ); + this.logger.info( + '[RegistryRepository:saveRegistry:new] id: %s, registryId: %s', + model.id, + model.registryId + ); return model; - } async removeRegistry(registryId: string): Promise { await this.Registry.remove({ registryId }); } - } diff --git a/app/repository/ScopeRepository.ts b/app/repository/ScopeRepository.ts index 9b16b540..6535cfbd 100644 --- a/app/repository/ScopeRepository.ts +++ b/app/repository/ScopeRepository.ts @@ -2,9 +2,10 @@ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { ModelConvertor } from './util/ModelConvertor.js'; import { AbstractRepository } from './AbstractRepository.js'; -import { Scope as ScopeModel } from './model/Scope.js'; +import type { Scope as ScopeModel } from './model/Scope.js'; import { Scope } from '../core/entity/Scope.js'; -import { EntityUtil, PageOptions, PageResult } from '../core/util/EntityUtil.js'; +import type { PageOptions, PageResult } from '../core/util/EntityUtil.js'; +import { EntityUtil } from '../core/util/EntityUtil.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -23,13 +24,20 @@ export class ScopeRepository extends AbstractRepository { } return ModelConvertor.convertModelToEntity(model, Scope); } - async listScopesByRegistryId(registryId: string, page: PageOptions): Promise> { + async listScopesByRegistryId( + registryId: string, + page: PageOptions + ): Promise> { const { offset, limit } = EntityUtil.convertPageOptionsToLimitOption(page); const count = await this.Scope.find({ registryId }).count(); - const models = await this.Scope.find({ registryId }).offset(offset).limit(limit); + const models = await this.Scope.find({ registryId }) + .offset(offset) + .limit(limit); return { count, - data: models.map(model => ModelConvertor.convertModelToEntity(model, Scope)), + data: models.map(model => + ModelConvertor.convertModelToEntity(model, Scope) + ), }; } @@ -39,7 +47,9 @@ export class ScopeRepository extends AbstractRepository { const models = await this.Scope.find().offset(offset).limit(limit); return { count, - data: models.map(model => ModelConvertor.convertModelToEntity(model, Scope)), + data: models.map(model => + ModelConvertor.convertModelToEntity(model, Scope) + ), }; } @@ -51,8 +61,11 @@ export class ScopeRepository extends AbstractRepository { return model; } const model = await ModelConvertor.convertEntityToModel(scope, this.Scope); - this.logger.info('[ScopeRepository:saveScope:new] id: %s, scopeId: %s', - model.id, model.scopeId); + this.logger.info( + '[ScopeRepository:saveScope:new] id: %s, scopeId: %s', + model.id, + model.scopeId + ); await model.save(); return model; } @@ -64,5 +77,4 @@ export class ScopeRepository extends AbstractRepository { async removeScopeByRegistryId(registryId: string): Promise { await this.Scope.remove({ registryId }); } - } diff --git a/app/repository/SearchRepository.ts b/app/repository/SearchRepository.ts index 537b8a5c..6f6d58f7 100644 --- a/app/repository/SearchRepository.ts +++ b/app/repository/SearchRepository.ts @@ -1,29 +1,41 @@ import { SingletonProto, AccessLevel, Inject } from '@eggjs/tegg'; -import { estypes } from '@elastic/elasticsearch'; +import type { estypes } from '@elastic/elasticsearch'; -import { SearchAdapter } from '../common/typing.js'; -import { AuthorType, CnpmcorePatchInfo, PackageManifestType } from './PackageRepository.js'; +import type { SearchAdapter } from '../common/typing.js'; +import type { + AuthorType, + CnpmcorePatchInfo, + PackageManifestType, +} from './PackageRepository.js'; -export type SearchJSONPickKey = '_rev' | 'name' | 'description' | 'keywords' | 'license' | 'maintainers' | 'dist-tags' | '_source_registry_name'; - -export type SearchMappingType = Pick & CnpmcorePatchInfo & { - scope: string; - version: string; - versions: string[]; - date: Date; - created: Date; - modified: Date; - author?: AuthorType | undefined; - _npmUser?: { - name: string; - email: string; - } - publisher?: { - username: string; - email: string; - } -}; +export type SearchJSONPickKey = + | '_rev' + | 'name' + | 'description' + | 'keywords' + | 'license' + | 'maintainers' + | 'dist-tags' + | '_source_registry_name'; +export type SearchMappingType = Pick & + CnpmcorePatchInfo & { + scope: string; + version: string; + versions: string[]; + date: Date; + created: Date; + modified: Date; + author?: AuthorType | undefined; + _npmUser?: { + name: string; + email: string; + }; + publisher?: { + username: string; + email: string; + }; + }; export type SearchManifestType = { package: SearchMappingType; @@ -39,8 +51,9 @@ export class SearchRepository { @Inject() private readonly searchAdapter: SearchAdapter; - - async searchPackage(query: any): Promise> { + async searchPackage( + query: any + ): Promise> { return await this.searchAdapter.search(query); } diff --git a/app/repository/TaskRepository.ts b/app/repository/TaskRepository.ts index c2c7df8e..5a68daf1 100644 --- a/app/repository/TaskRepository.ts +++ b/app/repository/TaskRepository.ts @@ -7,8 +7,9 @@ import { isDuplicateKeyError } from './util/ErrorUtil.js'; import type { Task as TaskModel } from './model/Task.js'; import type { HistoryTask as HistoryTaskModel } from './model/HistoryTask.js'; import { AbstractRepository } from './AbstractRepository.js'; -import { TaskType, TaskState } from '../../app/common/enum/Task.js'; -import { Task as TaskEntity, TaskUpdateCondition } from '../core/entity/Task.js'; +import type { TaskType, TaskState } from '../../app/common/enum/Task.js'; +import type { TaskUpdateCondition } from '../core/entity/Task.js'; +import { Task as TaskEntity } from '../core/entity/Task.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -48,13 +49,19 @@ export class TaskRepository extends AbstractRepository { } } - async idempotentSaveTask(task: TaskEntity, condition: TaskUpdateCondition): Promise { + async idempotentSaveTask( + task: TaskEntity, + condition: TaskUpdateCondition + ): Promise { assert(task.id, 'task have no save'); const changes = ModelConvertor.convertEntityToChanges(task, this.Task); - const updateRows = await this.Task.update({ - taskId: condition.taskId, - attempts: condition.attempts, - }, changes); + const updateRows = await this.Task.update( + { + taskId: condition.taskId, + attempts: condition.attempts, + }, + changes + ); return updateRows === 1; } @@ -70,12 +77,17 @@ export class TaskRepository extends AbstractRepository { await model.remove(); } - async updateSpecificVersionsOfWaitingTask(task: TaskEntity, specificVersions?: Array): Promise { + async updateSpecificVersionsOfWaitingTask( + task: TaskEntity, + specificVersions?: Array + ): Promise { const model = await this.Task.findOne({ id: task.id }); if (!model || !model.data.specificVersions) return; if (specificVersions) { const data = model.data; - const combinedVersions = uniq(data.specificVersions.concat(specificVersions)); + const combinedVersions = uniq( + data.specificVersions.concat(specificVersions) + ); data.specificVersions = combinedVersions; await model.update({ data }); } else { @@ -108,15 +120,27 @@ export class TaskRepository extends AbstractRepository { async findTasks(taskIds: Array): Promise> { const tasks = await this.HistoryTask.find({ taskId: { $in: taskIds } }); - return tasks.map(task => ModelConvertor.convertModelToEntity(task, TaskEntity)); + return tasks.map(task => + ModelConvertor.convertModelToEntity(task, TaskEntity) + ); } - async findTasksByCondition(where: { targetName?: string; state?: TaskState; type: TaskType }): Promise> { + async findTasksByCondition(where: { + targetName?: string; + state?: TaskState; + type: TaskType; + }): Promise> { const tasks = await this.Task.find(where); - return tasks.map(task => ModelConvertor.convertModelToEntity(task, TaskEntity)); + return tasks.map(task => + ModelConvertor.convertModelToEntity(task, TaskEntity) + ); } - async findTaskByTargetName(targetName: string, type: TaskType, state?: TaskState) { + async findTaskByTargetName( + targetName: string, + type: TaskType, + state?: TaskState + ) { const where: any = { targetName, type }; if (state) { where.state = state; @@ -137,7 +161,9 @@ export class TaskRepository extends AbstractRepository { $lt: timeoutDate, }, }).limit(1000); - return models.map(model => ModelConvertor.convertModelToEntity(model, TaskEntity)); + return models.map(model => + ModelConvertor.convertModelToEntity(model, TaskEntity) + ); } async findTaskByAuthorIpAndType(authorIp: string, type: TaskType) { @@ -145,6 +171,8 @@ export class TaskRepository extends AbstractRepository { type, authorIp, }).limit(1000); - return models.map(model => ModelConvertor.convertModelToEntity(model, TaskEntity)); + return models.map(model => + ModelConvertor.convertModelToEntity(model, TaskEntity) + ); } } diff --git a/app/repository/UserRepository.ts b/app/repository/UserRepository.ts index 160244fc..338c7ec7 100644 --- a/app/repository/UserRepository.ts +++ b/app/repository/UserRepository.ts @@ -9,9 +9,9 @@ import { User as UserEntity } from '../core/entity/User.js'; import { Token as TokenEntity, isGranularToken } from '../core/entity/Token.js'; import { WebauthnCredential as WebauthnCredentialEntity } from '../core/entity/WebauthnCredential.js'; import { AbstractRepository } from './AbstractRepository.js'; -import { TokenPackage as TokenPackageModel } from './model/TokenPackage.js'; +import type { TokenPackage as TokenPackageModel } from './model/TokenPackage.js'; import { getFullname, getScopeAndName } from '../common/PackageUtil.js'; -import { PackageRepository } from './PackageRepository.js'; +import type { PackageRepository } from './PackageRepository.js'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -42,7 +42,11 @@ export class UserRepository extends AbstractRepository { await ModelConvertor.saveEntityToModel(user, model); } else { const model = await ModelConvertor.convertEntityToModel(user, this.User); - this.logger.info('[UserRepository:saveUser:new] id: %s, userId: %s', model.id, model.userId); + this.logger.info( + '[UserRepository:saveUser:new] id: %s, userId: %s', + model.id, + model.userId + ); } } @@ -81,7 +85,9 @@ export class UserRepository extends AbstractRepository { private async _injectTokenPackages(token: TokenEntity) { if (isGranularToken(token)) { const models = await this.TokenPackage.find({ tokenId: token.tokenId }); - const packages = await this.Package.find({ packageId: models.map(m => m.packageId) }); + const packages = await this.Package.find({ + packageId: models.map(m => m.packageId), + }); if (Array.isArray(packages)) { token.allowedPackages = packages.map(p => getFullname(p.scope, p.name)); } @@ -100,13 +106,23 @@ export class UserRepository extends AbstractRepository { } else { if (isGranularToken(token)) { await this.TokenPackage.transaction(async transaction => { - model = await ModelConvertor.convertEntityToModel(token, this.Token, transaction); + model = await ModelConvertor.convertEntityToModel( + token, + this.Token, + transaction + ); if (Array.isArray(token.allowedPackages)) { for (const packageName of token.allowedPackages) { - const [ scope, name ] = getScopeAndName(packageName); - const packageId = await this.packageRepository.findPackageId(scope, name); + const [scope, name] = getScopeAndName(packageName); + const packageId = await this.packageRepository.findPackageId( + scope, + name + ); if (packageId) { - await this.TokenPackage.create({ packageId, tokenId: token.tokenId }, transaction); + await this.TokenPackage.create( + { packageId, tokenId: token.tokenId }, + transaction + ); } } } @@ -114,22 +130,35 @@ export class UserRepository extends AbstractRepository { } else { model = await ModelConvertor.convertEntityToModel(token, this.Token); } - this.logger.info('[UserRepository:saveToken:new] id: %s, tokenId: %s', model!.id, model!.tokenId); + this.logger.info( + '[UserRepository:saveToken:new] id: %s, tokenId: %s', + model!.id, + model!.tokenId + ); } } async removeToken(tokenId: string) { await this.Token.transaction(async transaction => { - const removeCount = await this.Token.remove({ tokenId }, true, transaction); + const removeCount = await this.Token.remove( + { tokenId }, + true, + transaction + ); await this.TokenPackage.remove({ tokenId }, true, transaction); - this.logger.info('[UserRepository:removeToken:remove] %d rows, tokenId: %s', - removeCount, tokenId); + this.logger.info( + '[UserRepository:removeToken:remove] %d rows, tokenId: %s', + removeCount, + tokenId + ); }); } async listTokens(userId: string): Promise { const models = await this.Token.find({ userId }); - const tokens = models.map(model => ModelConvertor.convertModelToEntity(model, TokenEntity)); + const tokens = models.map(model => + ModelConvertor.convertModelToEntity(model, TokenEntity) + ); for (const token of tokens) { await this._injectTokenPackages(token); } @@ -138,16 +167,28 @@ export class UserRepository extends AbstractRepository { async saveCredential(credential: WebauthnCredentialEntity): Promise { if (credential.id) { - const model = await this.WebauthnCredential.findOne({ id: credential.id }); + const model = await this.WebauthnCredential.findOne({ + id: credential.id, + }); if (!model) return; await ModelConvertor.saveEntityToModel(credential, model); } else { - const model = await ModelConvertor.convertEntityToModel(credential, this.WebauthnCredential); - this.logger.info('[UserRepository:saveCredential:new] id: %s, wancId: %s', model.id, model.wancId); + const model = await ModelConvertor.convertEntityToModel( + credential, + this.WebauthnCredential + ); + this.logger.info( + '[UserRepository:saveCredential:new] id: %s, wancId: %s', + model.id, + model.wancId + ); } } - async findCredentialByUserIdAndBrowserType(userId: string | undefined, browserType: string | null) { + async findCredentialByUserIdAndBrowserType( + userId: string | undefined, + browserType: string | null + ) { const model = await this.WebauthnCredential.findOne({ userId, browserType, @@ -158,6 +199,10 @@ export class UserRepository extends AbstractRepository { async removeCredential(wancId: string) { const removeCount = await this.WebauthnCredential.remove({ wancId }); - this.logger.info('[UserRepository:removeCredential:remove] %d rows, wancId: %s', removeCount, wancId); + this.logger.info( + '[UserRepository:removeCredential:remove] %d rows, wancId: %s', + removeCount, + wancId + ); } } diff --git a/app/repository/model/HistoryTask.ts b/app/repository/model/HistoryTask.ts index e8d8c38c..a86a049c 100644 --- a/app/repository/model/HistoryTask.ts +++ b/app/repository/model/HistoryTask.ts @@ -1,7 +1,7 @@ import { Attribute, Model } from '@eggjs/tegg/orm'; import { DataTypes, Bone, LENGTH_VARIANTS } from '../util/leoric.js'; -import { TaskState, TaskType } from '../../common/enum/Task.js'; +import type { TaskState, TaskType } from '../../common/enum/Task.js'; @Model() export class HistoryTask extends Bone { diff --git a/app/repository/model/Hook.ts b/app/repository/model/Hook.ts index 15bdb634..1b6cddbb 100644 --- a/app/repository/model/Hook.ts +++ b/app/repository/model/Hook.ts @@ -1,7 +1,7 @@ import { Attribute, Model } from '@eggjs/tegg/orm'; import { DataTypes, Bone } from '../util/leoric.js'; -import { HookType } from '../../common/enum/Hook.js'; +import type { HookType } from '../../common/enum/Hook.js'; @Model() export class Hook extends Bone { @@ -11,7 +11,6 @@ export class Hook extends Bone { }) id: bigint; - @Attribute(DataTypes.DATE, { name: 'gmt_create' }) createdAt: Date; diff --git a/app/repository/model/ProxyCache.ts b/app/repository/model/ProxyCache.ts index c62f1985..830a73ac 100644 --- a/app/repository/model/ProxyCache.ts +++ b/app/repository/model/ProxyCache.ts @@ -1,7 +1,7 @@ import { Attribute, Model } from '@eggjs/tegg/orm'; import { DataTypes, Bone } from '../util/leoric.js'; -import { DIST_NAMES } from '../../core/entity/Package.js'; +import type { DIST_NAMES } from '../../core/entity/Package.js'; @Model() export class ProxyCache extends Bone { @@ -30,5 +30,4 @@ export class ProxyCache extends Bone { @Attribute(DataTypes.STRING(214)) version?: string; - } diff --git a/app/repository/model/Registry.ts b/app/repository/model/Registry.ts index 0cad3a70..b3514729 100644 --- a/app/repository/model/Registry.ts +++ b/app/repository/model/Registry.ts @@ -1,7 +1,7 @@ import { Attribute, Model } from '@eggjs/tegg/orm'; import { DataTypes, Bone } from '../util/leoric.js'; -import { RegistryType } from '../../common/enum/Registry.js'; +import type { RegistryType } from '../../common/enum/Registry.js'; @Model() export class Registry extends Bone { @@ -39,5 +39,4 @@ export class Registry extends Bone { @Attribute(DataTypes.STRING(256), { name: 'auth_token' }) authToken?: string; - } diff --git a/app/repository/model/Task.ts b/app/repository/model/Task.ts index bdb91a1c..821c11c3 100644 --- a/app/repository/model/Task.ts +++ b/app/repository/model/Task.ts @@ -1,7 +1,7 @@ import { Attribute, Model } from '@eggjs/tegg/orm'; import { DataTypes, Bone, LENGTH_VARIANTS } from '../util/leoric.js'; -import { TaskState, TaskType } from '../../common/enum/Task.js'; +import type { TaskState, TaskType } from '../../common/enum/Task.js'; @Model() export class Task extends Bone { diff --git a/app/repository/util/EntityProperty.ts b/app/repository/util/EntityProperty.ts index cda9be03..4065fefb 100644 --- a/app/repository/util/EntityProperty.ts +++ b/app/repository/util/EntityProperty.ts @@ -1,14 +1,19 @@ import assert from 'node:assert'; -import { EggProtoImplClass } from '@eggjs/tegg'; +import type { EggProtoImplClass } from '@eggjs/tegg'; import { ModelConvertorUtil } from './ModelConvertorUtil.js'; export function EntityProperty(entityProperty: string) { - return function(target: any, modelProperty: PropertyKey) { + return function (target: any, modelProperty: PropertyKey) { const clazz = target.constructor as EggProtoImplClass; - assert(typeof modelProperty === 'string', - `[model/${clazz.name}] expect method name be typeof string, but now is ${String(modelProperty)}`); - ModelConvertorUtil.addEntityPropertyName(entityProperty, clazz, modelProperty as string); + assert( + typeof modelProperty === 'string', + `[model/${clazz.name}] expect method name be typeof string, but now is ${String(modelProperty)}` + ); + ModelConvertorUtil.addEntityPropertyName( + entityProperty, + clazz, + modelProperty as string + ); }; } - diff --git a/app/repository/util/ModelConvertor.ts b/app/repository/util/ModelConvertor.ts index fdb93653..afea71b1 100644 --- a/app/repository/util/ModelConvertor.ts +++ b/app/repository/util/ModelConvertor.ts @@ -1,19 +1,24 @@ import { ModelMetadataUtil } from '@eggjs/tegg/orm'; -import { EggProtoImplClass } from '@eggjs/tegg'; +import type { EggProtoImplClass } from '@eggjs/tegg'; import { get as lodashGet, set as lodashSet } from 'lodash-es'; -import { Bone, type LeoricBone } from './leoric.js'; +import type { Bone } from './leoric.js'; +import { type LeoricBone } from './leoric.js'; import { ModelConvertorUtil } from './ModelConvertorUtil.js'; const CREATED_AT = 'createdAt'; const UPDATED_AT = 'updatedAt'; const ID = 'id'; -type BonePatchInfo = { id?: bigint, updatedAt?: Date, createdAt?: Date }; +type BonePatchInfo = { id?: bigint; updatedAt?: Date; createdAt?: Date }; type PatchedBone = LeoricBone & BonePatchInfo; export class ModelConvertor { - static async convertEntityToModel(entity: object, ModelClazz: EggProtoImplClass, options?: object): Promise { + static async convertEntityToModel( + entity: object, + ModelClazz: EggProtoImplClass, + options?: object + ): Promise { const metadata = ModelMetadataUtil.getModelMetadata(ModelClazz); if (!metadata) { throw new Error(`Model ${ModelClazz.name} has no metadata`); @@ -21,12 +26,23 @@ export class ModelConvertor { const attributes: Record = {}; for (const attributeMeta of metadata.attributes) { const modelPropertyName = attributeMeta.propertyName; - const entityPropertyName = ModelConvertorUtil.getEntityPropertyName(ModelClazz, modelPropertyName); - if (entityPropertyName === UPDATED_AT || entityPropertyName === CREATED_AT || entityPropertyName === ID) continue; + const entityPropertyName = ModelConvertorUtil.getEntityPropertyName( + ModelClazz, + modelPropertyName + ); + if ( + entityPropertyName === UPDATED_AT || + entityPropertyName === CREATED_AT || + entityPropertyName === ID + ) + continue; const attributeValue = lodashGet(entity, entityPropertyName); attributes[modelPropertyName] = attributeValue; } - const model = await (ModelClazz as unknown as typeof Bone).create(attributes, options) as PatchedBone; + const model = (await (ModelClazz as unknown as typeof Bone).create( + attributes, + options + )) as PatchedBone; // auto set entity id to model id (entity as Record)[ID] = model[ID]; // use model dates @@ -35,7 +51,10 @@ export class ModelConvertor { return model as T; } - static convertEntityToChanges(entity: object, ModelClazz: EggProtoImplClass) { + static convertEntityToChanges( + entity: object, + ModelClazz: EggProtoImplClass + ) { const changes: Record = {}; const metadata = ModelMetadataUtil.getModelMetadata(ModelClazz); if (!metadata) { @@ -43,7 +62,10 @@ export class ModelConvertor { } for (const attributeMeta of metadata.attributes) { const modelPropertyName = attributeMeta.propertyName; - const entityPropertyName = ModelConvertorUtil.getEntityPropertyName(ModelClazz, modelPropertyName); + const entityPropertyName = ModelConvertorUtil.getEntityPropertyName( + ModelClazz, + modelPropertyName + ); if (entityPropertyName === CREATED_AT) continue; const attributeValue = lodashGet(entity, entityPropertyName); changes[modelPropertyName] = attributeValue; @@ -55,7 +77,11 @@ export class ModelConvertor { // TODO: options is QueryOptions, should let leoric export it to use // Find out which attributes changed and set `updatedAt` to now - static async saveEntityToModel(entity: object, model: T & PatchedBone, options?: object): Promise { + static async saveEntityToModel( + entity: object, + model: T & PatchedBone, + options?: object + ): Promise { const ModelClazz = model.constructor as EggProtoImplClass; const metadata = ModelMetadataUtil.getModelMetadata(ModelClazz); if (!metadata) { @@ -63,12 +89,16 @@ export class ModelConvertor { } for (const attributeMeta of metadata.attributes) { const modelPropertyName = attributeMeta.propertyName; - const entityPropertyName = ModelConvertorUtil.getEntityPropertyName(ModelClazz, modelPropertyName); + const entityPropertyName = ModelConvertorUtil.getEntityPropertyName( + ModelClazz, + modelPropertyName + ); if (entityPropertyName === CREATED_AT) continue; // Restricted updates to the primary key if (entityPropertyName === ID && model[ID]) continue; const attributeValue = lodashGet(entity, entityPropertyName); - (model as unknown as Record)[modelPropertyName] = attributeValue; + (model as unknown as Record)[modelPropertyName] = + attributeValue; } // Restricted updates to the UPDATED_AT @@ -79,7 +109,11 @@ export class ModelConvertor { return true; } - static convertModelToEntity(bone: LeoricBone, entityClazz: EggProtoImplClass, data?: object): T { + static convertModelToEntity( + bone: LeoricBone, + entityClazz: EggProtoImplClass, + data?: object + ): T { data = data || {}; const ModelClazz = bone.constructor as EggProtoImplClass; const metadata = ModelMetadataUtil.getModelMetadata(ModelClazz); @@ -88,11 +122,15 @@ export class ModelConvertor { } for (const attributeMeta of metadata.attributes) { const modelPropertyName = attributeMeta.propertyName; - const entityPropertyName = ModelConvertorUtil.getEntityPropertyName(ModelClazz, modelPropertyName); - const attributeValue = bone[attributeMeta.propertyName as keyof LeoricBone]; + const entityPropertyName = ModelConvertorUtil.getEntityPropertyName( + ModelClazz, + modelPropertyName + ); + const attributeValue = + bone[attributeMeta.propertyName as keyof LeoricBone]; lodashSet(data, entityPropertyName, attributeValue); } - const model = Reflect.construct(entityClazz, [ data ]); + const model = Reflect.construct(entityClazz, [data]); return model; } } diff --git a/app/repository/util/ModelConvertorUtil.ts b/app/repository/util/ModelConvertorUtil.ts index bddb677c..57179082 100644 --- a/app/repository/util/ModelConvertorUtil.ts +++ b/app/repository/util/ModelConvertorUtil.ts @@ -1,18 +1,33 @@ -import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg'; +import type { EggProtoImplClass } from '@eggjs/tegg'; +import { MetadataUtil } from '@eggjs/tegg'; -const ENTITY_PROPERTY_MAP_ATTRIBUTE = Symbol.for('EggPrototype#model#entityPropertyMap'); +const ENTITY_PROPERTY_MAP_ATTRIBUTE = Symbol.for( + 'EggPrototype#model#entityPropertyMap' +); export class ModelConvertorUtil { - static addEntityPropertyName(entityProperty: string, clazz: EggProtoImplClass, modelProperty: string) { - const propertyMap: Map = MetadataUtil.initOwnMapMetaData(ENTITY_PROPERTY_MAP_ATTRIBUTE, clazz, new Map()); + static addEntityPropertyName( + entityProperty: string, + clazz: EggProtoImplClass, + modelProperty: string + ) { + const propertyMap: Map = MetadataUtil.initOwnMapMetaData( + ENTITY_PROPERTY_MAP_ATTRIBUTE, + clazz, + new Map() + ); propertyMap.set(modelProperty, entityProperty); } /** * If has no entity property info, use modelProperty as default value */ - static getEntityPropertyName(clazz: EggProtoImplClass, modelProperty: string): string { - const propertyMap: Map | undefined = MetadataUtil.getMetaData(ENTITY_PROPERTY_MAP_ATTRIBUTE, clazz); + static getEntityPropertyName( + clazz: EggProtoImplClass, + modelProperty: string + ): string { + const propertyMap: Map | undefined = + MetadataUtil.getMetaData(ENTITY_PROPERTY_MAP_ATTRIBUTE, clazz); return propertyMap?.get(modelProperty) ?? modelProperty; } } diff --git a/config/config.default.ts b/config/config.default.ts index 640486b1..d361c91b 100644 --- a/config/config.default.ts +++ b/config/config.default.ts @@ -1,13 +1,18 @@ import { strict as assert } from 'node:assert'; import { randomUUID } from 'node:crypto'; import { join } from 'node:path'; -import { EggAppConfig, PowerPartial, Context } from 'egg'; +import type { EggAppConfig, PowerPartial, Context } from 'egg'; import OSSClient from 'oss-cnpm'; import S3Client from 's3-cnpmcore'; import { env } from 'read-env-value'; import { patchAjv } from '../app/port/typebox.js'; -import { ChangesStreamMode, NOT_IMPLEMENTED_PATH, SyncDeleteMode, SyncMode } from '../app/common/constants.js'; +import { + ChangesStreamMode, + NOT_IMPLEMENTED_PATH, + SyncDeleteMode, + SyncMode, +} from '../app/common/constants.js'; import type { CnpmcoreConfig } from '../app/port/config.js'; import { database } from './database.js'; @@ -15,8 +20,16 @@ export const cnpmcoreConfig: CnpmcoreConfig = { name: 'cnpm', hookEnable: false, hooksLimit: 20, - sourceRegistry: env('CNPMCORE_CONFIG_SOURCE_REGISTRY', 'string', 'https://registry.npmjs.org'), - sourceRegistryIsCNpm: env('CNPMCORE_CONFIG_SOURCE_REGISTRY_IS_CNPM', 'boolean', false), + sourceRegistry: env( + 'CNPMCORE_CONFIG_SOURCE_REGISTRY', + 'string', + 'https://registry.npmjs.org' + ), + sourceRegistryIsCNpm: env( + 'CNPMCORE_CONFIG_SOURCE_REGISTRY_IS_CNPM', + 'boolean', + false + ), syncUpstreamFirst: false, sourceRegistrySyncTimeout: 180000, taskQueueHighWaterSize: 100, @@ -38,11 +51,7 @@ export const cnpmcoreConfig: CnpmcoreConfig = { changesStreamRegistryMode: ChangesStreamMode.streaming, registry: env('CNPMCORE_CONFIG_REGISTRY', 'string', 'http://localhost:7001'), alwaysAuth: false, - allowScopes: [ - '@cnpm', - '@cnpmcore', - '@example', - ], + allowScopes: ['@cnpm', '@cnpmcore', '@example'], allowPublishNonScopePackage: false, allowPublicRegistration: false, admins: { @@ -77,14 +86,18 @@ interface NFSConfig { export type Config = PowerPartial & { nfs: NFSConfig }; -export default (appInfo: EggAppConfig): Config => { +export default function startConfig(appInfo: EggAppConfig) { const config = {} as Config; config.keys = env('CNPMCORE_EGG_KEYS', 'string', randomUUID()); config.cnpmcore = cnpmcoreConfig; // override config from framework / plugin - config.dataDir = env('CNPMCORE_DATA_DIR', 'string', join(appInfo.root, '.cnpmcore')); + config.dataDir = env( + 'CNPMCORE_DATA_DIR', + 'string', + join(appInfo.root, '.cnpmcore') + ); config.orm = { ...database, database: database.name ?? 'cnpmcore', @@ -127,7 +140,11 @@ export default (appInfo: EggAppConfig): Config => { config.nfs = { client: null, dir: env('CNPMCORE_NFS_DIR', 'string', join(config.dataDir, 'nfs')), - removeBeforeUpload: env('CNPMCORE_NFS_REMOVE_BEFORE_UPLOAD', 'boolean', false), + removeBeforeUpload: env( + 'CNPMCORE_NFS_REMOVE_BEFORE_UPLOAD', + 'boolean', + false + ), }; /* c8 ignore next 17 */ // enable oss nfs store by env values @@ -157,12 +174,22 @@ export default (appInfo: EggAppConfig): Config => { secretAccessKey: env('CNPMCORE_NFS_S3_CLIENT_SECRET', 'string', ''), }, bucket: env('CNPMCORE_NFS_S3_CLIENT_BUCKET', 'string', ''), - forcePathStyle: env('CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE', 'boolean', false), + forcePathStyle: env( + 'CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE', + 'boolean', + false + ), disableURL: env('CNPMCORE_NFS_S3_CLIENT_DISABLE_URL', 'boolean', false), }; assert(s3Config.endpoint, 'require env CNPMCORE_NFS_S3_CLIENT_ENDPOINT'); - assert(s3Config.credentials.accessKeyId, 'require env CNPMCORE_NFS_S3_CLIENT_ID'); - assert(s3Config.credentials.secretAccessKey, 'require env CNPMCORE_NFS_S3_CLIENT_SECRET'); + assert( + s3Config.credentials.accessKeyId, + 'require env CNPMCORE_NFS_S3_CLIENT_ID' + ); + assert( + s3Config.credentials.secretAccessKey, + 'require env CNPMCORE_NFS_S3_CLIENT_SECRET' + ); assert(s3Config.bucket, 'require env CNPMCORE_NFS_S3_CLIENT_BUCKET'); // @ts-expect-error has no construct signatures config.nfs.client = new S3Client(s3Config); @@ -171,7 +198,11 @@ export default (appInfo: EggAppConfig): Config => { config.logger = { enablePerformanceTimer: true, enableFastContextLogger: true, - appLogName: env('CNPMCORE_APP_LOG_NAME', 'string', `${appInfo.name}-web.log`), + appLogName: env( + 'CNPMCORE_APP_LOG_NAME', + 'string', + `${appInfo.name}-web.log` + ), coreLogName: env('CNPMCORE_CORE_LOG_NAME', 'string', 'egg-web.log'), agentLogName: env('CNPMCORE_AGENT_LOG_NAME', 'string', 'egg-agent.log'), errorLogName: env('CNPMCORE_ERROR_LOG_NAME', 'string', 'common-error.log'), @@ -221,12 +252,20 @@ export default (appInfo: EggAppConfig): Config => { client: { node: env('CNPMCORE_CONFIG_ES_CLIENT_NODE', 'string', ''), auth: { - username: env('CNPMCORE_CONFIG_ES_CLIENT_AUTH_USERNAME', 'string', ''), - password: env('CNPMCORE_CONFIG_ES_CLIENT_AUTH_PASSWORD', 'string', ''), + username: env( + 'CNPMCORE_CONFIG_ES_CLIENT_AUTH_USERNAME', + 'string', + '' + ), + password: env( + 'CNPMCORE_CONFIG_ES_CLIENT_AUTH_PASSWORD', + 'string', + '' + ), }, }, }; } return config; -}; +} diff --git a/config/config.unittest.ts b/config/config.unittest.ts index fb3451d0..24836fc9 100644 --- a/config/config.unittest.ts +++ b/config/config.unittest.ts @@ -1,5 +1,5 @@ import { join } from 'node:path'; -import { EggAppConfig, PowerPartial } from 'egg'; +import type { EggAppConfig, PowerPartial } from 'egg'; import Mock from '@elastic/elasticsearch-mock'; import { database } from './database.js'; @@ -7,7 +7,7 @@ import { database } from './database.js'; // @ts-expect-error has no construct signatures export const mockES = new Mock(); -export default (appInfo: EggAppConfig) => { +export default function startConfig(appInfo: EggAppConfig) { const config = {} as PowerPartial; config.dataDir = join(appInfo.root, '.cnpmcore_unittest'); @@ -31,4 +31,4 @@ export default (appInfo: EggAppConfig) => { }; return config; -}; +} diff --git a/config/plugin.ts b/config/plugin.ts index 0d826d7e..c23a5c22 100644 --- a/config/plugin.ts +++ b/config/plugin.ts @@ -1,4 +1,4 @@ -import { EggPlugin } from 'egg'; +import type { EggPlugin } from 'egg'; const plugin: EggPlugin = { tegg: { diff --git a/package.json b/package.json index c1813d87..1bfaedfe 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,8 @@ "predev": "npm run clean", "dev": "egg-bin dev", "dev:postgresql": "CNPMCORE_DATABASE_TYPE=PostgreSQL egg-bin dev", - "prelint": "oxlint", - "lint": "eslint --cache --ext .ts .", - "lint:fix": "eslint --cache --ext .ts --fix .", + "lint": "oxlint", + "lint:fix": "oxlint --fix", "test:postgresql": "npm run lint:fix && npm run test:local:postgresql", "pretest:local:postgresql": "bash prepare-database-postgresql.sh", "test:local:postgresql": "CNPMCORE_DATABASE_TYPE=PostgreSQL egg-bin test", @@ -60,7 +59,7 @@ "postci": "npm run tsc:prod && npm run clean", "ci:postgresql": "npm run lint && npm run cov:postgresql && npm run tsc:prod && npm run clean", "clean": "tsc -b --clean && rm -rf dist", - "tsc": "npm run clean && tsc -p ./tsconfig.json", + "tsc": "npm run clean && tsc -p ./tsconfig.prod.json", "tsc:prod": "npm run clean && tsc -p ./tsconfig.prod.json", "prepublishOnly": "npm run tsc:prod", "images": "npm run images:alpine && npm run images:debian", @@ -68,7 +67,8 @@ "images:debian": "docker build -t cnpmcore:latest -f .docker/debian/Dockerfile .", "start": "eggctl start --daemon && touch egg.status", "start:foreground": "eggctl start", - "stop": "rm -f egg.status && sleep 15 && eggctl stop" + "stop": "rm -f egg.status && sleep 15 && eggctl stop", + "prepare": "husky" }, "repository": { "type": "git", @@ -95,7 +95,6 @@ "@eggjs/tegg-plugin": "4.0.0-beta.2", "@eggjs/tegg-schedule-plugin": "4.0.0-beta.2", "@eggjs/tracer": "^3.0.0", - "@eggjs/tsconfig": "^1.0.0", "@elastic/elasticsearch": "^8.8.1", "@fengmk2/tar": "^6.2.0", "@node-rs/crc32": "^1.2.2", @@ -136,6 +135,7 @@ "devDependencies": { "@eggjs/bin": "^7.1.0", "@eggjs/mock": "^6.0.7", + "@eggjs/tsconfig": "^2.0.0", "@elastic/elasticsearch-mock": "^2.0.0", "@simplewebauthn/typescript-types": "^7.0.0", "@types/lodash-es": "^4.17.12", @@ -150,10 +150,11 @@ "@types/ua-parser-js": "^0.7.36", "@types/validate-npm-package-name": "^4.0.2", "coffee": "^5.4.0", - "eslint": "^8.29.0", - "eslint-config-egg": "^14.0.0", - "oxlint": "^0.15.12", - "typescript": "beta" + "husky": "^9.1.7", + "lint-staged": "^15.5.0", + "oxlint": "^0.15.15", + "prettier": "^3.5.3", + "typescript": "5" }, "author": "killagu", "license": "MIT", @@ -163,5 +164,12 @@ "homepage": "https://github.com/cnpm/npmcore#readme", "engines": { "node": ">= 20.18.0" + }, + "lint-staged": { + "*": "prettier --write --ignore-unknown --cache", + "*.{ts}": [ + "prettier --ignore-unknown --write", + "oxlint --fix" + ] } } diff --git a/test/TestUtil.ts b/test/TestUtil.ts index ab07f322..884a6dba 100644 --- a/test/TestUtil.ts +++ b/test/TestUtil.ts @@ -4,8 +4,8 @@ import coffee from 'coffee'; import { tmpdir } from 'node:os'; import { mkdtempSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; -import { Readable } from 'node:stream'; -import mysql from 'mysql2'; +import type { Readable } from 'node:stream'; +import mysql from 'mysql2/promise'; import pg from 'pg'; import path from 'node:path'; import crypto from 'node:crypto'; @@ -13,7 +13,7 @@ import semver from 'semver'; import { app as globalApp } from '@eggjs/mock/bootstrap'; import { cleanUserPrefix, getScopeAndName } from '../app/common/PackageUtil.js'; -import { PackageJSONType } from '../app/repository/PackageRepository.js'; +import type { PackageJSONType } from '../app/repository/PackageRepository.js'; import { database, DATABASE_TYPE } from '../config/database.js'; import { Package as PackageModel } from '../app/repository/model/Package.js'; @@ -74,38 +74,39 @@ export class TestUtil { // 不同的 npm 版本 cli 命令不同 // 通过 coffee 运行时获取对应版本号 static async getNpmVersion() { - const res = await coffee.spawn('npm', [ '-v' ]).end(); + const res = await coffee.spawn('npm', ['-v']).end(); return semver.clean(res.stdout); } static async query(sql: string): Promise { - const conn = this.getConnection(); - return new Promise((resolve, reject) => { - conn.query(sql, (err: Error, rows: any) => { - if (err) { - return reject(err); - } - if (rows.rows) { - // pg: { rows } - return resolve(rows.rows); - } - return resolve(rows); - }); - }); + const conn = await this.getConnection(); + try { + const result = await conn.query(sql); + if (result.rows) { + // pg: { rows } + return result.rows; + } else { + // mysql: [ RowDataPacket[], others ] + return result[0]; + } + } catch (err) { + console.error('[TestUtil] query %o error: %s', sql, err); + throw err; + } } - static getConnection() { + static async getConnection() { if (!this.connection) { const config = this.getDatabaseConfig(); if (process.env.CI) { console.log('[TestUtil] connection to database: %j', config); } if (config.type === DATABASE_TYPE.MySQL) { - this.connection = mysql.createConnection(config as any); + this.connection = await mysql.createConnection(config as any); } else if (config.type === DATABASE_TYPE.PostgreSQL) { this.connection = new pg.Client(config as any); } - this.connection.connect(); + await this.connection.connect(); } return this.connection; } @@ -123,11 +124,25 @@ export class TestUtil { if (config.type === DATABASE_TYPE.MySQL) { const sql = ` SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${config.database}';`; - const rows = await this.query(sql); + // [ + // { TABLE_NAME: 'binaries' }, + // { TABLE_NAME: 'changes' }, + // { TABLE_NAME: 'dists' }, + // { TABLE_NAME: 'history_tasks' }, + // { TABLE_NAME: 'hooks' }, + // ... + // { TABLE_NAME: 'token_packages' }, + // { TABLE_NAME: 'tokens' }, + // { TABLE_NAME: 'total' }, + // { TABLE_NAME: 'users' }, + // { TABLE_NAME: 'webauthn_credentials' } + // ] + const rows: { TABLE_NAME: string }[] = await this.query(sql); this.tables = rows.map(row => row.TABLE_NAME); } else if (config.type === DATABASE_TYPE.PostgreSQL) { - const sql = 'SELECT * FROM pg_catalog.pg_tables where schemaname = \'public\';'; - const rows = await this.query(sql); + const sql = + "SELECT * FROM pg_catalog.pg_tables where schemaname = 'public';"; + const rows: { tablename: string }[] = await this.query(sql); this.tables = rows.map(row => row.tablename); } } @@ -136,9 +151,11 @@ export class TestUtil { static async truncateDatabase() { const tables = await this.getTableNames(); - await Promise.all(tables.map(async (table: string) => { - await this.query(`TRUNCATE TABLE ${table};`); - })); + await Promise.all( + tables.map(async (table: string) => { + await this.query(`TRUNCATE TABLE ${table};`); + }) + ); } static get app() { @@ -177,7 +194,9 @@ export class TestUtil { return JSON.parse(bytes.toString()); } - static async getFullPackage(options?: PackageOptions): Promise }> { + static async getFullPackage( + options?: PackageOptions + ): Promise }> { const fullJSONFile = this.getFixtures('exampleFullPackage.json'); const pkg = JSON.parse((await fs.readFile(fullJSONFile)).toString()); if (options) { @@ -234,10 +253,14 @@ export class TestUtil { return pkg; } - static async createPackage(options?: PackageOptions, userOptions?: UserOptions) { + static async createPackage( + options?: PackageOptions, + userOptions?: UserOptions + ) { const pkg = await this.getFullPackage(options); const user = await this.createUser(userOptions); - await this.app.httpRequest() + await this.app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -245,8 +268,11 @@ export class TestUtil { .expect(201); if (options?.isPrivate === false) { - const [ scope, name ] = getScopeAndName(pkg.name); - await PackageModel.update({ scope, name }, { isPrivate: false, registryId: options?.registryId }); + const [scope, name] = getScopeAndName(pkg.name); + await PackageModel.update( + { scope, name }, + { isPrivate: false, registryId: options?.registryId } + ); } return { user, pkg }; } @@ -260,7 +286,8 @@ export class TestUtil { } const password = user.password ?? 'password-is-here'; const email = cleanUserPrefix(user.email ?? `${user.name}@example.com`); - let res = await this.app.httpRequest() + let res = await this.app + .httpRequest() .put(`/-/user/org.couchdb.user:${user.name}`) .send({ name: user.name, @@ -271,7 +298,8 @@ export class TestUtil { .expect(201); let token: string = res.body.token; if (user.tokenOptions) { - res = await this.app.httpRequest() + res = await this.app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', `Bearer ${token}`) .send({ @@ -299,7 +327,8 @@ export class TestUtil { automation?: true; cidr_whitelist?: string[]; }) { - const res = await this.app.httpRequest() + const res = await this.app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', `Bearer ${user.token}`) .set('user-agent', this.ua) @@ -322,16 +351,16 @@ export class TestUtil { static async createRegistryAndScope() { // create success const adminUser = await this.createAdmin(); - await this.app.httpRequest() + await this.app + .httpRequest() .post('/-/registry') .set('authorization', adminUser.authorization) - .send( - { - name: 'custom6', - host: 'https://r.cnpmjs.org/', - changeStream: 'https://r.cnpmjs.org/_changes', - type: 'cnpmcore', - }); + .send({ + name: 'custom6', + host: 'https://r.cnpmjs.org/', + changeStream: 'https://r.cnpmjs.org/_changes', + type: 'cnpmcore', + }); } static async readStreamToLog(urlOrStream: any) { diff --git a/test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts b/test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts index 597b840b..31d0eff3 100644 --- a/test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts +++ b/test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts @@ -1,10 +1,10 @@ import { strict as assert } from 'node:assert'; import { app } from '@eggjs/mock/bootstrap'; -import { ChangesStreamChange } from '../../../../app/common/adapter/changesStream/AbstractChangesStream.js'; +import type { ChangesStreamChange } from '../../../../app/common/adapter/changesStream/AbstractChangesStream.js'; import { CnpmcoreChangesStream } from '../../../../app/common/adapter/changesStream/CnpmcoreChangesStream.js'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; -import { Registry } from '../../../../app/core/entity/Registry.js'; +import type { Registry } from '../../../../app/core/entity/Registry.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; describe('test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts', () => { @@ -39,12 +39,20 @@ describe('test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts', () = app.mockHttpclient(/https:\/\/r\.cnpmjs\.org/, () => { throw new Error('mock request replicate _changes error'); }); - await assert.rejects(cnpmcoreChangesStream.getInitialSince(registry), /mock request/); + await assert.rejects( + cnpmcoreChangesStream.getInitialSince(registry), + /mock request/ + ); }); it('should throw error invalid seq', async () => { - app.mockHttpclient(/https:\/\/r\.cnpmjs\.org/, { data: { update_seqs: 'invalid' } }); - await assert.rejects(cnpmcoreChangesStream.getInitialSince(registry), /get getInitialSince failed/); + app.mockHttpclient(/https:\/\/r\.cnpmjs\.org/, { + data: { update_seqs: 'invalid' }, + }); + await assert.rejects( + cnpmcoreChangesStream.getInitialSince(registry), + /get getInitialSince failed/ + ); }); }); diff --git a/test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts b/test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts index 03c1c55b..868e61c4 100644 --- a/test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts +++ b/test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts @@ -1,10 +1,10 @@ import { strict as assert } from 'node:assert'; import { app } from '@eggjs/mock/bootstrap'; -import { ChangesStreamChange } from '../../../../app/common/adapter/changesStream/AbstractChangesStream.js'; +import type { ChangesStreamChange } from '../../../../app/common/adapter/changesStream/AbstractChangesStream.js'; import { CnpmjsorgChangesStream } from '../../../../app/common/adapter/changesStream/CnpmjsorgChangesStream.js'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; -import { Registry } from '../../../../app/core/entity/Registry.js'; +import type { Registry } from '../../../../app/core/entity/Registry.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; describe('test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts', () => { @@ -81,7 +81,7 @@ describe('test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts', () }, }); const stream = cnpmjsorgChangesStream.fetchChanges(registry, '1'); - const changes:ChangesStreamChange[] = []; + const changes: ChangesStreamChange[] = []; for await (const change of stream) { changes.push(change); } @@ -89,19 +89,23 @@ describe('test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts', () }); it('should reject max limit', async () => { - app.mockHttpclient('https://r2.cnpmjs.org/_changes?since=1&limit=', 'GET', (url = '') => { - const limit = (new URL(url)).searchParams.get('limit'); - return { - data: { - results: new Array(Number(limit)).fill(0).map((_, i) => ({ - type: 'PACKAGE_TAG_ADDED', - id: `abc-cli-${i}`, - changes: [{ tag: 'latest' }], - gmt_modified: '2014-01-15T19:35:09.000Z', - })), - }, - }; - }); + app.mockHttpclient( + 'https://r2.cnpmjs.org/_changes?since=1&limit=', + 'GET', + (url = '') => { + const limit = new URL(url).searchParams.get('limit'); + return { + data: { + results: Array.from({ length: Number(limit) }).map((_, i) => ({ + type: 'PACKAGE_TAG_ADDED', + id: `abc-cli-${i}`, + changes: [{ tag: 'latest' }], + gmt_modified: '2014-01-15T19:35:09.000Z', + })), + }, + }; + } + ); const stream = cnpmjsorgChangesStream.fetchChanges(registry, '1'); await assert.rejects(async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/test/common/adapter/changesStream/NpmChangesStream.test.ts b/test/common/adapter/changesStream/NpmChangesStream.test.ts index 6bb941f5..f20ab7d4 100644 --- a/test/common/adapter/changesStream/NpmChangesStream.test.ts +++ b/test/common/adapter/changesStream/NpmChangesStream.test.ts @@ -2,10 +2,10 @@ import { Readable, Duplex } from 'node:stream'; import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { ChangesStreamChange } from '../../../../app/common/adapter/changesStream/AbstractChangesStream.js'; +import type { ChangesStreamChange } from '../../../../app/common/adapter/changesStream/AbstractChangesStream.js'; import { NpmChangesStream } from '../../../../app/common/adapter/changesStream/NpmChangesStream.js'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; -import { Registry } from '../../../../app/core/entity/Registry.js'; +import type { Registry } from '../../../../app/core/entity/Registry.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; describe('test/common/adapter/changesStream/NpmChangesStream.test.ts', () => { @@ -40,12 +40,20 @@ describe('test/common/adapter/changesStream/NpmChangesStream.test.ts', () => { app.mockHttpclient(/https:\/\/replicate\.npmjs\.com/, () => { throw new Error('mock request replicate _changes error'); }); - await assert.rejects(npmChangesStream.getInitialSince(registry), /mock request/); + await assert.rejects( + npmChangesStream.getInitialSince(registry), + /mock request/ + ); }); it('should throw error invalid seq', async () => { - app.mockHttpclient(/https:\/\/replicate\.npmjs\.com/, { data: { update_seqs: 'invalid' } }); - await assert.rejects(npmChangesStream.getInitialSince(registry), /get getInitialSince failed/); + app.mockHttpclient(/https:\/\/replicate\.npmjs\.com/, { + data: { update_seqs: 'invalid' }, + }); + await assert.rejects( + npmChangesStream.getInitialSince(registry), + /get getInitialSince failed/ + ); }); }); @@ -79,7 +87,9 @@ describe('test/common/adapter/changesStream/NpmChangesStream.test.ts', () => { assert(stream); rStream.push('{"seq":2'); rStream.push(',"id":"bac'); - rStream.push('kbone.websql.deferred","changes":[{"rev":"4-f5150b238ab62cd890211fb57fc9eca5"}],"deleted":true}'); + rStream.push( + 'kbone.websql.deferred","changes":[{"rev":"4-f5150b238ab62cd890211fb57fc9eca5"}],"deleted":true}' + ); for await (const change of stream) { res.push(change); } diff --git a/test/core/entity/PaddingSemver.test.ts b/test/core/entity/PaddingSemver.test.ts index cda8383a..c8692b2d 100644 --- a/test/core/entity/PaddingSemver.test.ts +++ b/test/core/entity/PaddingSemver.test.ts @@ -2,10 +2,10 @@ import { strict as assert } from 'node:assert'; import { PaddingSemVer } from '../../../app/core/entity/PaddingSemVer.js'; -describe('test/npm/core/entity/PaddingSemver.test.ts', () => { +describe('test/core/entity/PaddingSemver.test.ts', () => { it('should parse 16 length version ok', () => { // http://npmjs.com/package/npm-test-playground const version = new PaddingSemVer('0.9007199254740991.0'); - assert(version.paddingVersion === '000000000000000090071992547409910000000000000000'); + assert.equal(version.paddingVersion, '000000000000000090071992547409910000000000000000'); }); }); diff --git a/test/core/service/BinarySyncerService/createTask.test.ts b/test/core/service/BinarySyncerService/createTask.test.ts index e5a27ea2..2211f7ff 100644 --- a/test/core/service/BinarySyncerService/createTask.test.ts +++ b/test/core/service/BinarySyncerService/createTask.test.ts @@ -2,7 +2,7 @@ import { strict as assert } from 'node:assert'; import { app } from '@eggjs/mock/bootstrap'; import { BinarySyncerService } from '../../../../app/core/service/BinarySyncerService.js'; -import { BinaryName } from '../../../../config/binaries.js'; +import type { BinaryName } from '../../../../config/binaries.js'; describe('test/core/service/BinarySyncerService/createTask.test.ts', () => { let binarySyncerService: BinarySyncerService; @@ -13,8 +13,14 @@ describe('test/core/service/BinarySyncerService/createTask.test.ts', () => { describe('createTask()', () => { it('should ignore duplicate binary task', async () => { - const task = await binarySyncerService.createTask('banana' as BinaryName, {}); - const newTask = await binarySyncerService.createTask('banana' as BinaryName, {}); + const task = await binarySyncerService.createTask( + 'banana' as BinaryName, + {} + ); + const newTask = await binarySyncerService.createTask( + 'banana' as BinaryName, + {} + ); assert(task?.taskId === newTask?.taskId); assert(task?.bizId === 'SyncBinary:banana'); }); diff --git a/test/core/service/ChangesStreamService.test.ts b/test/core/service/ChangesStreamService.test.ts index 26672b3a..cc1b02c5 100644 --- a/test/core/service/ChangesStreamService.test.ts +++ b/test/core/service/ChangesStreamService.test.ts @@ -5,11 +5,12 @@ import { app, mock } from '@eggjs/mock/bootstrap'; import { TestUtil } from '../../../test/TestUtil.js'; import { ChangesStreamService } from '../../../app/core/service/ChangesStreamService.js'; import { TaskService } from '../../../app/core/service/TaskService.js'; -import { ChangesStreamTask, Task } from '../../../app/core/entity/Task.js'; +import type { ChangesStreamTask } from '../../../app/core/entity/Task.js'; +import { Task } from '../../../app/core/entity/Task.js'; import { RegistryManagerService } from '../../../app/core/service/RegistryManagerService.js'; import { RegistryType } from '../../../app/common/enum/Registry.js'; import { ScopeManagerService } from '../../../app/core/service/ScopeManagerService.js'; -import { Registry } from '../../../app/core/entity/Registry.js'; +import type { Registry } from '../../../app/core/entity/Registry.js'; import { RedisQueueAdapter } from '../../../app/infra/QueueAdapter.js'; describe('test/core/service/ChangesStreamService.test.ts', () => { @@ -85,7 +86,10 @@ describe('test/core/service/ChangesStreamService.test.ts', () => { assert(registryId); await registryManagerService.remove({ registryId }); - await assert.rejects(changesStreamService.prepareRegistry(task), /invalid change stream registry/); + await assert.rejects( + changesStreamService.prepareRegistry(task), + /invalid change stream registry/ + ); }); }); @@ -96,7 +100,10 @@ describe('test/core/service/ChangesStreamService.test.ts', () => { isPrivate: false, registryId: npmRegistry.registryId, }); - const res = await changesStreamService.needSync(npmRegistry, '@cnpm/test'); + const res = await changesStreamService.needSync( + npmRegistry, + '@cnpm/test' + ); assert(res); }); @@ -106,12 +113,18 @@ describe('test/core/service/ChangesStreamService.test.ts', () => { }); it('scoped package should sync default registry', async () => { - const res = await changesStreamService.needSync(npmRegistry, '@gogogo/banana'); + const res = await changesStreamService.needSync( + npmRegistry, + '@gogogo/banana' + ); assert(res); }); it('scoped package should sync custom registry', async () => { - let res = await changesStreamService.needSync(cnpmRegistry, '@cnpm/banana'); + let res = await changesStreamService.needSync( + cnpmRegistry, + '@cnpm/banana' + ); assert(res); res = await changesStreamService.needSync(cnpmRegistry, '@dnpmjs/banana'); assert(!res); @@ -200,13 +213,17 @@ describe('test/core/service/ChangesStreamService.test.ts', () => { beforeEach(async () => { app.mockLog(); mock(app.config.cnpmcore, 'enableChangesStream', true); - app.mockHttpclient('https://replicate.npmjs.com/_changes?since=9527', 'GET', () => { - return { - data: { - res: Readable.from(''), - }, - }; - }); + app.mockHttpclient( + 'https://replicate.npmjs.com/_changes?since=9527', + 'GET', + () => { + return { + data: { + res: Readable.from(''), + }, + }; + } + ); }); it('should work', async () => { const task = await changesStreamService.findExecuteTask(); @@ -225,7 +242,6 @@ describe('test/core/service/ChangesStreamService.test.ts', () => { assert(len === 1); app.expectLog('[ChangesStreamService.suspendSync:suspend] taskId'); - }); it('should suspendSync when error', async () => { @@ -254,6 +270,5 @@ describe('test/core/service/ChangesStreamService.test.ts', () => { await changesStreamService.suspendSync(true); app.expectLog('[ChangesStreamService.suspendSync:finish]'); }); - }); }); diff --git a/test/core/service/CreateHookTriggerService.test.ts b/test/core/service/CreateHookTriggerService.test.ts index 58981b13..62a4a26c 100644 --- a/test/core/service/CreateHookTriggerService.test.ts +++ b/test/core/service/CreateHookTriggerService.test.ts @@ -12,7 +12,7 @@ import { Task } from '../../../app/core/entity/Task.js'; import { HookEvent } from '../../../app/core/entity/HookEvent.js'; import { CreateHookTriggerService } from '../../../app/core/service/CreateHookTriggerService.js'; import { TaskRepository } from '../../../app/repository/TaskRepository.js'; -import { Hook } from '../../../app/core/entity/Hook.js'; +import type { Hook } from '../../../app/core/entity/Hook.js'; describe('test/core/service/CreateHookTriggerService.test.ts', () => { let hookManageService: HookManageService; @@ -29,11 +29,14 @@ describe('test/core/service/CreateHookTriggerService.test.ts', () => { createHookTriggerService = await app.getEggObject(CreateHookTriggerService); taskRepository = await app.getEggObject(TaskRepository); const userRepository = await app.getEggObject(UserRepository); - await TestUtil.createPackage({ - name: pkgName, - }, { - name: username, - }); + await TestUtil.createPackage( + { + name: pkgName, + }, + { + name: username, + } + ); const user = await userRepository.findUserByName(username); userId = user!.userId; }); @@ -64,9 +67,13 @@ describe('test/core/service/CreateHookTriggerService.test.ts', () => { }); it('should create package hook trigger', async () => { - const task = Task.createCreateHookTask(HookEvent.createUnpublishEvent(pkgName, change.changeId)); + const task = Task.createCreateHookTask( + HookEvent.createUnpublishEvent(pkgName, change.changeId) + ); await createHookTriggerService.executeTask(task); - const pushTask = await taskRepository.findTaskByBizId(`TriggerHook:${change.changeId}:${hook.hookId}`); + const pushTask = await taskRepository.findTaskByBizId( + `TriggerHook:${change.changeId}:${hook.hookId}` + ); assert(pushTask); }); }); @@ -84,9 +91,13 @@ describe('test/core/service/CreateHookTriggerService.test.ts', () => { }); it('should create scope hook trigger', async () => { - const task = Task.createCreateHookTask(HookEvent.createUnpublishEvent(pkgName, change.changeId)); + const task = Task.createCreateHookTask( + HookEvent.createUnpublishEvent(pkgName, change.changeId) + ); await createHookTriggerService.executeTask(task); - const pushTask = await taskRepository.findTaskByBizId(`TriggerHook:${change.changeId}:${hook.hookId}`); + const pushTask = await taskRepository.findTaskByBizId( + `TriggerHook:${change.changeId}:${hook.hookId}` + ); assert(pushTask); }); }); @@ -104,9 +115,13 @@ describe('test/core/service/CreateHookTriggerService.test.ts', () => { }); it('should create scope hook trigger', async () => { - const task = Task.createCreateHookTask(HookEvent.createUnpublishEvent(pkgName, change.changeId)); + const task = Task.createCreateHookTask( + HookEvent.createUnpublishEvent(pkgName, change.changeId) + ); await createHookTriggerService.executeTask(task); - const pushTask = await taskRepository.findTaskByBizId(`TriggerHook:${change.changeId}:${hook.hookId}`); + const pushTask = await taskRepository.findTaskByBizId( + `TriggerHook:${change.changeId}:${hook.hookId}` + ); assert(pushTask); }); }); diff --git a/test/core/service/HookManageService/deleteHook.test.ts b/test/core/service/HookManageService/deleteHook.test.ts index 1c85665b..d543e8c8 100644 --- a/test/core/service/HookManageService/deleteHook.test.ts +++ b/test/core/service/HookManageService/deleteHook.test.ts @@ -3,7 +3,7 @@ import { app, mock } from '@eggjs/mock/bootstrap'; import { TestUtil } from '../../../../test/TestUtil.js'; import { HookManageService } from '../../../../app/core/service/HookManageService.js'; -import { Hook } from '../../../../app/core/entity/Hook.js'; +import type { Hook } from '../../../../app/core/entity/Hook.js'; import { HookType } from '../../../../app/common/enum/Hook.js'; describe('test/core/service/HookManageService/deleteHook.test.ts', () => { @@ -39,12 +39,15 @@ describe('test/core/service/HookManageService/deleteHook.test.ts', () => { describe('hook not belong to operator', () => { it('should throw error', async () => { - await assert.rejects(async () => { - await hookManageService.deleteHook({ - hookId: hook.hookId, - operatorId: 'not_exits_owner_id', - }); - }, new RegExp(`hook ${hook.hookId} not belong to not_exits_owner_id`)); + await assert.rejects( + async () => { + await hookManageService.deleteHook({ + hookId: hook.hookId, + operatorId: 'not_exits_owner_id', + }); + }, + new RegExp(`hook ${hook.hookId} not belong to not_exits_owner_id`) + ); }); }); diff --git a/test/core/service/HookManageService/getHookByOwnerId.test.ts b/test/core/service/HookManageService/getHookByOwnerId.test.ts index fe52e6e0..83b31a2d 100644 --- a/test/core/service/HookManageService/getHookByOwnerId.test.ts +++ b/test/core/service/HookManageService/getHookByOwnerId.test.ts @@ -3,7 +3,7 @@ import { app, mock } from '@eggjs/mock/bootstrap'; import { TestUtil } from '../../../../test/TestUtil.js'; import { HookManageService } from '../../../../app/core/service/HookManageService.js'; -import { Hook } from '../../../../app/core/entity/Hook.js'; +import type { Hook } from '../../../../app/core/entity/Hook.js'; import { HookType } from '../../../../app/common/enum/Hook.js'; describe('test/core/service/HookManageService/getHookByOwnerId.test.ts', () => { @@ -29,21 +29,33 @@ describe('test/core/service/HookManageService/getHookByOwnerId.test.ts', () => { describe('hook not found', () => { it('should throw error', async () => { await assert.rejects(async () => { - await hookManageService.getHookByOwnerId('not_exist_hook_id', 'mock_owner_id'); + await hookManageService.getHookByOwnerId( + 'not_exist_hook_id', + 'mock_owner_id' + ); }, /hook not_exist_hook_id not found/); }); }); describe('hook not belong to operator', () => { it('should throw error', async () => { - await assert.rejects(async () => { - await hookManageService.getHookByOwnerId(hook.hookId, 'not_exits_owner_id'); - }, new RegExp(`hook ${hook.hookId} not belong to not_exits_owner_id`)); + await assert.rejects( + async () => { + await hookManageService.getHookByOwnerId( + hook.hookId, + 'not_exits_owner_id' + ); + }, + new RegExp(`hook ${hook.hookId} not belong to not_exits_owner_id`) + ); }); }); it('should work', async () => { - const getHook = await hookManageService.getHookByOwnerId(hook.hookId, 'mock_owner_id'); + const getHook = await hookManageService.getHookByOwnerId( + hook.hookId, + 'mock_owner_id' + ); assert(getHook); }); }); diff --git a/test/core/service/HookManageService/updateHook.test.ts b/test/core/service/HookManageService/updateHook.test.ts index 5f0894fc..feb1dd49 100644 --- a/test/core/service/HookManageService/updateHook.test.ts +++ b/test/core/service/HookManageService/updateHook.test.ts @@ -3,7 +3,7 @@ import { app, mock } from '@eggjs/mock/bootstrap'; import { TestUtil } from '../../../../test/TestUtil.js'; import { HookManageService } from '../../../../app/core/service/HookManageService.js'; -import { Hook } from '../../../../app/core/entity/Hook.js'; +import type { Hook } from '../../../../app/core/entity/Hook.js'; import { HookType } from '../../../../app/common/enum/Hook.js'; describe('test/core/service/HookManageService/updateHook.test.ts', () => { @@ -41,14 +41,17 @@ describe('test/core/service/HookManageService/updateHook.test.ts', () => { describe('hook not belong to operator', () => { it('should throw error', async () => { - await assert.rejects(async () => { - await hookManageService.updateHook({ - hookId: hook.hookId, - operatorId: 'not_exits_owner_id', - endpoint: 'http://foo.com', - secret: 'mock_secret', - }); - }, new RegExp(`hook ${hook.hookId} not belong to not_exits_owner_id`)); + await assert.rejects( + async () => { + await hookManageService.updateHook({ + hookId: hook.hookId, + operatorId: 'not_exits_owner_id', + endpoint: 'http://foo.com', + secret: 'mock_secret', + }); + }, + new RegExp(`hook ${hook.hookId} not belong to not_exits_owner_id`) + ); }); }); diff --git a/test/core/service/HookTriggerService.test.ts b/test/core/service/HookTriggerService.test.ts index e205527b..be1caef4 100644 --- a/test/core/service/HookTriggerService.test.ts +++ b/test/core/service/HookTriggerService.test.ts @@ -1,19 +1,23 @@ import { strict as assert } from 'node:assert'; -import { HttpClientRequestOptions } from 'egg'; +import type { HttpClientRequestOptions } from 'egg'; import { app, mock } from '@eggjs/mock/bootstrap'; import { TestUtil } from '../../../test/TestUtil.js'; import { HookManageService } from '../../../app/core/service/HookManageService.js'; import { HookType } from '../../../app/common/enum/Hook.js'; import { UserRepository } from '../../../app/repository/UserRepository.js'; -import { PACKAGE_TAG_ADDED, PACKAGE_VERSION_ADDED } from '../../../app/core/event/index.js'; +import { + PACKAGE_TAG_ADDED, + PACKAGE_VERSION_ADDED, +} from '../../../app/core/event/index.js'; import { Change } from '../../../app/core/entity/Change.js'; import { ChangeRepository } from '../../../app/repository/ChangeRepository.js'; -import { Task, TriggerHookTask } from '../../../app/core/entity/Task.js'; +import type { TriggerHookTask } from '../../../app/core/entity/Task.js'; +import { Task } from '../../../app/core/entity/Task.js'; import { HookEvent } from '../../../app/core/entity/HookEvent.js'; import { CreateHookTriggerService } from '../../../app/core/service/CreateHookTriggerService.js'; import { TaskRepository } from '../../../app/repository/TaskRepository.js'; -import { Hook } from '../../../app/core/entity/Hook.js'; +import type { Hook } from '../../../app/core/entity/Hook.js'; import { HookTriggerService } from '../../../app/core/service/HookTriggerService.js'; describe('test/core/service/HookTriggerService.test.ts', () => { @@ -33,11 +37,14 @@ describe('test/core/service/HookTriggerService.test.ts', () => { taskRepository = await app.getEggObject(TaskRepository); const userRepository = await app.getEggObject(UserRepository); hookTriggerService = await app.getEggObject(HookTriggerService); - await TestUtil.createPackage({ - name: pkgName, - }, { - name: username, - }); + await TestUtil.createPackage( + { + name: pkgName, + }, + { + name: username, + } + ); const user = await userRepository.findUserByName(username); userId = user!.userId; }); @@ -76,25 +83,45 @@ describe('test/core/service/HookTriggerService.test.ts', () => { endpoint: 'http://foo.com', secret: 'mock_secret', }); - const versionTask = Task.createCreateHookTask(HookEvent.createPublishEvent(pkgName, versionChange.changeId, '1.0.0', 'latest')); - const tagTask = Task.createCreateHookTask(HookEvent.createPublishEvent(pkgName, tagChange.changeId, '1.0.0', 'latest')); + const versionTask = Task.createCreateHookTask( + HookEvent.createPublishEvent( + pkgName, + versionChange.changeId, + '1.0.0', + 'latest' + ) + ); + const tagTask = Task.createCreateHookTask( + HookEvent.createPublishEvent( + pkgName, + tagChange.changeId, + '1.0.0', + 'latest' + ) + ); await Promise.all([ createHookTriggerService.executeTask(versionTask), createHookTriggerService.executeTask(tagTask), ]); - mock(app.httpclient, 'request', async (url: string, options: HttpClientRequestOptions) => { - callEndpoint = url; - callOptions = options; - return { - status: 200, - }; - }); + mock( + app.httpclient, + 'request', + async (url: string, options: HttpClientRequestOptions) => { + callEndpoint = url; + callOptions = options; + return { + status: 200, + }; + } + ); }); it('should execute trigger', async () => { - const pushTask = await taskRepository.findTaskByBizId(`TriggerHook:${versionChange.changeId}:${hook.hookId}`) as TriggerHookTask; + const pushTask = (await taskRepository.findTaskByBizId( + `TriggerHook:${versionChange.changeId}:${hook.hookId}` + )) as TriggerHookTask; await hookTriggerService.executeTask(pushTask); assert(callEndpoint === hook.endpoint); assert(callOptions); @@ -117,7 +144,14 @@ describe('test/core/service/HookTriggerService.test.ts', () => { }); it('should create each event', async () => { - const tasks = await Promise.all([ taskRepository.findTaskByBizId(`TriggerHook:${versionChange.changeId}:${hook.hookId}`), taskRepository.findTaskByBizId(`TriggerHook:${tagChange.changeId}:${hook.hookId}`) ]); + const tasks = await Promise.all([ + taskRepository.findTaskByBizId( + `TriggerHook:${versionChange.changeId}:${hook.hookId}` + ), + taskRepository.findTaskByBizId( + `TriggerHook:${tagChange.changeId}:${hook.hookId}` + ), + ]); assert.equal(tasks.filter(Boolean).length, 2); }); }); diff --git a/test/core/service/PackageManagerService/publish.test.ts b/test/core/service/PackageManagerService/publish.test.ts index 3a85f134..ed3e4b91 100644 --- a/test/core/service/PackageManagerService/publish.test.ts +++ b/test/core/service/PackageManagerService/publish.test.ts @@ -5,7 +5,7 @@ import { TestUtil } from '../../../../test/TestUtil.js'; import { PackageManagerService } from '../../../../app/core/service/PackageManagerService.js'; import { UserService } from '../../../../app/core/service/UserService.js'; import { PackageRepository } from '../../../../app/repository/PackageRepository.js'; -import { User } from '../../../../app/core/entity/User.js'; +import type { User } from '../../../../app/core/entity/User.js'; describe('test/core/service/PackageManagerService/publish.test.ts', () => { let packageManagerService: PackageManagerService; @@ -35,37 +35,49 @@ describe('test/core/service/PackageManagerService/publish.test.ts', () => { describe('publish()', () => { it('should work with dist.content', async () => { app.mockLog(); - const { packageId } = await packageManagerService.publish({ - dist: { - content: Buffer.alloc(0), + const { packageId } = await packageManagerService.publish( + { + dist: { + content: Buffer.alloc(0), + }, + tags: [''], + scope: '', + name: 'foo', + description: 'foo description', + packageJson: await TestUtil.getFullPackage({ name: 'foo' }), + readme: '', + version: '1.0.0', + isPrivate: true, }, - tags: [ '' ], - scope: '', - name: 'foo', - description: 'foo description', - packageJson: await TestUtil.getFullPackage({ name: 'foo' }), - readme: '', - version: '1.0.0', - isPrivate: true, - }, publisher); - let pkgVersion = await packageRepository.findPackageVersion(packageId, '1.0.0'); + publisher + ); + let pkgVersion = await packageRepository.findPackageVersion( + packageId, + '1.0.0' + ); assert(pkgVersion); assert.equal(pkgVersion.version, '1.0.0'); // another version - await packageManagerService.publish({ - dist: { - content: Buffer.alloc(0), + await packageManagerService.publish( + { + dist: { + content: Buffer.alloc(0), + }, + tags: [''], + scope: '', + name: 'foo', + description: 'foo description new', + packageJson: { name: 'foo', test: 'test', version: '1.0.0' }, + readme: '', + version: '1.0.1', + isPrivate: true, }, - tags: [ '' ], - scope: '', - name: 'foo', - description: 'foo description new', - packageJson: { name: 'foo', test: 'test', version: '1.0.0' }, - readme: '', - version: '1.0.1', - isPrivate: true, - }, publisher); - pkgVersion = await packageRepository.findPackageVersion(packageId, '1.0.1'); + publisher + ); + pkgVersion = await packageRepository.findPackageVersion( + packageId, + '1.0.1' + ); assert(pkgVersion); assert.equal(pkgVersion.version, '1.0.1'); // expect aop async timer @@ -75,20 +87,26 @@ describe('test/core/service/PackageManagerService/publish.test.ts', () => { it('should work slice long description', async () => { app.mockLog(); - const { packageId } = await packageManagerService.publish({ - dist: { - content: Buffer.alloc(0), + const { packageId } = await packageManagerService.publish( + { + dist: { + content: Buffer.alloc(0), + }, + tags: [''], + scope: '', + name: 'foo', + description: '~'.repeat(1100 * 100), + packageJson: await TestUtil.getFullPackage({ name: 'foo' }), + readme: '', + version: '1.0.0', + isPrivate: true, }, - tags: [ '' ], - scope: '', - name: 'foo', - description: '~'.repeat(1100 * 100), - packageJson: await TestUtil.getFullPackage({ name: 'foo' }), - readme: '', - version: '1.0.0', - isPrivate: true, - }, publisher); - const pkgVersion = await packageRepository.findPackageVersion(packageId, '1.0.0'); + publisher + ); + const pkgVersion = await packageRepository.findPackageVersion( + packageId, + '1.0.0' + ); assert(pkgVersion); assert.equal(pkgVersion.version, '1.0.0'); const pkg = await packageRepository.findPackage('', 'foo'); @@ -96,20 +114,28 @@ describe('test/core/service/PackageManagerService/publish.test.ts', () => { }); it('should work with dist.localFile', async () => { - const { packageId } = await packageManagerService.publish({ - dist: { - localFile: TestUtil.getFixtures('registry.npmjs.org/pedding/-/pedding-1.1.0.tgz'), + const { packageId } = await packageManagerService.publish( + { + dist: { + localFile: TestUtil.getFixtures( + 'registry.npmjs.org/pedding/-/pedding-1.1.0.tgz' + ), + }, + tags: [''], + scope: '', + name: 'pedding', + description: 'pedding description', + packageJson: { name: 'pedding', test: 'test', version: '1.1.0' }, + readme: '', + version: '1.1.0', + isPrivate: false, }, - tags: [ '' ], - scope: '', - name: 'pedding', - description: 'pedding description', - packageJson: { name: 'pedding', test: 'test', version: '1.1.0' }, - readme: '', - version: '1.1.0', - isPrivate: false, - }, publisher); - const pkgVersion = await packageRepository.findPackageVersion(packageId, '1.1.0'); + publisher + ); + const pkgVersion = await packageRepository.findPackageVersion( + packageId, + '1.1.0' + ); assert(pkgVersion); assert.equal(pkgVersion.version, '1.1.0'); assert.equal(pkgVersion.tarDist.size, 2672); @@ -121,19 +147,29 @@ describe('test/core/service/PackageManagerService/publish.test.ts', () => { await assert.rejects(async () => { checked = true; - await packageManagerService.publish({ - dist: { - localFile: TestUtil.getFixtures('registry.npmjs.org/pedding/-/pedding-1.1.0.tgz'), + await packageManagerService.publish( + { + dist: { + localFile: TestUtil.getFixtures( + 'registry.npmjs.org/pedding/-/pedding-1.1.0.tgz' + ), + }, + tags: [''], + scope: '', + name: 'pedding', + description: 'pedding description', + packageJson: { + name: 'pedding', + test: 'test', + version: '1.1.0', + dependencies: { 'invalid-pkg': 'some-semver-not-exits' }, + }, + readme: '', + version: '1.1.0', + isPrivate: false, }, - tags: [ '' ], - scope: '', - name: 'pedding', - description: 'pedding description', - packageJson: { name: 'pedding', test: 'test', version: '1.1.0', dependencies: { 'invalid-pkg': 'some-semver-not-exits' } }, - readme: '', - version: '1.1.0', - isPrivate: false, - }, publisher); + publisher + ); }, /deps invalid-pkg@some-semver-not-exits not found/); assert(checked); diff --git a/test/core/service/PackageSyncerService/createTask.test.ts b/test/core/service/PackageSyncerService/createTask.test.ts index 4c79292e..fb9a4b54 100644 --- a/test/core/service/PackageSyncerService/createTask.test.ts +++ b/test/core/service/PackageSyncerService/createTask.test.ts @@ -4,7 +4,7 @@ import { app, mock } from '@eggjs/mock/bootstrap'; import { TestUtil } from '../../../../test/TestUtil.js'; import { PackageSyncerService } from '../../../../app/core/service/PackageSyncerService.js'; -import { Task } from '../../../../app/core/entity/Task.js'; +import type { Task } from '../../../../app/core/entity/Task.js'; import { TaskState } from '../../../../app/common/enum/Task.js'; import { TaskRepository } from '../../../../app/repository/TaskRepository.js'; import { TaskService } from '../../../../app/core/service/TaskService.js'; @@ -21,13 +21,16 @@ describe('test/core/service/PackageSyncerService/createTask.test.ts', () => { taskRepository = await app.getEggObject(TaskRepository); taskService = await app.getEggObject(TaskService); - await TestUtil.createPackage({ - name: pkgName, - registryId: 'mock_registry_id', - isPrivate: false, - }, { - name: username, - }); + await TestUtil.createPackage( + { + name: pkgName, + registryId: 'mock_registry_id', + isPrivate: false, + }, + { + name: username, + } + ); }); it('should ignore if registryId not same', async () => { @@ -40,12 +43,15 @@ describe('test/core/service/PackageSyncerService/createTask.test.ts', () => { it('should work when registryId is null', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - await TestUtil.createPackage({ - name: 'binary-mirror-config', - isPrivate: false, - }, { - name: username, - }); + await TestUtil.createPackage( + { + name: 'binary-mirror-config', + isPrivate: false, + }, + { + name: username, + } + ); const task = await packageSyncerService.createTask('binary-mirror-config', { registryId: 'sync_registry_id', @@ -54,9 +60,12 @@ describe('test/core/service/PackageSyncerService/createTask.test.ts', () => { }); it('should work when pkg not exists', async () => { - const task = await packageSyncerService.createTask('binary-mirror-config-not-exists', { - registryId: 'sync_registry_id', - }); + const task = await packageSyncerService.createTask( + 'binary-mirror-config-not-exists', + { + registryId: 'sync_registry_id', + } + ); assert(task); }); @@ -68,17 +77,24 @@ describe('test/core/service/PackageSyncerService/createTask.test.ts', () => { await taskService.finishTask(task, TaskState.Success); }); const task = await packageSyncerService.createTask(pkgName); - const res = await Promise.all([ packageSyncerService.executeTask(task), (async () => { - await setTimeout(1); - return await packageSyncerService.createTask(pkgName); - })() ]); + const res = await Promise.all([ + packageSyncerService.executeTask(task), + (async () => { + await setTimeout(1); + return await packageSyncerService.createTask(pkgName); + })(), + ]); assert(res[1].taskId === task.taskId); }); it('should append specific version to waiting task.', async () => { const name = '@cnpmcore/test-sync-package-has-two-versions'; - await packageSyncerService.createTask(name, { specificVersions: [ '1.0.0' ] }); - await packageSyncerService.createTask(name, { specificVersions: [ '2.0.0' ] }); + await packageSyncerService.createTask(name, { + specificVersions: ['1.0.0'], + }); + await packageSyncerService.createTask(name, { + specificVersions: ['2.0.0'], + }); const task = await packageSyncerService.findExecuteTask(); assert(task); assert.equal(task.targetName, name); @@ -88,7 +104,9 @@ describe('test/core/service/PackageSyncerService/createTask.test.ts', () => { it('should remove specific version, switch waiting task to sync all versions.', async () => { const name = '@cnpmcore/test-sync-package-has-two-versions'; - await packageSyncerService.createTask(name, { specificVersions: [ '1.0.0' ] }); + await packageSyncerService.createTask(name, { + specificVersions: ['1.0.0'], + }); await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); assert(task); diff --git a/test/core/service/PackageSyncerService/executeTask.test.ts b/test/core/service/PackageSyncerService/executeTask.test.ts index 96461246..d9f6e99d 100644 --- a/test/core/service/PackageSyncerService/executeTask.test.ts +++ b/test/core/service/PackageSyncerService/executeTask.test.ts @@ -13,7 +13,7 @@ import { NFSAdapter } from '../../../../app/common/adapter/NFSAdapter.js'; import { getScopeAndName } from '../../../../app/common/PackageUtil.js'; import { PackageRepository } from '../../../../app/repository/PackageRepository.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; -import { Registry } from '../../../../app/core/entity/Registry.js'; +import type { Registry } from '../../../../app/core/entity/Registry.js'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; import { TaskService } from '../../../../app/core/service/TaskService.js'; import { ScopeManagerService } from '../../../../app/core/service/ScopeManagerService.js'; @@ -52,19 +52,33 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { persist: false, repeats: 2, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.1.0.tgz' + ), + persist: false, + } + ); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.1.0.tgz'), - persist: false, - }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - assert(!await TaskModel.findOne({ taskId: task.taskId })); + assert(!(await TaskModel.findOne({ taskId: task.taskId }))); assert(await HistoryTaskModel.findOne({ taskId: task.taskId })); const stream = await packageSyncerService.findTaskLog(task); assert(stream); @@ -79,11 +93,18 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - const manifests = await packageManagerService.listPackageFullManifests('', 'foobar'); + const manifests = await packageManagerService.listPackageFullManifests( + '', + 'foobar' + ); // console.log(JSON.stringify(manifests, null, 2)); // should have 2 maintainers assert(manifests.data!.maintainers.length >= 1); - const abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', 'foobar'); + const abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests( + '', + 'foobar' + ); // console.log(JSON.stringify(abbreviatedManifests, null, 2)); assert.equal(abbreviatedManifests.data!.name, manifests.data!.name); app.mockAgent().assertNoPendingInterceptors(); @@ -95,22 +116,39 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { persist: false, repeats: 2, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - repeats: 2, + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + repeats: 2, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.1.0.tgz' + ), + persist: false, + repeats: 2, + } + ); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.1.0.tgz'), - persist: false, - repeats: 2, - }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - await packageSyncerService.createTask('foobar', { forceSyncHistory: true, skipDependencies: true }); + await packageSyncerService.createTask('foobar', { + forceSyncHistory: true, + skipDependencies: true, + }); task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); @@ -128,24 +166,40 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar.json'), persist: false, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.1.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.1.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'taskQueueHighWaterSize', 2); - await packageSyncerService.createTask('foobar', { skipDependencies: false }); + await packageSyncerService.createTask('foobar', { + skipDependencies: false, + }); await packageSyncerService.createTask('foo', { skipDependencies: false }); await packageSyncerService.createTask('bar', { skipDependencies: false }); - await packageSyncerService.createTask('foobarfoo', { skipDependencies: false }); + await packageSyncerService.createTask('foobarfoo', { + skipDependencies: false, + }); const task = await packageSyncerService.findExecuteTask(); assert(task); assert.equal(task.targetName, 'foobar'); await packageSyncerService.executeTask(task); - assert(!await TaskModel.findOne({ taskId: task.taskId })); + assert(!(await TaskModel.findOne({ taskId: task.taskId }))); assert(await HistoryTaskModel.findOne({ taskId: task.taskId })); const stream = await packageSyncerService.findTaskLog(task); assert(stream); @@ -159,25 +213,42 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should execute @node-rs/xxhash task, contains optionalDependencies', async () => { - app.mockHttpclient('https://registry.npmjs.org/%40node-rs%2Fxxhash', 'GET', { - data: '{"_id":"@node-rs/xxhash","_rev":"12-c16041461d207648f8785b03ff497026","name":"@node-rs/xxhash","dist-tags":{"latest":"1.2.1","depracated":"1.1.2"},"versions":{"1.0.0":{"name":"@node-rs/xxhash","version":"1.0.0","description":"Fastest xxhash implementation in Node.js","keywords":["hash","xxhash","xxhashjs","Rust","node-rs","napi","napi-rs","N-API","Node-API"],"author":{"name":"LongYinan","email":"lynweklm@gmail.com"},"homepage":"https://github.com/napi-rs/node-rs","license":"MIT","main":"index.js","typings":"index.d.ts","napi":{"name":"xxhash","triples":{"defaults":true,"additional":["i686-pc-windows-msvc","x86_64-unknown-linux-musl","aarch64-unknown-linux-gnu","armv7-unknown-linux-gnueabihf","aarch64-apple-darwin","aarch64-linux-android","x86_64-unknown-freebsd","aarch64-unknown-linux-musl","aarch64-pc-windows-msvc"]}},"engines":{"node":">= 12"},"publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"repository":{"type":"git","url":"git+https://github.com/napi-rs/node-rs.git"},"scripts":{"artifacts":"napi artifacts -d ../../artifacts","bench":"cross-env NODE_ENV=production node benchmark/xxhash.js","build":"napi build --platform --release","build:debug":"napi build --platform","prepublishOnly":"napi prepublish","version":"napi version"},"dependencies":{"@node-rs/helper":"^1.2.1","@node-rs/xxhash-win32-x64-msvc":"1.0.0","@node-rs/xxhash-darwin-x64":"1.0.0","@node-rs/xxhash-linux-x64-gnu":"1.0.0","@node-rs/xxhash-win32-ia32-msvc":"1.0.0","@node-rs/xxhash-linux-x64-musl":"1.0.0","@node-rs/xxhash-linux-arm64-gnu":"1.0.0","@node-rs/xxhash-linux-arm-gnueabihf":"1.0.0","@node-rs/xxhash-darwin-arm64":"1.0.0","@node-rs/xxhash-android-arm64":"1.0.0","@node-rs/xxhash-freebsd-x64":"1.0.0","@node-rs/xxhash-linux-arm64-musl":"1.0.0","@node-rs/xxhash-win32-arm64-msvc":"1.0.0"},"devDependencies":{"@types/xxhashjs":"^0.2.2","webpack":"^5.59.1","xxhash":"^0.3.0","xxhashjs":"^0.2.2"},"funding":{"type":"github","url":"https://github.com/sponsors/Brooooooklyn"},"gitHead":"dd157413b2c918c5d29c0d47071606bfabbddb64","optionalDependencies":{"@node-rs/xxhash-win32-x64-msvc":"1.0.0","@node-rs/xxhash-darwin-x64":"1.0.0","@node-rs/xxhash-linux-x64-gnu":"1.0.0","@node-rs/xxhash-win32-ia32-msvc":"1.0.0","@node-rs/xxhash-linux-x64-musl":"1.0.0","@node-rs/xxhash-linux-arm64-gnu":"1.0.0","@node-rs/xxhash-linux-arm-gnueabihf":"1.0.0","@node-rs/xxhash-darwin-arm64":"1.0.0","@node-rs/xxhash-android-arm64":"1.0.0","@node-rs/xxhash-freebsd-x64":"1.0.0","@node-rs/xxhash-linux-arm64-musl":"1.0.0","@node-rs/xxhash-win32-arm64-msvc":"1.0.0"},"_id":"@node-rs/xxhash@1.0.0","_nodeVersion":"14.18.1","_npmVersion":"lerna/4.0.0/node@v14.18.1+x64 (linux)","dist":{"integrity":"sha512-wVhbJT3QumfE7zlMLAZoAllaUufN5r3ia8vatKaqcG/Bau9SdFmcZpo8IuWDfSX+Jqyh9dViSRpUYChrVUvyFw==","shasum":"8c9a8c3be47b82de1a5cb42b2d38f652520bf73c","tarball":"https://registry.npmjs.org/@node-rs/xxhash/-/xxhash-1.0.0.tgz","fileCount":5,"unpackedSize":10143,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD+Dksaw6q6YpTDITfpfAwI15qCuQT84NvQKOpMqq4ZdQIgHvVlZ7+kJk4gzGM7hD10eILXlpOfqQa6Z6NZtcOvX+M="}]},"_npmUser":{"name":"broooooklyn","email":"lynweklm@gmail.com"},"directories":{},"maintainers":[{"name":"broooooklyn","email":"lynweklm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/xxhash_1.0.0_1634889276349_0.690262990489863"},"_hasShrinkwrap":false}},"time":{"created":"2021-10-22T07:54:36.303Z","1.0.0":"2021-10-22T07:54:36.493Z","modified":"2022-05-14T10:40:20.461Z"},"maintainers":[{"name":"dxd_sjtu","email":"dxd_sjtu@outlook.com"},{"name":"broooooklyn","email":"lynweklm@gmail.com"}],"description":"Fastest xxhash implementation in Node.js","homepage":"https://github.com/napi-rs/node-rs","keywords":["hash","xxhash","xxhashjs","Rust","node-rs","napi","napi-rs","N-API","Node-API"],"repository":{"type":"git","url":"git+https://github.com/napi-rs/node-rs.git"},"author":{"name":"LongYinan","email":"lynweklm@gmail.com"},"bugs":{"url":"https://github.com/napi-rs/node-rs/issues"},"license":"MIT","readme":"mock readme","readmeFilename":"README.md"}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@node-rs/xxhash/-/xxhash-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40node-rs%2Fxxhash', + 'GET', + { + data: '{"_id":"@node-rs/xxhash","_rev":"12-c16041461d207648f8785b03ff497026","name":"@node-rs/xxhash","dist-tags":{"latest":"1.2.1","depracated":"1.1.2"},"versions":{"1.0.0":{"name":"@node-rs/xxhash","version":"1.0.0","description":"Fastest xxhash implementation in Node.js","keywords":["hash","xxhash","xxhashjs","Rust","node-rs","napi","napi-rs","N-API","Node-API"],"author":{"name":"LongYinan","email":"lynweklm@gmail.com"},"homepage":"https://github.com/napi-rs/node-rs","license":"MIT","main":"index.js","typings":"index.d.ts","napi":{"name":"xxhash","triples":{"defaults":true,"additional":["i686-pc-windows-msvc","x86_64-unknown-linux-musl","aarch64-unknown-linux-gnu","armv7-unknown-linux-gnueabihf","aarch64-apple-darwin","aarch64-linux-android","x86_64-unknown-freebsd","aarch64-unknown-linux-musl","aarch64-pc-windows-msvc"]}},"engines":{"node":">= 12"},"publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"repository":{"type":"git","url":"git+https://github.com/napi-rs/node-rs.git"},"scripts":{"artifacts":"napi artifacts -d ../../artifacts","bench":"cross-env NODE_ENV=production node benchmark/xxhash.js","build":"napi build --platform --release","build:debug":"napi build --platform","prepublishOnly":"napi prepublish","version":"napi version"},"dependencies":{"@node-rs/helper":"^1.2.1","@node-rs/xxhash-win32-x64-msvc":"1.0.0","@node-rs/xxhash-darwin-x64":"1.0.0","@node-rs/xxhash-linux-x64-gnu":"1.0.0","@node-rs/xxhash-win32-ia32-msvc":"1.0.0","@node-rs/xxhash-linux-x64-musl":"1.0.0","@node-rs/xxhash-linux-arm64-gnu":"1.0.0","@node-rs/xxhash-linux-arm-gnueabihf":"1.0.0","@node-rs/xxhash-darwin-arm64":"1.0.0","@node-rs/xxhash-android-arm64":"1.0.0","@node-rs/xxhash-freebsd-x64":"1.0.0","@node-rs/xxhash-linux-arm64-musl":"1.0.0","@node-rs/xxhash-win32-arm64-msvc":"1.0.0"},"devDependencies":{"@types/xxhashjs":"^0.2.2","webpack":"^5.59.1","xxhash":"^0.3.0","xxhashjs":"^0.2.2"},"funding":{"type":"github","url":"https://github.com/sponsors/Brooooooklyn"},"gitHead":"dd157413b2c918c5d29c0d47071606bfabbddb64","optionalDependencies":{"@node-rs/xxhash-win32-x64-msvc":"1.0.0","@node-rs/xxhash-darwin-x64":"1.0.0","@node-rs/xxhash-linux-x64-gnu":"1.0.0","@node-rs/xxhash-win32-ia32-msvc":"1.0.0","@node-rs/xxhash-linux-x64-musl":"1.0.0","@node-rs/xxhash-linux-arm64-gnu":"1.0.0","@node-rs/xxhash-linux-arm-gnueabihf":"1.0.0","@node-rs/xxhash-darwin-arm64":"1.0.0","@node-rs/xxhash-android-arm64":"1.0.0","@node-rs/xxhash-freebsd-x64":"1.0.0","@node-rs/xxhash-linux-arm64-musl":"1.0.0","@node-rs/xxhash-win32-arm64-msvc":"1.0.0"},"_id":"@node-rs/xxhash@1.0.0","_nodeVersion":"14.18.1","_npmVersion":"lerna/4.0.0/node@v14.18.1+x64 (linux)","dist":{"integrity":"sha512-wVhbJT3QumfE7zlMLAZoAllaUufN5r3ia8vatKaqcG/Bau9SdFmcZpo8IuWDfSX+Jqyh9dViSRpUYChrVUvyFw==","shasum":"8c9a8c3be47b82de1a5cb42b2d38f652520bf73c","tarball":"https://registry.npmjs.org/@node-rs/xxhash/-/xxhash-1.0.0.tgz","fileCount":5,"unpackedSize":10143,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD+Dksaw6q6YpTDITfpfAwI15qCuQT84NvQKOpMqq4ZdQIgHvVlZ7+kJk4gzGM7hD10eILXlpOfqQa6Z6NZtcOvX+M="}]},"_npmUser":{"name":"broooooklyn","email":"lynweklm@gmail.com"},"directories":{},"maintainers":[{"name":"broooooklyn","email":"lynweklm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/xxhash_1.0.0_1634889276349_0.690262990489863"},"_hasShrinkwrap":false}},"time":{"created":"2021-10-22T07:54:36.303Z","1.0.0":"2021-10-22T07:54:36.493Z","modified":"2022-05-14T10:40:20.461Z"},"maintainers":[{"name":"dxd_sjtu","email":"dxd_sjtu@outlook.com"},{"name":"broooooklyn","email":"lynweklm@gmail.com"}],"description":"Fastest xxhash implementation in Node.js","homepage":"https://github.com/napi-rs/node-rs","keywords":["hash","xxhash","xxhashjs","Rust","node-rs","napi","napi-rs","N-API","Node-API"],"repository":{"type":"git","url":"git+https://github.com/napi-rs/node-rs.git"},"author":{"name":"LongYinan","email":"lynweklm@gmail.com"},"bugs":{"url":"https://github.com/napi-rs/node-rs/issues"},"license":"MIT","readme":"mock readme","readmeFilename":"README.md"}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@node-rs/xxhash/-/xxhash-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); await packageSyncerService.createTask('@node-rs/xxhash'); const task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - assert(!await TaskModel.findOne({ taskId: task.taskId })); + assert(!(await TaskModel.findOne({ taskId: task.taskId }))); assert(await HistoryTaskModel.findOne({ taskId: task.taskId })); - const manifests = await packageManagerService.listPackageFullManifests('@node-rs', 'xxhash'); + const manifests = await packageManagerService.listPackageFullManifests( + '@node-rs', + 'xxhash' + ); // console.log(JSON.stringify(manifests, null, 2)); // assert.equal(manifests.data.maintainers.length, 2); - const abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('@node-rs', 'xxhash'); + const abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests( + '@node-rs', + 'xxhash' + ); // console.log(JSON.stringify(abbreviatedManifests, null, 2)); assert.equal(abbreviatedManifests.data!.name, manifests.data!.name); assert(abbreviatedManifests.data!.versions['1.0.0']); @@ -186,36 +257,65 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should sync cnpmcore-test-sync-deprecated and mock 451', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - let manifests = await packageManagerService.listPackageFullManifests('', name); + let manifests = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(manifests.data); assert(manifests!.data!.versions['0.0.0']); - assert.equal(manifests.data.versions['0.0.0'].deprecated, 'only test for cnpmcore'); + assert.equal( + manifests.data.versions['0.0.0'].deprecated, + 'only test for cnpmcore' + ); assert.equal(manifests.data.versions['0.0.0']._hasShrinkwrap, false); - let abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); - assert.equal(abbreviatedManifests!.data!.versions['0.0.0']!.deprecated, 'only test for cnpmcore'); - assert.equal(abbreviatedManifests!.data!.versions['0.0.0']!._hasShrinkwrap, false); + let abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); + assert.equal( + abbreviatedManifests!.data!.versions['0.0.0']!.deprecated, + 'only test for cnpmcore' + ); + assert.equal( + abbreviatedManifests!.data!.versions['0.0.0']!._hasShrinkwrap, + false + ); app.mockAgent().assertNoPendingInterceptors(); // mock 451 and unpublished - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - status: 451, - data: '{"error":"Not found"}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + status: 451, + data: '{"error":"Not found"}', + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -224,24 +324,42 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); let log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes(`] 🟢 Package "${name}" was removed in remote registry`)); + assert( + log.includes(`] 🟢 Package "${name}" was removed in remote registry`) + ); - manifests = await packageManagerService.listPackageFullManifests('', name); + manifests = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(manifests.data); assert(manifests.data.time.unpublished); - abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); + abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); assert(abbreviatedManifests.data!.time!.unpublished); app.mockAgent().assertNoPendingInterceptors(); // sync again - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -250,48 +368,90 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - manifests = await packageManagerService.listPackageFullManifests('', name); + manifests = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(!manifests.data!.time.unpublished); - assert.equal(manifests.data!.versions['0.0.0']!.deprecated, 'only test for cnpmcore'); + assert.equal( + manifests.data!.versions['0.0.0']!.deprecated, + 'only test for cnpmcore' + ); assert.equal(manifests.data!.versions['0.0.0']!._hasShrinkwrap, false); - abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); + abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); assert(!abbreviatedManifests.data!.time?.unpublished); - assert.equal(abbreviatedManifests.data!.versions['0.0.0']!.deprecated, 'only test for cnpmcore'); - assert.equal(abbreviatedManifests.data!.versions['0.0.0']!._hasShrinkwrap, false); + assert.equal( + abbreviatedManifests.data!.versions['0.0.0']!.deprecated, + 'only test for cnpmcore' + ); + assert.equal( + abbreviatedManifests.data!.versions['0.0.0']!._hasShrinkwrap, + false + ); app.mockAgent().assertNoPendingInterceptors(); }); it('should sync cnpmcore-test-sync-deprecated and ignore 404 in removed', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - const manifests = await packageManagerService.listPackageFullManifests('', name); + const manifests = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(manifests.data); assert(manifests!.data!.versions['0.0.0']); - assert.equal(manifests.data.versions['0.0.0'].deprecated, 'only test for cnpmcore'); + assert.equal( + manifests.data.versions['0.0.0'].deprecated, + 'only test for cnpmcore' + ); assert.equal(manifests.data.versions['0.0.0']._hasShrinkwrap, false); - const abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); - assert.equal(abbreviatedManifests!.data!.versions['0.0.0']!.deprecated, 'only test for cnpmcore'); - assert.equal(abbreviatedManifests!.data!.versions['0.0.0']!._hasShrinkwrap, false); + const abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); + assert.equal( + abbreviatedManifests!.data!.versions['0.0.0']!.deprecated, + 'only test for cnpmcore' + ); + assert.equal( + abbreviatedManifests!.data!.versions['0.0.0']!._hasShrinkwrap, + false + ); app.mockAgent().assertNoPendingInterceptors(); // mock 404 and no unpublished - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - status: 404, - data: '{"error":"Not found"}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + status: 404, + data: '{"error":"Not found"}', + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -300,17 +460,23 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(!log.includes(`] 🟢 Package "${name}" was removed in remote registry`)); + assert( + !log.includes(`] 🟢 Package "${name}" was removed in remote registry`) + ); assert(log.includes('Package not found, status 404')); app.mockAgent().assertNoPendingInterceptors(); }); it('should sync fail when package not exists', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-package-not-exists', 'GET', { - status: 404, - data: '{"error":"Not found"}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-package-not-exists', + 'GET', + { + status: 404, + data: '{"error":"Not found"}', + persist: false, + } + ); const name = 'cnpmcore-test-sync-package-not-exists'; await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); @@ -325,14 +491,26 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should ignore PositionNotEqualToLength error', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org//foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org//foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const err = { name: 'PositionNotEqualToLengthError', message: 'Position is not equal to file length', @@ -354,14 +532,26 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should ignore ObjectNotAppendable error', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const err = { name: 'ObjectNotAppendableError', message: 'The object is not appendable', @@ -383,14 +573,26 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should sync cnpmcore-test-sync-dependencies => cnpmcore-test-sync-deprecated', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-dependencies', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-dependencies.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-dependencies/-/cnpmcore-test-sync-dependencies-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-dependencies', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-dependencies.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-dependencies/-/cnpmcore-test-sync-dependencies-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); let name = 'cnpmcore-test-sync-dependencies'; await packageSyncerService.createTask(name); let task = await packageSyncerService.findExecuteTask(); @@ -401,17 +603,33 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); let log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 📦 Add dependency "cnpmcore-test-sync-deprecated" sync task: ')); + assert( + log.includes( + '] 📦 Add dependency "cnpmcore-test-sync-deprecated" sync task: ' + ) + ); // will sync cnpmcore-test-sync-deprecated - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); @@ -422,7 +640,11 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('Sync cause by "cnpmcore-test-sync-dependencies" dependencies, parent task: ')); + assert( + log.includes( + 'Sync cause by "cnpmcore-test-sync-dependencies" dependencies, parent task: ' + ) + ); app.mockAgent().assertNoPendingInterceptors(); }); @@ -430,14 +652,26 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { let name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-dependencies', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-dependencies.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-dependencies/-/cnpmcore-test-sync-dependencies-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-dependencies', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-dependencies.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-dependencies/-/cnpmcore-test-sync-dependencies-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); name = 'cnpmcore-test-sync-dependencies'; const task = await packageSyncerService.createTask(name); assert(task); @@ -447,8 +681,16 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(!log.includes('] 📦 Add dependency "cnpmcore-test-sync-deprecated" sync task: ')); - assert(log.includes('] 📖 Has dependency "cnpmcore-test-sync-deprecated" sync task: ')); + assert( + !log.includes( + '] 📦 Add dependency "cnpmcore-test-sync-deprecated" sync task: ' + ) + ); + assert( + log.includes( + '] 📖 Has dependency "cnpmcore-test-sync-deprecated" sync task: ' + ) + ); app.mockAgent().assertNoPendingInterceptors(); }); @@ -456,13 +698,21 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const name = 'resvg-js'; const task = await packageSyncerService.createTask(name); app.mockHttpclient('https://registry.npmjs.org/resvg-js', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/resvg-js.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/resvg-js/-/resvg-js-2.4.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/resvg-js.json' + ), persist: false, }); + app.mockHttpclient( + 'https://registry.npmjs.org/resvg-js/-/resvg-js-2.4.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); assert.equal(task.targetName, name); await packageSyncerService.executeTask(task); app.mockAgent().assertNoPendingInterceptors(); @@ -470,43 +720,70 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 📦 Add dependency "@resvg/resvg-js-win32-x64-msvc" sync task: ')); + assert( + log.includes( + '] 📦 Add dependency "@resvg/resvg-js-win32-x64-msvc" sync task: ' + ) + ); }); it('should bring auth token which in registry database.', async () => { const testToken = 'test-auth-token'; const registry = await registryManagerService.ensureDefaultRegistry(); - await registryManagerService.updateRegistry(registry.registryId, { ...registry, authToken: testToken }); - const fullManifests = await TestUtil.readFixturesFile('registry.npmjs.org/foobar.json'); - const tgzBuffer1_0_0 = await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'); - const tgzBuffer1_1_0 = await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.1.0.tgz'); + await registryManagerService.updateRegistry(registry.registryId, { + ...registry, + authToken: testToken, + }); + const fullManifests = await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar.json' + ); + const tgzBuffer1_0_0 = await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ); + const tgzBuffer1_1_0 = await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.1.0.tgz' + ); let fullManifestsHeader: any; let tgzBuffer1_0_0Header: any; let tgzBuffer1_1_0Header: any; - app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', (_, opts) => { - fullManifestsHeader = opts.headers; - return { - data: fullManifests, - persist: false, - repeats: 2, - }; + app.mockHttpclient( + 'https://registry.npmjs.org/foobar', + 'GET', + (_, opts) => { + fullManifestsHeader = opts.headers; + return { + data: fullManifests, + persist: false, + repeats: 2, + }; + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', + 'GET', + (_, opts) => { + tgzBuffer1_0_0Header = opts.headers; + return { + data: tgzBuffer1_0_0, + persist: false, + }; + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', + 'GET', + (_, opts) => { + tgzBuffer1_1_0Header = opts.headers; + return { + data: tgzBuffer1_1_0, + persist: false, + }; + } + ); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', 'GET', (_, opts) => { - tgzBuffer1_0_0Header = opts.headers; - return { - data: tgzBuffer1_0_0, - persist: false, - }; - }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', 'GET', (_, opts) => { - tgzBuffer1_1_0Header = opts.headers; - return { - data: tgzBuffer1_1_0, - persist: false, - }; - }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); const task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); @@ -516,14 +793,26 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should ignore publish error on sync task', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-deprecated'; mock.error(packageManagerService.constructor.prototype, 'publish'); const task = await packageSyncerService.createTask(name); @@ -534,10 +823,15 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('❌ [1] Synced version 0.0.0 error, publish error: MockError: mm mock error')); + assert( + log.includes( + '❌ [1] Synced version 0.0.0 error, publish error: MockError: mm mock error' + ) + ); assert(log.includes('❌ All versions sync fail, package not exists')); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/-/package/${name}/syncs/${task.taskId}`) .expect(200); assert.equal(res.body.state, 'fail'); @@ -546,14 +840,26 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should ignore when all version publish ForbiddenError', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-deprecated'; const err = new Error('mock ForbiddenError'); err.name = 'ForbiddenError'; @@ -565,21 +871,35 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const stream = await packageSyncerService.findTaskLog(task); assert(stream); const log = await TestUtil.readStreamToLog(stream); - assert(log.includes('🐛 [1] Synced version 0.0.0 already exists, skip publish, try to set in local manifest')); + assert( + log.includes( + '🐛 [1] Synced version 0.0.0 already exists, skip publish, try to set in local manifest' + ) + ); app.mockAgent().assertNoPendingInterceptors(); }); it('should ignore download error error on sync task', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - status: 500, - data: 'mock request error', - persist: false, - repeats: 3, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + status: 500, + data: 'mock request error', + persist: false, + repeats: 3, + } + ); const name = 'cnpmcore-test-sync-deprecated'; const task = await packageSyncerService.createTask(name); assert(task); @@ -590,25 +910,45 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('❌ [1] Synced version 0.0.0 fail, download tarball error: DownloadStatusInvalidError: Download https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz status(500) invalid')); + assert( + log.includes( + '❌ [1] Synced version 0.0.0 fail, download tarball error: DownloadStatusInvalidError: Download https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz status(500) invalid' + ) + ); assert(log.includes('❌ All versions sync fail, package not exists')); }); describe('sync version idempotence', async () => { beforeEach(async () => { - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - repeats: 2, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + repeats: 2, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); }); it('should sync 2 versions package: @cnpmcore/test-sync-package-has-two-versions', async () => { @@ -623,17 +963,26 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); let log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 🟢 Synced updated 2 versions, removed 0 versions')); + assert( + log.includes('] 🟢 Synced updated 2 versions, removed 0 versions') + ); assert(log.includes('] 🚧 Syncing versions 0 => 2')); // mock listPackageFullManifests return only one version // 如果 version publish 同步中断了,没有刷新 manifests,会导致下一次同步重新 version publish,然后报错 // Avoid: Can't modify pre-existing version: 1.0.0 const scopedAndName = getScopeAndName(name); - const manifests = await packageManagerService.listPackageFullManifests(scopedAndName[0], scopedAndName[1]); + const manifests = await packageManagerService.listPackageFullManifests( + scopedAndName[0], + scopedAndName[1] + ); assert(manifests.data); delete manifests.data.versions['1.0.0']; - mock.data(PackageManagerService.prototype, 'listPackageFullManifests', manifests); + mock.data( + PackageManagerService.prototype, + 'listPackageFullManifests', + manifests + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); @@ -644,20 +993,36 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('Synced version 1.0.0 already exists, skip publish, try to set in local manifest')); + assert( + log.includes( + 'Synced version 1.0.0 already exists, skip publish, try to set in local manifest' + ) + ); assert(log.includes('] 🟢 Synced updated')); assert(log.includes('] 🚧 Syncing versions 1 => 2')); app.mockAgent().assertNoPendingInterceptors(); await mock.restore(); - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); - const abbrs = await packageManagerService.listPackageAbbreviatedManifests(scopedAndName[0], scopedAndName[1]); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); + const abbrs = + await packageManagerService.listPackageAbbreviatedManifests( + scopedAndName[0], + scopedAndName[1] + ); assert(abbrs.data); delete abbrs.data.versions['1.0.0']; - mock.data(PackageManagerService.prototype, 'listPackageAbbreviatedManifests', abbrs); + mock.data( + PackageManagerService.prototype, + 'listPackageAbbreviatedManifests', + abbrs + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); @@ -668,7 +1033,11 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 🐛 Remote version 1.0.0 not exists on local abbreviated manifests, need to refresh')); + assert( + log.includes( + '] 🐛 Remote version 1.0.0 not exists on local abbreviated manifests, need to refresh' + ) + ); assert(log.includes('] 🟢 Synced updated')); assert(log.includes('] 🚧 Syncing versions 2 => 2')); app.mockAgent().assertNoPendingInterceptors(); @@ -676,10 +1045,14 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // mock tag on database but not on manifest dist // https://github.com/cnpm/cnpmcore/issues/97 - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); const result = await npmRegistry.getFullManifests(name); const latestVersion = result.data['dist-tags'].latest; result.data['dist-tags'].foo = '2.0.0'; @@ -693,10 +1066,18 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 🚧 Remote tag(foo: 2.0.0) not exists in local dist-tags')); + assert( + log.includes( + '] 🚧 Remote tag(foo: 2.0.0) not exists in local dist-tags' + ) + ); assert(!log.includes('] 🚧 Refreshing manifests to dists ......')); assert(log.includes('] 🚧 Syncing versions 2 => 2')); - assert(log.includes(`] 📖 @cnpmcore/test-sync-package-has-two-versions latest version: ${latestVersion}, published time: ${JSON.stringify(result.data.time[latestVersion])}`)); + assert( + log.includes( + `] 📖 @cnpmcore/test-sync-package-has-two-versions latest version: ${latestVersion}, published time: ${JSON.stringify(result.data.time[latestVersion])}` + ) + ); app.mockAgent().assertNoPendingInterceptors(); }); @@ -730,7 +1111,10 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { publishTime: new Date(), skipRefreshPackageManifests: false, }; - const pkgVersion = await packageManagerService.publish(publishCmd, user); + const pkgVersion = await packageManagerService.publish( + publishCmd, + user + ); assert(pkgVersion.version === '1.0.0'); const publishCmd2 = { @@ -748,7 +1132,10 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { publishTime: new Date(), skipRefreshPackageManifests: true, }; - const pkgVersion2 = await packageManagerService.publish(publishCmd2, user); + const pkgVersion2 = await packageManagerService.publish( + publishCmd2, + user + ); assert(pkgVersion2.version === '2.0.0'); await packageSyncerService.executeTask(task); @@ -757,13 +1144,20 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('Synced version 2.0.0 already exists, skip publish, try to set in local manifest')); + assert( + log.includes( + 'Synced version 2.0.0 already exists, skip publish, try to set in local manifest' + ) + ); assert(log.includes('] 🚧 Syncing versions 1 => 2')); - const fullManifests = await packageManagerService.listPackageFullManifests('@cnpmcore', 'test-sync-package-has-two-versions'); + const fullManifests = + await packageManagerService.listPackageFullManifests( + '@cnpmcore', + 'test-sync-package-has-two-versions' + ); assert(fullManifests.data); assert(fullManifests.data.versions['2.0.0']); - }); it('should skip self registry', async () => { @@ -792,7 +1186,10 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { publishTime: new Date(), skipRefreshPackageManifests: false, }; - const pkgVersion = await packageManagerService.publish(publishCmd, user); + const pkgVersion = await packageManagerService.publish( + publishCmd, + user + ); assert(pkgVersion.version === '1.0.0'); await packageSyncerService.createTask(name); @@ -804,8 +1201,11 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes(`${name} has been published to the self registry, skip sync ❌❌❌❌❌`)); - + assert( + log.includes( + `${name} has been published to the self registry, skip sync ❌❌❌❌❌` + ) + ); }); it('should updated package manifests when version insert duplicated', async () => { @@ -838,7 +1238,10 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { publishTime: new Date(), skipRefreshPackageManifests: false, }; - const pkgVersion = await packageManagerService.publish(publishCmd, user); + const pkgVersion = await packageManagerService.publish( + publishCmd, + user + ); assert(pkgVersion.version === '1.0.0'); const publishCmd2 = { @@ -856,7 +1259,10 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { publishTime: new Date(), skipRefreshPackageManifests: true, }; - const pkgVersion2 = await packageManagerService.publish(publishCmd2, user); + const pkgVersion2 = await packageManagerService.publish( + publishCmd2, + user + ); assert(pkgVersion2.version === '2.0.0'); // 模拟查询未发现版本重复,写入时异常 @@ -869,9 +1275,12 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('Synced version 2.0.0 already exists, skip publish, try to set in local manifest')); + assert( + log.includes( + 'Synced version 2.0.0 already exists, skip publish, try to set in local manifest' + ) + ); assert(log.includes('] 🚧 Syncing versions 1 => 2')); - }); it('should skip version when insert error', async () => { @@ -903,7 +1312,10 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { publishTime: new Date(), skipRefreshPackageManifests: false, }; - const pkgVersion = await packageManagerService.publish(publishCmd, user); + const pkgVersion = await packageManagerService.publish( + publishCmd, + user + ); assert(pkgVersion.version === '1.0.0'); // 模拟查询未发现版本重复,写入时异常 @@ -920,7 +1332,6 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); assert(log.includes(' Synced updated 0 versions, removed 0 versions')); - }); it('event cork should work', async () => { @@ -931,21 +1342,21 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(task); assert.equal(task.targetName, name); - await packageSyncerService.executeTask(task); const stream = await packageSyncerService.findTaskLog(task); assert(stream); - const finishedTask = await taskService.findTask(task.taskId) as TaskEntity; + const finishedTask = (await taskService.findTask( + task.taskId + )) as TaskEntity; const changes = await changeRepository.query(0, 100); - const [ firstChange ] = changes; + const [firstChange] = changes; const firstChangeDate = new Date(firstChange.createdAt); const taskFinishedDate = new Date(finishedTask!.updatedAt); // 任务结束后一起触发 assert(firstChangeDate.getTime() - taskFinishedDate.getTime() > 0); - }); it('should bulk update maintainer dist', async () => { @@ -957,20 +1368,24 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert.equal(task.targetName, name); let called = 0; - mock(packageManagerService, 'refreshPackageMaintainersToDists', async () => { - called++; - }); + mock( + packageManagerService, + 'refreshPackageMaintainersToDists', + async () => { + called++; + } + ); await packageSyncerService.executeTask(task); const stream = await packageSyncerService.findTaskLog(task); assert(stream); - const finishedTask = await taskService.findTask(task.taskId) as TaskEntity; + const finishedTask = (await taskService.findTask( + task.taskId + )) as TaskEntity; assert.equal(finishedTask.state, TaskState.Success); assert.equal(called, 1); - }); - }); it.skip('should sync missing versions in database', async () => { @@ -985,10 +1400,15 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); let log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 🟢 Synced updated 2 versions, removed 0 versions')); + assert( + log.includes('] 🟢 Synced updated 2 versions, removed 0 versions') + ); assert(log.includes('] 🚧 Syncing versions 0 => 2')); - const pkg = await packageRepository.findPackage('@cnpmcore', 'test-sync-package-has-two-versions'); + const pkg = await packageRepository.findPackage( + '@cnpmcore', + 'test-sync-package-has-two-versions' + ); assert(pkg); await packageRepository.removePackageVersions(pkg.packageId); @@ -1001,25 +1421,43 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(!log.includes('] 🟢 Synced updated 0 versions, removed 0 versions')); + assert( + !log.includes('] 🟢 Synced updated 0 versions, removed 0 versions') + ); assert(log.includes('] 🐛 Remote version 1.0.0 not exists on database')); assert(log.includes('] 🐛 Remote version 2.0.0 not exists on database')); assert(log.includes('] 🚧 Syncing versions 2 => 2')); }); it('should sync removed versions', async () => { - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = '@cnpmcore/test-sync-package-has-two-versions'; await packageSyncerService.createTask(name); let task = await packageSyncerService.findExecuteTask(); @@ -1033,11 +1471,15 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(log.includes('] 🟢 Synced updated 2 versions')); app.mockAgent().assertNoPendingInterceptors(); - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - repeats: 2, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + repeats: 2, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -1049,16 +1491,23 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // console.log(log); assert(log.includes('removed 1 versions')); assert(log.includes('] 🟢 Removed version 1.0.0 success')); - const r = await packageManagerService.listPackageFullManifests('@cnpmcore', 'test-sync-package-has-two-versions'); + const r = await packageManagerService.listPackageFullManifests( + '@cnpmcore', + 'test-sync-package-has-two-versions' + ); assert(Object.keys(r.data!.versions).length === 1); assert(!r.data!.versions['1.0.0'], '1.0.0 should not exists'); }); it('should work on unpublished package', async () => { - app.mockHttpclient('https://registry.npmjs.org/rollup-config-mbp', 'GET', { - data: '{"_id":"rollup-config-mbp","name":"rollup-config-mbp","time":{"created":"2020-09-25T09:18:36.405Z","0.0.1-alpha.1":"2020-09-25T09:18:36.552Z","modified":"2022-01-14T12:34:32.620Z","unpublished":{"time":"2022-01-14T12:34:32.620Z","versions":["0.0.1-alpha.1"]}}}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/rollup-config-mbp', + 'GET', + { + data: '{"_id":"rollup-config-mbp","name":"rollup-config-mbp","time":{"created":"2020-09-25T09:18:36.405Z","0.0.1-alpha.1":"2020-09-25T09:18:36.552Z","modified":"2022-01-14T12:34:32.620Z","unpublished":{"time":"2022-01-14T12:34:32.620Z","versions":["0.0.1-alpha.1"]}}}', + persist: false, + } + ); let name = 'rollup-config-mbp'; // ignore unpublished when local package not exists await packageSyncerService.createTask(name); @@ -1069,19 +1518,33 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); let log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes(`] 🟢 Package "${name}" was removed in remote registry`)); + assert( + log.includes(`] 🟢 Package "${name}" was removed in remote registry`) + ); let data = await packageManagerService.listPackageFullManifests('', name); assert(data.data === null); // sync unpublished - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); @@ -1098,10 +1561,14 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(data.data!.maintainers); app.mockAgent().assertNoPendingInterceptors(); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","name":"cnpmcore-test-sync-deprecated","time":{"created":"2020-09-25T09:18:36.405Z","0.0.1-alpha.1":"2020-09-25T09:18:36.552Z","modified":"2022-01-14T12:34:32.620Z","unpublished":{"time":"2022-01-14T12:34:32.620Z","versions":["0.0.1-alpha.1"]}}}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","name":"cnpmcore-test-sync-deprecated","time":{"created":"2020-09-25T09:18:36.405Z","0.0.1-alpha.1":"2020-09-25T09:18:36.552Z","modified":"2022-01-14T12:34:32.620Z","unpublished":{"time":"2022-01-14T12:34:32.620Z","versions":["0.0.1-alpha.1"]}}}', + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -1110,7 +1577,9 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes(`] 🟢 Package "${name}" was removed in remote registry`)); + assert( + log.includes(`] 🟢 Package "${name}" was removed in remote registry`) + ); data = await packageManagerService.listPackageFullManifests('', name); // console.log(data.data); assert(data.data!.time.unpublished); @@ -1119,16 +1588,30 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should work on mock package.readme is undefined', async () => { - const pkg = await TestUtil.readJSONFile(TestUtil.getFixtures('registry.npmjs.org/cnpmcore-test-sync-dependencies.json')); + const pkg = await TestUtil.readJSONFile( + TestUtil.getFixtures( + 'registry.npmjs.org/cnpmcore-test-sync-dependencies.json' + ) + ); delete pkg.readme; - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-dependencies', 'GET', { - data: JSON.stringify(pkg), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-dependencies/-/cnpmcore-test-sync-dependencies-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-dependencies', + 'GET', + { + data: JSON.stringify(pkg), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-dependencies/-/cnpmcore-test-sync-dependencies-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-dependencies'; await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); @@ -1138,20 +1621,37 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 📦 Add dependency "cnpmcore-test-sync-deprecated" sync task: ')); - const { data } = await packageManagerService.listPackageFullManifests('', name); + assert( + log.includes( + '] 📦 Add dependency "cnpmcore-test-sync-deprecated" sync task: ' + ) + ); + const { data } = await packageManagerService.listPackageFullManifests( + '', + name + ); assert.equal(data!.readme, ''); }); it('should auto sync missing hasInstallScript property', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); // https://github.com/cnpm/cnpm/issues/374 const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); @@ -1166,13 +1666,20 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { let res = await packageManagerService.listPackageFullManifests('', name); assert(res.data); assert(res.data.versions); - assert((res.data as any).versions[res.data['dist-tags'].latest].hasInstallScript === undefined); + assert( + (res.data as any).versions[res.data['dist-tags'].latest] + .hasInstallScript === undefined + ); app.mockAgent().assertNoPendingInterceptors(); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","version":"0.0.0","description":"","main":"index.js","scripts":{"install":"echo 1"},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","version":"0.0.0","description":"","main":"index.js","scripts":{"install":"echo 1"},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -1184,21 +1691,38 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(log.includes('🚧 Syncing versions 1 => 1')); assert(res.data); res = await packageManagerService.listPackageFullManifests('', name); - assert(res.data!.versions?.[res.data!['dist-tags'].latest]?.hasInstallScript === true); - const abbrRes = await packageManagerService.listPackageAbbreviatedManifests('', name); - assert(abbrRes.data!.versions[abbrRes.data!['dist-tags'].latest]?.hasInstallScript === true); + assert( + res.data!.versions?.[res.data!['dist-tags'].latest] + ?.hasInstallScript === true + ); + const abbrRes = + await packageManagerService.listPackageAbbreviatedManifests('', name); + assert( + abbrRes.data!.versions[abbrRes.data!['dist-tags'].latest] + ?.hasInstallScript === true + ); app.mockAgent().assertNoPendingInterceptors(); }); it('should ignore package.version.readme exists', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":"mock readme content","readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":"mock readme content","readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); @@ -1208,21 +1732,38 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('] 🔗 https://registry.npmjs.org/cnpmcore-test-sync-deprecated')); - const { data } = await packageManagerService.listPackageFullManifests('', name); + assert( + log.includes( + '] 🔗 https://registry.npmjs.org/cnpmcore-test-sync-deprecated' + ) + ); + const { data } = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(data!.readme === 'mock readme content'); assert(data!.versions['0.0.0']!.readme === undefined); }); it('should work on mock package.readme is object', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); @@ -1230,20 +1771,33 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { await packageSyncerService.executeTask(task); const stream = await packageSyncerService.findTaskLog(task); assert(stream); - const data = await packageManagerService.listPackageFullManifests('', name); + const data = await packageManagerService.listPackageFullManifests( + '', + name + ); assert.equal(data!.data!.readme, '{"foo":"mock readme is object"}'); app.mockAgent().assertNoPendingInterceptors(); }); it('should sync dist-tags change', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); let task = await packageSyncerService.findExecuteTask(); @@ -1254,15 +1808,23 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { let log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(log.includes('[1] Synced version 0.0.0 success')); - assert(log.includes('🟢 Synced 1 tags: [{"action":"change","tag":"latest","version":"0.0.0"}]')); + assert( + log.includes( + '🟢 Synced 1 tags: [{"action":"change","tag":"latest","version":"0.0.0"}]' + ) + ); let data = await packageManagerService.listPackageFullManifests('', name); assert.deepEqual(data!.data!['dist-tags'], { latest: '0.0.0' }); // update tags, add beta tag - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0","beta":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0","beta":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); @@ -1273,16 +1835,27 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(!log.includes('[1] Synced version 0.0.0 success')); - assert(log.includes('🟢 Synced 1 tags: [{"action":"change","tag":"beta","version":"0.0.0"}]')); + assert( + log.includes( + '🟢 Synced 1 tags: [{"action":"change","tag":"beta","version":"0.0.0"}]' + ) + ); data = await packageManagerService.listPackageFullManifests('', name); assert(data.data); - assert.deepEqual(data.data['dist-tags'], { latest: '0.0.0', beta: '0.0.0' }); + assert.deepEqual(data.data['dist-tags'], { + latest: '0.0.0', + beta: '0.0.0', + }); // all tags exists - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0","beta":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0","beta":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -1294,13 +1867,20 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(!log.includes('[1] Synced version 0.0.0 success')); assert(!log.includes('🟢 Synced 1 tags: ')); data = await packageManagerService.listPackageFullManifests('', name); - assert.deepEqual(data!.data!['dist-tags'], { latest: '0.0.0', beta: '0.0.0' }); + assert.deepEqual(data!.data!['dist-tags'], { + latest: '0.0.0', + beta: '0.0.0', + }); // sync remove beta tags - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqvSEQ9eD3eZ09kfQOKO1j6LnjPeqAfbyYLWlEpxmJHAiAzD+2a4RHF8Vu5N+2wT4kagARnRb47FqpgD08elWVBgA=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cnpmcore-test-sync-deprecated_0.0.0_1639246164624_0.5739637441745657"},"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -1319,20 +1899,38 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { it('should only sync specific version when strictSyncSpecivicVersion is true.', async () => { mock(app.config.cnpmcore, 'strictSyncSpecivicVersion', true); - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = '@cnpmcore/test-sync-package-has-two-versions'; - await packageSyncerService.createTask(name, { specificVersions: [ '1.0.0' ] }); + await packageSyncerService.createTask(name, { + specificVersions: ['1.0.0'], + }); const task = await packageSyncerService.findExecuteTask(); assert(task); assert.equal(task.targetName, name); @@ -1345,20 +1943,38 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should sync specific versions and latest version.', async () => { - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = '@cnpmcore/test-sync-package-has-two-versions'; - await packageSyncerService.createTask(name, { specificVersions: [ '1.0.0' ] }); + await packageSyncerService.createTask(name, { + specificVersions: ['1.0.0'], + }); const task = await packageSyncerService.findExecuteTask(); assert(task); assert.equal(task.targetName, name); @@ -1371,16 +1987,28 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should sync specific versions and latest version.', async () => { - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = '@cnpmcore/test-sync-package-has-two-versions'; - await packageSyncerService.createTask(name, { specificVersions: [ '1.0.0', '9.99.9' ] }); + await packageSyncerService.createTask(name, { + specificVersions: ['1.0.0', '9.99.9'], + }); const task = await packageSyncerService.findExecuteTask(); assert(task); assert.equal(task.targetName, name); @@ -1388,21 +2016,35 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const stream = await packageSyncerService.findTaskLog(task); assert(stream); const log = await TestUtil.readStreamToLog(stream); - assert(log.includes('🚧 Some specific versions are not available: 👉 9.99.9')); + assert( + log.includes('🚧 Some specific versions are not available: 👉 9.99.9') + ); }); it('should not sync nonexistent specific versions in strict mode.', async () => { mock(app.config.cnpmcore, 'strictSyncSpecivicVersion', true); - app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', { - data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', + 'GET', + { + data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = '@cnpmcore/test-sync-package-has-two-versions'; - await packageSyncerService.createTask(name, { specificVersions: [ '1.0.0', '9.99.9' ] }); + await packageSyncerService.createTask(name, { + specificVersions: ['1.0.0', '9.99.9'], + }); const task = await packageSyncerService.findExecuteTask(); assert(task); assert.equal(task.targetName, name); @@ -1410,22 +2052,40 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const stream = await packageSyncerService.findTaskLog(task); assert(stream); const log = await TestUtil.readStreamToLog(stream); - assert(log.includes('🚧 Some specific versions are not available: 👉 9.99.9')); - const manifest = await packageManagerService.listPackageAbbreviatedManifests('@cnpmcore', 'test-sync-package-has-two-versions'); + assert( + log.includes('🚧 Some specific versions are not available: 👉 9.99.9') + ); + const manifest = + await packageManagerService.listPackageAbbreviatedManifests( + '@cnpmcore', + 'test-sync-package-has-two-versions' + ); assert(manifest.data?.['dist-tags']); assert.equal(manifest.data?.['dist-tags'].latest, '1.0.0'); }); // 有任务积压,不一定能够同步完 it.skip('should sync sourceRegistryIsCNpm = true && syncUpstreamFirst = true', async () => { - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', true); @@ -1440,19 +2100,39 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(log.includes(', syncUpstream: true')); - assert(log.includes('][UP] 🚧🚧🚧🚧🚧 Waiting sync "cnpmcore-test-sync-deprecated" task on https://r.cnpmjs.org 🚧')); - assert(log.includes('][UP] 🟢🟢🟢🟢🟢 https://r.cnpmjs.org/cnpmcore-test-sync-deprecated 🟢')); + assert( + log.includes( + '][UP] 🚧🚧🚧🚧🚧 Waiting sync "cnpmcore-test-sync-deprecated" task on https://r.cnpmjs.org 🚧' + ) + ); + assert( + log.includes( + '][UP] 🟢🟢🟢🟢🟢 https://r.cnpmjs.org/cnpmcore-test-sync-deprecated 🟢' + ) + ); }); it('should not sync upstream when task queue too high', async () => { - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', true); @@ -1470,18 +2150,34 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // console.log(log); assert(log.includes(', syncUpstream: false')); assert(log.includes(', taskQueue: 1/1')); - assert(!log.includes('][UP] 🚧🚧🚧🚧🚧 Waiting sync "cnpmcore-test-sync-deprecated" task on https://r.cnpmjs.org 🚧')); + assert( + !log.includes( + '][UP] 🚧🚧🚧🚧🚧 Waiting sync "cnpmcore-test-sync-deprecated" task on https://r.cnpmjs.org 🚧' + ) + ); }); it('should sync sourceRegistryIsCNpm = true && syncUpstreamFirst = false', async () => { - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', false); @@ -1495,19 +2191,35 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(!log.includes('][UP] 🟢🟢🟢🟢🟢 https://r.cnpmjs.org/cnpmcore-test-sync-deprecated 🟢')); + assert( + !log.includes( + '][UP] 🟢🟢🟢🟢🟢 https://r.cnpmjs.org/cnpmcore-test-sync-deprecated 🟢' + ) + ); app.mockAgent().assertNoPendingInterceptors(); }); it('should sync sourceRegistryIsCNpm = true and mock createSyncTask error', async () => { - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', true); @@ -1523,23 +2235,40 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(log.includes('🚮 give up 🚮 ❌❌❌❌❌')); - assert(log.includes(`][UP] ❌ Sync ${name} fail, create sync task error:`)); + assert( + log.includes(`][UP] ❌ Sync ${name} fail, create sync task error:`) + ); app.mockAgent().assertNoPendingInterceptors(); }); it('should sync sourceRegistryIsCNpm = true and mock createSyncTask return missing logId', async () => { - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', true); - mock.data(NPMRegistry.prototype, 'createSyncTask', { data: { ok: true }, res: {} }); + mock.data(NPMRegistry.prototype, 'createSyncTask', { + data: { ok: true }, + res: {}, + }); const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); @@ -1556,21 +2285,37 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should sync sourceRegistryIsCNpm = true and mock getSyncTask syncDone = false', async () => { - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/sync', 'PUT', { - data: { - ok: true, - logId: '633eea1359147b6066fae99f', - }, - persist: false, - }); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/sync', + 'PUT', + { + data: { + ok: true, + logId: '633eea1359147b6066fae99f', + }, + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', true); @@ -1600,29 +2345,49 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should sync sourceRegistryIsCNpm = true and mock sync upstream timeout', async () => { - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/sync', 'PUT', { - data: { - ok: true, - logId: '633eea1359147b6066fae99f', - }, - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/sync/log/633eea1359147b6066fae99f', 'GET', { - data: { - ok: false, - syncDone: false, - log: '', - }, - persist: true, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/sync', + 'PUT', + { + data: { + ok: true, + logId: '633eea1359147b6066fae99f', + }, + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/sync/log/633eea1359147b6066fae99f', + 'GET', + { + data: { + ok: false, + syncDone: false, + log: '', + }, + persist: true, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://r.cnpmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', true); @@ -1685,14 +2450,24 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should try to use latest tag version maintainers instead', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', { - data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated', + 'GET', + { + data: '{"_id":"cnpmcore-test-sync-deprecated","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-sync-deprecated","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-sync-deprecated","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-sync-deprecated@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz","fileCount":1,"unpackedSize":250},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-deprecated/-/cnpmcore-test-sync-deprecated-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); // https://registry.npmjs.org/postman-jsdoc-theme const name = 'cnpmcore-test-sync-deprecated'; await packageSyncerService.createTask(name); @@ -1704,7 +2479,9 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('📖 Use the latest version(0.0.0) maintainers instead')); + assert( + log.includes('📖 Use the latest version(0.0.0) maintainers instead') + ); assert(log.includes('] 🔗')); app.mockAgent().assertNoPendingInterceptors(); }); @@ -1716,16 +2493,25 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { data: '{"_id":"cnpmcore-test-block-from-upstream","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-block-from-upstream","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-block-from-upstream","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-block-from-upstream@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-block-from-upstream/-/cnpmcore-test-block-from-upstream-0.0.0.tgz","fileCount":1,"unpackedSize":250},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}', persist: false, }); - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-block-from-upstream/-/cnpmcore-test-block-from-upstream-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-block-from-upstream/-/cnpmcore-test-block-from-upstream-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); await packageSyncerService.createTask(name); let task = await packageSyncerService.findExecuteTask(); await packageSyncerService.executeTask(task); const pkg = await packageRepository.findPackage('', name); - const pkgVersion = await packageRepository.findPackageVersion(pkg!.packageId, '0.0.0'); + const pkgVersion = await packageRepository.findPackageVersion( + pkg!.packageId, + '0.0.0' + ); assert(pkg?.name === name); assert(pkgVersion); @@ -1745,12 +2531,14 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes(`] 🟢 Package "${name}" was removed in remote registry`)); + assert( + log.includes(`] 🟢 Package "${name}" was removed in remote registry`) + ); }); it('should stop sync by block list', async () => { const name = 'cnpmcore-test-sync-blocklist'; - mock(app.config.cnpmcore, 'syncPackageBlockList', [ name, 'foo' ]); + mock(app.config.cnpmcore, 'syncPackageBlockList', [name, 'foo']); await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); assert(task); @@ -1761,7 +2549,11 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(log.includes(`❌❌❌❌❌ ${name} ❌❌❌❌❌`)); - assert(log.includes('❌ stop sync by block list: ["cnpmcore-test-sync-blocklist","foo"]')); + assert( + log.includes( + '❌ stop sync by block list: ["cnpmcore-test-sync-blocklist","foo"]' + ) + ); }); it('should sync upper case "D" success', async () => { @@ -1770,11 +2562,15 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { persist: false, }); app.mockHttpclient('https://registry.npmjs.org/D/-/D-0.0.1.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), persist: false, }); app.mockHttpclient('https://registry.npmjs.org/D/-/D-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), persist: false, }); const name = 'D'; @@ -1787,16 +2583,21 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('🚧🚧🚧🚧🚧 Syncing from https://registry.npmjs.org/D, ')); + assert( + log.includes('🚧🚧🚧🚧🚧 Syncing from https://registry.npmjs.org/D, ') + ); assert(log.includes('🔗')); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const data = res.body; assert(data.name === name); // assert(data.dist === name); - assert(data.versions[data['dist-tags'].latest].dist.tarball.includes('/D/-/D-')); + assert( + data.versions[data['dist-tags'].latest].dist.tarball.includes('/D/-/D-') + ); app.mockAgent().assertNoPendingInterceptors(); }); @@ -1805,10 +2606,16 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { data: '{"_id":"Buffer","_rev":"5-b918bb11193c501a415c51047d6d68c7","name":"Buffer","description":"API-compatible Node.JS Buffer for Ender.js (browser)","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"author":{"name":"AJ ONeal","email":"coolaj86@gmail.com","url":"http://coolaj86.info"},"name":"Buffer","description":"API-compatible Node.JS Buffer for Ender.js (browser)","version":"0.0.0","repository":{"type":"git","url":"git://github.com/coolaj86/browser-buffer.git"},"main":"index.js","engines":{"node":">= 0.2.0"},"dependencies":{},"devDependencies":{},"_npmJsonOpts":{"file":"/Users/coolaj86/.npm/Buffer/0.0.0/package/package.json","wscript":false,"contributors":false,"serverjs":false},"_id":"Buffer@0.0.0","_engineSupported":true,"_npmVersion":"1.0.15","_nodeVersion":"v0.4.8","_defaultsLoaded":true,"dist":{"shasum":"82cf8e986a2109ff6d1d6f1c436e47d07127aea4","tarball":"https://registry.npmjs.org/Buffer/-/Buffer-0.0.0.tgz","integrity":"sha512-+zdncl8lI5TCkARStn9F1BwcuJYofYmD0oEHe5FNfCvGfeDJwf6+dSikCdQN6BMXXmHMhNNUagBN367WST1AIQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbPvXxxdsc/1aMyduDYXMbRVTFU71ajZ5BztZ3S07ofQIgBy/kpAuBDMIhlfOQW2l2XIq8eMs6BNqjEPXo3CJ5WrE="}]},"scripts":{},"maintainers":[{"name":"coolaj86","email":"coolaj86@gmail.com"}]}},"maintainers":[{"name":"coolaj86","email":"coolaj86@gmail.com"}],"time":{"modified":"2022-06-13T02:13:16.055Z","created":"2011-08-01T20:40:41.355Z","0.0.0":"2011-08-01T20:40:41.710Z"},"author":{"name":"AJ ONeal","email":"coolaj86@gmail.com","url":"http://coolaj86.info"},"repository":{"type":"git","url":"git://github.com/coolaj86/browser-buffer.git"}}', persist: false, }); - app.mockHttpclient('https://registry.npmjs.org/Buffer/-/Buffer-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/Buffer/-/Buffer-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'Buffer'; await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); @@ -1819,27 +2626,48 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('🚧🚧🚧🚧🚧 Syncing from https://registry.npmjs.org/Buffer, ')); + assert( + log.includes( + '🚧🚧🚧🚧🚧 Syncing from https://registry.npmjs.org/Buffer, ' + ) + ); assert(log.includes('🔗')); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const data = res.body; assert(data.name === name); - assert(data.versions[data['dist-tags'].latest].dist.tarball.includes('/Buffer/-/Buffer-')); + assert( + data.versions[data['dist-tags'].latest].dist.tarball.includes( + '/Buffer/-/Buffer-' + ) + ); app.mockAgent().assertNoPendingInterceptors(); }); it('should mock security holding package', async () => { - app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-security-holding-package', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/security-holding-package.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/webpack.js.org/-/webpack.js.org-0.0.1-security.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/cnpmcore-test-sync-security-holding-package', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/security-holding-package.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/webpack.js.org/-/webpack.js.org-0.0.1-security.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'cnpmcore-test-sync-security-holding-package'; await packageSyncerService.createTask(name); const task = await packageSyncerService.findExecuteTask(); @@ -1850,7 +2678,9 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); const log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes(`] 🟢 Package "${name}" was removed in remote registry`)); + assert( + log.includes(`] 🟢 Package "${name}" was removed in remote registry`) + ); }); it('should mock getFullManifests missing tarball error and downloadTarball error', async () => { @@ -1889,7 +2719,9 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(log.includes(`❌❌❌❌❌ ${name} ❌❌❌❌❌`)); - assert(log.includes('Synced version 1.0.0 fail, missing tarball, dist: ')); + assert( + log.includes('Synced version 1.0.0 fail, missing tarball, dist: ') + ); assert(log.includes('❌ All versions sync fail, package not exists')); assert(log.includes('Synced version 2.0.0 fail, download tarball error')); app.mockAgent().assertNoPendingInterceptors(); @@ -1927,28 +2759,56 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // console.log(log); assert(log.includes(`❌❌❌❌❌ ${name} ❌❌❌❌❌`)); assert(log.includes('❌ All versions sync fail, package not exists')); - assert(log.includes('Synced version 2.0.0 fail, download tarball error: DownloadStatusInvalidError: Download http://foo.com/a.tgz status(500) invalid')); + assert( + log.includes( + 'Synced version 2.0.0 fail, download tarball error: DownloadStatusInvalidError: Download http://foo.com/a.tgz status(500) invalid' + ) + ); app.mockAgent().assertNoPendingInterceptors(); }); it('should sync mk2test-module-cnpmsync with different metas', async () => { - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/mk2test-module-cnpmsync.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync/-/mk2test-module-cnpmsync-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync/-/mk2test-module-cnpmsync-3.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/mk2test-module-cnpmsync.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync/-/mk2test-module-cnpmsync-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync/-/mk2test-module-cnpmsync-3.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'mk2test-module-cnpmsync'; mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - await TestUtil.createPackage({ name, version: '2.0.0', isPrivate: false }); - await packageSyncerService.createTask(name, { tips: 'sync test tips here' }); + await TestUtil.createPackage({ + name, + version: '2.0.0', + isPrivate: false, + }); + await packageSyncerService.createTask(name, { + tips: 'sync test tips here', + }); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); @@ -1956,29 +2816,59 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); let log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('Synced version 2.0.0 success, different meta: {"peerDependenciesMeta":{"bufferutil":{"optional":true},"utf-8-validate":{"optional":true}},"os":["linux"],"cpu":["x64"],"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"}}')); - assert(log.includes('Z] 👉👉👉👉👉 Tips: sync test tips here 👈👈👈👈👈')); + assert( + log.includes( + 'Synced version 2.0.0 success, different meta: {"peerDependenciesMeta":{"bufferutil":{"optional":true},"utf-8-validate":{"optional":true}},"os":["linux"],"cpu":["x64"],"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"}}' + ) + ); + assert( + log.includes('Z] 👉👉👉👉👉 Tips: sync test tips here 👈👈👈👈👈') + ); assert(log.includes(', skipDependencies: false')); - let manifests = await packageManagerService.listPackageFullManifests('', name); + let manifests = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(manifests.data?.versions['2.0.0']); - assert.equal(manifests.data.versions['2.0.0'].peerDependenciesMeta?.bufferutil.optional, true); + assert.equal( + manifests.data.versions['2.0.0'].peerDependenciesMeta?.bufferutil + .optional, + true + ); assert.equal(manifests.data.versions['2.0.0'].os?.[0], 'linux'); assert.equal(manifests.data.versions['2.0.0'].cpu?.[0], 'x64'); // publishTime assert.equal(manifests.data.time['1.0.0'], '2021-09-27T08:10:48.747Z'); - let abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); + let abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); // console.log(JSON.stringify(abbreviatedManifests.data, null, 2)); assert(abbreviatedManifests.data?.versions['2.0.0']); - assert.equal(abbreviatedManifests.data!.versions['2.0.0'].peerDependenciesMeta?.bufferutil.optional, true); - assert.equal(abbreviatedManifests.data!.versions['2.0.0'].os?.[0], 'linux'); - assert.equal(abbreviatedManifests.data!.versions['2.0.0'].cpu?.[0], 'x64'); + assert.equal( + abbreviatedManifests.data!.versions['2.0.0'].peerDependenciesMeta + ?.bufferutil.optional, + true + ); + assert.equal( + abbreviatedManifests.data!.versions['2.0.0'].os?.[0], + 'linux' + ); + assert.equal( + abbreviatedManifests.data!.versions['2.0.0'].cpu?.[0], + 'x64' + ); app.mockAgent().assertNoPendingInterceptors(); // again should skip sync different metas - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/mk2test-module-cnpmsync.json'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/mk2test-module-cnpmsync.json' + ), + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -1991,12 +2881,22 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { app.mockAgent().assertNoPendingInterceptors(); // should delete readme - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/mk2test-module-cnpmsync.json'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/mk2test-module-cnpmsync.json' + ), + persist: false, + } + ); manifests.data.versions['2.0.0'].readme = 'mock version readme content'; - mock.data(PackageManagerService.prototype, 'listPackageFullManifests', manifests); + mock.data( + PackageManagerService.prototype, + 'listPackageFullManifests', + manifests + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -2005,27 +2905,53 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('🟢 Synced version 2.0.0 success, different meta: {}, delete exists readme')); + assert( + log.includes( + '🟢 Synced version 2.0.0 success, different meta: {}, delete exists readme' + ) + ); app.mockAgent().assertNoPendingInterceptors(); await mock.restore(); - manifests = await packageManagerService.listPackageFullManifests('', name); + manifests = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(manifests.data?.versions['2.0.0']); assert(manifests.data.versions['2.0.0'].readme === undefined); // should sync missing cpu on abbreviated manifests const pkg = await packageRepository.findPackage('', name); - const pkgVersion = await packageRepository.findPackageVersion(pkg!.packageId, '2.0.0'); + const pkgVersion = await packageRepository.findPackageVersion( + pkg!.packageId, + '2.0.0' + ); assert(pkgVersion); - await packageManagerService.savePackageVersionManifest(pkgVersion, {}, { cpu: undefined, libc: [ 'glibc' ] }); - await packageManagerService.refreshPackageChangeVersionsToDists(pkg!, [ '2.0.0' ]); - abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); + await packageManagerService.savePackageVersionManifest( + pkgVersion, + {}, + { cpu: undefined, libc: ['glibc'] } + ); + await packageManagerService.refreshPackageChangeVersionsToDists(pkg!, [ + '2.0.0', + ]); + abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); assert(!abbreviatedManifests.data!.versions['2.0.0'].cpu); - assert.deepStrictEqual(abbreviatedManifests.data!.versions['2.0.0'].libc, [ 'glibc' ]); + assert.deepStrictEqual( + abbreviatedManifests.data!.versions['2.0.0'].libc, + ['glibc'] + ); - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/mk2test-module-cnpmsync.json'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/mk2test-module-cnpmsync.json' + ), + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -2034,33 +2960,62 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('🟢 Synced version 2.0.0 success, different meta: {"cpu":["x64"]}')); + assert( + log.includes( + '🟢 Synced version 2.0.0 success, different meta: {"cpu":["x64"]}' + ) + ); app.mockAgent().assertNoPendingInterceptors(); await mock.restore(); - abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); + abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); assert(abbreviatedManifests.data?.versions['2.0.0']); assert.equal(abbreviatedManifests.data.versions['2.0.0'].cpu?.[0], 'x64'); assert(!abbreviatedManifests.data!.versions['2.0.0'].libc); }); it('should sync missing acceptDependencies with different metas', async () => { - app.mockHttpclient('https://registry.npmjs.org/accept-dependencies-module-cnpmsync', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/accept-dependencies-module-cnpmsync.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/accept-dependencies-module-cnpmsync/-/accept-dependencies-module-cnpmsync-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/accept-dependencies-module-cnpmsync/-/accept-dependencies-module-cnpmsync-3.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/accept-dependencies-module-cnpmsync', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/accept-dependencies-module-cnpmsync.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/accept-dependencies-module-cnpmsync/-/accept-dependencies-module-cnpmsync-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/accept-dependencies-module-cnpmsync/-/accept-dependencies-module-cnpmsync-3.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'accept-dependencies-module-cnpmsync'; mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - await TestUtil.createPackage({ name, version: '2.0.0', isPrivate: false }); - await packageSyncerService.createTask(name, { tips: 'sync test tips here' }); + await TestUtil.createPackage({ + name, + version: '2.0.0', + isPrivate: false, + }); + await packageSyncerService.createTask(name, { + tips: 'sync test tips here', + }); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); @@ -2068,30 +3023,64 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream); let log = await TestUtil.readStreamToLog(stream); // console.log(log); - assert(log.includes('Synced version 2.0.0 success, different meta: {"peerDependenciesMeta":{"bufferutil":{"optional":true},"utf-8-validate":{"optional":true}},"os":["linux"],"cpu":["x64"],"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"acceptDependencies":{"webpack":"^4.46.x"}}')); - assert(log.includes('Z] 👉👉👉👉👉 Tips: sync test tips here 👈👈👈👈👈')); + assert( + log.includes( + 'Synced version 2.0.0 success, different meta: {"peerDependenciesMeta":{"bufferutil":{"optional":true},"utf-8-validate":{"optional":true}},"os":["linux"],"cpu":["x64"],"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"acceptDependencies":{"webpack":"^4.46.x"}}' + ) + ); + assert( + log.includes('Z] 👉👉👉👉👉 Tips: sync test tips here 👈👈👈👈👈') + ); assert(log.includes(', skipDependencies: false')); - const manifests = await packageManagerService.listPackageFullManifests('', name); + const manifests = await packageManagerService.listPackageFullManifests( + '', + name + ); assert(manifests.data?.versions['2.0.0']); - assert.equal(manifests.data.versions['2.0.0'].peerDependenciesMeta?.bufferutil.optional, true); + assert.equal( + manifests.data.versions['2.0.0'].peerDependenciesMeta?.bufferutil + .optional, + true + ); assert.equal(manifests.data.versions['2.0.0'].os?.[0], 'linux'); assert.equal(manifests.data.versions['2.0.0'].cpu?.[0], 'x64'); // publishTime assert.equal(manifests.data.time['1.0.0'], '2021-09-27T08:10:48.747Z'); - const abbreviatedManifests = await packageManagerService.listPackageAbbreviatedManifests('', name); + const abbreviatedManifests = + await packageManagerService.listPackageAbbreviatedManifests('', name); // console.log(JSON.stringify(abbreviatedManifests.data, null, 2)); assert(abbreviatedManifests.data?.versions['2.0.0']); - assert.equal(abbreviatedManifests.data!.versions['2.0.0'].peerDependenciesMeta?.bufferutil.optional, true); - assert.equal(abbreviatedManifests.data!.versions['2.0.0'].os?.[0], 'linux'); - assert.equal(abbreviatedManifests.data!.versions['2.0.0'].cpu?.[0], 'x64'); - assert.equal(abbreviatedManifests.data!.versions['2.0.0'].acceptDependencies?.webpack, '^4.46.x'); + assert.equal( + abbreviatedManifests.data!.versions['2.0.0'].peerDependenciesMeta + ?.bufferutil.optional, + true + ); + assert.equal( + abbreviatedManifests.data!.versions['2.0.0'].os?.[0], + 'linux' + ); + assert.equal( + abbreviatedManifests.data!.versions['2.0.0'].cpu?.[0], + 'x64' + ); + assert.equal( + abbreviatedManifests.data!.versions['2.0.0'].acceptDependencies + ?.webpack, + '^4.46.x' + ); app.mockAgent().assertNoPendingInterceptors(); // again should skip sync different metas - app.mockHttpclient('https://registry.npmjs.org/accept-dependencies-module-cnpmsync', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/accept-dependencies-module-cnpmsync.json'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/accept-dependencies-module-cnpmsync', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/accept-dependencies-module-cnpmsync.json' + ), + persist: false, + } + ); await packageSyncerService.createTask(name); task = await packageSyncerService.findExecuteTask(); assert(task); @@ -2105,26 +3094,50 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); it('should sync download data work on enableSyncDownloadData = true', async () => { - mock(app.config.cnpmcore, 'syncDownloadDataSourceRegistry', 'https://rold.cnpmjs.org'); + mock( + app.config.cnpmcore, + 'syncDownloadDataSourceRegistry', + 'https://rold.cnpmjs.org' + ); mock(app.config.cnpmcore, 'enableSyncDownloadData', true); mock(app.config.cnpmcore, 'syncDownloadDataMaxDate', '2021-12-28'); app.mockHttpclient('https://registry.npmjs.org/pedding', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/pedding.json'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/pedding.json' + ), persist: false, }); - app.mockHttpclient('https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/pedding/-/pedding-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - const response = await TestUtil.readJSONFile(TestUtil.getFixtures('downloads.json')); - app.mockHttpclient('https://rold.cnpmjs.org/downloads/range/2011-01-01:2021-12-28/pedding', 'GET', { - data: response, - status: 200, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/pedding/-/pedding-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + const response = await TestUtil.readJSONFile( + TestUtil.getFixtures('downloads.json') + ); + app.mockHttpclient( + 'https://rold.cnpmjs.org/downloads/range/2011-01-01:2021-12-28/pedding', + 'GET', + { + data: response, + status: 200, + } + ); const name = 'pedding'; await packageSyncerService.createTask(name, { syncDownloadData: true }); @@ -2140,7 +3153,8 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(log.includes('][DownloadData] 🟢🟢🟢🟢🟢')); assert(log.includes('] 🟢🟢🟢🟢🟢')); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get(`/downloads/range/2020-12-28:2021-12-28/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -2161,9 +3175,14 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // console.log(log); assert(log.includes('][DownloadData] 🟢 202110: 31 days')); assert(log.includes('][DownloadData] 🟢🟢🟢🟢🟢')); - assert(log.includes(`] 🟢🟢🟢🟢🟢 Sync "${name}" download data success 🟢🟢🟢🟢🟢`)); + assert( + log.includes( + `] 🟢🟢🟢🟢🟢 Sync "${name}" download data success 🟢🟢🟢🟢🟢` + ) + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/downloads/range/2020-12-28:2021-12-28/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -2176,17 +3195,31 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { it('should ignore sync download data work on enableSyncDownloadData = false', async () => { app.mockHttpclient('https://registry.npmjs.org/pedding', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/pedding.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/pedding/-/pedding-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/pedding.json' + ), persist: false, }); + app.mockHttpclient( + 'https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/pedding/-/pedding-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'enableSyncDownloadData', false); const name = 'pedding'; await packageSyncerService.createTask(name, { syncDownloadData: true }); @@ -2201,7 +3234,8 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(!log.includes('][DownloadData] 🟢 202111: 10 days')); assert(!log.includes('][DownloadData] 🟢🟢🟢🟢🟢')); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/downloads/range/2020-12-28:2021-12-28/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -2212,18 +3246,36 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { it('should sync download data and mock getDownloadRanges error', async () => { app.mockHttpclient('https://registry.npmjs.org/pedding', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/pedding.json'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/pedding.json' + ), persist: false, }); - app.mockHttpclient('https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/pedding/-/pedding-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); - mock(app.config.cnpmcore, 'syncDownloadDataSourceRegistry', 'https://rold.cnpmjs.org'); + app.mockHttpclient( + 'https://registry.npmjs.org/pedding/-/pedding-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/pedding/-/pedding-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + mock( + app.config.cnpmcore, + 'syncDownloadDataSourceRegistry', + 'https://rold.cnpmjs.org' + ); mock(app.config.cnpmcore, 'enableSyncDownloadData', true); mock(app.config.cnpmcore, 'syncDownloadDataMaxDate', '2021-12-28'); mock.error(NPMRegistry.prototype, 'getDownloadRanges'); @@ -2239,7 +3291,9 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(log.includes('][DownloadData] ❌ Get download data error: ')); - assert(log.includes('][DownloadData] ❌❌❌❌❌ 🚮 give up 🚮 ❌❌❌❌❌')); + assert( + log.includes('][DownloadData] ❌❌❌❌❌ 🚮 give up 🚮 ❌❌❌❌❌') + ); app.mockAgent().assertNoPendingInterceptors(); }); @@ -2261,13 +3315,21 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { registryId: registry.registryId, }); app.mockHttpclient('https://custom.npmjs.com/@dnpm/banana', 'GET', { - data: await TestUtil.readFixturesFile('r.cnpmjs.org/cnpmcore-test-sync-deprecated.json'), - persist: false, - }); - app.mockHttpclient('https://custom.npmjs.com/@dnpm/banana/-/banana-0.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), + data: await TestUtil.readFixturesFile( + 'r.cnpmjs.org/cnpmcore-test-sync-deprecated.json' + ), persist: false, }); + app.mockHttpclient( + 'https://custom.npmjs.com/@dnpm/banana/-/banana-0.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); mock(app.config.cnpmcore, 'sourceRegistry', 'https://r.cnpmjs.org'); mock(app.config.cnpmcore, 'sourceRegistryIsCNpm', true); mock(app.config.cnpmcore, 'syncUpstreamFirst', true); @@ -2283,11 +3345,12 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); // console.log(log); assert(log.includes('syncUpstream: false')); - }); it('should sync from target registry & default registry', async () => { - await packageSyncerService.createTask('cnpm-pkg', { registryId: registry.registryId }); + await packageSyncerService.createTask('cnpm-pkg', { + registryId: registry.registryId, + }); await packageSyncerService.createTask('npm-pkg'); // custom registry @@ -2312,7 +3375,11 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { repeats: 3, }); task = await packageSyncerService.findExecuteTask(); - mock(app.config.cnpmcore, 'sourceRegistry', 'https://default.npmjs.org'); + mock( + app.config.cnpmcore, + 'sourceRegistry', + 'https://default.npmjs.org' + ); await packageSyncerService.executeTask(task); stream = await packageSyncerService.findTaskLog(task); assert(stream); @@ -2323,40 +3390,56 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { it('should sync from default registry when pkg.registryId is undefined', async () => { const pkgName = '@cnpmcore/sync_not_match_registry_name'; - await TestUtil.createPackage({ - name: pkgName, - registryId: undefined, - isPrivate: false, - }, { - name: 'mock_username', - }); + await TestUtil.createPackage( + { + name: pkgName, + registryId: undefined, + isPrivate: false, + }, + { + name: 'mock_username', + } + ); // default registry - app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/sync_not_match_registry_name', 'GET', { - status: 500, - data: 'mock default.npmjs.org error', - persist: false, - repeats: 3, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/@cnpmcore/sync_not_match_registry_name', + 'GET', + { + status: 500, + data: 'mock default.npmjs.org error', + persist: false, + repeats: 3, + } + ); - await taskService.createTask(TaskEntity.createSyncPackage(pkgName, {}), true); + await taskService.createTask( + TaskEntity.createSyncPackage(pkgName, {}), + true + ); const task = await packageSyncerService.findExecuteTask(); await packageSyncerService.executeTask(task); const stream = await packageSyncerService.findTaskLog(task); assert(stream); const log = await TestUtil.readStreamToLog(stream); - assert(log.includes('Syncing from https://registry.npmjs.org/@cnpmcore/sync_not_match_registry_name')); - + assert( + log.includes( + 'Syncing from https://registry.npmjs.org/@cnpmcore/sync_not_match_registry_name' + ) + ); }); it('should sync from target registry when pkg.registryId is undefined', async () => { const pkgName = '@cnpm/banana'; - await TestUtil.createPackage({ - name: pkgName, - isPrivate: false, - }, { - name: 'mock_username', - }); + await TestUtil.createPackage( + { + name: pkgName, + isPrivate: false, + }, + { + name: 'mock_username', + } + ); await packageSyncerService.createTask(pkgName); const task = await packageSyncerService.findExecuteTask(); @@ -2376,7 +3459,9 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const stream = await packageSyncerService.findTaskLog(task); assert(stream); const log = await TestUtil.readStreamToLog(stream); - assert(log.includes('Syncing from https://custom.npmjs.com/@cnpm/banana')); + assert( + log.includes('Syncing from https://custom.npmjs.com/@cnpm/banana') + ); const pkg = await packageRepository.findPackage('@cnpm', 'banana'); assert(pkg!.registryId === registry.registryId); @@ -2403,22 +3488,29 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const stream = await packageSyncerService.findTaskLog(task); assert(stream); const log = await TestUtil.readStreamToLog(stream); - assert(log.includes('Syncing from https://custom.npmjs.com/@cnpm/banana')); + assert( + log.includes('Syncing from https://custom.npmjs.com/@cnpm/banana') + ); }); - it('should not sync from target registry if not match', async () => { const pkgName = '@cnpmcore/sync_not_match_registry_name'; - await TestUtil.createPackage({ - name: pkgName, - registryId: 'mock_registry_id', - isPrivate: false, - }, { - name: 'mock_username', - }); - await taskService.createTask(TaskEntity.createSyncPackage(pkgName, { - registryId: registry.registryId, - }), true); + await TestUtil.createPackage( + { + name: pkgName, + registryId: 'mock_registry_id', + isPrivate: false, + }, + { + name: 'mock_username', + } + ); + await taskService.createTask( + TaskEntity.createSyncPackage(pkgName, { + registryId: registry.registryId, + }), + true + ); const task = await packageSyncerService.findExecuteTask(); await packageSyncerService.executeTask(task); const stream = await packageSyncerService.findTaskLog(task); @@ -2429,42 +3521,60 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { }); describe('syncDeleteMode = ignore', async () => { - // already synced pkg beforeEach(async () => { app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar.json'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar.json' + ), persist: false, repeats: 1, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.1.0.tgz' + ), + persist: false, + } + ); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.1.0.tgz'), - persist: false, - }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); const task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - assert(!await TaskModel.findOne({ taskId: task.taskId })); + assert(!(await TaskModel.findOne({ taskId: task.taskId }))); assert(await HistoryTaskModel.findOne({ taskId: task.taskId })); - }); it('should ignore when upstream is removed', async () => { // removed in remote mock(app.config.cnpmcore, 'syncDeleteMode', 'ignore'); app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/security-holding-package.json'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/security-holding-package.json' + ), + }); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); const task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - assert(!await TaskModel.findOne({ taskId: task.taskId })); + assert(!(await TaskModel.findOne({ taskId: task.taskId }))); assert(await HistoryTaskModel.findOne({ taskId: task.taskId })); const stream = await packageSyncerService.findTaskLog(task); assert(stream); @@ -2473,23 +3583,28 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // console.log(log); const model = await PackageModel.findOne({ scope: '', name: 'foobar' }); assert(model); - const versions = await PackageVersion.find({ packageId: model.packageId }); + const versions = await PackageVersion.find({ + packageId: model.packageId, + }); assert.equal(model!.isPrivate, false); assert(versions.length === 2); - }); it('should block when upstream is removed', async () => { // removed in remote mock(app.config.cnpmcore, 'syncDeleteMode', 'block'); app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/security-holding-package.json'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/security-holding-package.json' + ), + }); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); const task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - assert(!await TaskModel.findOne({ taskId: task.taskId })); + assert(!(await TaskModel.findOne({ taskId: task.taskId }))); assert(await HistoryTaskModel.findOne({ taskId: task.taskId })); const stream = await packageSyncerService.findTaskLog(task); assert(stream); @@ -2498,39 +3613,43 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // console.log(log); const model = await PackageModel.findOne({ scope: '', name: 'foobar' }); assert(model); - const versions = await PackageVersion.find({ packageId: model.packageId }); + const versions = await PackageVersion.find({ + packageId: model.packageId, + }); assert.equal(model!.isPrivate, false); assert(versions.length === 2); - const manifests = await packageManagerService.listPackageFullManifests('', 'foobar'); + const manifests = await packageManagerService.listPackageFullManifests( + '', + 'foobar' + ); assert(manifests.blockReason === 'Removed in remote registry'); assert(manifests.data); assert(manifests.data.block === 'Removed in remote registry'); const pkg = await packageRepository.findPackage('', 'foobar'); assert(pkg); - await app.httpRequest() - .get(`/${pkg.name}`) - .expect(451); + await app.httpRequest().get(`/${pkg.name}`).expect(451); // could resotre await packageManagerService.unblockPackage(pkg); - await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); - + await app.httpRequest().get(`/${pkg.name}`).expect(200); }); it('unpublish package idempotent', async () => { app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/security-holding-package.json'), + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/security-holding-package.json' + ), + }); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); - assert(!await TaskModel.findOne({ taskId: task.taskId })); + assert(!(await TaskModel.findOne({ taskId: task.taskId }))); assert(await HistoryTaskModel.findOne({ taskId: task.taskId })); const stream = await packageSyncerService.findTaskLog(task); assert(stream); @@ -2539,56 +3658,93 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { // console.log(log); const model = await PackageModel.findOne({ scope: '', name: 'foobar' }); assert(model); - const versions = await PackageVersion.find({ packageId: model.packageId }); + const versions = await PackageVersion.find({ + packageId: model.packageId, + }); assert(versions.length === 0); // resync app.mockLog(); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, + }); task = await packageSyncerService.findExecuteTask(); await packageSyncerService.executeTask(task); assert(task); const pkg = await packageRepository.findPackage('', 'foobar'); - app.expectLog(`[packageManagerService.unpublishPackage:skip] ${pkg?.packageId} already unpublished`); - + app.expectLog( + `[packageManagerService.unpublishPackage:skip] ${pkg?.packageId} already unpublished` + ); }); it('should resync history version if forceSyncHistory is true', async () => { - const manifest = JSON.parse((await TestUtil.readFixturesFile('registry.npmjs.org/foobar.json')).toString()); - manifest.versions['1.0.0']._npmUser = { name: 'apple', email: 'apple@cnpmjs.org' }; - manifest.maintainers = [ ...manifest.maintainers, { name: 'apple', email: 'apple@cnpmjs.org' }]; + const manifest = JSON.parse( + ( + await TestUtil.readFixturesFile('registry.npmjs.org/foobar.json') + ).toString() + ); + manifest.versions['1.0.0']._npmUser = { + name: 'apple', + email: 'apple@cnpmjs.org', + }; + manifest.maintainers = [ + ...manifest.maintainers, + { name: 'apple', email: 'apple@cnpmjs.org' }, + ]; app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', { data: manifest, persist: false, repeats: 1, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - repeats: 2, + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + repeats: 2, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.1.0.tgz' + ), + persist: false, + repeats: 2, + } + ); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, }); - app.mockHttpclient('https://registry.npmjs.org/foobar/-/foobar-1.1.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.1.0.tgz'), - persist: false, - repeats: 2, - }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); let task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); // should sync publisher - const syncInfo = await packageManagerService.listPackageFullManifests('', 'foobar'); + const syncInfo = await packageManagerService.listPackageFullManifests( + '', + 'foobar' + ); assert.equal(syncInfo.data?.versions['1.0.0']?._npmUser?.name, 'apple'); // resync - manifest.versions['1.0.0']._npmUser = { name: 'banana', email: 'banana@cnpmjs.org' }; + manifest.versions['1.0.0']._npmUser = { + name: 'banana', + email: 'banana@cnpmjs.org', + }; app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', { data: manifest, persist: false, repeats: 1, }); - await packageSyncerService.createTask('foobar', { skipDependencies: true }); + await packageSyncerService.createTask('foobar', { + skipDependencies: true, + }); task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); @@ -2596,29 +3752,46 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { assert(stream2); const log2 = await TestUtil.readStreamToLog(stream2); // console.log(log2); - assert(/different meta: {"_npmUser":{"name":"banana","email":"banana@cnpmjs.org"}}/.test(log2)); + assert( + /different meta: {"_npmUser":{"name":"banana","email":"banana@cnpmjs.org"}}/.test( + log2 + ) + ); }); }); describe('strictValidatePackageDeps = true', async () => { - // already synced pkg beforeEach(async () => { - app.mockHttpclient(/^https:\/\/registry\.npmjs\.org\/invalid-deps/, 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/invalid-deps.json'), - persist: false, - }); + app.mockHttpclient( + /^https:\/\/registry\.npmjs\.org\/invalid-deps/, + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/invalid-deps.json' + ), + persist: false, + } + ); - app.mockHttpclient('https://registry.npmjs.org/invalid-deps/-/invalid-deps-1.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/invalid-deps/-/invalid-deps-1.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); }); it('should not create pkg when invalid deps', async () => { // removed in remote mock(app.config.cnpmcore, 'strictValidatePackageDeps', true); - await packageSyncerService.createTask('invalid-deps', { skipDependencies: true }); + await packageSyncerService.createTask('invalid-deps', { + skipDependencies: true, + }); const task = await packageSyncerService.findExecuteTask(); assert(task); await packageSyncerService.executeTask(task); @@ -2629,15 +3802,16 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => { const log = await TestUtil.readStreamToLog(stream); assert(log); // console.log(log); - const model = await PackageModel.findOne({ scope: '', name: 'invalid-deps' }); + const model = await PackageModel.findOne({ + scope: '', + name: 'invalid-deps', + }); assert(!model); // shoud requeue const reTask = await packageSyncerService.findExecuteTask(); assert(reTask.attempts === 2); - }); - }); }); }); diff --git a/test/core/service/PackageSyncerService/getTaskRegistry.test.ts b/test/core/service/PackageSyncerService/getTaskRegistry.test.ts index 27269214..e05c9cba 100644 --- a/test/core/service/PackageSyncerService/getTaskRegistry.test.ts +++ b/test/core/service/PackageSyncerService/getTaskRegistry.test.ts @@ -3,9 +3,9 @@ import { app } from '@eggjs/mock/bootstrap'; import { PackageSyncerService } from '../../../../app/core/service/PackageSyncerService.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; -import { Registry } from '../../../../app/core/entity/Registry.js'; +import type { Registry } from '../../../../app/core/entity/Registry.js'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; -import { Task } from '../../../../app/core/entity/Task.js'; +import type { Task } from '../../../../app/core/entity/Task.js'; describe('test/core/service/PackageSyncerService/getTaskRegistry.test.ts', () => { let packageSyncerService: PackageSyncerService; @@ -35,7 +35,11 @@ describe('test/core/service/PackageSyncerService/getTaskRegistry.test.ts', () => describe('getTaskRegistry()', () => { it('should work', async () => { - const taskRegistry = await packageSyncerService.initSpecRegistry(task, null, '@cnpm'); + const taskRegistry = await packageSyncerService.initSpecRegistry( + task, + null, + '@cnpm' + ); assert(taskRegistry); assert(taskRegistry.registryId === registry.registryId); }); @@ -47,7 +51,11 @@ describe('test/core/service/PackageSyncerService/getTaskRegistry.test.ts', () => tips: `Sync cause by changes_stream(${registry.changeStream}) update seq: 1`, }); - const taskRegistry = await packageSyncerService.initSpecRegistry(task, null, '@cnpm'); + const taskRegistry = await packageSyncerService.initSpecRegistry( + task, + null, + '@cnpm' + ); assert(taskRegistry!.name === 'default'); }); }); diff --git a/test/core/service/RegistryManagerService/index.test.ts b/test/core/service/RegistryManagerService/index.test.ts index a63abb3e..fbd61bc0 100644 --- a/test/core/service/RegistryManagerService/index.test.ts +++ b/test/core/service/RegistryManagerService/index.test.ts @@ -4,10 +4,10 @@ import { app } from '@eggjs/mock/bootstrap'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; import { ScopeManagerService } from '../../../../app/core/service/ScopeManagerService.js'; -import { Registry } from '../../../../app/core/entity/Registry.js'; +import type { Registry } from '../../../../app/core/entity/Registry.js'; import { TaskRepository } from '../../../../app/repository/TaskRepository.js'; import { TaskType } from '../../../../app/common/enum/Task.js'; -import { ChangesStreamTaskData } from '../../../../app/core/entity/Task.js'; +import type { ChangesStreamTaskData } from '../../../../app/core/entity/Task.js'; describe('test/core/service/RegistryManagerService/index.test.ts', () => { let registryManagerService: RegistryManagerService; @@ -32,7 +32,6 @@ describe('test/core/service/RegistryManagerService/index.test.ts', () => { }); describe('RegistryManagerService', () => { - describe('query should work', async () => { beforeEach(async () => { // create another @@ -49,31 +48,36 @@ describe('test/core/service/RegistryManagerService/index.test.ts', () => { // query success const queryRes = await registryManagerService.listRegistries({}); assert.equal(queryRes.count, 2); - const [ _, registry ] = queryRes.data; + const [_, registry] = queryRes.data; assert(_); assert.equal(registry.name, 'custom2'); }); it('pageOptions should work', async () => { // pageOptions should work - let queryRes = await registryManagerService.listRegistries({ pageIndex: 0, pageSize: 1 }); + let queryRes = await registryManagerService.listRegistries({ + pageIndex: 0, + pageSize: 1, + }); assert.equal(queryRes.count, 2); assert.equal(queryRes.data.length, 1); - const [ firstRegistry ] = queryRes.data; + const [firstRegistry] = queryRes.data; assert.equal(firstRegistry.name, 'custom'); - queryRes = await registryManagerService.listRegistries({ pageIndex: 1, pageSize: 1 }); + queryRes = await registryManagerService.listRegistries({ + pageIndex: 1, + pageSize: 1, + }); assert.equal(queryRes.count, 2); assert.equal(queryRes.data.length, 1); - const [ secondRegistry ] = queryRes.data; + const [secondRegistry] = queryRes.data; assert.equal(secondRegistry.name, 'custom2'); }); - }); it('update work', async () => { let queryRes = await registryManagerService.listRegistries({}); - const [ registry ] = queryRes.data; + const [registry] = queryRes.data; await registryManagerService.updateRegistry(registry.registryId, { ...registry, @@ -87,20 +91,22 @@ describe('test/core/service/RegistryManagerService/index.test.ts', () => { it('update should check registry', async () => { const queryRes = await registryManagerService.listRegistries({}); assert.equal(queryRes.count, 1); - const [ registry ] = queryRes.data; + const [registry] = queryRes.data; await assert.rejects( registryManagerService.updateRegistry('not_exist', { ...registry, name: 'boo', }), - /not found/, + /not found/ ); }); it('remove should work', async () => { let queryRes = await registryManagerService.listRegistries({}); assert.equal(queryRes.count, 1); - await registryManagerService.remove({ registryId: queryRes.data[0].registryId }); + await registryManagerService.remove({ + registryId: queryRes.data[0].registryId, + }); queryRes = await registryManagerService.listRegistries({}); assert.equal(queryRes.count, 0); }); @@ -109,22 +115,36 @@ describe('test/core/service/RegistryManagerService/index.test.ts', () => { let registry: Registry; beforeEach(async () => { // create scope - [ registry ] = (await registryManagerService.listRegistries({})).data; - await scopeManagerService.createScope({ name: '@cnpm', registryId: registry.registryId }); + [registry] = (await registryManagerService.listRegistries({})).data; + await scopeManagerService.createScope({ + name: '@cnpm', + registryId: registry.registryId, + }); }); it('should work', async () => { // create success - await registryManagerService.createSyncChangesStream({ registryId: registry.registryId }); + await registryManagerService.createSyncChangesStream({ + registryId: registry.registryId, + }); const targetName = 'CUSTOM_WORKER'; - const task = await taskRepository.findTaskByTargetName(targetName, TaskType.ChangesStream); + const task = await taskRepository.findTaskByTargetName( + targetName, + TaskType.ChangesStream + ); assert(task); - assert.equal((task.data as ChangesStreamTaskData).registryId, registry.registryId); + assert.equal( + (task.data as ChangesStreamTaskData).registryId, + registry.registryId + ); }); it('should preCheck registry', async () => { - await assert.rejects(registryManagerService.createSyncChangesStream({ registryId: 'mock_invalid_registry_id' }), - /not found/, + await assert.rejects( + registryManagerService.createSyncChangesStream({ + registryId: 'mock_invalid_registry_id', + }), + /not found/ ); }); @@ -136,19 +156,32 @@ describe('test/core/service/RegistryManagerService/index.test.ts', () => { userPrefix: 'cnpm:', type: RegistryType.Cnpmcore, }); - await assert.rejects(registryManagerService.createSyncChangesStream({ registryId: newRegistry.registryId }), - /please create scopes first/, + await assert.rejects( + registryManagerService.createSyncChangesStream({ + registryId: newRegistry.registryId, + }), + /please create scopes first/ ); }); it('should create only once', async () => { // create success - await registryManagerService.createSyncChangesStream({ registryId: registry.registryId }); - await registryManagerService.createSyncChangesStream({ registryId: registry.registryId }); + await registryManagerService.createSyncChangesStream({ + registryId: registry.registryId, + }); + await registryManagerService.createSyncChangesStream({ + registryId: registry.registryId, + }); // won't create new task - await registryManagerService.createSyncChangesStream({ registryId: registry.registryId, since: '100' }); + await registryManagerService.createSyncChangesStream({ + registryId: registry.registryId, + since: '100', + }); const targetName = 'CUSTOM_WORKER'; - const task = await taskRepository.findTaskByTargetName(targetName, TaskType.ChangesStream); + const task = await taskRepository.findTaskByTargetName( + targetName, + TaskType.ChangesStream + ); assert(task); assert.equal((task.data as ChangesStreamTaskData).since, ''); }); diff --git a/test/port/controller/DownloadController/showPackageDownloads.test.ts b/test/port/controller/DownloadController/showPackageDownloads.test.ts index 266862ba..1a7ee457 100644 --- a/test/port/controller/DownloadController/showPackageDownloads.test.ts +++ b/test/port/controller/DownloadController/showPackageDownloads.test.ts @@ -4,11 +4,15 @@ import path from 'node:path'; import { app, mock } from '@eggjs/mock/bootstrap'; import dayjs from '../../../../app/common/dayjs.js'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const SavePackageVersionDownloadCounterPath = path.join(__dirname, '../../../../app/port/schedule/SavePackageVersionDownloadCounter.ts'); +const SavePackageVersionDownloadCounterPath = path.join( + __dirname, + '../../../../app/port/schedule/SavePackageVersionDownloadCounter.ts' +); describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', () => { let publisher: TestUser; @@ -18,23 +22,35 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', describe('[GET /downloads/range/:range/:fullname] showPackageDownloads()', () => { it('should get package download infos', async () => { - let pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + let pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '2.0.0' }); - await app.httpRequest() + pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '2.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - const pkg2 = await TestUtil.getFullPackage({ name: 'foo', version: '1.0.0' }); - await app.httpRequest() + const pkg2 = await TestUtil.getFullPackage({ + name: 'foo', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -45,53 +61,54 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', name: '@malware-test-cloth-diner-bosks-zante/foo', version: '1.0.0', }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg3.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg3) .expect(201); if (app.config.nfs.client) { - await app.httpRequest() - .get(`/${pkg.name}/-/koa-1.0.0.tgz`) - .expect(302); - await app.httpRequest() - .get(`/${pkg.name}/-/koa-1.0.0.tgz`) - .expect(302); - await app.httpRequest() - .get(`/${pkg.name}/-/koa-1.0.0.tgz`) - .expect(302); - await app.httpRequest() - .get(`/${pkg.name}/-/koa-2.0.0.tgz`) - .expect(302); - await app.httpRequest() + await app.httpRequest().get(`/${pkg.name}/-/koa-1.0.0.tgz`).expect(302); + await app.httpRequest().get(`/${pkg.name}/-/koa-1.0.0.tgz`).expect(302); + await app.httpRequest().get(`/${pkg.name}/-/koa-1.0.0.tgz`).expect(302); + await app.httpRequest().get(`/${pkg.name}/-/koa-2.0.0.tgz`).expect(302); + await app + .httpRequest() .get(`/${pkg2.name}/-/foo-1.0.0.tgz`) .expect(302); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg3.name}/-/foo-1.0.0.tgz`) .expect(302); } else { - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/-/koa-1.0.0.tgz`) .expect('content-type', 'application/octet-stream') .expect(200); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/-/koa-1.0.0.tgz`) .expect('content-type', 'application/octet-stream') .expect(200); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/-/koa-1.0.0.tgz`) .expect('content-type', 'application/octet-stream') .expect(200); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/-/koa-2.0.0.tgz`) .expect('content-type', 'application/octet-stream') .expect(200); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg2.name}/-/foo-1.0.0.tgz`) .expect('content-type', 'application/octet-stream') .expect(200); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg3.name}/-/foo-1.0.0.tgz`) .expect('content-type', 'application/octet-stream') .expect(200); @@ -101,7 +118,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', const start = dayjs().subtract(100, 'days').format('YYYY-MM-DD'); const end = dayjs().add(100, 'days').format('YYYY-MM-DD'); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get(`/downloads/range/${start}:${end}/@cnpm/koa`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -111,7 +129,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', assert.equal(data.downloads[0].downloads, 4); assert.equal(data.versions['1.0.0'][0].downloads, 3); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/downloads/range/${start}:${end}/foo`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -122,7 +141,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', assert(data.versions['1.0.0'][0].downloads === 1); // __total__ - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/downloads/total/${start}:${end}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -133,7 +153,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', assert(!data.versions); // scope - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/downloads/@cnpm/${start}:${end}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -143,7 +164,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', assert.equal(data.downloads[0].downloads, 4); assert(!data.versions); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/downloads/@malware-test-cloth-diner-bosks-zante/${start}:${end}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -155,19 +177,22 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', }); it('should get package download infos auto handle start and end position', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); if (app.config.nfs.client) { - await app.httpRequest() - .get(`/${pkg.name}/-/koa-1.0.0.tgz`) - .expect(302); + await app.httpRequest().get(`/${pkg.name}/-/koa-1.0.0.tgz`).expect(302); } else { - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/-/koa-1.0.0.tgz`) .expect('content-type', 'application/octet-stream') .expect(200); @@ -177,7 +202,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', const start = dayjs().format('YYYY-MM-DD'); const end = dayjs().add(100, 'days').format('YYYY-MM-DD'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/downloads/range/${end}:${start}/@cnpm/koa`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -188,8 +214,12 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', }); it('should get package download infos with empty data', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -200,7 +230,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', const start = dayjs().format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/downloads/range/${start}:${end}/@cnpm/koa`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -211,7 +242,8 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', it('should 404 when package not exists', async () => { const start = dayjs().format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/downloads/range/${start}:${end}/@cnpm/koa-not-exists`) .expect(404) .expect('content-type', 'application/json; charset=utf-8'); @@ -221,9 +253,9 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', it('should 422 when out of range', async () => { const start = dayjs().format('YYYY-MM-DD'); - const end = dayjs().add(1, 'year').add(1, 'day') - .format('YYYY-MM-DD'); - const res = await app.httpRequest() + const end = dayjs().add(1, 'year').add(1, 'day').format('YYYY-MM-DD'); + const res = await app + .httpRequest() .get(`/downloads/range/${start}:${end}/@cnpm/koa`) .expect(422) .expect('content-type', 'application/json; charset=utf-8'); @@ -233,9 +265,9 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', it('should 422 when out of range, switch start and end range', async () => { const start = dayjs().format('YYYY-MM-DD'); - const end = dayjs().add(1, 'year').add(10, 'day') - .format('YYYY-MM-DD'); - const res = await app.httpRequest() + const end = dayjs().add(1, 'year').add(10, 'day').format('YYYY-MM-DD'); + const res = await app + .httpRequest() .get(`/downloads/range/${end}:${start}/@cnpm/koa`) .expect(422) .expect('content-type', 'application/json; charset=utf-8'); @@ -244,40 +276,60 @@ describe('test/port/controller/DownloadController/showPackageDownloads.test.ts', }); it('should 422 when range format invalid', async () => { - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/downloads/range/f:b/@cnpm/koa') .expect('content-type', 'application/json; charset=utf-8'); let data = res.body; assert.equal(res.status, 422); - assert.equal(data.error, '[UNPROCESSABLE_ENTITY] range(f:b) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style'); + assert.equal( + data.error, + '[UNPROCESSABLE_ENTITY] range(f:b) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/downloads/range/2017-10-1:2017-09-10/@cnpm/koa') .expect('content-type', 'application/json; charset=utf-8'); data = res.body; assert.equal(res.status, 422); - assert.equal(data.error, '[UNPROCESSABLE_ENTITY] range(2017-10-1:2017-09-10) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style'); + assert.equal( + data.error, + '[UNPROCESSABLE_ENTITY] range(2017-10-1:2017-09-10) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/downloads/range/2017-10-91:2017-09-10/@cnpm/koa') .expect('content-type', 'application/json; charset=utf-8'); data = res.body; assert.equal(res.status, 422); - assert.equal(data.error, '[UNPROCESSABLE_ENTITY] range(2017-10-91:2017-09-10) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style'); + assert.equal( + data.error, + '[UNPROCESSABLE_ENTITY] range(2017-10-91:2017-09-10) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/downloads/range/2017-10-91:2017-00-10/@cnpm/koa') .expect('content-type', 'application/json; charset=utf-8'); data = res.body; assert.equal(res.status, 422); - assert.equal(data.error, '[UNPROCESSABLE_ENTITY] range(2017-10-91:2017-00-10) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style'); + assert.equal( + data.error, + '[UNPROCESSABLE_ENTITY] range(2017-10-91:2017-00-10) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/downloads/range/2017-10-11:2017-09-99/@cnpm/koa') .expect('content-type', 'application/json; charset=utf-8'); data = res.body; assert.equal(res.status, 422); - assert.equal(data.error, '[UNPROCESSABLE_ENTITY] range(2017-10-11:2017-09-99) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style'); + assert.equal( + data.error, + '[UNPROCESSABLE_ENTITY] range(2017-10-11:2017-09-99) format invalid, must be "YYYY-MM-DD:YYYY-MM-DD" style' + ); }); }); }); diff --git a/test/port/controller/HomeController/showTotal.test.ts b/test/port/controller/HomeController/showTotal.test.ts index 8d3701d4..532ed194 100644 --- a/test/port/controller/HomeController/showTotal.test.ts +++ b/test/port/controller/HomeController/showTotal.test.ts @@ -9,15 +9,21 @@ import { RegistryManagerService } from '../../../../app/core/service/RegistryMan import { ChangesStreamService } from '../../../../app/core/service/ChangesStreamService.js'; import { TaskRepository } from '../../../../app/repository/TaskRepository.js'; import { TaskType } from '../../../../app/common/enum/Task.js'; -import { ChangesStreamTask } from '../../../../app/core/entity/Task.js'; +import type { ChangesStreamTask } from '../../../../app/core/entity/Task.js'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; import { ScopeManagerService } from '../../../../app/core/service/ScopeManagerService.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const SavePackageVersionDownloadCounterPath = path.join(__dirname, '../../../../app/port/schedule/SavePackageVersionDownloadCounter.js'); -const UpdateTotalDataPath = path.join(__dirname, '../../../../app/port/schedule/UpdateTotalData.js'); +const SavePackageVersionDownloadCounterPath = path.join( + __dirname, + '../../../../app/port/schedule/SavePackageVersionDownloadCounter.js' +); +const UpdateTotalDataPath = path.join( + __dirname, + '../../../../app/port/schedule/UpdateTotalData.js' +); describe('test/port/controller/HomeController/showTotal.test.ts', () => { describe('[GET /] showTotal()', () => { @@ -26,8 +32,7 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { let taskRepository: TaskRepository; let scopeManagerService: ScopeManagerService; it('should total information', async () => { - let res = await app.httpRequest() - .get('/'); + let res = await app.httpRequest().get('/'); assert(res.status === 200); assert(res.headers['content-type'] === 'application/json; charset=utf-8'); let data = res.body; @@ -44,40 +49,47 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { // downloads count const publisher = await TestUtil.createUser(); - let pkg = await TestUtil.getFullPackage({ name: '@cnpm/home1', version: '1.0.0' }); - await app.httpRequest() + let pkg = await TestUtil.getFullPackage({ + name: '@cnpm/home1', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .expect(201) .send(pkg); - pkg = await TestUtil.getFullPackage({ name: '@cnpm/home2', version: '2.0.0' }); - await app.httpRequest() + pkg = await TestUtil.getFullPackage({ + name: '@cnpm/home2', + version: '2.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .expect(201) .send(pkg); - pkg = await TestUtil.getFullPackage({ name: '@cnpm/home1', version: '1.0.1' }); - await app.httpRequest() + pkg = await TestUtil.getFullPackage({ + name: '@cnpm/home1', + version: '1.0.1', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .expect(201) .send(pkg); - await app.httpRequest() - .get('/@cnpm/home1/-/home1-1.0.0.tgz'); - await app.httpRequest() - .get('/@cnpm/home1/-/home1-1.0.1.tgz'); - await app.httpRequest() - .get('/@cnpm/home2/-/home2-2.0.0.tgz'); + await app.httpRequest().get('/@cnpm/home1/-/home1-1.0.0.tgz'); + await app.httpRequest().get('/@cnpm/home1/-/home1-1.0.1.tgz'); + await app.httpRequest().get('/@cnpm/home2/-/home2-2.0.0.tgz'); await app.runSchedule(SavePackageVersionDownloadCounterPath); await app.runSchedule(UpdateTotalDataPath); - - res = await app.httpRequest() - .get('/'); + res = await app.httpRequest().get('/'); assert(res.status === 200); data = res.body; assert(data.last_package === '@cnpm/home2'); @@ -95,15 +107,35 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { // mock yesterday lastweek lastmonth const today = dayjs(); - const yesterdayYearMonthInt = Number(today.subtract(1, 'day').format('YYYYMM')); + const yesterdayYearMonthInt = Number( + today.subtract(1, 'day').format('YYYYMM') + ); const yesterdayDate = today.subtract(1, 'day').format('DD'); - const lastWeekYearMonthInt = Number(today.subtract(1, 'week').startOf('week').format('YYYYMM')); - const lastWeekDate = today.subtract(1, 'week').startOf('week').format('DD'); - const lastMonthYearMonthInt = Number(today.subtract(1, 'month').startOf('month').format('YYYYMM')); - const lastMonthDate = today.subtract(1, 'month').startOf('month').format('DD'); - const lastYearYearMonthInt = Number(today.subtract(1, 'year').startOf('year').format('YYYYMM')); - const lastYearDate = today.subtract(1, 'month').startOf('year').format('DD'); - let row: any = await PackageVersionDownload.findOne({ packageId: 'total', yearMonth: yesterdayYearMonthInt }); + const lastWeekYearMonthInt = Number( + today.subtract(1, 'week').startOf('week').format('YYYYMM') + ); + const lastWeekDate = today + .subtract(1, 'week') + .startOf('week') + .format('DD'); + const lastMonthYearMonthInt = Number( + today.subtract(1, 'month').startOf('month').format('YYYYMM') + ); + const lastMonthDate = today + .subtract(1, 'month') + .startOf('month') + .format('DD'); + const lastYearYearMonthInt = Number( + today.subtract(1, 'year').startOf('year').format('YYYYMM') + ); + const lastYearDate = today + .subtract(1, 'month') + .startOf('year') + .format('DD'); + let row: any = await PackageVersionDownload.findOne({ + packageId: 'total', + yearMonth: yesterdayYearMonthInt, + }); if (!row) { row = await PackageVersionDownload.create({ packageId: 'total', @@ -114,7 +146,10 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { row[`d${yesterdayDate}`] = 1; await row.save(); - row = await PackageVersionDownload.findOne({ packageId: 'total', yearMonth: lastWeekYearMonthInt }); + row = await PackageVersionDownload.findOne({ + packageId: 'total', + yearMonth: lastWeekYearMonthInt, + }); if (!row) { row = await PackageVersionDownload.create({ packageId: 'total', @@ -125,7 +160,10 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { row[`d${lastWeekDate}`] = 1; await row.save(); - row = await PackageVersionDownload.findOne({ packageId: 'total', yearMonth: lastMonthYearMonthInt }); + row = await PackageVersionDownload.findOne({ + packageId: 'total', + yearMonth: lastMonthYearMonthInt, + }); if (!row) { row = await PackageVersionDownload.create({ packageId: 'total', @@ -136,7 +174,10 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { row[`d${lastMonthDate}`] = 1; await row.save(); - row = await PackageVersionDownload.findOne({ packageId: 'total', yearMonth: lastYearYearMonthInt }); + row = await PackageVersionDownload.findOne({ + packageId: 'total', + yearMonth: lastYearYearMonthInt, + }); if (!row) { row = await PackageVersionDownload.create({ packageId: 'total', @@ -148,8 +189,7 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { await row.save(); await app.runSchedule(UpdateTotalDataPath); - res = await app.httpRequest() - .get('/'); + res = await app.httpRequest().get('/'); assert(res.status === 200); data = res.body; assert(data.last_package === '@cnpm/home2'); @@ -172,7 +212,8 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { it('should show sync mode = all', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -182,7 +223,8 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { it('should show sync enableSyncBinary = true', async () => { mock(app.config.cnpmcore, 'enableSyncBinary', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -199,7 +241,8 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { await app.runSchedule(UpdateTotalDataPath); }); it('should show empty upstream_registries when no changesStreamTasks', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -210,21 +253,26 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { // create default registry await changesStreamService.findExecuteTask(); - const tasks = await taskRepository.findTasksByCondition({ type: TaskType.ChangesStream }); + const tasks = await taskRepository.findTasksByCondition({ + type: TaskType.ChangesStream, + }); await changesStreamService.executeTask(tasks[0] as ChangesStreamTask); assert(tasks.length === 1); assert(registryManagerService); await app.runSchedule(UpdateTotalDataPath); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const data = res.body; assert(data.upstream_registries.length === 1); - const [ upstream ] = data.upstream_registries; + const [upstream] = data.upstream_registries; assert(upstream.registry_name === 'default'); - assert(upstream.changes_stream_url === 'https://replicate.npmjs.com/_changes'); + assert( + upstream.changes_stream_url === 'https://replicate.npmjs.com/_changes' + ); assert(upstream.source_registry === 'https://registry.npmjs.org'); }); @@ -240,11 +288,18 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { userPrefix: 'cnpm:', type: RegistryType.Cnpmcore, }); - await scopeManagerService.createScope({ name: '@cnpm', registryId: registry.registryId }); - await registryManagerService.createSyncChangesStream({ registryId: registry.registryId }); + await scopeManagerService.createScope({ + name: '@cnpm', + registryId: registry.registryId, + }); + await registryManagerService.createSyncChangesStream({ + registryId: registry.registryId, + }); // start sync - const tasks = await taskRepository.findTasksByCondition({ type: TaskType.ChangesStream }); + const tasks = await taskRepository.findTasksByCondition({ + type: TaskType.ChangesStream, + }); assert(tasks.length === 2); for (const task of tasks) { await changesStreamService.executeTask(task as ChangesStreamTask); @@ -252,24 +307,34 @@ describe('test/port/controller/HomeController/showTotal.test.ts', () => { // refresh total await app.runSchedule(UpdateTotalDataPath); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const data = res.body; assert(data.upstream_registries.length === 2); - const [ defaultRegistry ] = data.upstream_registries.filter((item: any) => item.registry_name === 'default'); + const [defaultRegistry] = data.upstream_registries.filter( + (item: any) => item.registry_name === 'default' + ); assert(defaultRegistry.registry_name === 'default'); - assert(defaultRegistry.changes_stream_url === 'https://replicate.npmjs.com/_changes'); - assert(defaultRegistry.source_registry === 'https://registry.npmjs.org'); + assert( + defaultRegistry.changes_stream_url === + 'https://replicate.npmjs.com/_changes' + ); + assert( + defaultRegistry.source_registry === 'https://registry.npmjs.org' + ); - const [ customRegistry ] = data.upstream_registries.filter((item: any) => item.registry_name === 'custom'); + const [customRegistry] = data.upstream_registries.filter( + (item: any) => item.registry_name === 'custom' + ); assert(customRegistry.registry_name === 'custom'); - assert(customRegistry.changes_stream_url === 'https://r.cnpmjs.org/_changes'); + assert( + customRegistry.changes_stream_url === 'https://r.cnpmjs.org/_changes' + ); assert(customRegistry.source_registry === 'https://cnpmjs.org'); - }); }); - }); }); diff --git a/test/port/controller/PackageSyncController/showSyncTask.test.ts b/test/port/controller/PackageSyncController/showSyncTask.test.ts index 9cd8332d..8b6ee898 100644 --- a/test/port/controller/PackageSyncController/showSyncTask.test.ts +++ b/test/port/controller/PackageSyncController/showSyncTask.test.ts @@ -3,13 +3,17 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUtil, TestUser } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { TaskRepository } from '../../../../app/repository/TaskRepository.js'; import { TaskState } from '../../../../app/common/enum/Task.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const SyncPackageWorkerPath = path.join(__dirname, '../../../../app/port/schedule/SyncPackageWorker.ts'); +const SyncPackageWorkerPath = path.join( + __dirname, + '../../../../app/port/schedule/SyncPackageWorker.ts' +); describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () => { let publisher: TestUser; @@ -22,8 +26,12 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = describe('[GET /-/package/:fullname/syncs/:taskId] showSyncTask()', () => { it('should 401 if user not login when alwaysAuth = true', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -31,28 +39,31 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = .expect(201); mock(app.config.cnpmcore, 'alwaysAuth', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/-/package/${pkg.name}/syncs/mock-task-id`) .expect(401); assert.equal(res.body.error, '[UNAUTHORIZED] Login first'); }); it('should 404 when task not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/-/package/koa/syncs/mock-task-id') .expect(404); - assert.equal(res.body.error, '[NOT_FOUND] Package "koa" sync task "mock-task-id" not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] Package "koa" sync task "mock-task-id" not found' + ); }); it('should 200', async () => { - let res = await app.httpRequest() - .put('/-/package/koa/syncs'); + let res = await app.httpRequest().put('/-/package/koa/syncs'); assert(res.status === 201); assert(res.body.id); const task = await taskRepository.findTask(res.body.id); assert(task); - res = await app.httpRequest() - .get(`/-/package/koa/syncs/${task.taskId}`); + res = await app.httpRequest().get(`/-/package/koa/syncs/${task.taskId}`); assert(res.status === 200); assert(res.body.id); // waiting state logUrl is not exists @@ -60,8 +71,7 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = task.state = TaskState.Processing; await taskRepository.saveTask(task!); - res = await app.httpRequest() - .get(`/-/package/koa/syncs/${task.taskId}`); + res = await app.httpRequest().get(`/-/package/koa/syncs/${task.taskId}`); assert(res.status === 200); assert(res.body.id); assert(res.body.logUrl); @@ -70,21 +80,35 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = }); it('should get sucess task after schedule run', async () => { - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync-issue-1667', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/mk2test-module-cnpmsync-issue-1667.json'), - persist: false, - }); - app.mockHttpclient('https://registry.npmjs.org/mk2test-module-cnpmsync-issue-1667/-/mk2test-module-cnpmsync-issue-1667-3.0.0.tgz', 'GET', { - data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'), - persist: false, - }); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync-issue-1667', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/mk2test-module-cnpmsync-issue-1667.json' + ), + persist: false, + } + ); + app.mockHttpclient( + 'https://registry.npmjs.org/mk2test-module-cnpmsync-issue-1667/-/mk2test-module-cnpmsync-issue-1667-3.0.0.tgz', + 'GET', + { + data: await TestUtil.readFixturesFile( + 'registry.npmjs.org/foobar/-/foobar-1.0.0.tgz' + ), + persist: false, + } + ); const name = 'mk2test-module-cnpmsync-issue-1667'; - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/-/package/${name}/syncs`) .expect(201); const taskId = res.body.id; assert(taskId); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/-/package/${name}/syncs/${taskId}`) .expect(200); // waiting state logUrl is not exists @@ -93,13 +117,15 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = // again should work await app.runSchedule(SyncPackageWorkerPath); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/-/package/${name}/syncs/${taskId}`) .expect(200); assert.equal(res.body.state, TaskState.Success); assert(res.body.logUrl); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/-/package/${name}/syncs/${taskId}/log`); let log = ''; if (res.status === 200) { @@ -111,13 +137,12 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = assert.match(log, /🔗/); // check hasInstallScript - res = await app.httpRequest() - .get(`/${name}`) - .expect(200); + res = await app.httpRequest().get(`/${name}`).expect(200); let pkg = res.body.versions['3.0.0']; assert(!('hasInstallScript' in pkg)); assert(pkg.scripts); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${name}`) .set('accept', 'application/vnd.npm.install-v1+json') .expect(200); @@ -129,21 +154,24 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = describe('[GET /:fullname/sync/log/:taskId] deprecatedShowSyncTask()', () => { it('should 404 when task not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/koa/sync/log/mock-task-id') .expect(404); - assert.equal(res.body.error, '[NOT_FOUND] Package "koa" sync task "mock-task-id" not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] Package "koa" sync task "mock-task-id" not found' + ); }); it('should 200', async () => { - let res = await app.httpRequest() - .put('/koa/sync') - .expect(201); + let res = await app.httpRequest().put('/koa/sync').expect(201); assert(res.body.logId); const task = await taskRepository.findTask(res.body.logId); assert(task); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/koa/sync/log/${task.taskId}`) .expect(200); assert(res.body.ok); @@ -155,7 +183,8 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = task!.state = TaskState.Processing; await taskRepository.saveTask(task!); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/koa/sync/log/${task.taskId}?t=123`) .expect(200); assert(res.body.logUrl); @@ -168,7 +197,8 @@ describe('test/port/controller/PackageSyncController/showSyncTask.test.ts', () = task.state = TaskState.Success; await taskRepository.saveTask(task!); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/koa/sync/log/${task.taskId}`) .expect(200); assert(res.body.logUrl); diff --git a/test/port/controller/PackageSyncController/showSyncTaskLog.test.ts b/test/port/controller/PackageSyncController/showSyncTaskLog.test.ts index 76a61fe7..a8269f38 100644 --- a/test/port/controller/PackageSyncController/showSyncTaskLog.test.ts +++ b/test/port/controller/PackageSyncController/showSyncTaskLog.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { TaskRepository } from '../../../../app/repository/TaskRepository.js'; import { TaskState } from '../../../../app/common/enum/Task.js'; import { NFSAdapter } from '../../../../app/common/adapter/NFSAdapter.js'; @@ -20,7 +21,8 @@ describe('test/port/controller/PackageSyncController/showSyncTaskLog.test.ts', ( describe('[GET /-/package/:fullname/syncs/:taskId/log] showSyncTaskLog()', () => { it('should 401 if user not login when alwaysAuth = true', async () => { const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa' }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -28,30 +30,37 @@ describe('test/port/controller/PackageSyncController/showSyncTaskLog.test.ts', ( .expect(201); mock(app.config.cnpmcore, 'alwaysAuth', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/-/package/${pkg.name}/syncs/mock-task-id/log`) .expect(401); assert.equal(res.body.error, '[UNAUTHORIZED] Login first'); }); it('should 404 when task not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/-/package/koa/syncs/mock-task-id/log') .expect(404); - assert.equal(res.body.error, '[NOT_FOUND] Package "koa" sync task "mock-task-id" not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] Package "koa" sync task "mock-task-id" not found' + ); }); it('should 200 and 302', async () => { - let res = await app.httpRequest() - .put('/-/package/koa/syncs') - .expect(201); + let res = await app.httpRequest().put('/-/package/koa/syncs').expect(201); assert(res.body.id); const task = await taskRepository.findTask(res.body.id); // waiting state logUrl is not exists - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/-/package/koa/syncs/${task!.taskId}/log`); if (res.status === 404) { - assert.equal(res.body.error, `[NOT_FOUND] Package "koa" sync task "${task!.taskId}" log not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] Package "koa" sync task "${task!.taskId}" log not found` + ); } else { assert.equal(res.status, 302); const { status } = await app.curl(res.headers.location); @@ -62,10 +71,14 @@ describe('test/port/controller/PackageSyncController/showSyncTaskLog.test.ts', ( await taskRepository.saveTask(task!); // log file not exists - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/-/package/koa/syncs/${task!.taskId}/log`); if (res.status === 404) { - assert.equal(res.body.error, `[NOT_FOUND] Package "koa" sync task "${task!.taskId}" log not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] Package "koa" sync task "${task!.taskId}" log not found` + ); } else { assert.equal(res.status, 302); const { status } = await app.curl(res.headers.location); @@ -73,8 +86,12 @@ describe('test/port/controller/PackageSyncController/showSyncTaskLog.test.ts', ( } // save log file - await nfsAdapter.uploadBytes(task!.logPath, Buffer.from('hello log file 😄\nsencod line here')); - res = await app.httpRequest() + await nfsAdapter.uploadBytes( + task!.logPath, + Buffer.from('hello log file 😄\nsencod line here') + ); + res = await app + .httpRequest() .get(`/-/package/koa/syncs/${task!.taskId}/log`); if (res.status === 200) { assert.equal(res.text, 'hello log file 😄\nsencod line here'); @@ -87,8 +104,13 @@ describe('test/port/controller/PackageSyncController/showSyncTaskLog.test.ts', ( } // mock redirect - mock.data(nfsAdapter.constructor.prototype, 'getDownloadUrlOrStream', 'http://mock.com/some.log'); - res = await app.httpRequest() + mock.data( + nfsAdapter.constructor.prototype, + 'getDownloadUrlOrStream', + 'http://mock.com/some.log' + ); + res = await app + .httpRequest() .get(`/-/package/koa/syncs/${task!.taskId}/log`) .expect('location', 'http://mock.com/some.log') .expect(302); diff --git a/test/port/controller/PackageTagController/removeTag.test.ts b/test/port/controller/PackageTagController/removeTag.test.ts index 151ac9ec..5dbd0517 100644 --- a/test/port/controller/PackageTagController/removeTag.test.ts +++ b/test/port/controller/PackageTagController/removeTag.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; describe('test/port/controller/PackageTagController/removeTag.test.ts', () => { let publisher: TestUser; @@ -11,8 +12,12 @@ describe('test/port/controller/PackageTagController/removeTag.test.ts', () => { describe('[DELETE /-/package/:fullname/dist-tags/:tag] removeTag()', () => { it('should 401 when readonly token', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -23,7 +28,8 @@ describe('test/port/controller/PackageTagController/removeTag.test.ts', () => { token: publisher.token, readonly: true, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', userReadonly.authorization) .set('user-agent', publisher.ua) @@ -32,31 +38,44 @@ describe('test/port/controller/PackageTagController/removeTag.test.ts', () => { }); it('should 403 when non-maintainer add tag', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); const other = await TestUtil.createUser(); - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', other.authorization) .set('user-agent', publisher.ua) .expect(403); - assert.equal(res.body.error, `[FORBIDDEN] "${other.name}" not authorized to modify @cnpm/koa, please contact maintainers: "${publisher.name}"`); + assert.equal( + res.body.error, + `[FORBIDDEN] "${other.name}" not authorized to modify @cnpm/koa, please contact maintainers: "${publisher.name}"` + ); }); it('should 200 when tag not exists', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -65,54 +84,79 @@ describe('test/port/controller/PackageTagController/removeTag.test.ts', () => { }); it('should 422 when tag invalid', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - let res = await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); assert.equal(res.body.ok, true); - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`/-/package/${pkg.name}/dist-tags/1.0`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] tag: must match format "semver-tag"'); + assert.equal( + res.body.error, + '[INVALID_PARAM] tag: must match format "semver-tag"' + ); }); it('should 422 when tag is latest', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/package/${pkg.name}/dist-tags/latest`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .expect(403); - assert.equal(res.body.error, '[FORBIDDEN] Can\'t remove the "latest" tag'); + assert.equal( + res.body.error, + '[FORBIDDEN] Can\'t remove the "latest" tag' + ); }); it('should 200', async () => { - let pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + let pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '2.0.0' }); - await app.httpRequest() + pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '2.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -120,40 +164,38 @@ describe('test/port/controller/PackageTagController/removeTag.test.ts', () => { .send(JSON.stringify('1.0.0')) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.deepEqual(res.body['dist-tags'], { latest: '2.0.0', beta: '1.0.0', }); - res = await app.httpRequest() - .put(`/-/package/${pkg.name}/dist-tags/${encodeURIComponent(' beta2 ')}`) + res = await app + .httpRequest() + .put( + `/-/package/${pkg.name}/dist-tags/${encodeURIComponent(' beta2 ')}` + ) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .set('content-type', 'application/json') .send(JSON.stringify('2.0.0')) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.deepEqual(res.body['dist-tags'], { latest: '2.0.0', beta: '1.0.0', beta2: '2.0.0', }); - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.deepEqual(res.body['dist-tags'], { latest: '2.0.0', beta2: '2.0.0', diff --git a/test/port/controller/PackageTagController/saveTag.test.ts b/test/port/controller/PackageTagController/saveTag.test.ts index 3894c23b..431cf208 100644 --- a/test/port/controller/PackageTagController/saveTag.test.ts +++ b/test/port/controller/PackageTagController/saveTag.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { let publisher: TestUser; @@ -11,8 +12,12 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { describe('[PUT /-/package/:fullname/dist-tags/:tag] saveTag()', () => { it('should 401 when readonly token', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -23,7 +28,8 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { token: publisher.token, readonly: true, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', userReadonly.authorization) .set('user-agent', publisher.ua) @@ -34,33 +40,46 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { }); it('should 403 when non-maintainer add tag', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); const other = await TestUtil.createUser(); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', other.authorization) .set('user-agent', publisher.ua) .set('content-type', 'application/json') .send(JSON.stringify('1.0.0')) .expect(403); - assert.equal(res.body.error, `[FORBIDDEN] "${other.name}" not authorized to modify @cnpm/koa, please contact maintainers: "${publisher.name}"`); + assert.equal( + res.body.error, + `[FORBIDDEN] "${other.name}" not authorized to modify @cnpm/koa, please contact maintainers: "${publisher.name}"` + ); }); it('should 404 when version not exists', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -71,61 +90,89 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { }); it('should 422 when version invalid', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .set('content-type', 'application/json') .send(JSON.stringify(' ')) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] version: must NOT have fewer than 5 characters'); - res = await app.httpRequest() + assert.equal( + res.body.error, + '[INVALID_PARAM] version: must NOT have fewer than 5 characters' + ); + res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .set('content-type', 'application/json') .send(JSON.stringify('')) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] version: must NOT have fewer than 5 characters'); - res = await app.httpRequest() + assert.equal( + res.body.error, + '[INVALID_PARAM] version: must NOT have fewer than 5 characters' + ); + res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .set('content-type', 'application/json') .send(JSON.stringify('wrong.ver.1')) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] version: must match format "semver-version"'); + assert.equal( + res.body.error, + '[INVALID_PARAM] version: must match format "semver-version"' + ); }); it('should 422 when tag invalid', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/111`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .set('content-type', 'application/json') .send(JSON.stringify('1.0.0')) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] tag: must match format "semver-tag"'); + assert.equal( + res.body.error, + '[INVALID_PARAM] tag: must match format "semver-tag"' + ); }); it('should 200 when publish package in current registry', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - const { pkg, user } = await TestUtil.createPackage({ name: 'non_scope_pkg', version: '1.0.0' }); - await app.httpRequest() + const { pkg, user } = await TestUtil.createPackage({ + name: 'non_scope_pkg', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -135,14 +182,19 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { }); it('should 200', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -150,15 +202,14 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { .send(JSON.stringify('1.0.0')) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.deepEqual(res.body['dist-tags'], { latest: '1.0.0', beta: '1.0.0', }); // save tag and version ignore - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -167,7 +218,8 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { .expect(200); assert.equal(res.body.ok, true); // support latest tag - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/latest`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -175,9 +227,7 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { .send(JSON.stringify('1.0.0')) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.deepEqual(res.body['dist-tags'], { latest: '1.0.0', beta: '1.0.0', @@ -185,8 +235,12 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { }); it('should 200 on automation token', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -197,7 +251,8 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { token: publisher.token, automation: true, }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/automation`) .set('authorization', userAutomation.authorization) .set('user-agent', publisher.ua) @@ -205,14 +260,13 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { .send(JSON.stringify('1.0.0')) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.deepEqual(res.body['dist-tags'], { latest: '1.0.0', automation: '1.0.0', }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/latest-3`) .set('authorization', userAutomation.authorization) .set('user-agent', publisher.ua) @@ -220,9 +274,7 @@ describe('test/port/controller/PackageTagController/saveTag.test.ts', () => { .send(JSON.stringify('1.0.0')) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.deepEqual(res.body['dist-tags'], { latest: '1.0.0', 'latest-3': '1.0.0', diff --git a/test/port/controller/PackageTagController/showTags.test.ts b/test/port/controller/PackageTagController/showTags.test.ts index 93140767..d33b9c52 100644 --- a/test/port/controller/PackageTagController/showTags.test.ts +++ b/test/port/controller/PackageTagController/showTags.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; describe('test/port/controller/PackageTagController/showTags.test.ts', () => { let publisher: TestUser; @@ -11,7 +12,8 @@ describe('test/port/controller/PackageTagController/showTags.test.ts', () => { describe('[GET /-/package/:fullname/dist-tags] showTags()', () => { it('should 404 when package not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/-/package/@cnpm/not-exists/dist-tags') .expect(404); assert.equal(res.body.error, '[NOT_FOUND] @cnpm/not-exists not found'); @@ -19,44 +21,51 @@ describe('test/port/controller/PackageTagController/showTags.test.ts', () => { it('should 404 when package not exists on syncMode=all', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - let res = await app.httpRequest() - .get('/-/package/not-exists/dist-tags'); + let res = await app.httpRequest().get('/-/package/not-exists/dist-tags'); assert(res.status === 404); assert(res.body.error === '[NOT_FOUND] not-exists not found'); - res = await app.httpRequest() - .get('/-/package/@foo/not-exists/dist-tags'); + res = await app.httpRequest().get('/-/package/@foo/not-exists/dist-tags'); assert(res.status === 404); assert(res.body.error === '[NOT_FOUND] @foo/not-exists not found'); }); it('should 302 when package not exists on syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - let res = await app.httpRequest() - .get('/-/package/not-exists/dist-tags'); + let res = await app.httpRequest().get('/-/package/not-exists/dist-tags'); assert(res.status === 302); - assert(res.headers.location === 'https://registry.npmjs.org/-/package/not-exists/dist-tags'); + assert( + res.headers.location === + 'https://registry.npmjs.org/-/package/not-exists/dist-tags' + ); - res = await app.httpRequest() - .get('/-/package/@foo/not-exists/dist-tags'); + res = await app.httpRequest().get('/-/package/@foo/not-exists/dist-tags'); assert(res.status === 302); - assert(res.headers.location === 'https://registry.npmjs.org/-/package/@foo/not-exists/dist-tags'); + assert( + res.headers.location === + 'https://registry.npmjs.org/-/package/@foo/not-exists/dist-tags' + ); }); it('should get package tags', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/koa', version: '1.0.0' }); - await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/koa', + version: '1.0.0', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/-/package/${pkg.name}/dist-tags`) .expect(200); assert.equal(res.body.latest, '1.0.0'); - assert.deepEqual(Object.keys(res.body), [ 'latest' ]); + assert.deepEqual(Object.keys(res.body), ['latest']); }); }); }); diff --git a/test/port/controller/PackageVersionFileController/listFiles.test.ts b/test/port/controller/PackageVersionFileController/listFiles.test.ts index 08a835bc..80d27c07 100644 --- a/test/port/controller/PackageVersionFileController/listFiles.test.ts +++ b/test/port/controller/PackageVersionFileController/listFiles.test.ts @@ -2,7 +2,8 @@ import { strict as assert } from 'node:assert'; import { setTimeout } from 'node:timers/promises'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { PackageVersionFileService } from '../../../../app/core/service/PackageVersionFileService.js'; import { calculateIntegrity } from '../../../../app/common/PackageUtil.js'; import { database, DATABASE_TYPE } from '../../../../config/database.js'; @@ -26,18 +27,21 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foo/1.0.0/files') .expect(404) .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.body.error, '[NOT_FOUND] Not Found'); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.0/files/package.json') .expect(404) .expect('content-type', 'application/json; charset=utf-8'); @@ -55,31 +59,41 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', description: 'empty main', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foo/1.0.0/files') .expect(302) .expect('location', '/foo/1.0.0/files/index.js'); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.0/files/index.js') .expect(404) .expect('content-type', 'application/json; charset=utf-8'); - assert.equal(res.body.error, '[NOT_FOUND] File foo@1.0.0/index.js not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] File foo@1.0.0/index.js not found' + ); }); it('should 422 when invalid spec', async () => { mock(app.config.cnpmcore, 'enableUnpkg', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foo/@invalid-spec/files') .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] must match format "semver-spec"'); + assert.equal( + res.body.error, + '[INVALID_PARAM] must match format "semver-spec"' + ); }); it('should list one package version files', async () => { @@ -91,23 +105,26 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() - .get('/foo/1.0.0') - .expect(200); + let res = await app.httpRequest().get('/foo/1.0.0').expect(200); const publishTime = new Date(res.body.publish_time).toISOString(); const oldReadme = res.body.readme; - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.0/files/') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); // console.log(res.body); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.deepEqual(res.body, { path: '/', @@ -117,25 +134,28 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', + integrity: + 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', lastModified: publishTime, size: 209, }, ], }); // not found README.md file, readme not change - res = await app.httpRequest() - .get('/foo/1.0.0') - .expect(200); + res = await app.httpRequest().get('/foo/1.0.0').expect(200); assert.equal(res.body.readme, oldReadme); // again should work - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.0/files?meta') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); // console.log(res.body); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.deepEqual(res.body, { path: '/', @@ -145,7 +165,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', + integrity: + 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', lastModified: publishTime, size: 209, }, @@ -155,7 +176,9 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', it('should return the current directory files and directories instead all sub items', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - const tarball = await TestUtil.readFixturesFile('unpkg.com/openapi-7.3.3.tgz'); + const tarball = await TestUtil.readFixturesFile( + 'unpkg.com/openapi-7.3.3.tgz' + ); const { integrity } = await calculateIntegrity(tarball); const pkg = await TestUtil.getFullPackage({ name: 'openapi', @@ -172,14 +195,14 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }, main: './lib/index.js', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/`); assert.equal(res.status, 200); for (const file of res.body.files) { if (!file.lastModified) continue; @@ -197,7 +220,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/LICENSE', type: 'file', contentType: 'text/plain', - integrity: 'sha512-OJCAthMtPqrngGSNaZg5DYzHGQhWG84JV44nxUKqGp8xIuAfZAxbAb7nMATCOqTp8gZv5e4MogcsJCBXiyjXHw==', + integrity: + 'sha512-OJCAthMtPqrngGSNaZg5DYzHGQhWG84JV44nxUKqGp8xIuAfZAxbAb7nMATCOqTp8gZv5e4MogcsJCBXiyjXHw==', lastModified: '2024-05-18T16:00:18.307Z', size: 11357, }, @@ -205,7 +229,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/index.html', type: 'file', contentType: 'text/html', - integrity: 'sha512-L4Vxx8DW1PtZfPut4uwP9DSK9+DbFbKDWWGp4KK5TRKGTHSjYoMExqY50WiTKs/bGu1Ecpneiu3dnYlRZ/sDdw==', + integrity: + 'sha512-L4Vxx8DW1PtZfPut4uwP9DSK9+DbFbKDWWGp4KK5TRKGTHSjYoMExqY50WiTKs/bGu1Ecpneiu3dnYlRZ/sDdw==', lastModified: '2024-05-18T16:00:18.307Z', size: 1437, }, @@ -213,7 +238,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-ke5ybpErJgl+Mul1XCSMvly0uYAt8/5mWa5/yYykxfMCE0OBpzgWoFHC+/RM9AQfNgic3bW/ssHXDUUPZiEKkg==', + integrity: + 'sha512-ke5ybpErJgl+Mul1XCSMvly0uYAt8/5mWa5/yYykxfMCE0OBpzgWoFHC+/RM9AQfNgic3bW/ssHXDUUPZiEKkg==', lastModified: '2024-05-18T16:00:18.307Z', size: 2852, }, @@ -221,7 +247,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/CHANGES.md', type: 'file', contentType: 'text/markdown', - integrity: 'sha512-xxD+0Mdep4Pprq0JsudGLCKtSfHBeIqJVoGqM0qK1b2B/0sXjSQYinxgAwjK8rKSD0jNSo3R5aK8VbgOXLtbjw==', + integrity: + 'sha512-xxD+0Mdep4Pprq0JsudGLCKtSfHBeIqJVoGqM0qK1b2B/0sXjSQYinxgAwjK8rKSD0jNSo3R5aK8VbgOXLtbjw==', lastModified: '2024-05-18T16:00:18.307Z', size: 12346, }, @@ -229,7 +256,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/README.md', type: 'file', contentType: 'text/markdown', - integrity: 'sha512-Nnj8b9SsDDobga1LsV7FVE46YrxkdZf5MOMboVHICw56tPHnQ0v1lXvXkWz7k12kTFWbA0z42daaW7WE+AQWfw==', + integrity: + 'sha512-Nnj8b9SsDDobga1LsV7FVE46YrxkdZf5MOMboVHICw56tPHnQ0v1lXvXkWz7k12kTFWbA0z42daaW7WE+AQWfw==', lastModified: '2024-05-18T16:00:18.307Z', size: 4409, }, @@ -237,7 +265,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/.npmcheckrc.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-EYTJJ5StGM9DUpAbF8XHV4Z02rlmzN9O6k93fu1YXpf1wDBtmFYG64xaTXk2UfB8x0BCotga+Upm1yOgJVIZTQ==', + integrity: + 'sha512-EYTJJ5StGM9DUpAbF8XHV4Z02rlmzN9O6k93fu1YXpf1wDBtmFYG64xaTXk2UfB8x0BCotga+Upm1yOgJVIZTQ==', lastModified: '2024-05-18T16:00:18.307Z', size: 105, }, @@ -245,7 +274,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/.redocly.lint-ignore.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-tyPeiIaOGIXb3PNFb2ELAZawxGHSdPZ7IoLdl+tEcDARVFlq6B9yJVAzL5R8L26iCBbvPtlfNGnYkHj4H/5ZMA==', + integrity: + 'sha512-tyPeiIaOGIXb3PNFb2ELAZawxGHSdPZ7IoLdl+tEcDARVFlq6B9yJVAzL5R8L26iCBbvPtlfNGnYkHj4H/5ZMA==', lastModified: '2024-05-18T16:00:18.307Z', size: 644, }, @@ -253,7 +283,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/index.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-KW7xaZW5F8NOGt72kc9WvLcvkFDmXbm65JdWPM2pYfy9HMX0/6obJD5jhzQSX5ZU8ww0HMlXGXkRviFnDr88ZA==', + integrity: + 'sha512-KW7xaZW5F8NOGt72kc9WvLcvkFDmXbm65JdWPM2pYfy9HMX0/6obJD5jhzQSX5ZU8ww0HMlXGXkRviFnDr88ZA==', lastModified: '2024-05-18T16:00:18.307Z', size: 21379, }, @@ -261,7 +292,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/.eslintrc.yml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-3q0aghG4dBd7pgE4UrbtVn52cfg3BqOPkuNcCSwHZKMSFnKZxWr+sH7/OgnBDaifVsXGK7AN8q7sX0Eds6Ditw==', + integrity: + 'sha512-3q0aghG4dBd7pgE4UrbtVn52cfg3BqOPkuNcCSwHZKMSFnKZxWr+sH7/OgnBDaifVsXGK7AN8q7sX0Eds6Ditw==', lastModified: '2024-05-18T16:00:18.307Z', size: 149, }, @@ -324,8 +356,7 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }); } - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/id/?meta`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/id/?meta`); assert.equal(res.status, 200); for (const file of res.body.files) { if (!file.lastModified) continue; @@ -343,7 +374,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/AccountId.d.ts', type: 'file', contentType: 'text/plain', - integrity: 'sha512-xj1/RCRAp72pukals97C98DG0b38Gl2xNrUwOi2SRj+EnJKIfQX8WisDpCOSKLFq5j++sGbL0/4wCttrPvi37w==', + integrity: + 'sha512-xj1/RCRAp72pukals97C98DG0b38Gl2xNrUwOi2SRj+EnJKIfQX8WisDpCOSKLFq5j++sGbL0/4wCttrPvi37w==', lastModified: '2024-05-18T16:00:18.307Z', size: 787, }, @@ -351,7 +383,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/AccountId.js', type: 'file', contentType: 'application/javascript', - integrity: 'sha512-kFa+SXSMGbCh2DiuSGmlCS8OCBSE4VRGlq/A2IyY3QxL794soFq4zO3F+UEx4ANUG33urAa4LG4IY2OiUc2Mng==', + integrity: + 'sha512-kFa+SXSMGbCh2DiuSGmlCS8OCBSE4VRGlq/A2IyY3QxL794soFq4zO3F+UEx4ANUG33urAa4LG4IY2OiUc2Mng==', lastModified: '2024-05-18T16:00:18.307Z', size: 1343, }, @@ -359,7 +392,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/AccountId.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-R6WB9dXEaNpvqIAH6OdRQ77gSEBlq1GeH2jv2tv1wQEVOmzQtErHlpj+ukvZUwzqf9wTXIPxKjeUhqk6VbfBkA==', + integrity: + 'sha512-R6WB9dXEaNpvqIAH6OdRQ77gSEBlq1GeH2jv2tv1wQEVOmzQtErHlpj+ukvZUwzqf9wTXIPxKjeUhqk6VbfBkA==', lastModified: '2024-05-18T16:00:18.307Z', size: 571, }, @@ -367,7 +401,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/Mode.js', type: 'file', contentType: 'application/javascript', - integrity: 'sha512-jfMuIff4LW/ZQ8el9iCww8c9gw+12UK7eZn+6TMDAlStfLhlu8u7jcCSWSEG1zBTty9DIHn4Nbp+dMDjRUnVWQ==', + integrity: + 'sha512-jfMuIff4LW/ZQ8el9iCww8c9gw+12UK7eZn+6TMDAlStfLhlu8u7jcCSWSEG1zBTty9DIHn4Nbp+dMDjRUnVWQ==', lastModified: '2024-05-18T16:00:18.307Z', size: 3357, }, @@ -375,7 +410,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/mode.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-er9S1Da52G8fxwfgxhNbcXPdYz9bzABM7VifDXhgVGX+hwtu8tve9y2aZhPAHcJOy3dClMDQ1eYLAHp7k8TMNQ==', + integrity: + 'sha512-er9S1Da52G8fxwfgxhNbcXPdYz9bzABM7VifDXhgVGX+hwtu8tve9y2aZhPAHcJOy3dClMDQ1eYLAHp7k8TMNQ==', lastModified: '2024-05-18T16:00:18.307Z', size: 1222, }, @@ -383,7 +419,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/UUID.js', type: 'file', contentType: 'application/javascript', - integrity: 'sha512-bo/JyxOZeRRjbN0OR8vNRz3cTY2GcJfRmRnp3QTGXE5iuKYjrpjYzj+vEXopZV1QYPdZaXUK671EoysPE59pQQ==', + integrity: + 'sha512-bo/JyxOZeRRjbN0OR8vNRz3cTY2GcJfRmRnp3QTGXE5iuKYjrpjYzj+vEXopZV1QYPdZaXUK671EoysPE59pQQ==', lastModified: '2024-05-18T16:00:18.307Z', size: 992, }, @@ -391,7 +428,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/UUID.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-Gjr0LNqWQcO5/oaCyMm9oZWpc/D9K6Qe37sGuYv4kbq0I8teZL92xbR81L+2VShkhLSXdg2Qw5WRjwCkSWyfoA==', + integrity: + 'sha512-Gjr0LNqWQcO5/oaCyMm9oZWpc/D9K6Qe37sGuYv4kbq0I8teZL92xbR81L+2VShkhLSXdg2Qw5WRjwCkSWyfoA==', lastModified: '2024-05-18T16:00:18.307Z', size: 659, }, @@ -414,7 +452,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }); } - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/id/legalPerson/?meta`); assert.equal(res.status, 200); // console.log(JSON.stringify(res.body, null, 2)); @@ -430,7 +469,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', ], }); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/id/legalPerson/be/?meta`); assert.equal(res.status, 200); for (const file of res.body.files) { @@ -446,7 +486,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/legalPerson/be/CRN.js', type: 'file', contentType: 'application/javascript', - integrity: 'sha512-K7fRjnkAkNnSYbWZW4A+xcdYbI2J1fk49AxFVut2Kk6LXOZbLH6nU9CFeo0YixDLa1Hl5sjLiUQ7Mur2HQgvNw==', + integrity: + 'sha512-K7fRjnkAkNnSYbWZW4A+xcdYbI2J1fk49AxFVut2Kk6LXOZbLH6nU9CFeo0YixDLa1Hl5sjLiUQ7Mur2HQgvNw==', lastModified: '2024-05-18T16:00:18.307Z', size: 3285, }, @@ -454,7 +495,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/legalPerson/be/CRN.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-pG12081uMexKHGfmetjZ5p6sB1z+Y/StRyRC1BOW/CGcuLW8iDdY848C6gS9qEXq0DAQwIg9jv18uf4uP1lOwg==', + integrity: + 'sha512-pG12081uMexKHGfmetjZ5p6sB1z+Y/StRyRC1BOW/CGcuLW8iDdY848C6gS9qEXq0DAQwIg9jv18uf4uP1lOwg==', lastModified: '2024-05-18T16:00:18.307Z', size: 2793, }, @@ -462,7 +504,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/id/legalPerson/be/KBO.yaml', type: 'file', contentType: 'text/yaml', - integrity: 'sha512-8s8lUEsYAJfPw1ar9l6fUxOapU1q5GzuhsprQrOmsGRbDNildPvzdO5KPVXQdoz4aHxMkOIxaVDDQl1NB1OPAA==', + integrity: + 'sha512-8s8lUEsYAJfPw1ar9l6fUxOapU1q5GzuhsprQrOmsGRbDNildPvzdO5KPVXQdoz4aHxMkOIxaVDDQl1NB1OPAA==', lastModified: '2024-05-18T16:00:18.307Z', size: 700, }, @@ -478,51 +521,67 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', description: 'foo latest description', }, }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg.name}/latest`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/latest`).expect(200); const publishTime = new Date(res.body.publish_time).toISOString(); - res = await app.httpRequest() - .get(`/${pkg.name}/latest/files`); + res = await app.httpRequest().get(`/${pkg.name}/latest/files`); assert.equal(res.status, 302); assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - res = await app.httpRequest() - .get(`/${pkg.name}/^1.0.0/files`); + res = await app.httpRequest().get(`/${pkg.name}/^1.0.0/files`); assert.equal(res.status, 302); assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - res = await app.httpRequest() - .get(`/${pkg.name}/%5E1.0.0/files`); + res = await app.httpRequest().get(`/${pkg.name}/%5E1.0.0/files`); assert.equal(res.status, 302); assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/latest/files?meta&foo=bar`); assert.equal(res.status, 302); - assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files?meta&foo=bar`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers.location, + `/${pkg.name}/1.0.0/files?meta&foo=bar` + ); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - res = await app.httpRequest() - .get(`/${pkg.name}/latest/files/`); + res = await app.httpRequest().get(`/${pkg.name}/latest/files/`); assert.equal(res.status, 302); assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files/`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files?meta=1`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files?meta=1`); assert.equal(res.status, 200); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.deepEqual(res.body, { path: '/', @@ -532,15 +591,18 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', + integrity: + 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', lastModified: publishTime, size: 209, }, ], }); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/`); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.deepEqual(res.body, { path: '/', @@ -550,7 +612,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', + integrity: + 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', lastModified: publishTime, size: 209, }, @@ -567,13 +630,15 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foo/1.0.0/files/foo/') .expect(404) .expect('content-type', 'application/json; charset=utf-8'); @@ -588,13 +653,15 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/package.json`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -602,24 +669,33 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', it('should 451 when package block', async () => { const { pkg } = await TestUtil.createPackage({ isPrivate: false }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/-/package/${pkg.name}/blocks`) .set('authorization', adminUser.authorization) .send({ reason: 'only for tests again', }); assert.equal(res.status, 201); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/foo/`) .expect(451) .expect('content-type', 'application/json; charset=utf-8'); - assert.match(res.body.error, /\[UNAVAILABLE_FOR_LEGAL_REASONS] @cnpm\/testmodule@1.0.0 was blocked, reason: only for tests again/); + assert.match( + res.body.error, + /\[UNAVAILABLE_FOR_LEGAL_REASONS] @cnpm\/testmodule@1.0.0 was blocked, reason: only for tests again/ + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files`) .expect(451) .expect('content-type', 'application/json; charset=utf-8'); - assert.match(res.body.error, /\[UNAVAILABLE_FOR_LEGAL_REASONS] @cnpm\/testmodule@1.0.0 was blocked, reason: only for tests again/); + assert.match( + res.body.error, + /\[UNAVAILABLE_FOR_LEGAL_REASONS] @cnpm\/testmodule@1.0.0 was blocked, reason: only for tests again/ + ); }); it('should 404 when version not exists', async () => { @@ -630,28 +706,37 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', description: 'foo description', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${pkg.name}/1.0.40000404/files`) .expect(404); assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert.equal(res.body.error, `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found` + ); }); it('should 404 when package not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/@cnpm/foonot-exists/1.0.40000404/files') .expect(404); assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert.equal(res.body.error, '[NOT_FOUND] @cnpm/foonot-exists@1.0.40000404 not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] @cnpm/foonot-exists@1.0.40000404 not found' + ); }); it('should conflict when syncing', async () => { @@ -664,17 +749,33 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }, }); let called = 0; - mock(PackageVersionFileService.prototype, 'syncPackageVersionFiles', async () => { - called++; - await setTimeout(50); - }); - const resList = await Promise.all([ 0, 1 ].map(() => app.httpRequest().get(`/${pkg.name}/1.0.0/files/`))); + mock( + PackageVersionFileService.prototype, + 'syncPackageVersionFiles', + async () => { + called++; + await setTimeout(50); + } + ); + const resList = await Promise.all( + [0, 1].map(() => app.httpRequest().get(`/${pkg.name}/1.0.0/files/`)) + ); assert.equal(called, 1); - assert.equal(resList.filter(res => res.status === 409 && res.body.error === '[CONFLICT] Package version file sync is currently in progress. Please try again later.').length, 1); + assert.equal( + resList.filter( + res => + res.status === 409 && + res.body.error === + '[CONFLICT] Package version file sync is currently in progress. Please try again later.' + ).length, + 1 + ); }); it('should redirect to possible entry', async () => { - const tarball = await TestUtil.readFixturesFile('@cnpm/cnpm-test-find-entry-1.0.0.tgz'); + const tarball = await TestUtil.readFixturesFile( + '@cnpm/cnpm-test-find-entry-1.0.0.tgz' + ); const { integrity } = await calculateIntegrity(tarball); const pkg = await TestUtil.getFullPackage({ name: '@cnpm/test-find-entry', @@ -691,29 +792,34 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/es/array/at`) .expect(302) .expect('location', `/${pkg.name}/1.0.0/files/es/array/at.js`); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/es/array`) .expect(302) .expect('location', `/${pkg.name}/1.0.0/files/es/array/index.js`); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/es/json/test`) .expect(302) .expect('location', `/${pkg.name}/1.0.0/files/es/json/test.json`); - await app.httpRequest() + await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/es/json`) .expect(302) .expect('location', `/${pkg.name}/1.0.0/files/es/json/index.json`); @@ -729,20 +835,26 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'foo', version: '1.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foo/1.0.0/files/index.js') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 403); - assert.equal(res.body.error, '[FORBIDDEN] "foo" is not allow to unpkg files, see https://github.com/cnpm/unpkg-white-list'); + assert.equal( + res.body.error, + '[FORBIDDEN] "foo" is not allow to unpkg files, see https://github.com/cnpm/unpkg-white-list' + ); }); it('should 403 package version not match', async () => { @@ -754,7 +866,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'unpkg-white-list', version: '0.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', allowPackages: { foo: { version: '0.0.0', @@ -762,7 +875,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }, }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -772,20 +886,26 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'foo', version: '1.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foo/1.0.0/files/index.js') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 403); - assert.equal(res.body.error, '[FORBIDDEN] "foo@1.0.0" not satisfies "0.0.0" to unpkg files, see https://github.com/cnpm/unpkg-white-list'); + assert.equal( + res.body.error, + '[FORBIDDEN] "foo@1.0.0" not satisfies "0.0.0" to unpkg files, see https://github.com/cnpm/unpkg-white-list' + ); }); it('should 200 when scope in white list', async () => { @@ -797,11 +917,13 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'unpkg-white-list', version: '1.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', - allowScopes: [ '@cnpm' ], + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + allowScopes: ['@cnpm'], }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -811,16 +933,19 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: '@cnpm/foo', version: '1.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/@cnpm/foo/1.0.0/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 200); @@ -836,8 +961,9 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'unpkg-white-list', version: '2.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', - allowScopes: [ '@cnpm' ], + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + allowScopes: ['@cnpm'], allowPackages: { foo: { version: '*', @@ -845,7 +971,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }, }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -855,17 +982,20 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'foo', version: '1.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foo/1.0.0/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 200); @@ -875,16 +1005,19 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'foo', version: '1.0.1', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.1/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 200); @@ -895,8 +1028,9 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'unpkg-white-list', version: '2.0.1', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', - allowScopes: [ '@cnpm' ], + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + allowScopes: ['@cnpm'], allowPackages: { foo: { version: '3', @@ -904,7 +1038,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }, }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -914,21 +1049,27 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'foo', version: '1.0.2', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.2/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 403); - assert.equal(res.body.error, '[FORBIDDEN] "foo@1.0.2" not satisfies "3" to unpkg files, see https://github.com/cnpm/unpkg-white-list'); + assert.equal( + res.body.error, + '[FORBIDDEN] "foo@1.0.2" not satisfies "3" to unpkg files, see https://github.com/cnpm/unpkg-white-list' + ); }); it('bugfix: should support rc version', async () => { @@ -941,8 +1082,9 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'unpkg-white-list', version: '2.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', - allowScopes: [ '@cnpm' ], + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + allowScopes: ['@cnpm'], allowPackages: { foo: { version: '*', @@ -956,7 +1098,8 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', }, }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -966,17 +1109,20 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'foo', version: '0.0.0', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foo/0.0.0/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 200); @@ -986,16 +1132,19 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'foo', version: '0.3.0-rc15', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/0.3.0-rc15/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 200); @@ -1005,16 +1154,19 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'baz', version: '0.3.0-rc15', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/baz/0.3.0-rc15/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 200); @@ -1024,21 +1176,26 @@ describe('test/port/controller/PackageVersionFileController/listFiles.test.ts', name: 'bar', version: '0.3.0-rc15', versionObject: { - description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + description: + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/bar/0.3.0-rc15/files/package.json') .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.status, 403); - assert.equal(res.body.error, - '[FORBIDDEN] "bar@0.3.0-rc15" not satisfies "1.0.0" to unpkg files, see https://github.com/cnpm/unpkg-white-list'); + assert.equal( + res.body.error, + '[FORBIDDEN] "bar@0.3.0-rc15" not satisfies "1.0.0" to unpkg files, see https://github.com/cnpm/unpkg-white-list' + ); }); }); }); diff --git a/test/port/controller/PackageVersionFileController/raw.test.ts b/test/port/controller/PackageVersionFileController/raw.test.ts index 5187bb0d..c7275013 100644 --- a/test/port/controller/PackageVersionFileController/raw.test.ts +++ b/test/port/controller/PackageVersionFileController/raw.test.ts @@ -2,7 +2,8 @@ import { strict as assert } from 'node:assert'; import { setTimeout } from 'node:timers/promises'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { calculateIntegrity } from '../../../../app/common/PackageUtil.js'; import { PackageTagChangedSyncPackageVersionFileEvent, @@ -27,13 +28,15 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foo/1.0.0/files/package.json') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -51,7 +54,8 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }); // again should work - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.0/files/package.json') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -70,25 +74,46 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }); // should redirect on tag request - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/latest/files/package.json`); assert.equal(res.status, 302); - assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files/package.json`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers.location, + `/${pkg.name}/1.0.0/files/package.json` + ); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/^1.0.0/files/package.json`); assert.equal(res.status, 302); - assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files/package.json`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers.location, + `/${pkg.name}/1.0.0/files/package.json` + ); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/%5E1.0.0/files/package.json`); assert.equal(res.status, 302); - assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files/package.json`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers.location, + `/${pkg.name}/1.0.0/files/package.json` + ); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); }); @@ -101,13 +126,15 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foo-block-again/1.0.0/files/package.json') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -127,12 +154,15 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => await setTimeout(1); mock(app.config.cnpmcore, 'enableSyncUnpkgFilesWhiteList', true); // should block - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo-block-again/1.0.0/files/package.json') .expect(403) .expect('content-type', 'application/json; charset=utf-8'); - assert.equal(res.body.error, - '[FORBIDDEN] "foo-block-again" is not allow to unpkg files, see https://github.com/cnpm/unpkg-white-list'); + assert.equal( + res.body.error, + '[FORBIDDEN] "foo-block-again" is not allow to unpkg files, see https://github.com/cnpm/unpkg-white-list' + ); // add white list pkg = await TestUtil.getFullPackage({ @@ -147,14 +177,16 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }, }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); await setTimeout(1); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo-block-again/1.0.0/files/package.json') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -181,59 +213,80 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`) - .expect(200); + let res = await app.httpRequest().get(`/${pkg.name}/1.0.0`).expect(200); const publishTime = new Date(res.body.publish_time).toISOString(); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/package.json?meta`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); // console.log(res.body); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.deepEqual(res.body, { path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', + integrity: + 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', lastModified: publishTime, size: 209, }); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/latest/files/package.json?meta=2`); assert.equal(res.status, 302); - assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files/package.json?meta=2`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers.location, + `/${pkg.name}/1.0.0/files/package.json?meta=2` + ); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); // file path not exists - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/1.0.0/files/package2.json?meta') .expect(404); assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert.equal(res.body.error, `[NOT_FOUND] File ${pkg.name}@1.0.0/package2.json not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] File ${pkg.name}@1.0.0/package2.json not found` + ); }); it('should 422 when invalid spec', async () => { mock(app.config.cnpmcore, 'enableUnpkg', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foo/@invalid-spec/files/package.json?meta') .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] must match format "semver-spec"'); + assert.equal( + res.body.error, + '[INVALID_PARAM] must match format "semver-spec"' + ); }); it('should ignore not exists file on tar onentry', async () => { - const tarball = await TestUtil.readFixturesFile('unpkg.com/ide-metrics-api-grpc-0.0.1-main-gha.8962.tgz'); + const tarball = await TestUtil.readFixturesFile( + 'unpkg.com/ide-metrics-api-grpc-0.0.1-main-gha.8962.tgz' + ); const { integrity } = await calculateIntegrity(tarball); const pkg = await TestUtil.getFullPackage({ name: '@cnpm/foo-tag-latest', @@ -250,20 +303,22 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }, main: './lib/index.js', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/`); assert.equal(res.status, 200); }); it('should support non-ascii file name', async () => { // https://unpkg.com/browse/@ppwcode/openapi@7.3.3/resource/ToOneFrom%CF%87.js - const tarball = await TestUtil.readFixturesFile('unpkg.com/openapi-7.3.3.tgz'); + const tarball = await TestUtil.readFixturesFile( + 'unpkg.com/openapi-7.3.3.tgz' + ); const { integrity } = await calculateIntegrity(tarball); const pkg = await TestUtil.getFullPackage({ name: '@cnpm/foo-tag-latest', @@ -280,30 +335,40 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }, main: './lib/index.js', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/resource/`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/resource/`); assert.equal(res.status, 200); // console.log(res.body); - assert(res.body.files.find((file: { path: string }) => file.path === '/resource/ToOneFromχ.js')); + assert( + res.body.files.find( + (file: { path: string }) => file.path === '/resource/ToOneFromχ.js' + ) + ); // res = await app.httpRequest() // .get(`/${pkg.name}/1.0.0/files/resource/ToOneFromχ.js`); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/resource/ToOneFrom%CF%87.js`); assert.equal(res.status, 200); - assert.equal(res.headers['content-type'], 'application/javascript; charset=utf-8'); + assert.equal( + res.headers['content-type'], + 'application/javascript; charset=utf-8' + ); // console.log(res.text); assert.match(res.text, /ToOneFromχ/); }); it('should support non-npm pack tgz file', async () => { // https://github.com/cnpm/cnpmcore/issues/452#issuecomment-1570077310 - const tarball = await TestUtil.readFixturesFile('unpkg.com/lodash-es-4.17.7.tgz'); + const tarball = await TestUtil.readFixturesFile( + 'unpkg.com/lodash-es-4.17.7.tgz' + ); const { integrity } = await calculateIntegrity(tarball); const pkg = await TestUtil.getFullPackage({ name: '@cnpm/lodash-es', @@ -320,30 +385,36 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }, main: '', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/`); assert.equal(res.status, 200); - assert(res.body.files.find((file: { path: string }) => file.path === '/package.json')); - res = await app.httpRequest() + assert( + res.body.files.find( + (file: { path: string }) => file.path === '/package.json' + ) + ); + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/zipObjectDeep.d.ts`); assert.equal(res.status, 200); assert.equal(res.headers['content-type'], 'text/plain; charset=utf-8'); assert.match(res.text, /export default zipObjectDeep/); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files`); assert.equal(res.status, 302); assert.equal(res.header.location, `/${pkg.name}/1.0.0/files/index.js`); }); it('should ignore "." hidden dir', async () => { // https://unpkg.com/browse/bovo-ui@0.0.4-36/ - const tarball = await TestUtil.readFixturesFile('unpkg.com/bovo-ui-0.0.4-36.tgz'); + const tarball = await TestUtil.readFixturesFile( + 'unpkg.com/bovo-ui-0.0.4-36.tgz' + ); const { integrity } = await calculateIntegrity(tarball); const pkg = await TestUtil.getFullPackage({ name: '@cnpm/bovo-ui', @@ -360,33 +431,40 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }, main: './lib/index.js', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/`); assert.equal(res.status, 200); // console.log(res.body); - assert.equal(res.body.files.find((file: { path: string }) => file.path === '/.'), undefined); - assert(res.body.files.find((file: { path: string }) => file.path === '/dist')); - const packageTagAdded = await app.getEggObject(PackageTagAddedSyncPackageVersionFileEvent); + assert.equal( + res.body.files.find((file: { path: string }) => file.path === '/.'), + undefined + ); + assert( + res.body.files.find((file: { path: string }) => file.path === '/dist') + ); + const packageTagAdded = await app.getEggObject( + PackageTagAddedSyncPackageVersionFileEvent + ); await packageTagAdded.handle(pkg.name, 'foo'); await packageTagAdded.handle(pkg.name, 'latest'); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0`); const readme = res.body.readme; assert.match(readme, /# bovo-ui/); // pkg readme change to latest - res = await app.httpRequest() - .get(`/${pkg.name}`); + res = await app.httpRequest().get(`/${pkg.name}`); assert.equal(res.body.readme, readme); }); it('should handle big tgz file', async () => { - const tarball = await TestUtil.readFixturesFile('unpkg.com/pouchdb-3.2.1.tgz'); + const tarball = await TestUtil.readFixturesFile( + 'unpkg.com/pouchdb-3.2.1.tgz' + ); const { integrity } = await calculateIntegrity(tarball); const pkg = await TestUtil.getFullPackage({ name: '@cnpm/foo-tag-latest', @@ -403,143 +481,194 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => }, main: './lib/index.js', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0`).expect(200); const publishTime = new Date(res.body.publish_time).toISOString(); const oldReadme = res.body.readme; - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/`); assert.equal(res.status, 200); // console.log('%o', res.body); - assert(res.body.files.find((file: { path: string }) => file.path === '/CONTRIBUTING.md')); - let testDir = res.body.files.find((file: { path: string }) => file.path === '/tests'); + assert( + res.body.files.find( + (file: { path: string }) => file.path === '/CONTRIBUTING.md' + ) + ); + let testDir = res.body.files.find( + (file: { path: string }) => file.path === '/tests' + ); assert(testDir); assert.equal(testDir.files.length, 0); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.equal(res.body.path, '/'); // pkg version readme should change - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0`).expect(200); assert.notEqual(res.body.readme, oldReadme); assert.match(res.body.readme, /The Javascript Database that Syncs/); - const packageTagChanged = await app.getEggObject(PackageTagChangedSyncPackageVersionFileEvent); + const packageTagChanged = await app.getEggObject( + PackageTagChangedSyncPackageVersionFileEvent + ); await packageTagChanged.handle(pkg.name, 'foo'); await packageTagChanged.handle(pkg.name, 'latest'); // pkg version change too - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.match(res.body.readme, /The Javascript Database that Syncs/); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files?meta=true`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files?meta=true`); assert.equal(res.status, 200); // console.log('%o', res.body); - assert(res.body.files.find((file: { path: string }) => file.path === '/CONTRIBUTING.md')); - testDir = res.body.files.find((file: { path: string }) => file.path === '/tests'); + assert( + res.body.files.find( + (file: { path: string }) => file.path === '/CONTRIBUTING.md' + ) + ); + testDir = res.body.files.find( + (file: { path: string }) => file.path === '/tests' + ); assert(testDir); assert.equal(testDir.files.length, 0); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.equal(res.body.path, '/'); // redirect to main file - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files`); assert.equal(res.status, 302); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - assert.equal(res.headers.location, `/${pkg.name}/1.0.0/files/lib/index.js`); + assert.equal( + res.headers.location, + `/${pkg.name}/1.0.0/files/lib/index.js` + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/lib/index.js`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - assert.equal(res.headers['content-type'], 'application/javascript; charset=utf-8'); + assert.equal( + res.headers['content-type'], + 'application/javascript; charset=utf-8' + ); assert.equal(res.headers['transfer-encoding'], 'chunked'); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/docs/_site/getting-started.html`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.equal(res.headers['content-type'], 'text/html; charset=utf-8'); - assert.equal(res.headers['content-disposition'], 'attachment; filename="getting-started.html"'); + assert.equal( + res.headers['content-disposition'], + 'attachment; filename="getting-started.html"' + ); assert.equal(res.headers['transfer-encoding'], 'chunked'); assert.match(res.text, //); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/docs/_site/getting-started.html?meta`); assert.equal(res.status, 200); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - assert.equal(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.equal( + res.headers['content-type'], + 'application/json; charset=utf-8' + ); assert(!res.headers['content-disposition']); assert.deepEqual(res.body, { path: '/docs/_site/getting-started.html', type: 'file', contentType: 'text/html', - integrity: 'sha512-o/nCeU2MBJpIWhA8gIbf6YW49Ss3Spga5M70LJjjyRMlALQDmeh8IVMXagAe79l1Yznci/otKtNjWhVMOM38hg==', + integrity: + 'sha512-o/nCeU2MBJpIWhA8gIbf6YW49Ss3Spga5M70LJjjyRMlALQDmeh8IVMXagAe79l1Yznci/otKtNjWhVMOM38hg==', lastModified: publishTime, size: 26716, }); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/tests`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/tests`); assert.equal(res.status, 404); - assert.equal(res.body.error, '[NOT_FOUND] File @cnpm/foo-tag-latest@1.0.0/tests not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] File @cnpm/foo-tag-latest@1.0.0/tests not found' + ); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/tests/`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/tests/`); assert.equal(res.status, 200); // console.log('%o', res.body); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); assert.equal(res.body.path, '/tests'); // make sure sub dirs exists - const integrationDir = res.body.files.find((file: { path: string }) => file.path === '/tests/integration'); + const integrationDir = res.body.files.find( + (file: { path: string }) => file.path === '/tests/integration' + ); assert(integrationDir); assert.equal(integrationDir.files.length, 0); assert.equal(integrationDir.type, 'directory'); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/tests/integration/test.http.js`); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - assert.equal(res.headers['content-type'], 'application/javascript; charset=utf-8'); + assert.equal( + res.headers['content-type'], + 'application/javascript; charset=utf-8' + ); assert(!res.headers['content-disposition']); assert.equal(res.headers['transfer-encoding'], 'chunked'); assert.match(res.text, /describe\(/); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/tests/integration/test.http.js?meta`); - assert.equal(res.headers['cache-control'], 'public, s-maxage=600, max-age=60'); + assert.equal( + res.headers['cache-control'], + 'public, s-maxage=600, max-age=60' + ); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - assert.equal(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.equal( + res.headers['content-type'], + 'application/json; charset=utf-8' + ); assert(!res.headers['content-disposition']); assert.deepEqual(res.body, { path: '/tests/integration/test.http.js', type: 'file', contentType: 'application/javascript', - integrity: 'sha512-yysF4V48yKDI9yWuROuPd9cn9dn3nFQaAGkGMe46l6htQ6ZsoX4SAw9+FkhmmPez2VjxW/lYhWy21R1oOOu8Fw==', + integrity: + 'sha512-yysF4V48yKDI9yWuROuPd9cn9dn3nFQaAGkGMe46l6htQ6ZsoX4SAw9+FkhmmPez2VjxW/lYhWy21R1oOOu8Fw==', lastModified: publishTime, size: 1917, }); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/README.md`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/README.md`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); @@ -548,8 +677,7 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => assert.equal(res.headers['transfer-encoding'], 'chunked'); assert.match(res.text, /The Javascript Database that Syncs/); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/.travis.yml`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/.travis.yml`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); @@ -558,8 +686,7 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => assert.equal(res.headers['transfer-encoding'], 'chunked'); assert.match(res.text, /language: node_js/); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/LICENSE`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/LICENSE`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); @@ -569,8 +696,7 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => assert.equal(res.headers['transfer-encoding'], 'chunked'); assert.match(res.text, /Apache License/); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0/files/.npmignore`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0/files/.npmignore`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); @@ -579,7 +705,8 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => assert(!res.headers['content-disposition']); assert.equal(res.headers['transfer-encoding'], 'chunked'); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/bin/release.sh`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); @@ -589,12 +716,16 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => assert.equal(res.headers['transfer-encoding'], 'chunked'); assert.match(res.text, /#!\/bin\/bash/); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/docs/manifest.appcache`); assert.equal(res.status, 200); assert.equal(res.headers['cache-control'], 'public, max-age=31536000'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); - assert.equal(res.headers['content-type'], 'text/cache-manifest; charset=utf-8'); + assert.equal( + res.headers['content-type'], + 'text/cache-manifest; charset=utf-8' + ); assert(!res.headers['content-disposition']); assert.equal(res.headers['transfer-encoding'], 'chunked'); assert.match(res.text, /CACHE MANIFEST/); @@ -602,18 +733,23 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => it('should 451 when package block', async () => { const { pkg } = await TestUtil.createPackage({ isPrivate: false }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/-/package/${pkg.name}/blocks`) .set('authorization', adminUser.authorization) .send({ reason: 'only for tests again', }); assert.equal(res.status, 201); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.0/files/index.js`) .expect(451) .expect('content-type', 'application/json; charset=utf-8'); - assert.match(res.body.error, /\[UNAVAILABLE_FOR_LEGAL_REASONS] @cnpm\/testmodule@1.0.0 was blocked, reason: only for tests again/); + assert.match( + res.body.error, + /\[UNAVAILABLE_FOR_LEGAL_REASONS] @cnpm\/testmodule@1.0.0 was blocked, reason: only for tests again/ + ); }); it('should 404 when version not exists', async () => { @@ -624,26 +760,35 @@ describe('test/port/controller/PackageVersionFileController/raw.test.ts', () => description: 'foo description', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get(`/${pkg.name}/1.0.40000404/files/foo.json`) .expect(404); assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert.equal(res.body.error, `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found` + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.40000404/files/bin/foo/bar.js`) .expect(404); assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert.equal(res.body.error, `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found` + ); }); }); }); diff --git a/test/port/controller/PackageVersionFileController/sync.test.ts b/test/port/controller/PackageVersionFileController/sync.test.ts index 106a9ab6..c99fad45 100644 --- a/test/port/controller/PackageVersionFileController/sync.test.ts +++ b/test/port/controller/PackageVersionFileController/sync.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; describe('test/port/controller/PackageVersionFileController/sync.test.ts', () => { let publisher: TestUser; @@ -22,13 +23,15 @@ describe('test/port/controller/PackageVersionFileController/sync.test.ts', () => description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put('/foo/1.0.0/files') .set('authorization', adminUser.authorization) .expect(404) @@ -45,17 +48,17 @@ describe('test/port/controller/PackageVersionFileController/sync.test.ts', () => description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`) - .expect(200); + let res = await app.httpRequest().get(`/${pkg.name}/1.0.0`).expect(200); const publishTime = new Date(res.body.publish_time).toISOString(); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/foo/1.0.0/files') .set('authorization', adminUser.authorization) .expect(200) @@ -65,13 +68,15 @@ describe('test/port/controller/PackageVersionFileController/sync.test.ts', () => path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', + integrity: + 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', lastModified: publishTime, size: 209, }, ]); // again should work - res = await app.httpRequest() + res = await app + .httpRequest() .put('/foo/1.0.0/files') .set('authorization', adminUser.authorization) .expect(200) @@ -81,7 +86,8 @@ describe('test/port/controller/PackageVersionFileController/sync.test.ts', () => path: '/package.json', type: 'file', contentType: 'application/json', - integrity: 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', + integrity: + 'sha512-yTg/L7tUtFK54aNH3iwgIp7sF3PiAcUrIEUo06bSNq3haIKRnagy6qOwxiEmtfAtNarbjmEpl31ZymySsECi3Q==', lastModified: publishTime, size: 209, }, @@ -89,15 +95,20 @@ describe('test/port/controller/PackageVersionFileController/sync.test.ts', () => }); it('should 404 when package not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .put('/@cnpm/foonot-exists/1.0.40000404/files') .set('authorization', adminUser.authorization) .expect(404); - assert.equal(res.body.error, '[NOT_FOUND] @cnpm/foonot-exists@1.0.40000404 not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] @cnpm/foonot-exists@1.0.40000404 not found' + ); }); it('should 403 when non-admin request', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .put('/@cnpm/foonot-exists/1.0.40000404/files') .expect(403); assert.equal(res.body.error, '[FORBIDDEN] Not allow to access'); diff --git a/test/port/controller/RegistryController/index.test.ts b/test/port/controller/RegistryController/index.test.ts index c7b42253..4b447941 100644 --- a/test/port/controller/RegistryController/index.test.ts +++ b/test/port/controller/RegistryController/index.test.ts @@ -2,8 +2,8 @@ import { strict as assert } from 'node:assert'; import { app } from '@eggjs/mock/bootstrap'; import { TaskType } from '../../../../app/common/enum/Task.js'; -import { Registry } from '../../../../app/core/entity/Registry.js'; -import { ChangesStreamTaskData } from '../../../../app/core/entity/Task.js'; +import type { Registry } from '../../../../app/core/entity/Registry.js'; +import type { ChangesStreamTaskData } from '../../../../app/core/entity/Task.js'; import { TaskService } from '../../../../app/core/service/TaskService.js'; import { TestUtil } from '../../../../test/TestUtil.js'; @@ -17,27 +17,26 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { beforeEach(async () => { adminUser = await TestUtil.createAdmin(); // create success - await app.httpRequest() + await app + .httpRequest() .post('/-/registry') .set('authorization', adminUser.authorization) - .send( - { - name: 'custom3', - host: 'https://r.cnpmjs.org/', - changeStream: 'https://r.cnpmjs.org/_changes', - type: 'cnpmcore', - }) + .send({ + name: 'custom3', + host: 'https://r.cnpmjs.org/', + changeStream: 'https://r.cnpmjs.org/_changes', + type: 'cnpmcore', + }) .expect(200); // query success - const res = await app.httpRequest() - .get('/-/registry') - .expect(200); + const res = await app.httpRequest().get('/-/registry').expect(200); registry = res.body.data[0]; // create scope - await app.httpRequest() + await app + .httpRequest() .post('/-/scope') .set('authorization', adminUser.authorization) .send({ @@ -50,72 +49,71 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { describe('[POST /-/registry] createRegistry()', () => { it('should 200', async () => { // create success - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/registry') .set('authorization', adminUser.authorization) - .send( - { - name: 'custom6', - host: 'https://r.cnpmjs.org/', - changeStream: 'https://r.cnpmjs.org/_changes', - type: 'cnpmcore', - }); + .send({ + name: 'custom6', + host: 'https://r.cnpmjs.org/', + changeStream: 'https://r.cnpmjs.org/_changes', + type: 'cnpmcore', + }); assert(res.body.ok); }); it('should verify params', async () => { // create success - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/registry') .set('authorization', adminUser.authorization) - .send( - { - name: 'custom', - type: 'cnpmcore', - }) + .send({ + name: 'custom', + type: 'cnpmcore', + }) .expect(422); - assert(res.body.error === '[INVALID_PARAM] must have required property \'host\''); + assert( + res.body.error === "[INVALID_PARAM] must have required property 'host'" + ); }); it('should 403', async () => { // create forbidden - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/registry') - .send( - { - name: 'custom', - host: 'https://r.cnpmjs.org/', - changeStream: 'https://r.cnpmjs.org/_changes', - type: 'cnpmcore', - }) + .send({ + name: 'custom', + host: 'https://r.cnpmjs.org/', + changeStream: 'https://r.cnpmjs.org/_changes', + type: 'cnpmcore', + }) .expect(403); assert(res.body.error === '[FORBIDDEN] Not allow to access'); }); - }); describe('[GET /-/registry] listRegistries()', () => { it('should 200', async () => { // create success - await app.httpRequest() + await app + .httpRequest() .post('/-/registry') .set('authorization', adminUser.authorization) - .send( - { - name: 'custom5', - host: 'https://r.cnpmjs.org/', - changeStream: 'https://r.cnpmjs.org/_changes', - type: 'cnpmcore', - }) + .send({ + name: 'custom5', + host: 'https://r.cnpmjs.org/', + changeStream: 'https://r.cnpmjs.org/_changes', + type: 'cnpmcore', + }) .expect(200); // query success - const res = await app.httpRequest() - .get('/-/registry') - .expect(200); + const res = await app.httpRequest().get('/-/registry').expect(200); assert(res.body.count === 2); assert(res.body.data[1].name === 'custom5'); @@ -125,7 +123,8 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { describe('[GET /-/registry/:id/scopes] showRegistryScopes()', () => { it('should 200', async () => { // create scope - await app.httpRequest() + await app + .httpRequest() .post('/-/scope') .set('authorization', adminUser.authorization) .send({ @@ -133,7 +132,8 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { name: '@banana', }); - await app.httpRequest() + await app + .httpRequest() .post('/-/scope') .set('authorization', adminUser.authorization) .send({ @@ -141,7 +141,8 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { name: '@apple', }); - await app.httpRequest() + await app + .httpRequest() .post('/-/scope') .set('authorization', adminUser.authorization) .send({ @@ -149,27 +150,30 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { name: '@orange', }); - let scopRes = await app.httpRequest() + let scopRes = await app + .httpRequest() .get(`/-/registry/${registry.registryId}/scopes`) .expect(200); assert(scopRes.body.count === 4); assert(scopRes.body.data.length === 4); - scopRes = await app.httpRequest() + scopRes = await app + .httpRequest() .get(`/-/registry/${registry.registryId}/scopes?pageSize=1`) .expect(200); assert(scopRes.body.count === 4); assert(scopRes.body.data.length === 1); - scopRes = await app.httpRequest() + scopRes = await app + .httpRequest() .get(`/-/registry/${registry.registryId}/scopes?pageSize=2&pageIndex=1`) .expect(200); assert(scopRes.body.count === 4); assert(scopRes.body.data.length === 2); - }); it('should error', async () => { - await app.httpRequest() + await app + .httpRequest() .get('/-/registry/not_exist_id/scopes') .expect(404); }); @@ -177,27 +181,28 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { describe('[GET /-/registry/:id] showRegistry()', () => { it('should 200', async () => { - const queryRes = await app.httpRequest() + const queryRes = await app + .httpRequest() .get(`/-/registry/${registry.registryId}`); assert.deepEqual(queryRes.body, registry); }); it('should error', async () => { - await app.httpRequest() - .get('/-/registry/not_exist_id') - .expect(404); + await app.httpRequest().get('/-/registry/not_exist_id').expect(404); }); }); describe('[DELETE /-/registry] deleteRegistry()', () => { it('should 200', async () => { - await app.httpRequest() + await app + .httpRequest() .delete(`/-/registry/${registry.registryId}`) .set('authorization', adminUser.authorization) .expect(200); // query success - const queryRes = await app.httpRequest() + const queryRes = await app + .httpRequest() .get('/-/registry') .set('authorization', adminUser.authorization) .expect(200); @@ -208,13 +213,15 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { describe('[POST /-/registry/:id/sync] createRegistrySyncTask()', () => { it('should 403', async () => { - await app.httpRequest() + await app + .httpRequest() .post(`/-/registry/${registry.registryId}/sync`) .expect(403); }); it('should error when invalid registryId', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/registry/in_valid/sync') .set('authorization', adminUser.authorization) .expect(404); @@ -222,7 +229,8 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { }); it('should 200', async () => { - await app.httpRequest() + await app + .httpRequest() .post(`/-/registry/${registry.registryId}/sync`) .set('authorization', adminUser.authorization) .expect(200); @@ -232,7 +240,8 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { }); it('since params', async () => { - await app.httpRequest() + await app + .httpRequest() .post(`/-/registry/${registry.registryId}/sync`) .set('authorization', adminUser.authorization) .send({ @@ -249,20 +258,23 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { describe('[PATCH /-/registry/:id] updateRegistry()', () => { it('should 403', async () => { - await app.httpRequest() + await app + .httpRequest() .patch(`/-/registry/${registry.registryId}`) .expect(403); }); it('should 404 when not found', async () => { - await app.httpRequest() + await app + .httpRequest() .patch('/-/registry/registry-not-exists') .set('authorization', adminUser.authorization) .expect(404); }); it('should update auth token success', async () => { - await app.httpRequest() + await app + .httpRequest() .patch(`/-/registry/${registry.registryId}`) .set('authorization', adminUser.authorization) .send({ @@ -270,13 +282,10 @@ describe('test/port/controller/RegistryController/index.test.ts', () => { }) .expect(200); - const registList = await app.httpRequest() - .get('/-/registry') - .expect(200); + const registList = await app.httpRequest().get('/-/registry').expect(200); const latestToken = await registList.body.data[0].authToken; assert.equal(latestToken, 'testAuthToekn'); }); }); - }); diff --git a/test/port/controller/ScopeController/index.test.ts b/test/port/controller/ScopeController/index.test.ts index 9f4fc39b..b9cca1c3 100644 --- a/test/port/controller/ScopeController/index.test.ts +++ b/test/port/controller/ScopeController/index.test.ts @@ -4,7 +4,7 @@ import { app } from '@eggjs/mock/bootstrap'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; import { TestUtil } from '../../../TestUtil.js'; -import { Scope } from '../../../../app/core/entity/Scope.js'; +import type { Scope } from '../../../../app/core/entity/Scope.js'; describe('test/port/controller/ScopeController/index.test.ts', () => { let adminUser: any; @@ -24,33 +24,32 @@ describe('test/port/controller/ScopeController/index.test.ts', () => { describe('[POST /-/scope] createScope()', () => { it('should 200', async () => { - const queryRes = await registryManagerService.listRegistries({}); - const [ registry ] = queryRes.data; + const [registry] = queryRes.data; // create success - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/scope') .set('authorization', adminUser.authorization) - .send( - { - name: '@cnpm', - registryId: registry.registryId, - }) + .send({ + name: '@cnpm', + registryId: registry.registryId, + }) .expect(200); assert(res.body.ok); }); it('should 400', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/scope') .set('authorization', adminUser.authorization) - .send( - { - name: '@cnpmbanana', - registryId: 'banana', - }) + .send({ + name: '@cnpmbanana', + registryId: 'banana', + }) .expect(400); assert(res.body.error === '[BAD_REQUEST] registry banana not found'); @@ -58,17 +57,16 @@ describe('test/port/controller/ScopeController/index.test.ts', () => { it('should 403', async () => { // create success - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/scope') - .send( - { - name: '@cnpm', - }) + .send({ + name: '@cnpm', + }) .expect(403); assert(res.body.error === '[FORBIDDEN] Not allow to access'); }); - }); describe('[DELETE /-/scope/:id] deleteScope()', () => { @@ -76,21 +74,23 @@ describe('test/port/controller/ScopeController/index.test.ts', () => { beforeEach(async () => { const queryRes = await registryManagerService.listRegistries({}); const registry = queryRes.data[0]; - let res = await app.httpRequest() + let res = await app + .httpRequest() .post('/-/scope') .set('authorization', adminUser.authorization) - .send( - { - name: '@cnpmjsa', - registryId: registry.registryId, - }); - res = await app.httpRequest() + .send({ + name: '@cnpmjsa', + registryId: registry.registryId, + }); + res = await app + .httpRequest() .get(`/-/registry/${registry.registryId}/scopes`); scope = res.body.data[0]; }); it('should 200', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/scope/${scope.scopeId}`) .set('authorization', adminUser.authorization) .expect(200); diff --git a/test/port/controller/TokenController/createToken.test.ts b/test/port/controller/TokenController/createToken.test.ts index 5377717d..0d62e320 100644 --- a/test/port/controller/TokenController/createToken.test.ts +++ b/test/port/controller/TokenController/createToken.test.ts @@ -2,7 +2,8 @@ import { strict as assert } from 'node:assert'; import dayjs from 'dayjs'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { Token, TokenType } from '../../../../app/core/entity/Token.js'; +import type { Token } from '../../../../app/core/entity/Token.js'; +import { TokenType } from '../../../../app/core/entity/Token.js'; import { AuthAdapter } from '../../../../app/infra/AuthAdapter.js'; import { UserRepository } from '../../../../app/repository/UserRepository.js'; import { TestUtil } from '../../../../test/TestUtil.js'; @@ -11,7 +12,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { describe('[POST /-/npm/v1/tokens] createToken()', () => { it('should 200', async () => { const { authorization, password, ua } = await TestUtil.createUser(); - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) @@ -19,7 +21,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { password, }) .expect(200); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(200); @@ -30,7 +33,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { assert.deepEqual(tokens[1].cidr_whitelist, []); // readonly - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) @@ -39,7 +43,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { readonly: true, }) .expect(200); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(200); @@ -50,7 +55,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { assert.deepEqual(tokens[2].cidr_whitelist, []); // automation - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) @@ -60,7 +66,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { }) .expect(200); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(200); @@ -71,18 +78,20 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { assert.deepEqual(tokens[3].cidr_whitelist, []); // cidr_whitelist - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) .send({ password, automation: true, - cidr_whitelist: [ '192.168.1.1/24' ], + cidr_whitelist: ['192.168.1.1/24'], }) .expect(200); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(200); @@ -90,32 +99,43 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { assert.equal(tokens.length, 5); assert.equal(tokens[4].readonly, false); assert.equal(tokens[4].automation, true); - assert.deepEqual(tokens[4].cidr_whitelist, [ '192.168.1.1/24' ]); + assert.deepEqual(tokens[4].cidr_whitelist, ['192.168.1.1/24']); }); it('should 401 when readonly token access', async () => { - const { authorization, password, ua } = await TestUtil.createUser({ tokenOptions: { readonly: true } }); - const res = await app.httpRequest() + const { authorization, password, ua } = await TestUtil.createUser({ + tokenOptions: { readonly: true }, + }); + const res = await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) .send({ password }) .expect(403); - assert.match(res.body.error, /\[FORBIDDEN\] Read-only Token "cnpm_\w+" can't setting/); + assert.match( + res.body.error, + /\[FORBIDDEN\] Read-only Token "cnpm_\w+" can't setting/ + ); }); }); describe('[POST /-/npm/v1/tokens/gat] createGranularToken()', () => { it('should 422 when invalid options', async () => { - let res = await app.httpRequest() + let res = await app + .httpRequest() .post('/-/npm/v1/tokens/gat') .send({ name: 'banana', }) .expect(422); - assert.match(res.body.error, /\[INVALID_PARAM\] must have required property 'expires'/); + assert.match( + res.body.error, + /\[INVALID_PARAM\] must have required property 'expires'/ + ); - res = await app.httpRequest() + res = await app + .httpRequest() .post('/-/npm/v1/tokens/gat') .send({ name: 'banana', @@ -126,7 +146,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { }); it('should 403 when no login', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/npm/v1/tokens/gat') .send({ name: 'banana', @@ -143,7 +164,8 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { email: 'banana@fruits.com', }; }); - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens/gat') .send({ name: 'banana', @@ -166,13 +188,14 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { it('should work', async () => { const start = Date.now(); await TestUtil.createPackage({ name: '@cnpm/banana' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .post('/-/npm/v1/tokens/gat') .send({ name: 'apple', description: 'lets play', - allowedPackages: [ '@cnpm/banana' ], - allowedScopes: [ '@banana' ], + allowedPackages: ['@cnpm/banana'], + allowedScopes: ['@banana'], expires: 30, }) .expect(200); @@ -181,61 +204,71 @@ describe('test/port/controller/TokenController/createToken.test.ts', () => { const user = await userRepository.findUserByName('banana'); const tokens = await userRepository.listTokens(user!.userId); - let granularToken = tokens.find(token => token.type === TokenType.granular); + let granularToken = tokens.find( + token => token.type === TokenType.granular + ); assert(granularToken); assert(granularToken.lastUsedAt === null); assert.equal(granularToken.name, 'apple'); - assert.deepEqual(granularToken.allowedScopes, [ '@banana' ]); + assert.deepEqual(granularToken.allowedScopes, ['@banana']); const expiredDate = dayjs(granularToken.expiredAt); assert(expiredDate.isAfter(dayjs().add(29, 'days'))); assert(expiredDate.isBefore(dayjs().add(30, 'days'))); // should ignore granularToken when use v1 query - res = await app.httpRequest() + res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', 'Bearer ' + res.body.token); assert(res.body.objects.length > 0); - assert(res.body.objects.every((token: Token) => token.type !== TokenType.granular)); + assert( + res.body.objects.every( + (token: Token) => token.type !== TokenType.granular + ) + ); // should update lastUsedAt - res = await app.httpRequest() - .get('/-/npm/v1/tokens/gat') - .expect(200); + res = await app.httpRequest().get('/-/npm/v1/tokens/gat').expect(200); - granularToken = res.body.objects.find((token: Token) => token.type === TokenType.granular); + granularToken = res.body.objects.find( + (token: Token) => token.type === TokenType.granular + ); assert(granularToken?.lastUsedAt); assert(dayjs(granularToken?.lastUsedAt).isAfter(start)); - }); it('should check for uniq name', async () => { await TestUtil.createPackage({ name: '@cnpm/banana' }); - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens/gat') .send({ name: 'apple', description: 'lets play', - allowedPackages: [ '@cnpm/banana' ], - allowedScopes: [ '@banana' ], + allowedPackages: ['@cnpm/banana'], + allowedScopes: ['@banana'], expires: 30, }) .expect(200); - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/npm/v1/tokens/gat') .send({ name: 'apple', description: 'lets play', - allowedPackages: [ '@cnpm/banana' ], - allowedScopes: [ '@banana' ], + allowedPackages: ['@cnpm/banana'], + allowedScopes: ['@banana'], expires: 30, }); - assert.match(res.body.error, /ER_DUP_ENTRY|duplicate key value violates unique constraint/); + assert.match( + res.body.error, + /ER_DUP_ENTRY|duplicate key value violates unique constraint/ + ); }); }); - }); }); diff --git a/test/port/controller/TokenController/removeToken.test.ts b/test/port/controller/TokenController/removeToken.test.ts index 5f025bbe..392e1d6d 100644 --- a/test/port/controller/TokenController/removeToken.test.ts +++ b/test/port/controller/TokenController/removeToken.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { Token, TokenType } from '../../../../app/core/entity/Token.js'; +import type { Token } from '../../../../app/core/entity/Token.js'; +import { TokenType } from '../../../../app/core/entity/Token.js'; import { UserService } from '../../../../app/core/service/UserService.js'; import { AuthAdapter } from '../../../../app/infra/AuthAdapter.js'; import { TokenPackage } from '../../../../app/repository/model/TokenPackage.js'; @@ -10,8 +11,10 @@ import { TestUtil } from '../../../../test/TestUtil.js'; describe('test/port/controller/TokenController/removeToken.test.ts', () => { describe('[DELETE /-/npm/v1/tokens/token/:tokenKey] removeToken()', () => { it('should 200', async () => { - const { authorization, password, token, ua } = await TestUtil.createUser(); - await app.httpRequest() + const { authorization, password, token, ua } = + await TestUtil.createUser(); + await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) @@ -20,7 +23,8 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { }) .expect(200); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) @@ -28,13 +32,15 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { let tokens = res.body.objects; assert.equal(tokens.length, 2); - await app.httpRequest() + await app + .httpRequest() .delete(`/-/npm/v1/tokens/token/${tokens[1].key}`) .set('authorization', authorization) .set('user-agent', ua) .expect(200); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(200); @@ -42,13 +48,15 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { assert.equal(tokens.length, 1); // remove token itself - await app.httpRequest() + await app + .httpRequest() .delete(`/-/npm/v1/tokens/token/${token}`) .set('authorization', authorization) .set('user-agent', ua) .expect(200); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(401); @@ -56,28 +64,41 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { }); it('should 403 when readonly token access', async () => { - const { authorization, token, ua } = await TestUtil.createUser({ tokenOptions: { readonly: true } }); - const res = await app.httpRequest() + const { authorization, token, ua } = await TestUtil.createUser({ + tokenOptions: { readonly: true }, + }); + const res = await app + .httpRequest() .delete(`/-/npm/v1/tokens/token/${token}`) .set('authorization', authorization) .set('user-agent', ua) .expect(403); - assert.match(res.body.error, /\[FORBIDDEN\] Read-only Token "cnpm_\w+" can't setting/); + assert.match( + res.body.error, + /\[FORBIDDEN\] Read-only Token "cnpm_\w+" can't setting/ + ); }); it('should 403 when automation token access', async () => { - const { authorization, token, ua } = await TestUtil.createUser({ tokenOptions: { automation: true } }); - const res = await app.httpRequest() + const { authorization, token, ua } = await TestUtil.createUser({ + tokenOptions: { automation: true }, + }); + const res = await app + .httpRequest() .delete(`/-/npm/v1/tokens/token/${token}`) .set('authorization', authorization) .set('user-agent', ua) .expect(403); - assert.match(res.body.error, /\[FORBIDDEN\] Automation Token "cnpm_\w+" can't setting/); + assert.match( + res.body.error, + /\[FORBIDDEN\] Automation Token "cnpm_\w+" can't setting/ + ); }); it('should 404 when token key not exists', async () => { const { authorization, password, ua } = await TestUtil.createUser(); - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) @@ -86,24 +107,30 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { }) .expect(200); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(200); const tokens = res.body.objects; assert.equal(tokens.length, 2); - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`/-/npm/v1/tokens/token/${tokens[1].key}-not-exists`) .set('authorization', authorization) .set('user-agent', ua) .expect(404); - assert.equal(res.body.error, `[NOT_FOUND] Token "${tokens[1].key}-not-exists" not exists`); + assert.equal( + res.body.error, + `[NOT_FOUND] Token "${tokens[1].key}-not-exists" not exists` + ); }); it('should 401 when remove other user token', async () => { const { authorization, password, ua } = await TestUtil.createUser(); - await app.httpRequest() + await app + .httpRequest() .post('/-/npm/v1/tokens') .set('authorization', authorization) .set('user-agent', ua) @@ -112,7 +139,8 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { }) .expect(200); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/-/npm/v1/tokens') .set('authorization', authorization) .expect(200); @@ -121,12 +149,16 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { const otherUser = await TestUtil.createUser(); - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`/-/npm/v1/tokens/token/${tokens[1].key}`) .set('authorization', otherUser.authorization) .set('user-agent', ua) .expect(403); - assert.equal(res.body.error, `[FORBIDDEN] Not authorized to remove token "${tokens[1].key}"`); + assert.equal( + res.body.error, + `[FORBIDDEN] Not authorized to remove token "${tokens[1].key}"` + ); }); }); @@ -141,8 +173,8 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { token = await userService.createToken(user.userId, { name: 'good', type: TokenType.granular, - allowedPackages: [ '@cnpm/foo' ], - allowedScopes: [ '@cnpmjs' ], + allowedPackages: ['@cnpm/foo'], + allowedScopes: ['@cnpmjs'], expires: 1, }); @@ -155,27 +187,25 @@ describe('test/port/controller/TokenController/removeToken.test.ts', () => { }); it('should 200', async () => { - let res = await app.httpRequest() - .get('/-/npm/v1/tokens/gat') - .expect(200); + let res = await app.httpRequest().get('/-/npm/v1/tokens/gat').expect(200); assert.equal(res.body.objects.length, 1); - let pkgsCount = await TokenPackage.find({ tokenId: token.tokenId }).count(); + let pkgsCount = await TokenPackage.find({ + tokenId: token.tokenId, + }).count(); assert.equal(pkgsCount, 1); - await app.httpRequest() + await app + .httpRequest() .delete(`/-/npm/v1/tokens/gat/${token.tokenKey}`) .expect(204); - res = await app.httpRequest() - .get('/-/npm/v1/tokens/gat') - .expect(200); + res = await app.httpRequest().get('/-/npm/v1/tokens/gat').expect(200); assert.equal(res.body.objects.length, 0); pkgsCount = await TokenPackage.find({ tokenId: token.tokenId }).count(); assert.equal(pkgsCount, 0); }); - }); }); diff --git a/test/port/controller/hook/HookController.test.ts b/test/port/controller/hook/HookController.test.ts index 924cc76a..73da7db6 100644 --- a/test/port/controller/hook/HookController.test.ts +++ b/test/port/controller/hook/HookController.test.ts @@ -1,9 +1,10 @@ import { strict as assert } from 'node:assert'; import { app } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { HookManageService } from '../../../../app/core/service/HookManageService.js'; -import { Hook } from '../../../../app/core/entity/Hook.js'; +import type { Hook } from '../../../../app/core/entity/Hook.js'; import { UserRepository } from '../../../../app/repository/UserRepository.js'; import { HookType } from '../../../../app/common/enum/Hook.js'; @@ -22,7 +23,8 @@ describe('test/port/controller/hook/HookController.test.ts', () => { describe('POST /-/npm/v1/hooks/hook', () => { it('should work', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .post('/-/npm/v1/hooks/hook') .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -62,7 +64,8 @@ describe('test/port/controller/hook/HookController.test.ts', () => { }); it('should work', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/-/npm/v1/hooks/hook/${hook.hookId}`) .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -89,7 +92,8 @@ describe('test/port/controller/hook/HookController.test.ts', () => { }); it('should work', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/npm/v1/hooks/hook/${hook.hookId}`) .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -110,7 +114,8 @@ describe('test/port/controller/hook/HookController.test.ts', () => { }); it('should work', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/-/npm/v1/hooks') .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -132,7 +137,8 @@ describe('test/port/controller/hook/HookController.test.ts', () => { }); it('should work', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/-/npm/v1/hooks/hook/${hook.hookId}`) .set('authorization', user.authorization) .set('user-agent', user.ua) diff --git a/test/port/controller/package/RemovePackageVersionController.test.ts b/test/port/controller/package/RemovePackageVersionController.test.ts index eace2432..07b22727 100644 --- a/test/port/controller/package/RemovePackageVersionController.test.ts +++ b/test/port/controller/package/RemovePackageVersionController.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { PackageRepository } from '../../../../app/repository/PackageRepository.js'; describe('test/port/controller/package/RemovePackageVersionController.test.ts', () => { @@ -20,14 +21,14 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', version: '1.0.0', isPrivate: false, }); - let res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`); + let res = await app.httpRequest().get(`/${pkg.name}/1.0.0`); assert(res.status === 200); const adminUser = await TestUtil.createUser({ name: 'cnpmcore_admin' }); const pkgVersion = res.body; const tarballUrl = new URL(pkgVersion.dist.tarball).pathname; - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`${tarballUrl}/-rev/${pkgVersion._rev}`) .set('authorization', adminUser.authorization) .set('npm-command', 'unpublish') @@ -35,8 +36,7 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', assert(res.status === 200); assert(res.body.ok === true); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0`); assert(res.status === 404); }); @@ -47,13 +47,15 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', version: '1.0.0', isPrivate: false, }); - let res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`); + let res = await app.httpRequest().get(`/${pkg.name}/1.0.0`); assert(res.status === 200); const pkgEntity = await packageRepository.findPackage('', 'foo'); assert(pkgEntity); - const pkgVersionEntity = await packageRepository.findPackageVersion(pkgEntity.packageId, '1.0.0'); + const pkgVersionEntity = await packageRepository.findPackageVersion( + pkgEntity.packageId, + '1.0.0' + ); assert(pkgVersionEntity); pkgVersionEntity.publishTime = new Date(Date.now() - 72 * 3600000 - 100); await packageRepository.savePackageVersion(pkgVersionEntity!); @@ -61,7 +63,8 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', const adminUser = await TestUtil.createUser({ name: 'cnpmcore_admin' }); const pkgVersion = res.body; const tarballUrl = new URL(pkgVersion.dist.tarball).pathname; - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`${tarballUrl}/-rev/${pkgVersion._rev}`) .set('authorization', adminUser.authorization) .set('npm-command', 'unpublish') @@ -69,8 +72,7 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', assert(res.status === 200); assert(res.body.ok === true); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0`); assert(res.status === 404); }); @@ -81,24 +83,25 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', version: '1.0.0', isPrivate: false, }); - let res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`); + let res = await app.httpRequest().get(`/${pkg.name}/1.0.0`); assert(res.status === 200); const normalUser = await TestUtil.createUser(); const pkgVersion = res.body; const tarballUrl = new URL(pkgVersion.dist.tarball).pathname; - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`${tarballUrl}/-rev/${pkgVersion._rev}`) .set('authorization', normalUser.authorization) .set('npm-command', 'unpublish') .set('user-agent', normalUser.ua); assert(res.status === 403); // console.log(res.body); - assert(res.body.error === '[FORBIDDEN] Can\'t modify npm public package "foo"'); + assert( + res.body.error === '[FORBIDDEN] Can\'t modify npm public package "foo"' + ); - res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`); + res = await app.httpRequest().get(`/${pkg.name}/1.0.0`); assert(res.status === 200); }); @@ -107,7 +110,8 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', name: '@cnpm/foo', version: '1.0.0', }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -118,28 +122,25 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', name: '@cnpm/foo', version: '2.0.0', }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() - .get(`/${pkg.name}/2.0.0`) - .expect(200); + let res = await app.httpRequest().get(`/${pkg.name}/2.0.0`).expect(200); let pkgVersion = res.body; let tarballUrl = new URL(pkgVersion.dist.tarball).pathname; - res = await app.httpRequest() - .get(`${tarballUrl}`); + res = await app.httpRequest().get(`${tarballUrl}`); assert(res.status === 200 || res.status === 302); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert(res.body['dist-tags'].latest === '2.0.0'); - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`${tarballUrl}/-rev/${pkgVersion._rev}`) .set('authorization', publisher.authorization) .set('npm-command', 'unpublish') @@ -147,13 +148,10 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert(res.body['dist-tags'].latest === '1.0.0'); - res = await app.httpRequest() - .get(`${tarballUrl}`); + res = await app.httpRequest().get(`${tarballUrl}`); if (res.status === 404) { assert.equal(res.body.error, '[NOT_FOUND] @cnpm/foo@2.0.0 not found'); } else { @@ -163,24 +161,23 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', assert.equal(status, 404); } - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert(!res.body.versions['2.0.0']); assert(res.body.versions['1.0.0']); assert.equal(res.body['dist-tags'].latest, '1.0.0'); // remove all versions - res = await app.httpRequest() - .delete(`${tarballUrl.replace('2.0.0', '1.0.0')}/-rev/${pkgVersion._rev}`) + res = await app + .httpRequest() + .delete( + `${tarballUrl.replace('2.0.0', '1.0.0')}/-rev/${pkgVersion._rev}` + ) .set('authorization', publisher.authorization) .set('npm-command', 'unpublish') .set('user-agent', publisher.ua) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert(!res.body.versions); assert.equal(res.body.name, pkg.name); assert(res.body.time.unpublished); @@ -191,25 +188,21 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', name: '@cnpm/foo', version: '2.0.0', }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - res = await app.httpRequest() - .get(`/${pkg.name}/2.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/2.0.0`).expect(200); pkgVersion = res.body; tarballUrl = new URL(pkgVersion.dist.tarball).pathname; - res = await app.httpRequest() - .get(`${tarballUrl}`); + res = await app.httpRequest().get(`${tarballUrl}`); assert(res.status === 200 || res.status === 302); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert(res.body['dist-tags'].latest === '2.0.0'); }); @@ -218,19 +211,22 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', name: '@cnpm/foo', version: '1.0.0', }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`) - .expect(200); + let res = await app.httpRequest().get(`/${pkg.name}/1.0.0`).expect(200); const pkgVersion = res.body; - const tarballUrl = new URL(pkgVersion.dist.tarball).pathname.replace('1.0.0', '2.0.0'); + const tarballUrl = new URL(pkgVersion.dist.tarball).pathname.replace( + '1.0.0', + '2.0.0' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`${tarballUrl}/-rev/${pkg._rev}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -240,7 +236,8 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', }); it('should 404 when package not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete('/@cnpm/foo/-/foo-4.0.0.tgz/-rev/1-61af62d6295fcbd9f8f1c08f') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -250,12 +247,16 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', }); it('should 400 when npm-command header invalid', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete('/@cnpm/foo/-/foo-4.0.0.tgz/-rev/1-61af62d6295fcbd9f8f1c08f') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .expect(400); - assert.equal(res.body.error, '[BAD_REQUEST] Only allow "unpublish" npm-command'); + assert.equal( + res.body.error, + '[BAD_REQUEST] Only allow "unpublish" npm-command' + ); }); it('should 403 when published over 72 hours', async () => { @@ -263,32 +264,38 @@ describe('test/port/controller/package/RemovePackageVersionController.test.ts', name: '@cnpm/foo', version: '1.0.0', }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() - .get(`/${pkg.name}/1.0.0`) - .expect(200); + let res = await app.httpRequest().get(`/${pkg.name}/1.0.0`).expect(200); const pkgVersion = res.body; const tarballUrl = new URL(pkgVersion.dist.tarball).pathname; const pkgEntity = await packageRepository.findPackage('@cnpm', 'foo'); assert(pkgEntity); - const pkgVersionEntity = await packageRepository.findPackageVersion(pkgEntity.packageId, '1.0.0'); + const pkgVersionEntity = await packageRepository.findPackageVersion( + pkgEntity.packageId, + '1.0.0' + ); assert(pkgVersionEntity); pkgVersionEntity.publishTime = new Date(Date.now() - 72 * 3600000 - 100); await packageRepository.savePackageVersion(pkgVersionEntity!); - res = await app.httpRequest() + res = await app + .httpRequest() .delete(`${tarballUrl}/-rev/${pkg._rev}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .set('npm-command', 'unpublish') .expect(403); - assert.equal(res.body.error, '[FORBIDDEN] @cnpm/foo@1.0.0 unpublish is not allowed after 72 hours of released'); + assert.equal( + res.body.error, + '[FORBIDDEN] @cnpm/foo@1.0.0 unpublish is not allowed after 72 hours of released' + ); }); }); }); diff --git a/test/port/controller/package/SavePackageVersionController.test.ts b/test/port/controller/package/SavePackageVersionController.test.ts index 1e403d64..9faf0097 100644 --- a/test/port/controller/package/SavePackageVersionController.test.ts +++ b/test/port/controller/package/SavePackageVersionController.test.ts @@ -4,15 +4,17 @@ import { app, mock } from '@eggjs/mock/bootstrap'; import { ForbiddenError } from 'egg-errors'; import dayjs from 'dayjs'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { UserRepository } from '../../../../app/repository/UserRepository.js'; import { calculateIntegrity } from '../../../../app/common/PackageUtil.js'; import { PackageRepository } from '../../../../app/repository/PackageRepository.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; import { UserService } from '../../../../app/core/service/UserService.js'; -import { Token, TokenType } from '../../../../app/core/entity/Token.js'; +import type { Token } from '../../../../app/core/entity/Token.js'; +import { TokenType } from '../../../../app/core/entity/Token.js'; import { Token as TokenModel } from '../../../../app/repository/model/Token.js'; -import { User } from '../../../../app/core/entity/User.js'; +import type { User } from '../../../../app/core/entity/User.js'; import { PackageManagerService } from '../../../../app/core/service/PackageManagerService.js'; describe('test/port/controller/package/SavePackageVersionController.test.ts', () => { @@ -26,9 +28,16 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () describe('[PUT /:fullname] save()', () => { it('should set registry field after publish', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - const { pkg, user } = await TestUtil.createPackage({ name: 'non_scope_pkg', version: '1.0.0' }); - const pkg2 = await TestUtil.getFullPackage({ name: pkg.name, version: '2.0.0' }); - let res = await app.httpRequest() + const { pkg, user } = await TestUtil.createPackage({ + name: 'non_scope_pkg', + version: '1.0.0', + }); + const pkg2 = await TestUtil.getFullPackage({ + name: pkg.name, + version: '2.0.0', + }); + let res = await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -36,20 +45,19 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.status, 201); - res = await app.httpRequest() - .get(`/${pkg2.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg2.name}`).expect(200); const fullManifest = res.body; - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg2.name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200); const abbreviatedManifest = res.body; - [ fullManifest, abbreviatedManifest ].forEach(manifest => { + [fullManifest, abbreviatedManifest].forEach(manifest => { Object.keys(manifest.versions).forEach(v => { const version = manifest.versions[v]; assert(version); @@ -67,13 +75,19 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () email: user.email, }); }); - }); it('should 200 when package in current registry', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - const { pkg, user } = await TestUtil.createPackage({ name: 'non_scope_pkg', version: '1.0.0' }); - const pkg2 = await TestUtil.getFullPackage({ name: pkg.name, version: '2.0.0' }); - const res = await app.httpRequest() + const { pkg, user } = await TestUtil.createPackage({ + name: 'non_scope_pkg', + version: '1.0.0', + }); + const pkg2 = await TestUtil.getFullPackage({ + name: pkg.name, + version: '2.0.0', + }); + const res = await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -83,7 +97,9 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () const packageRepository = await app.getEggObject(PackageRepository); const pkgEntity = await packageRepository.findPackage('', pkg.name); - const registryManagerService = await app.getEggObject(RegistryManagerService); + const registryManagerService = await app.getEggObject( + RegistryManagerService + ); const selfRegistry = await registryManagerService.ensureSelfRegistry(); assert(pkgEntity); @@ -91,24 +107,31 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () }); it('should 409 when lock failed', async () => { - const { pkg, user } = await TestUtil.createPackage({ name: '@cnpm/banana', version: '1.0.0' }); + const { pkg, user } = await TestUtil.createPackage({ + name: '@cnpm/banana', + version: '1.0.0', + }); - const packageManagerService = await app.getEggObject(PackageManagerService); + const packageManagerService = await app.getEggObject( + PackageManagerService + ); mock(packageManagerService, 'publish', async () => { await setTimeout(50); throw new ForbiddenError('mock error'); }); - const [ errorRes, conflictRes ] = await Promise.all([ - app.httpRequest() + const [errorRes, conflictRes] = await Promise.all([ + app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) .send(pkg), (async () => { await setTimeout(10); - return app.httpRequest() + return app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) @@ -117,57 +140,85 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () ]); assert(errorRes.error, '[FORBIDDEN] mock error'); assert.equal(conflictRes.status, 409); - assert(conflictRes.error, '[CONFLICT] Unable to create the publication lock, please try again later.'); + assert( + conflictRes.error, + '[CONFLICT] Unable to create the publication lock, please try again later.' + ); // release lock await setTimeout(50); - const nextErrorRes = await app.httpRequest() + const nextErrorRes = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) .send(pkg); assert(nextErrorRes.error, '[FORBIDDEN] mock error'); - }); it('should verify tgz and manifest', async () => { - const { pkg, user } = await TestUtil.createPackage({ name: '@cnpm/banana', version: '1.0.0' }); - const pkg2 = await TestUtil.getFullPackage({ name: pkg.name, version: '0.0.1' }); + const { pkg, user } = await TestUtil.createPackage({ + name: '@cnpm/banana', + version: '1.0.0', + }); + const pkg2 = await TestUtil.getFullPackage({ + name: pkg.name, + version: '0.0.1', + }); pkg2.versions['0.0.1'].name = '@cnpm/orange'; mock(app.config.cnpmcore, 'strictValidateTarballPkg', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) .send(pkg2) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] name mismatch between tarball and manifest'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] name mismatch between tarball and manifest' + ); }); it('should verify tgz and manifest with multiple fields', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - const { pkg, user } = await TestUtil.createPackage({ name: 'non_scope_pkg', version: '1.0.0' }); - const pkg2 = await TestUtil.getFullPackage({ name: pkg.name, version: '0.0.1' }); + const { pkg, user } = await TestUtil.createPackage({ + name: 'non_scope_pkg', + version: '1.0.0', + }); + const pkg2 = await TestUtil.getFullPackage({ + name: pkg.name, + version: '0.0.1', + }); pkg2.versions['0.0.1'].dependencies = { lodash: 'latest' }; mock(app.config.cnpmcore, 'strictValidateTarballPkg', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', user.authorization) .set('user-agent', user.ua) .send(pkg2) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] name,dependencies mismatch between tarball and manifest'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] name,dependencies mismatch between tarball and manifest' + ); }); it('should add new version success on scoped package', async () => { const name = '@cnpm/publish-package-test'; - const pkg = await TestUtil.getFullPackage({ name, version: '0.0.0', description: 'init description' }); - let res = await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name, + version: '0.0.0', + description: 'init description', + }); + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -175,20 +226,17 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.version, '0.0.0'); assert(res.body.description === 'init description'); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert(res.body['dist-tags'].latest === '0.0.0'); assert(res.body.description === 'init description'); // add other version const pkg2 = await TestUtil.getFullPackage({ name, version: '1.0.0' }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -197,8 +245,13 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - const pkg3 = await TestUtil.getFullPackage({ name, version: '2.0.0', description: '2.0.0 description' }); - res = await app.httpRequest() + const pkg3 = await TestUtil.getFullPackage({ + name, + version: '2.0.0', + description: '2.0.0 description', + }); + res = await app + .httpRequest() .put(`/${encodeURIComponent(pkg3.name)}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -207,14 +260,10 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/2.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/2.0.0`).expect(200); assert(res.body.version === '2.0.0'); assert(res.body.description === '2.0.0 description'); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert(res.body['dist-tags'].latest === '2.0.0'); assert(res.body.description === '2.0.0 description'); }); @@ -222,27 +271,36 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () it('should 403 on not allow scoped package', async () => { const name = '@somescope/publish-package-test'; let pkg = await TestUtil.getFullPackage({ name }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(403); - assert.equal(res.body.error, '[FORBIDDEN] Scope "@somescope" not match legal scopes: "@cnpm, @cnpmcore, @example"'); + assert.equal( + res.body.error, + '[FORBIDDEN] Scope "@somescope" not match legal scopes: "@cnpm, @cnpmcore, @example"' + ); pkg = await TestUtil.getFullPackage({ name: 'foo' }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(403); - assert.equal(res.body.error, '[FORBIDDEN] Package scope required, legal scopes: "@cnpm, @cnpmcore, @example"'); + assert.equal( + res.body.error, + '[FORBIDDEN] Package scope required, legal scopes: "@cnpm, @cnpmcore, @example"' + ); }); it('should 200 when migrate scoped package', async () => { const name = '@cnpm/publish-package-test'; let pkg = await TestUtil.getFullPackage({ name, version: '1.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -251,28 +309,33 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert(res); const packageRepository = await app.getEggObject(PackageRepository); - let pkgEntity = await packageRepository.findPackage('@cnpm', 'publish-package-test'); + let pkgEntity = await packageRepository.findPackage( + '@cnpm', + 'publish-package-test' + ); assert(pkgEntity); pkgEntity.registryId = ''; await packageRepository.savePackage(pkgEntity!); - res = await app.httpRequest() - .get(`/${pkg.name}`); + res = await app.httpRequest().get(`/${pkg.name}`); assert.equal(res.body._source_registry_name, 'default'); pkg = await TestUtil.getFullPackage({ name, version: '2.0.0' }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - pkgEntity = await packageRepository.findPackage('@cnpm', 'publish-package-test'); + pkgEntity = await packageRepository.findPackage( + '@cnpm', + 'publish-package-test' + ); assert(pkgEntity?.registryId); - res = await app.httpRequest() - .get(`/${pkg.name}`); + res = await app.httpRequest().get(`/${pkg.name}`); assert.equal(res.body._source_registry_name, 'self'); }); @@ -280,11 +343,12 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () // add user.scopes const user = await userRepository.findUserByName(publisher.name); assert(user); - user.scopes = [ '@somescope' ]; + user.scopes = ['@somescope']; await userRepository.saveUser(user); const name = '@somescope/publish-package-test'; const pkg = await TestUtil.getFullPackage({ name }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -298,12 +362,14 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () // https://github.com/cnpm/cnpmcore/issues/36 const user = await userRepository.findUserByName(publisher.name); assert(user); - user.scopes = [ '@inrupt' ]; + user.scopes = ['@inrupt']; await userRepository.saveUser(user); const name = '@inrupt/solid-client'; - const version = '0.0.2-dependabotnpmandyarnwebsitedocusauruspreset-classic-200-alpha61-192892303-618.0'; + const version = + '0.0.2-dependabotnpmandyarnwebsitedocusauruspreset-classic-200-alpha61-192892303-618.0'; const pkg = await TestUtil.getFullPackage({ name, version }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -312,9 +378,7 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.equal(res.body.versions[version].version, version); }); @@ -322,12 +386,14 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () // https://github.com/cnpm/cnpmcore/issues/36 const user = await userRepository.findUserByName(publisher.name); assert(user); - user.scopes = [ '@inrupt' ]; + user.scopes = ['@inrupt']; await userRepository.saveUser(user); - const name = '@inrupt/solid-client-0.0.2-dependabotnpmandyarnwebsitedocusauruspreset-classic-200-alpha61-192892303-618.0'; + const name = + '@inrupt/solid-client-0.0.2-dependabotnpmandyarnwebsitedocusauruspreset-classic-200-alpha61-192892303-618.0'; const version = '0.0.2-200-alpha61-192892303-618.0'; const pkg = await TestUtil.getFullPackage({ name, version }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -336,9 +402,7 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.equal(res.body.versions[version].version, version); }); @@ -354,7 +418,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () integrity, }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -375,7 +440,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () integrity, }, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -386,7 +452,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () it('should add new version success', async () => { const pkg = await TestUtil.getFullPackage({ version: '0.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -394,13 +461,12 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.version, '0.0.0'); // add other version const pkg2 = await TestUtil.getFullPackage({ version: '1.0.0' }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -417,13 +483,17 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () beta: '0.1.0', }, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] dist-tags version "0.1.0" not match package version "0.0.0"'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] dist-tags version "0.1.0" not match package version "0.0.0"' + ); }); it('should 422 when dist-tags format error', async () => { @@ -431,7 +501,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () version: '0.0.0', distTags: {}, }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -443,7 +514,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () version: '0.0.0', distTags: null, }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -457,18 +529,23 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () '0.0': '0.0.0', }, }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] tag: must match format "semver-tag"'); + assert.equal( + res.body.error, + '[INVALID_PARAM] tag: must match format "semver-tag"' + ); }); it('should 404 save deprecated message when package not exists', async () => { const pkg = await TestUtil.getFullPackage({ version: '0.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -477,7 +554,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.body.ok, true); const notExistsName = `${pkg.name}-not-exists`; - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${notExistsName}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -487,17 +565,22 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () '0.0.0': { version: '0.0.0', name: notExistsName, - deprecated: 'is deprecated, 模块被抛弃, work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + deprecated: + 'is deprecated, 模块被抛弃, work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }, }) .expect(404); - assert.equal(res.body.error, '[NOT_FOUND] @cnpm/testmodule-not-exists not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] @cnpm/testmodule-not-exists not found' + ); }); it('should 403 save deprecated message when other user request', async () => { const pkg = await TestUtil.getFullPackage({ version: '0.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -506,7 +589,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.body.ok, true); const other = await TestUtil.createUser(); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', other.authorization) .set('user-agent', publisher.ua) @@ -516,17 +600,22 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () '0.0.0': { version: '0.0.0', name: pkg.name, - deprecated: 'is deprecated, 模块被抛弃, work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', + deprecated: + 'is deprecated, 模块被抛弃, work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }, }) .expect(403); - assert.match(res.body.error, /not authorized to modify @cnpm\/testmodule, please contact maintainers/); + assert.match( + res.body.error, + /not authorized to modify @cnpm\/testmodule, please contact maintainers/ + ); }); it('should save package version deprecated message', async () => { const pkg = await TestUtil.getFullPackage({ version: '0.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -534,8 +623,10 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); - const deprecated = 'is deprecated, 模块被抛弃, work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻'; - res = await app.httpRequest() + const deprecated = + 'is deprecated, 模块被抛弃, work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻'; + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -556,18 +647,15 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () }) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.deprecated, deprecated); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.equal(res.body.versions['0.0.0'].deprecated, deprecated); assert(!res.body.versions['1.0.0']); // remove deprecated message - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -588,23 +676,23 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () }) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.deprecated, undefined); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); assert.equal(res.body.versions['0.0.0'].deprecated, undefined); assert(!res.body.versions['1.0.0']); }); it('should add new version without dist success', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/without-dist', version: '0.0.0' }); + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/without-dist', + version: '0.0.0', + }); assert(pkg.versions); const version = Object.keys(pkg.versions)[0]; pkg.versions[version].dist = undefined; - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -612,16 +700,19 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.version, '0.0.0'); assert.equal(res.body.readme, 'ERROR: No README data found!'); }); it('should add new version without readme success', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/without-readme', version: '0.0.0', readme: null }); - let res = await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/without-readme', + version: '0.0.0', + readme: null, + }); + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -629,16 +720,19 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.version, '0.0.0'); assert.equal(res.body.readme, ''); }); it('should add new version with libc field success', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/without-readme', version: '0.0.0', libc: [ 'glibc' ] }); - let res = await app.httpRequest() + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/without-readme', + version: '0.0.0', + libc: ['glibc'], + }); + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -646,24 +740,27 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}`) - .expect(200); - assert.deepStrictEqual(res.body.versions['0.0.0'].libc, [ 'glibc' ]); + res = await app.httpRequest().get(`/${pkg.name}`).expect(200); + assert.deepStrictEqual(res.body.versions['0.0.0'].libc, ['glibc']); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('accept', 'application/vnd.npm.install-v1+json') .expect(200); - assert.deepStrictEqual(res.body.versions['0.0.0'].libc, [ 'glibc' ]); + assert.deepStrictEqual(res.body.versions['0.0.0'].libc, ['glibc']); }); it('should add new version without readme(object type) success', async () => { - const pkg = await TestUtil.getFullPackage({ name: '@cnpm/with-readme-object', version: '0.0.0' }); + const pkg = await TestUtil.getFullPackage({ + name: '@cnpm/with-readme-object', + version: '0.0.0', + }); assert(pkg.versions); const version = Object.keys(pkg.versions)[0]; Reflect.set(pkg.versions[version], 'readme', { foo: 'bar' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -671,18 +768,20 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.version, '0.0.0'); assert.equal(res.body.readme, ''); }); it('should add new version without description(object type) success', async () => { - const pkg = (await TestUtil.getFullPackage({ name: '@cnpm/with-description-object', version: '0.0.0' })) as any; + const pkg = (await TestUtil.getFullPackage({ + name: '@cnpm/with-description-object', + version: '0.0.0', + })) as any; const version = Object.keys(pkg.versions)[0]; (pkg.versions[version] as any).description = { foo: 'bar' }; - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -690,16 +789,15 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/0.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/0.0.0`).expect(200); assert.equal(res.body.version, '0.0.0'); assert.equal(res.body.description, ''); }); it('should add same version throw error', async () => { const pkg = await TestUtil.getFullPackage({ version: '99.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -707,52 +805,63 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(201); assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .get(`/${pkg.name}/99.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/99.0.0`).expect(200); assert.equal(res.body.version, '99.0.0'); // add other version const pkg2 = await TestUtil.getFullPackage({ version: '99.0.0' }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg2.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg2) .expect(403); - assert.equal(res.body.error, `[FORBIDDEN] Can't modify pre-existing version: ${pkg2.name}@99.0.0`); + assert.equal( + res.body.error, + `[FORBIDDEN] Can't modify pre-existing version: ${pkg2.name}@99.0.0` + ); }); it('should 422 when version format error', async () => { const pkg = await TestUtil.getFullPackage({ version: '1.0.woring-version', }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] version: must match format "semver-version"'); + assert.equal( + res.body.error, + '[INVALID_PARAM] version: must match format "semver-version"' + ); }); it('should 422 when version empty error', async () => { let pkg = await TestUtil.getFullPackage({ version: ' ', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] version: must NOT have fewer than 5 characters'); + assert.equal( + res.body.error, + '[INVALID_PARAM] version: must NOT have fewer than 5 characters' + ); // auto fix trim empty string pkg = await TestUtil.getFullPackage({ version: ' 1.0.0 ', }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -766,66 +875,82 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () let pkg = await TestUtil.getFullPackage({ name: 'excited!', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert(res.status === 422); - assert(res.body.error === '[UNPROCESSABLE_ENTITY] package.name invalid, errors: name can no longer contain special characters ("~\'!()*")'); + assert( + res.body.error === + '[UNPROCESSABLE_ENTITY] package.name invalid, errors: name can no longer contain special characters ("~\'!()*")' + ); pkg = await TestUtil.getFullPackage({ name: ' leading-space:and:weirdchars', }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert(res.status === 422); - assert(res.body.error === '[UNPROCESSABLE_ENTITY] package.name invalid, errors: name can only contain URL-friendly characters'); + assert( + res.body.error === + '[UNPROCESSABLE_ENTITY] package.name invalid, errors: name can only contain URL-friendly characters' + ); pkg = await TestUtil.getFullPackage({ name: 'eLaBorAtE-paCkAgE-with-mixed-case-and-more-than-214-characters-----------------------------------------------------------------------------------------------------------------------------------------------------------', }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert(res.status === 422); - assert(res.body.error === '[UNPROCESSABLE_ENTITY] package.name invalid, errors: name can no longer contain more than 214 characters, name can no longer contain capital letters'); + assert( + res.body.error === + '[UNPROCESSABLE_ENTITY] package.name invalid, errors: name can no longer contain more than 214 characters, name can no longer contain capital letters' + ); }); it('should allow to publish exists pkg', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - const packageManagerService = await app.getEggObject(PackageManagerService); + const packageManagerService = await app.getEggObject( + PackageManagerService + ); const pkg = await TestUtil.getFullPackage({ name: '@cnpm/LegacyName', }); const user = await userRepository.findUserByName(publisher.name); - await packageManagerService.publish({ - scope: '@cnpm', - name: 'LegacyName', - version: '1.0.0', - description: '-', - packageJson: pkg, - readme: '', - dist: { - content: Buffer.from('', 'base64'), + await packageManagerService.publish( + { + scope: '@cnpm', + name: 'LegacyName', + version: '1.0.0', + description: '-', + packageJson: pkg, + readme: '', + dist: { + content: Buffer.from('', 'base64'), + }, + tags: ['latest'], + isPrivate: true, }, - tags: [ 'latest' ], - isPrivate: true, - }, user!); + user! + ); - - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert(res.status === 201); - }); it('should 422 when attachment data format invalid', async () => { @@ -834,39 +959,51 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () data: null, }, }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] attachment.data format invalid'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] attachment.data format invalid' + ); pkg = await TestUtil.getFullPackage({ attachment: { data: 'xyz.ddd!', }, }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] attachment.data string format invalid'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] attachment.data string format invalid' + ); pkg = await TestUtil.getFullPackage({ attachment: { data: '', }, }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] attachment.data format invalid'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] attachment.data format invalid' + ); }); it('should 422 when attachment size not match', async () => { @@ -875,41 +1012,54 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () length: 3, }, }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] attachment size 3 not match download size 251'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] attachment size 3 not match download size 251' + ); pkg = await TestUtil.getFullPackage({ attachment: { data: 'H4sIAAAAAAAAA+2SsWrDMBCGPfspDg2Zine123OyEgeylg6Zau2YR8rVRHEtGkkOg5N0jWaFdujVQAv6W4/7/', }, }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] attachment size 251 not match download size 63'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] attachment size 251 not match download size 63' + ); }); it('should 422 dist.integrity invalid', async () => { const pkg = await TestUtil.getFullPackage({ dist: { - integrity: 'sha512-n+4CQg0Rp1Qo0p9a0R5E5io67T9iD3Lcgg6exmpmt0s8kd4XcOoHu2kiu6U7xd69c', + integrity: + 'sha512-n+4CQg0Rp1Qo0p9a0R5E5io67T9iD3Lcgg6exmpmt0s8kd4XcOoHu2kiu6U7xd69c', }, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] dist.integrity invalid'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] dist.integrity invalid' + ); }); it('should 422 dist.shasum invalid', async () => { @@ -919,31 +1069,40 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () shasum: 'wrongshasum', }, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] dist.shasum invalid'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] dist.shasum invalid' + ); }); it('should 422 when name not match pkg.name', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); const pkg = await TestUtil.getFullPackage(); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] fullname(foo) not match package.name(@cnpm/testmodule)'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] fullname(foo) not match package.name(@cnpm/testmodule)' + ); }); it('should 422 _attachments is empty', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -960,7 +1119,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(422); assert(res.body.error === '[UNPROCESSABLE_ENTITY] _attachments is empty'); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -976,7 +1136,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(422); assert(res.body.error === '[UNPROCESSABLE_ENTITY] _attachments is empty'); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -996,7 +1157,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () it('should 422 versions is empty', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1009,7 +1171,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(422); assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] versions is empty'); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1025,7 +1188,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () it('should 422 dist-tags is empty', async () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1045,7 +1209,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () .expect(422); assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] dist-tags is empty'); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1068,7 +1233,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () }); it('should 402 when star / unstar request', async () => { - let res = await app.httpRequest() + let res = await app + .httpRequest() .put('/@cnpm/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1077,7 +1243,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert(res.status === 403); assert(res.body.error === '[FORBIDDEN] npm star is not allowed'); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/@cnpm/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1086,7 +1253,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert(res.status === 403); assert(res.body.error === '[FORBIDDEN] npm unstar is not allowed'); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/@cnpm/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1095,7 +1263,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert(res.status === 403); assert(res.body.error === '[FORBIDDEN] npm star is not allowed'); - res = await app.httpRequest() + res = await app + .httpRequest() .put('/@cnpm/foo') .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -1106,7 +1275,7 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () }); describe('granular token', async () => { - let token:Token; + let token: Token; let userService: UserService; let user: User | null; @@ -1118,23 +1287,26 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () token = await userService.createToken(user.userId, { name: publisher.name, type: TokenType.granular, - allowedPackages: [ '@dnpm/foo' ], - allowedScopes: [ '@cnpm', '@cnpmjs' ], + allowedPackages: ['@dnpm/foo'], + allowedScopes: ['@cnpm', '@cnpmjs'], expires: 1, }); - }); it('should 401 when expired', async () => { - await TokenModel.update({ - tokenId: token.tokenId, - }, { - expiredAt: dayjs(token.createdAt).add(1, 'millisecond').toDate(), - }); + await TokenModel.update( + { + tokenId: token.tokenId, + }, + { + expiredAt: dayjs(token.createdAt).add(1, 'millisecond').toDate(), + } + ); const name = '@cnpm/new_pkg'; const pkg = await TestUtil.getFullPackage({ name, version: '2.0.0' }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${name}`) .set('authorization', `Bearer ${token.token}`) .set('user-agent', publisher.ua) @@ -1142,22 +1314,23 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert(res.body.error, 'Token expired'); assert.equal(res.status, 401); - }); it('should 403 when publish pkg no access', async () => { - const name = '@enpm/new_pkg'; const pkg = await TestUtil.getFullPackage({ name, version: '2.0.0' }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${name}`) .set('authorization', `Bearer ${token.token}`) .set('user-agent', publisher.ua) .send(pkg); assert.equal(res.status, 403); - assert.equal(res.body.error, `[FORBIDDEN] can't access package "${name}"`); - + assert.equal( + res.body.error, + `[FORBIDDEN] can't access package "${name}"` + ); }); it('should 200 when token has no limit', async () => { @@ -1169,7 +1342,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () const name = '@cnpm/new_pkg'; const pkg = await TestUtil.getFullPackage({ name, version: '2.0.0' }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${name}`) .set('authorization', `Bearer ${token.token}`) .set('user-agent', publisher.ua) @@ -1182,13 +1356,14 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () token = await userService.createToken(user!.userId, { name: 'new-token', type: TokenType.granular, - allowedScopes: [ '@cnpm' ], + allowedScopes: ['@cnpm'], expires: 1, }); const name = '@cnpm/new_pkg'; const pkg = await TestUtil.getFullPackage({ name, version: '3.0.0' }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${name}`) .set('authorization', `Bearer ${token.token}`) .set('user-agent', publisher.ua) @@ -1198,17 +1373,21 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () }); it('should 200 when allowedPackages', async () => { - await TestUtil.createPackage({ name: '@cnpm/other_new_pkg' }, { name: user!.name }); + await TestUtil.createPackage( + { name: '@cnpm/other_new_pkg' }, + { name: user!.name } + ); token = await userService.createToken(user!.userId, { name: 'new-token', type: TokenType.granular, - allowedPackages: [ '@cnpm/other_new_pkg' ], + allowedPackages: ['@cnpm/other_new_pkg'], expires: 1, }); const name = '@cnpm/other_new_pkg'; const pkg = await TestUtil.getFullPackage({ name, version: '3.0.0' }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${name}`) .set('authorization', `Bearer ${token.token}`) .set('user-agent', publisher.ua) @@ -1222,7 +1401,7 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () token = await userService.createToken(user!.userId, { name: 'new-token', type: TokenType.granular, - allowedPackages: [ name ], + allowedPackages: [name], expires: 1, }); @@ -1230,7 +1409,8 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () pkg['dist-tags'] = { beta: '3.0.0', }; - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${name}`) .set('authorization', `Bearer ${token.token}`) .set('user-agent', publisher.ua) @@ -1238,11 +1418,9 @@ describe('test/port/controller/package/SavePackageVersionController.test.ts', () assert.equal(res.status, 201); - const queryRes = await app.httpRequest() - .get(`/${name}/latest`); + const queryRes = await app.httpRequest().get(`/${name}/latest`); assert.equal(queryRes.body.version, '3.0.0'); - }); }); }); diff --git a/test/port/controller/package/SearchPackageController.test.ts b/test/port/controller/package/SearchPackageController.test.ts index 13699361..11ec7c8a 100644 --- a/test/port/controller/package/SearchPackageController.test.ts +++ b/test/port/controller/package/SearchPackageController.test.ts @@ -2,7 +2,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; import { errors } from '@elastic/elasticsearch'; import { mockES } from '../../../../config/config.unittest.js'; -import { TestUser, TestUtil } from '../../../TestUtil.js'; +import type { TestUser } from '../../../TestUtil.js'; +import { TestUtil } from '../../../TestUtil.js'; describe('test/port/controller/package/SearchPackageController.test.ts', () => { let publisher: TestUser; @@ -20,63 +21,74 @@ describe('test/port/controller/package/SearchPackageController.test.ts', () => { describe('[GET /-/v1/search] search()', async () => { it('should throw 451 when enableElasticsearch is false', async () => { mock(app.config.cnpmcore, 'enableElasticsearch', false); - await app.httpRequest() + await app + .httpRequest() .get('/-/v1/search?text=example&from=0&size=1') .expect(451); }); it('should get example package', async () => { - mockES.add({ - method: 'POST', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, - }, () => { - return { - hits: { - total: { value: 1, relation: 'eq' }, - hits: [{ - _source: { - downloads: { - all: 0, + mockES.add( + { + method: 'POST', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, + }, + () => { + return { + hits: { + total: { value: 1, relation: 'eq' }, + hits: [ + { + _source: { + downloads: { + all: 0, + }, + package: { + name: 'example', + description: 'example package', + }, + }, }, - package: { - name: 'example', - description: 'example package', - }, - }, - }], - }, - }; - }); - const res = await app.httpRequest() + ], + }, + }; + } + ); + const res = await app + .httpRequest() .get('/-/v1/search?text=example&from=0&size=1'); assert.equal(res.body.objects[0].package.name, 'example'); assert.equal(res.body.total, 1); }); it('should get example package when search text is empty', async () => { - mockES.add({ - method: 'POST', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, - }, () => { - return { - hits: { - total: { value: 1, relation: 'eq' }, - hits: [{ - _source: { - downloads: { - all: 0, + mockES.add( + { + method: 'POST', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, + }, + () => { + return { + hits: { + total: { value: 1, relation: 'eq' }, + hits: [ + { + _source: { + downloads: { + all: 0, + }, + package: { + name: 'example', + description: 'example package', + }, + }, }, - package: { - name: 'example', - description: 'example package', - }, - }, - }], - }, - }; - }); - const res = await app.httpRequest() - .get('/-/v1/search?from=0&size=1'); + ], + }, + }; + } + ); + const res = await app.httpRequest().get('/-/v1/search?from=0&size=1'); assert.equal(res.body.objects[0].package.name, 'example'); assert.equal(res.body.total, 1); }); @@ -85,25 +97,27 @@ describe('test/port/controller/package/SearchPackageController.test.ts', () => { describe('[PUT /-/v1/search/sync/:fullname] sync()', async () => { it('should throw 451 when enableElasticsearch is false', async () => { mock(app.config.cnpmcore, 'enableElasticsearch', false); - await app.httpRequest() - .put('/-/v1/search/sync/example') - .expect(451); + await app.httpRequest().put('/-/v1/search/sync/example').expect(451); }); it('should upsert a example package', async () => { const name = 'testmodule-search-package'; - mockES.add({ - method: 'PUT', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, - }, () => { - return { - _id: name, - }; - }); + mockES.add( + { + method: 'PUT', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, + }, + () => { + return { + _id: name, + }; + } + ); mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); mock(app.config.cnpmcore, 'registry', 'https://registry.example.com'); const pkg = await TestUtil.getFullPackage({ name, version: '1.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -112,8 +126,7 @@ describe('test/port/controller/package/SearchPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() - .put(`/-/v1/search/sync/${name}`); + res = await app.httpRequest().put(`/-/v1/search/sync/${name}`); assert.equal(res.body.package, name); }); }); @@ -125,7 +138,8 @@ describe('test/port/controller/package/SearchPackageController.test.ts', () => { }); it('should throw 451 when enableElasticsearch is false', async () => { mock(app.config.cnpmcore, 'enableElasticsearch', false); - await app.httpRequest() + await app + .httpRequest() .delete('/-/v1/search/sync/example') .set('authorization', admin.authorization) .expect(451); @@ -133,19 +147,23 @@ describe('test/port/controller/package/SearchPackageController.test.ts', () => { it('should delete a example package', async () => { const name = 'testmodule-search-package'; - mockES.add({ - method: 'DELETE', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, - }, () => { - return { - _id: name, - }; - }); + mockES.add( + { + method: 'DELETE', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, + }, + () => { + return { + _id: name, + }; + } + ); mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); mock(app.config.cnpmcore, 'enableElasticsearch', true); mock(app.config.cnpmcore, 'registry', 'https://registry.example.com'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/v1/search/sync/${name}`) .set('authorization', admin.authorization); assert.equal(res.body.package, name); @@ -153,36 +171,40 @@ describe('test/port/controller/package/SearchPackageController.test.ts', () => { it('should delete a non existent package', async () => { const name = 'non-existent-search-package'; - mockES.add({ - method: 'DELETE', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, - }, () => { - return new errors.ResponseError({ - body: { errors: {}, status: 404 }, - statusCode: 404, - warnings: null, - meta: { - name: '', - context: '', - request: { - params: { - method: 'delete', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, + mockES.add( + { + method: 'DELETE', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, + }, + () => { + return new errors.ResponseError({ + body: { errors: {}, status: 404 }, + statusCode: 404, + warnings: null, + meta: { + name: '', + context: '', + request: { + params: { + method: 'delete', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, + }, + options: {}, + id: '', }, - options: {}, - id: '', + connection: null, + attempts: 1, + aborted: true, }, - connection: null, - attempts: 1, - aborted: true, - }, - }); - }); + }); + } + ); mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); mock(app.config.cnpmcore, 'enableElasticsearch', true); mock(app.config.cnpmcore, 'registry', 'https://registry.example.com'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .delete(`/-/v1/search/sync/${name}`) .set('authorization', admin.authorization); assert.equal(res.body.package, name); diff --git a/test/port/controller/package/ShowPackageController.test.ts b/test/port/controller/package/ShowPackageController.test.ts index 68c81d9f..ba08d3af 100644 --- a/test/port/controller/package/ShowPackageController.test.ts +++ b/test/port/controller/package/ShowPackageController.test.ts @@ -1,8 +1,10 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; -import { PackageManifestType, PackageRepository } from '../../../../app/repository/PackageRepository.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; +import type { PackageManifestType } from '../../../../app/repository/PackageRepository.js'; +import { PackageRepository } from '../../../../app/repository/PackageRepository.js'; import { BugVersion } from '../../../../app/core/entity/BugVersion.js'; import { PackageManagerService } from '../../../../app/core/service/PackageManagerService.js'; import { CacheService } from '../../../../app/core/service/CacheService.js'; @@ -25,7 +27,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); mock(app.config.cnpmcore, 'registry', 'https://registry.example.com'); let pkg = await TestUtil.getFullPackage({ name, version: '1.0.0' }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -35,7 +38,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.match(res.body.rev, /^\d+-\w{24}$/); pkg = await TestUtil.getFullPackage({ name, version: '2.0.0' }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -44,8 +48,12 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - pkg = await TestUtil.getFullPackage({ name: scopedName, version: '1.0.0' }); - res = await app.httpRequest() + pkg = await TestUtil.getFullPackage({ + name: scopedName, + version: '1.0.0', + }); + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -54,8 +62,12 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - pkg = await TestUtil.getFullPackage({ name: scopedName, version: '2.0.0' }); - res = await app.httpRequest() + pkg = await TestUtil.getFullPackage({ + name: scopedName, + version: '2.0.0', + }); + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -71,9 +83,7 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { mock(CacheService.prototype, 'getPackageEtag', async () => { throw new Error('mock get etag error'); }); - await app.httpRequest() - .get(`/${name}`) - .expect(200); + await app.httpRequest().get(`/${name}`).expect(200); app.expectLog(/ShowPackageController.show:error/); }); @@ -86,9 +96,7 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { mock(CacheService.prototype, 'getPackageManifests', async () => { throw new Error('mock get etag error'); }); - await app.httpRequest() - .get(`/${name}`) - .expect(200); + await app.httpRequest().get(`/${name}`).expect(200); app.expectLog(/ShowPackageController.show:error/); }); @@ -96,7 +104,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { it('should show one package with full manifests', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -112,8 +121,10 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(pkg._id, name); assert(pkg._rev); assert(versionOne._id); - assert.equal(versionOne.dist.tarball, - `https://registry.example.com/${name}/-/${name}-1.0.0.tgz`); + assert.equal( + versionOne.dist.tarball, + `https://registry.example.com/${name}/-/${name}-1.0.0.tgz` + ); // should has etag assert.match(res.headers.etag, /^W\/"\w{40}"$/); // maintainers @@ -125,28 +136,29 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { }, ]); - let res2 = await app.httpRequest() - .get(`/${name}`) - .expect(200); + let res2 = await app.httpRequest().get(`/${name}`).expect(200); // etag is same assert.equal(res2.headers.etag, res.headers.etag); // request with etag mock(app.config.cnpmcore, 'enableCDN', false); - await app.httpRequest() + await app + .httpRequest() .get(`/${name}`) .set('If-None-Match', res.headers.etag) .expect('vary', 'Origin') .expect(304); // ignore sync request - res2 = await app.httpRequest() + res2 = await app + .httpRequest() .get(`/${name}?cache=0`) .set('If-None-Match', res.headers.etag) .expect('vary', 'Origin') .expect(200); assert(res2.body.name); assert.equal(res2.headers.etag, res.headers.etag); - res2 = await app.httpRequest() + res2 = await app + .httpRequest() .get(`/${name}`) .set('If-None-Match', res.headers.etag) .set('user-agent', 'npm_service.cnpmjs.org/1.0.0') @@ -156,7 +168,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res2.headers.etag, res.headers.etag); mock(app.config.cnpmcore, 'enableCDN', true); - await app.httpRequest() + await app + .httpRequest() .get(`/${name}`) .set('If-None-Match', res.headers.etag) .expect('vary', 'Origin, Accept, Accept-Encoding') @@ -164,7 +177,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { // application/vnd.npm.install-v1+json request should not same etag mock(app.config.cnpmcore, 'enableCDN', false); - res2 = await app.httpRequest() + res2 = await app + .httpRequest() .get(`/${name}`) .set('If-None-Match', res.headers.etag) .set('Accept', 'application/vnd.npm.install-v1+json'); @@ -173,7 +187,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert(res2.headers.vary === 'Origin'); mock(app.config.cnpmcore, 'enableCDN', true); - res2 = await app.httpRequest() + res2 = await app + .httpRequest() .get(`/${name}`) .set('If-None-Match', res.headers.etag) .set('Accept', 'application/vnd.npm.install-v1+json'); @@ -183,7 +198,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { // remove W/ still work mock(app.config.cnpmcore, 'enableCDN', false); - let resEmpty = await app.httpRequest() + let resEmpty = await app + .httpRequest() .get(`/${name}`) .set('if-none-match', res.headers.etag.replace('W/', '')) .expect(304); @@ -191,7 +207,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert(resEmpty.headers.vary === 'Origin'); mock(app.config.cnpmcore, 'enableCDN', true); - resEmpty = await app.httpRequest() + resEmpty = await app + .httpRequest() .get(`/${name}`) .set('if-none-match', res.headers.etag.replace('W/', '')) .expect(304); @@ -199,37 +216,44 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert(resEmpty.headers.vary === 'Origin, Accept, Accept-Encoding'); // etag not match - const resNew = await app.httpRequest() + const resNew = await app + .httpRequest() .get(`/${name}`) .set('if-none-match', res.headers.etag.replace('"', '"change')) .expect(200); assert(resNew.text); - assert(resNew.headers['content-type'] === 'application/json; charset=utf-8'); + assert( + resNew.headers['content-type'] === 'application/json; charset=utf-8' + ); assert(resNew.body.name === name); // HEAD work - const resHead = await app.httpRequest() - .head(`/${name}`) - .expect(200); + const resHead = await app.httpRequest().head(`/${name}`).expect(200); assert(!resHead.text); assert.match(resHead.headers.etag, /^W\/"\w{40}"$/); // new version, cache should update - const pkgNew = await TestUtil.getFullPackage({ name, version: '101.0.1' }); - await app.httpRequest() + const pkgNew = await TestUtil.getFullPackage({ + name, + version: '101.0.1', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkgNew) .expect(201); - await app.httpRequest() + await app + .httpRequest() .get(`/${name}`) .set('If-None-Match', res.headers.etag) .expect(200); }); it('should show one scoped package with full manifests', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${scopedName}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -246,14 +270,18 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert(pkg._id === scopedName); assert(pkg._rev); assert(versionOne._id); - assert(versionOne.dist.tarball === `https://registry.example.com/${scopedName}/-/${name}-1.0.0.tgz`); + assert( + versionOne.dist.tarball === + `https://registry.example.com/${scopedName}/-/${name}-1.0.0.tgz` + ); assert(!res.headers['cache-control']); assert(res.headers.vary === 'Origin'); }); it('should show one scoped package with full manifests with CDN enable', async () => { mock(app.config.cnpmcore, 'enableCDN', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${scopedName}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -267,14 +295,18 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(pkg._id, scopedName); assert(pkg._rev); assert(versionOne._id); - assert(versionOne.dist.tarball === `https://registry.example.com/${scopedName}/-/${name}-1.0.0.tgz`); + assert( + versionOne.dist.tarball === + `https://registry.example.com/${scopedName}/-/${name}-1.0.0.tgz` + ); assert.equal(res.headers['cache-control'], 'public, max-age=300'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); }); it('should show one package with abbreviated manifests', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200) @@ -292,18 +324,22 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert(!pkg._rev); assert(!pkg._id); assert(!versionOne._id); - assert.equal(versionOne.dist.tarball, - `https://registry.example.com/${name}/-/${name}-2.0.0.tgz`); + assert.equal( + versionOne.dist.tarball, + `https://registry.example.com/${name}/-/${name}-2.0.0.tgz` + ); // request with etag - await app.httpRequest() + await app + .httpRequest() .get(`/${name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .set('If-None-Match', res.headers.etag) .expect(304); // remove W/ still work - const resEmpty = await app.httpRequest() + const resEmpty = await app + .httpRequest() .get(`/${name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .set('if-none-match', res.headers.etag.replace('W/', '')) @@ -311,17 +347,21 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(resEmpty.text, ''); // etag not match - const resNew = await app.httpRequest() + const resNew = await app + .httpRequest() .get(`/${name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .set('if-none-match', res.headers.etag.replace('"', '"change')) .expect(200); assert(resNew.text); - assert(resNew.headers['content-type'] === 'application/json; charset=utf-8'); + assert( + resNew.headers['content-type'] === 'application/json; charset=utf-8' + ); assert(resNew.body.name === name); // HEAD work - const resHead = await app.httpRequest() + const resHead = await app + .httpRequest() .head(`/${name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200); @@ -329,8 +369,12 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.match(resHead.headers.etag, /^W\/"\w{40}"$/); // new version, cache should update - const pkgNew = await TestUtil.getFullPackage({ name, version: '101.0.1' }); - await app.httpRequest() + const pkgNew = await TestUtil.getFullPackage({ + name, + version: '101.0.1', + }); + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -339,7 +383,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { }); it('should show one scoped package with abbreviated manifests', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${scopedName}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200) @@ -356,14 +401,18 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert(!pkg._rev); assert(!pkg._id); assert(!versionOne._id); - assert(versionOne.dist.tarball === `https://registry.example.com/${scopedName}/-/${name}-2.0.0.tgz`); + assert( + versionOne.dist.tarball === + `https://registry.example.com/${scopedName}/-/${name}-2.0.0.tgz` + ); assert(!res.headers['cache-control']); assert(res.headers.vary === 'Origin'); }); it('should show one scoped package with abbreviated manifests with CDN enable', async () => { mock(app.config.cnpmcore, 'enableCDN', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${scopedName}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200) @@ -380,13 +429,17 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert(!pkg._rev); assert(!pkg._id); assert(!versionOne._id); - assert(versionOne.dist.tarball === `https://registry.example.com/${scopedName}/-/${name}-2.0.0.tgz`); + assert( + versionOne.dist.tarball === + `https://registry.example.com/${scopedName}/-/${name}-2.0.0.tgz` + ); assert.equal(res.headers['cache-control'], 'public, max-age=300'); assert.equal(res.headers.vary, 'Origin, Accept, Accept-Encoding'); }); it('should 404 when package not exists on abbreviated manifest', async () => { - let res = await app.httpRequest() + let res = await app + .httpRequest() .get(`/${scopedName}-not-exists`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(404) @@ -394,11 +447,15 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { let data = res.body; assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert(data.error === '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found'); + assert( + data.error === + '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found' + ); // should not set cdn cache header mock(app.config.cnpmcore, 'enableCDN', true); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${scopedName}-not-exists`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(404) @@ -406,11 +463,15 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { data = res.body; assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert(data.error === '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found'); + assert( + data.error === + '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found' + ); }); it('should 404 when package not exists full manifest', async () => { - let res = await app.httpRequest() + let res = await app + .httpRequest() .get(`/${scopedName}-not-exists`) .set('Accept', 'application/json') .expect(404) @@ -418,11 +479,15 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { let data = res.body; assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert(data.error === '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found'); + assert( + data.error === + '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found' + ); // should not set cdn cache header mock(app.config.cnpmcore, 'enableCDN', true); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${scopedName}-not-exists`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(404) @@ -430,7 +495,10 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { data = res.body; assert(!res.headers.etag); assert(!res.headers['cache-control']); - assert(data.error === '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found'); + assert( + data.error === + '[NOT_FOUND] @cnpm/testmodule-show-package-not-exists not found' + ); }); it('should abbreviated manifests work with install scripts', async () => { @@ -443,7 +511,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { }, }, }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -452,7 +521,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200) @@ -470,7 +540,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { }, }, }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -479,7 +550,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200) @@ -497,7 +569,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { }, }, }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -506,7 +579,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200) @@ -521,7 +595,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { name: '@cnpm/test-module-mock-dist-not-exists', version: '1.0.0', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -530,10 +605,14 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - const pkgModel = await packageRepository.findPackage('@cnpm', 'test-module-mock-dist-not-exists'); + const pkgModel = await packageRepository.findPackage( + '@cnpm', + 'test-module-mock-dist-not-exists' + ); await packageRepository.removePackageDist(pkgModel!, false); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect(200) @@ -549,7 +628,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { name: '@cnpm/test-module-mock-dist-not-exists-full-manifests', version: '1.0.0', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -558,10 +638,14 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { assert.equal(res.body.ok, true); assert.match(res.body.rev, /^\d+-\w{24}$/); - const pkgModel = await packageRepository.findPackage('@cnpm', 'test-module-mock-dist-not-exists-full-manifests'); + const pkgModel = await packageRepository.findPackage( + '@cnpm', + 'test-module-mock-dist-not-exists-full-manifests' + ); await packageRepository.removePackageDist(pkgModel!, true); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('Accept', 'application/json') .expect(200) @@ -578,7 +662,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { name: `@cnpm/${name}`, version: '1.0.0', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -592,7 +677,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { await packageRepository.removePackageDist(pkgEntity, true); await packageRepository.removePackageVersions(pkgEntity.packageId); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('Accept', 'application/json') .expect('content-type', 'application/json; charset=utf-8'); @@ -601,12 +687,14 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { }); it('should abbreviated manifests work when all versions not exists', async () => { - const name = 'test-module-mock-dist-not-exists-abbreviated-manifests-no-verions'; + const name = + 'test-module-mock-dist-not-exists-abbreviated-manifests-no-verions'; const pkg = await TestUtil.getFullPackage({ name: `@cnpm/${name}`, version: '1.0.0', }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -620,7 +708,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { await packageRepository.removePackageDist(pkgEntity, false); await packageRepository.removePackageVersions(pkgEntity.packageId); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}`) .set('Accept', 'application/vnd.npm.install-v1+json') .expect('content-type', 'application/json; charset=utf-8'); @@ -630,50 +719,61 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { it('should redirect to source registry if public package not exists when syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - await app.httpRequest() + await app + .httpRequest() .get('/123') .expect('location', 'https://registry.npmjs.org/123') .expect(302); - await app.httpRequest() + await app + .httpRequest() .get('/cnpmcore') .set('Accept', 'application/vnd.npm.install-v1+json') .expect('location', 'https://registry.npmjs.org/cnpmcore') .expect(302); - await app.httpRequest() + await app + .httpRequest() .get('/cnpmcore') .query({ t: '0123123', foo: 'bar' }) .set('Accept', 'application/json') - .expect('location', 'https://registry.npmjs.org/cnpmcore?t=0123123&foo=bar') + .expect( + 'location', + 'https://registry.npmjs.org/cnpmcore?t=0123123&foo=bar' + ) .expect(302); - await app.httpRequest() + await app + .httpRequest() .get('/@eggjs/cnpmcore') .query({ t: '0123123', foo: 'bar' }) .set('Accept', 'application/json') - .expect('location', 'https://registry.npmjs.org/@eggjs/cnpmcore?t=0123123&foo=bar') + .expect( + 'location', + 'https://registry.npmjs.org/@eggjs/cnpmcore?t=0123123&foo=bar' + ) .expect(302); }); it('should not redirect to source registry if public package not exists when syncMode=all', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - const res = await app.httpRequest() - .get('/123'); + const res = await app.httpRequest().get('/123'); assert(res.status === 404); assert(res.body.error === '[NOT_FOUND] 123 not found'); }); it('should not redirect private scope package to source registry if package not exists when syncMode=all', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/@cnpm/cnpmcore') .set('Accept', 'application/vnd.npm.install-v1+json') .expect('content-type', 'application/json; charset=utf-8'); assert(res.status === 404); assert(res.body.error === '[NOT_FOUND] @cnpm/cnpmcore not found'); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/@cnpm/cnpmcore') .query({ t: '0123123', foo: 'bar' }) .set('Accept', 'application/json') @@ -684,14 +784,16 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { it('should not redirect private scope package to source registry if package not exists when syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/@cnpm/cnpmcore') .set('Accept', 'application/vnd.npm.install-v1+json') .expect(404) .expect('content-type', 'application/json; charset=utf-8'); assert(res.body.error === '[NOT_FOUND] @cnpm/cnpmcore not found'); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/@cnpm/cnpmcore') .query({ t: '0123123', foo: 'bar' }) .set('Accept', 'application/json') @@ -702,24 +804,33 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { it('should redirect public scope package to source registry if package not exists when syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/@eggjs/tegg-metadata') .set('Accept', 'application/vnd.npm.install-v1+json'); assert(res.status === 302); - assert(res.headers.location === 'https://registry.npmjs.org/@eggjs/tegg-metadata'); + assert( + res.headers.location === + 'https://registry.npmjs.org/@eggjs/tegg-metadata' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/@eggjs/tegg-metadata') .query({ t: '0123123', foo: 'bar' }) .set('Accept', 'application/json'); assert(res.status === 302); - assert(res.headers.location === 'https://registry.npmjs.org/@eggjs/tegg-metadata?t=0123123&foo=bar'); + assert( + res.headers.location === + 'https://registry.npmjs.org/@eggjs/tegg-metadata?t=0123123&foo=bar' + ); }); it('should not redirect to source registry when redirectNotFound is false and sync mode is none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); mock(app.config.cnpmcore, 'redirectNotFound', false); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/@eggjs/tegg-metadata') .set('Accept', 'application/vnd.npm.install-v1+json'); assert(res.status === 404); @@ -727,18 +838,23 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { it('should redirect public non-scope package to source registry if package not exists when syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/egg') .set('Accept', 'application/vnd.npm.install-v1+json'); assert(res.status === 302); assert(res.headers.location === 'https://registry.npmjs.org/egg'); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/egg') .query({ t: '0123123', foo: 'bar' }) .set('Accept', 'application/json'); assert(res.status === 302); - assert(res.headers.location === 'https://registry.npmjs.org/egg?t=0123123&foo=bar'); + assert( + res.headers.location === + 'https://registry.npmjs.org/egg?t=0123123&foo=bar' + ); }); it('should fix bug version', async () => { @@ -756,23 +872,34 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { mock(CacheService.prototype, 'getPackageEtag', async () => { return null; }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get(`/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const shouldFixVersion = res.body.versions['2.0.0']; - assert(shouldFixVersion.dist.tarball === 'https://registry.example.com/testmodule-show-package/-/testmodule-show-package-1.0.0.tgz'); - assert(shouldFixVersion.deprecated === '[WARNING] Use 1.0.0 instead of 2.0.0, reason: mock reason'); + assert( + shouldFixVersion.dist.tarball === + 'https://registry.example.com/testmodule-show-package/-/testmodule-show-package-1.0.0.tgz' + ); + assert( + shouldFixVersion.deprecated === + '[WARNING] Use 1.0.0 instead of 2.0.0, reason: mock reason' + ); // don't change version assert(shouldFixVersion.version === '2.0.0'); // sync worker request should not effect - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${name}?cache=0`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const orginalVersion = res.body.versions['2.0.0']; - assert(orginalVersion.dist.tarball === 'https://registry.example.com/testmodule-show-package/-/testmodule-show-package-2.0.0.tgz'); + assert( + orginalVersion.dist.tarball === + 'https://registry.example.com/testmodule-show-package/-/testmodule-show-package-2.0.0.tgz' + ); assert(!orginalVersion.deprecated); assert(orginalVersion.version === '2.0.0'); }); @@ -781,28 +908,38 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { mock(CacheService.prototype, 'getPackageEtag', async () => { return null; }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const data = res.body as PackageManifestType; assert(data._source_registry_name === 'self'); - assert(Object.values(data.versions).every(v => v!._source_registry_name === 'self')); + assert( + Object.values(data.versions).every( + v => v!._source_registry_name === 'self' + ) + ); }); it('should show _source_registry_name for abbreviated', async () => { mock(CacheService.prototype, 'getPackageEtag', async () => { return null; }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get(`/${name}`) .set('accept', 'application/vnd.npm.install-v1+json') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); const data = res.body as PackageManifestType; - assert(Object.values(data.versions).every(v => v!._source_registry_name === 'self')); + assert( + Object.values(data.versions).every( + v => v!._source_registry_name === 'self' + ) + ); }); it('should not throw error if no versions', async () => { @@ -836,7 +973,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { }, }; }); - await app.httpRequest() + await app + .httpRequest() .get(`/${name}`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); @@ -846,7 +984,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { mock(app.config.cnpmcore, 'syncMode', 'exist'); mock(app.config.cnpmcore, 'syncNotFound', true); mock(app.config.cnpmcore, 'redirectNotFound', false); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/lodash') .set('user-agent', publisher.ua + ' node/16.0.0') .set('Accept', 'application/vnd.npm.install-v1+json'); @@ -858,7 +997,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { mock(app.config.cnpmcore, 'syncMode', 'exist'); mock(app.config.cnpmcore, 'syncNotFound', false); mock(app.config.cnpmcore, 'redirectNotFound', true); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/egg') .set('user-agent', publisher.ua + ' node/16.0.0') .set('Accept', 'application/vnd.npm.install-v1+json'); @@ -869,18 +1009,25 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => { it('should read manifest from source in proxy mode', async () => { mock(app.config.cnpmcore, 'syncMode', SyncMode.proxy); mock(app.config.cnpmcore, 'redirectNotFound', false); - const data = await TestUtil.readJSONFile(TestUtil.getFixtures('registry.npmjs.org/abbreviated_foobar.json')); + const data = await TestUtil.readJSONFile( + TestUtil.getFixtures('registry.npmjs.org/abbreviated_foobar.json') + ); app.mockHttpclient('https://registry.npmjs.org/foobar', 'GET', { data, persist: false, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foobar') .set('user-agent', publisher.ua + ' node/16.0.0') .set('Accept', 'application/vnd.npm.install-v1+json'); assert(res.status === 200); assert(res.body.description === 'cnpmcore mock json'); - assert(res.body.versions['1.0.0'].dist.tarball.includes(app.config.cnpmcore.registry)); + assert( + res.body.versions['1.0.0'].dist.tarball.includes( + app.config.cnpmcore.registry + ) + ); }); }); }); diff --git a/test/port/controller/package/ShowPackageVersionController.test.ts b/test/port/controller/package/ShowPackageVersionController.test.ts index 9a38a5d9..bb4daed3 100644 --- a/test/port/controller/package/ShowPackageVersionController.test.ts +++ b/test/port/controller/package/ShowPackageVersionController.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; import { BugVersion } from '../../../../app/core/entity/BugVersion.js'; import { BugVersionService } from '../../../../app/core/service/BugVersionService.js'; import { SyncMode } from '../../../../app/common/constants.js'; @@ -22,39 +23,45 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () description: 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foo/1.0.0') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); assert.equal(res.body.name, 'foo'); assert.match(res.body.dist.tarball, /^http:\/\//); assert(res.body.dist.tarball.endsWith('/foo/-/foo-1.0.0.tgz')); - assert.equal(res.body.dist.shasum, 'fa475605f88bab9b1127833633ca3ae0a477224c'); - assert.equal(res.body.dist.integrity, 'sha512-n+4CQg0Rp1Qo0p9a0R5E5io67T9iD3Lcgg6exmpmt0s8kd4XcOoHu2kiu6U7xd69cGq0efkNGWUBP229ObfRSA=='); + assert.equal( + res.body.dist.shasum, + 'fa475605f88bab9b1127833633ca3ae0a477224c' + ); + assert.equal( + res.body.dist.integrity, + 'sha512-n+4CQg0Rp1Qo0p9a0R5E5io67T9iD3Lcgg6exmpmt0s8kd4XcOoHu2kiu6U7xd69cGq0efkNGWUBP229ObfRSA==' + ); assert.equal(res.body.dist.size, 251); - assert.equal(res.body.description, 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻'); + assert.equal( + res.body.description, + 'work with utf8mb4 💩, 𝌆 utf8_unicode_ci, foo𝌆bar 🍻' + ); // support semver spec - await app.httpRequest() - .get('/foo/%5E1.0') - .expect(200); + await app.httpRequest().get('/foo/%5E1.0').expect(200); - await app.httpRequest() - .get('/foo/^1.0') - .expect(200); + await app.httpRequest().get('/foo/^1.0').expect(200); // not support alias - await app.httpRequest() - .get('/alias-a-pkg/npm:foo@^1.0') - .expect(422); + await app.httpRequest().get('/alias-a-pkg/npm:foo@^1.0').expect(422); - await app.httpRequest() + await app + .httpRequest() .get('/npm/@babel%2fhelper-compilation-targets ') .expect(422); }); @@ -97,56 +104,72 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () mock(BugVersionService.prototype, 'getBugVersion', async () => { return bugVersion; }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkgV1.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkgV1) .expect(201); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkgV2.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkgV2) .expect(201); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkgV3.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkgV3) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foo/2.0.0') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); - assert(new URL(res.body.dist.tarball).pathname === '/foo/-/foo-1.0.0.tgz'); - assert(res.body.deprecated === '[WARNING] Use 1.0.0 instead of 2.0.0, reason: mock reason'); + assert( + new URL(res.body.dist.tarball).pathname === '/foo/-/foo-1.0.0.tgz' + ); + assert( + res.body.deprecated === + '[WARNING] Use 1.0.0 instead of 2.0.0, reason: mock reason' + ); // don't change version assert(res.body.version === '2.0.0'); // same version not fix bug version - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo/3.0.0') .expect(200) .expect('content-type', 'application/json; charset=utf-8'); - assert(new URL(res.body.dist.tarball).pathname === '/foo/-/foo-3.0.0.tgz'); + assert( + new URL(res.body.dist.tarball).pathname === '/foo/-/foo-3.0.0.tgz' + ); assert(!res.body.deprecated); assert(res.body.version === '3.0.0'); // sync worker request should not effect - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkgV1.name}/2.0.0?cache=0`) .expect(200) .expect('content-type', 'application/json; charset=utf-8'); - assert(new URL(res.body.dist.tarball).pathname === '/foo/-/foo-2.0.0.tgz'); + assert( + new URL(res.body.dist.tarball).pathname === '/foo/-/foo-2.0.0.tgz' + ); assert(!res.body.deprecated); assert(res.body.version === '2.0.0'); }); it('should 422 with invalid spec', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foo/@invalid-spec') .expect(422) .expect('content-type', 'application/json; charset=utf-8'); @@ -161,14 +184,16 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () description: 'foo description', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - await app.httpRequest() + await app + .httpRequest() .get('/@cnpm/foo/1.0.0') .expect(200) .expect(res => { @@ -184,22 +209,21 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () description: 'foo latest description', }, }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg); assert(res.status === 201); - res = await app.httpRequest() - .get(`/${pkg.name}/latest`); + res = await app.httpRequest().get(`/${pkg.name}/latest`); assert(res.status === 200); assert(res.body.version === '1.0.0'); assert(!res.headers['cache-control']); assert(res.headers.vary === 'Origin'); mock(app.config.cnpmcore, 'enableCDN', true); - res = await app.httpRequest() - .get(`/${pkg.name}/latest`); + res = await app.httpRequest().get(`/${pkg.name}/latest`); assert(res.status === 200); assert(res.body.version === '1.0.0'); assert.equal(res.headers['cache-control'], 'public, max-age=300'); @@ -215,46 +239,42 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () description: 'foo latest description', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() - .get(`/${pkg.name}/latest`) - .expect(200); + let res = await app.httpRequest().get(`/${pkg.name}/latest`).expect(200); assert.equal(res.body.version, '2.0.0'); - res = await app.httpRequest() - .get(`/${pkg.name}/^2.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/^2.0.0`).expect(200); assert.equal(res.body.version, '2.0.0'); - res = await app.httpRequest() - .get(`/${pkg.name}/%5E2.0.0`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/%5E2.0.0`).expect(200); assert.equal(res.body.version, '2.0.0'); // new beta tag - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/-/package/${pkg.name}/dist-tags/beta`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .set('content-type', 'application/json') .send(JSON.stringify('2.0.0')) .expect(200); - res = await app.httpRequest() - .get(`/${pkg.name}/beta`) - .expect(200); + res = await app.httpRequest().get(`/${pkg.name}/beta`).expect(200); assert.equal(res.body.version, '2.0.0'); // 404 when tag not exists - res = await app.httpRequest() - .get(`/${pkg.name}/beta-not-exists`); + res = await app.httpRequest().get(`/${pkg.name}/beta-not-exists`); assert.equal(res.status, 404); assert(!res.headers.etag); - assert.equal(res.body.error, `[NOT_FOUND] ${pkg.name}@beta-not-exists not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] ${pkg.name}@beta-not-exists not found` + ); }); it('should 404 when version not exists', async () => { @@ -265,30 +285,39 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () description: 'foo description', }, }); - await app.httpRequest() + await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) .send(pkg) .expect(201); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get(`/${pkg.name}/1.0.40000404`) .expect(404); assert(!res.headers.etag); - assert.equal(res.body.error, `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found`); + assert.equal( + res.body.error, + `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found` + ); // should 404 on syncMode=all when package exists mock(app.config.cnpmcore, 'syncMode', 'all'); - res = await app.httpRequest() + res = await app + .httpRequest() .get(`/${pkg.name}/1.0.40000404`) .expect(404); assert(!res.headers.etag); - assert(res.body.error === `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found`); + assert( + res.body.error === `[NOT_FOUND] ${pkg.name}@1.0.40000404 not found` + ); }); it('should 404 when package not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/@cnpm/foonot-exists/1.0.40000404') .expect(404); assert(!res.headers.etag); @@ -297,22 +326,28 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () it('should not redirect public package version to source registry when syncMode=all', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - let res = await app.httpRequest() + let res = await app + .httpRequest() .get('/foonot-not-exists/1.0.40000404') .expect(404); assert(res.body.error === '[NOT_FOUND] foonot-not-exists not found'); mock(app.config.cnpmcore, 'allowPublishNonScopePackage', true); await TestUtil.createPackage({ name: 'foo-exists', isPrivate: false }); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/foo-exists/1.0.40000404?t=123') .expect(404); - assert.equal(res.body.error, '[NOT_FOUND] foo-exists@1.0.40000404 not found'); + assert.equal( + res.body.error, + '[NOT_FOUND] foo-exists@1.0.40000404 not found' + ); }); it('should not redirect private scope package to source registry when syncMode=all', async () => { mock(app.config.cnpmcore, 'syncMode', 'all'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/@cnpm/foonot-exists/1.0.40000404') .expect(404); assert.equal(res.body.error, '[NOT_FOUND] @cnpm/foonot-exists not found'); @@ -320,7 +355,8 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () it('should not redirect private scope package to source registry when syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/@cnpm/foonot-exists/1.0.40000404') .expect(404); assert.equal(res.body.error, '[NOT_FOUND] @cnpm/foonot-exists not found'); @@ -328,43 +364,56 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () it('should redirect public scope package to source registry when syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - let res = await app.httpRequest() - .get('/@egg/foonot-exists/1.0.40000404'); + let res = await app.httpRequest().get('/@egg/foonot-exists/1.0.40000404'); assert(res.status === 302); assert(!res.headers.etag); - assert(res.headers.location === 'https://registry.npmjs.org/@egg/foonot-exists/1.0.40000404'); + assert( + res.headers.location === + 'https://registry.npmjs.org/@egg/foonot-exists/1.0.40000404' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .get('/@egg/foonot-exists/1.0.40000404?t=123'); assert(res.status === 302); assert(!res.headers.etag); - assert(res.headers.location === 'https://registry.npmjs.org/@egg/foonot-exists/1.0.40000404?t=123'); + assert( + res.headers.location === + 'https://registry.npmjs.org/@egg/foonot-exists/1.0.40000404?t=123' + ); }); it('should redirect public non scope package to source registry when syncMode=none', async () => { mock(app.config.cnpmcore, 'syncMode', 'none'); - await app.httpRequest() + await app + .httpRequest() .get('/foonot-exists/1.0.40000404') - .expect('location', 'https://registry.npmjs.org/foonot-exists/1.0.40000404') + .expect( + 'location', + 'https://registry.npmjs.org/foonot-exists/1.0.40000404' + ) .expect(302); - await app.httpRequest() + await app + .httpRequest() .get('/foonot-exists/1.0.40000404?t=123') - .expect('location', 'https://registry.npmjs.org/foonot-exists/1.0.40000404?t=123') + .expect( + 'location', + 'https://registry.npmjs.org/foonot-exists/1.0.40000404?t=123' + ) .expect(302); }); it('should show _source_registry_name in version manifest', async () => { await TestUtil.createPackage({ name: '@cnpm/foo', version: '1.0.0' }); - const res = await app.httpRequest() - .get('/@cnpm/foo/1.0.0') - .expect(200); + const res = await app.httpRequest().get('/@cnpm/foo/1.0.0').expect(200); assert(res.body._source_registry_name === 'self'); }); it('should show _source_registry_name in version manifest for abbreviated', async () => { await TestUtil.createPackage({ name: '@cnpm/foo', version: '1.0.0' }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/@cnpm/foo/1.0.0') .set('accept', 'application/vnd.npm.install-v1+json') .expect(200); @@ -374,12 +423,15 @@ describe('test/port/controller/package/ShowPackageVersionController.test.ts', () it('should read package version manifest from source in proxy mode', async () => { mock(app.config.cnpmcore, 'syncMode', SyncMode.proxy); mock(app.config.cnpmcore, 'redirectNotFound', false); - const data = await TestUtil.readJSONFile(TestUtil.getFixtures('registry.npmjs.org/foobar/1.0.0/abbreviated.json')); + const data = await TestUtil.readJSONFile( + TestUtil.getFixtures('registry.npmjs.org/foobar/1.0.0/abbreviated.json') + ); app.mockHttpclient('https://registry.npmjs.org/foobar/1.0.0', 'GET', { data, persist: false, }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .get('/foobar/1.0.0') .set('user-agent', publisher.ua + ' node/16.0.0') .set('Accept', 'application/vnd.npm.install-v1+json'); diff --git a/test/port/controller/package/UpdatePackageController.test.ts b/test/port/controller/package/UpdatePackageController.test.ts index 78499589..1dc8277c 100644 --- a/test/port/controller/package/UpdatePackageController.test.ts +++ b/test/port/controller/package/UpdatePackageController.test.ts @@ -3,7 +3,8 @@ import { app, mock } from '@eggjs/mock/bootstrap'; import { RegistryType } from '../../../../app/common/enum/Registry.js'; import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js'; -import { TestUser, TestUtil } from '../../../../test/TestUtil.js'; +import type { TestUser } from '../../../../test/TestUtil.js'; +import { TestUtil } from '../../../../test/TestUtil.js'; describe('test/port/controller/package/UpdatePackageController.test.ts', () => { let publisher: TestUser; @@ -17,7 +18,8 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { beforeEach(async () => { const pkg = await TestUtil.getFullPackage({ name: scopedName }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${pkg.name}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -31,7 +33,8 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { it('should 404 when pkg not exists', async () => { const user = await TestUtil.createUser(); mock(app.config.cnpmcore, 'admins', { [user.name]: user.email }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put('/banana/-rev/123') .set('authorization', user.authorization) .set('user-agent', publisher.ua) @@ -39,15 +42,14 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }); assert.equal(res.statusCode, 404); }); it('should 422 when maintainters empty', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -58,11 +60,15 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { maintainers: [], }) .expect(422); - assert.equal(res.body.error, '[INVALID_PARAM] maintainers: must NOT have fewer than 1 items'); + assert.equal( + res.body.error, + '[INVALID_PARAM] maintainers: must NOT have fewer than 1 items' + ); }); it('should 422 when some maintainters not exists', async () => { - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -78,12 +84,16 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { ], }) .expect(422); - assert.equal(res.body.error, '[UNPROCESSABLE_ENTITY] Maintainer "foo" not exists'); + assert.equal( + res.body.error, + '[UNPROCESSABLE_ENTITY] Maintainer "foo" not exists' + ); }); it('should 403 request user is not maintainer', async () => { const user = await TestUtil.createUser(); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', publisher.ua) @@ -91,18 +101,20 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(403); - assert.equal(res.body.error, `[FORBIDDEN] "${user.name}" not authorized to modify ${scopedName}, please contact maintainers: "${publisher.name}"`); + assert.equal( + res.body.error, + `[FORBIDDEN] "${user.name}" not authorized to modify ${scopedName}, please contact maintainers: "${publisher.name}"` + ); }); it('should 200 request when user is admin and user is not maintainer', async () => { const user = await TestUtil.createUser(); mock(app.config.cnpmcore, 'admins', { [user.name]: user.email }); - const res = await app.httpRequest() + const res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', publisher.ua) @@ -110,9 +122,7 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(200); assert.equal(res.statusCode, 200); @@ -125,7 +135,9 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { name: 'dnpm:banana', }); - const registryManagerService = await app.getEggObject(RegistryManagerService); + const registryManagerService = await app.getEggObject( + RegistryManagerService + ); const registry = await registryManagerService.createRegistry({ name: 'dnpmcore', changeStream: 'https://d.cnpmjs.org/_changes', @@ -141,7 +153,8 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { }); mock(app.config.cnpmcore, 'admins', { [user.name]: user.email }); - const updateRes = await app.httpRequest() + const updateRes = await app + .httpRequest() .put('/@cnpm/banana/-rev/1') .set('authorization', user.authorization) .set('user-agent', publisher.ua) @@ -149,9 +162,7 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: 'banana', email: user.email }, - ], + maintainers: [{ name: 'banana', email: user.email }], }); assert.equal(updateRes.statusCode, 200); @@ -160,20 +171,23 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { it('should 400 when npm-command invalid', async () => { const user = await TestUtil.createUser(); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', publisher.ua) .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(400); - assert.equal(res.body.error, '[BAD_REQUEST] header: npm-command expected "owner", but got ""'); - res = await app.httpRequest() + assert.equal( + res.body.error, + '[BAD_REQUEST] header: npm-command expected "owner", but got ""' + ); + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', publisher.ua) @@ -181,17 +195,19 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(400); - assert.equal(res.body.error, '[BAD_REQUEST] header: npm-command expected "owner", but got "adduser"'); + assert.equal( + res.body.error, + '[BAD_REQUEST] header: npm-command expected "owner", but got "adduser"' + ); // npm@6: referer: 'xxx [REDACTED]' // npm@>=7: 'npm-command': 'xxx' // when npm version < 7, npm command can get from referer - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', 'npm/6.3.1') @@ -199,12 +215,13 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(400); - assert.equal(res.body.error, '[BAD_REQUEST] header: npm-command expected "owner", but got "addUser"'); + assert.equal( + res.body.error, + '[BAD_REQUEST] header: npm-command expected "owner", but got "addUser"' + ); }); it('should 200 when npm command is npm owner add', async () => { @@ -214,7 +231,8 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { // npm version < 7 const user = await TestUtil.createUser(); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', publisher.authorization) .set('user-agent', 'npm/6.3.1') @@ -222,16 +240,15 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(200); assert.equal(res.statusCode, 200); assert.deepEqual(res.body, { ok: true }); // npm version >= 7 - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', 'npm/7.3.1') @@ -240,9 +257,7 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(200); assert.equal(res.statusCode, 200); @@ -251,7 +266,8 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { it('should 403 when npm client invalid', async () => { const user = await TestUtil.createUser(); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', '') @@ -259,14 +275,16 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(403); - assert.equal(res.body.error, '[FORBIDDEN] Only allow npm client to access'); + assert.equal( + res.body.error, + '[FORBIDDEN] Only allow npm client to access' + ); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', 'npm/6.3.1') @@ -274,19 +292,21 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(403); - assert.equal(res.body.error, '[FORBIDDEN] Only allow npm@>=7.0.0 client to access'); + assert.equal( + res.body.error, + '[FORBIDDEN] Only allow npm@>=7.0.0 client to access' + ); }); it('should 200 when enableNpmClientAndVersionCheck is false', async () => { mock(app.config.cnpmcore, 'enableNpmClientAndVersionCheck', false); const user = await TestUtil.createUser(); mock(app.config.cnpmcore, 'admins', { [user.name]: user.email }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', '') @@ -294,14 +314,13 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(200); assert.equal(res.statusCode, 200); assert.deepEqual(res.body, { ok: true }); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', 'npm/6.3.1') @@ -309,9 +328,7 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(200); assert.equal(res.statusCode, 200); @@ -322,7 +339,8 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { mock(app.config.cnpmcore, 'enableNpmClientAndVersionCheck', true); const user = await TestUtil.createUser(); mock(app.config.cnpmcore, 'admins', { [user.name]: user.email }); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', '') @@ -330,14 +348,16 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(403); assert.equal(res.statusCode, 403); - assert.equal(res.body.error, '[FORBIDDEN] Only allow npm client to access'); - res = await app.httpRequest() + assert.equal( + res.body.error, + '[FORBIDDEN] Only allow npm client to access' + ); + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', 'npm/6.3.1') @@ -345,24 +365,24 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(403); assert.equal(res.statusCode, 403); - assert.equal(res.body.error, '[FORBIDDEN] Only allow npm@>=7.0.0 client to access'); + assert.equal( + res.body.error, + '[FORBIDDEN] Only allow npm@>=7.0.0 client to access' + ); }); it('should 200 and get latest maintainers', async () => { - let res = await app.httpRequest() - .get(`/${scopedName}`) - .expect(200); + let res = await app.httpRequest().get(`/${scopedName}`).expect(200); assert.equal(res.body.maintainers.length, 1); const user = await TestUtil.createUser(); const user2 = await TestUtil.createUser(); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -378,12 +398,11 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { }) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${scopedName}`) - .expect(200); + res = await app.httpRequest().get(`/${scopedName}`).expect(200); assert.equal(res.body.maintainers.length, 3); - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -398,13 +417,12 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { }) .expect(200); assert.equal(res.body.ok, true); - res = await app.httpRequest() - .get(`/${scopedName}`) - .expect(200); + res = await app.httpRequest().get(`/${scopedName}`).expect(200); assert.equal(res.body.maintainers.length, 2); // publisher is remove from maintainers - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', publisher.authorization) .set('user-agent', publisher.ua) @@ -412,17 +430,19 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: publisher.name, email: publisher.email }, - ], + maintainers: [{ name: publisher.name, email: publisher.email }], }) .expect(403); - assert.equal(res.body.error, `[FORBIDDEN] "${publisher.name}" not authorized to modify ${scopedName}, please contact maintainers: "${user.name}, ${user2.name}"`); + assert.equal( + res.body.error, + `[FORBIDDEN] "${publisher.name}" not authorized to modify ${scopedName}, please contact maintainers: "${user.name}, ${user2.name}"` + ); }); it('should support pnpm and other npm clients', async () => { const user = await TestUtil.createUser(); - let res = await app.httpRequest() + let res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', user.authorization) .set('user-agent', 'pnpm/7.0.0 npm/6.3.1') @@ -430,15 +450,17 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => { .send({ _id: rev, _rev: rev, - maintainers: [ - { name: user.name, email: user.email }, - ], + maintainers: [{ name: user.name, email: user.email }], }) .expect(403); - assert.equal(res.body.error, '[FORBIDDEN] Only allow npm@>=7.0.0 client to access'); + assert.equal( + res.body.error, + '[FORBIDDEN] Only allow npm@>=7.0.0 client to access' + ); // should valid with pnpm6 and npm>10 - res = await app.httpRequest() + res = await app + .httpRequest() .put(`/${scopedName}/-rev/${rev}`) .set('authorization', publisher.authorization) .set('user-agent', 'pnpm/6.0.0 npm/17.1.0') diff --git a/test/repository/RegistryRepository.test.ts b/test/repository/RegistryRepository.test.ts index ef116a71..4df29403 100644 --- a/test/repository/RegistryRepository.test.ts +++ b/test/repository/RegistryRepository.test.ts @@ -3,7 +3,7 @@ import { app } from '@eggjs/mock/bootstrap'; import { RegistryRepository } from '../../app/repository/RegistryRepository.js'; import { Registry } from '../../app/core/entity/Registry.js'; -import { RegistryType } from '../../app/common/enum/Registry.js'; +import type { RegistryType } from '../../app/common/enum/Registry.js'; describe('test/repository/RegistryRepository.test.ts', () => { let registryRepository: RegistryRepository; @@ -11,26 +11,30 @@ describe('test/repository/RegistryRepository.test.ts', () => { beforeEach(async () => { registryRepository = await app.getEggObject(RegistryRepository); - registryModel = await registryRepository.saveRegistry(Registry.create({ - name: 'cnpmcore', - userPrefix: 'cnpm:', - changeStream: 'https://r.npmjs.com/_changes', - host: 'https://registry.npmjs.org', - type: 'cnpmcore' as RegistryType, - authToken: '', - })) as Registry; + registryModel = (await registryRepository.saveRegistry( + Registry.create({ + name: 'cnpmcore', + userPrefix: 'cnpm:', + changeStream: 'https://r.npmjs.com/_changes', + host: 'https://registry.npmjs.org', + type: 'cnpmcore' as RegistryType, + authToken: '', + }) + )) as Registry; }); describe('RegistryRepository', () => { it('create work', async () => { - const newRegistry = await registryRepository.saveRegistry(Registry.create({ - name: 'npm', - userPrefix: 'npm:', - changeStream: 'https://ra.npmjs.com/_changes', - host: 'https://registry.npmjs.org', - type: 'cnpmcore' as RegistryType, - authToken: '', - })) as Registry; + const newRegistry = (await registryRepository.saveRegistry( + Registry.create({ + name: 'npm', + userPrefix: 'npm:', + changeStream: 'https://ra.npmjs.com/_changes', + host: 'https://registry.npmjs.org', + type: 'cnpmcore' as RegistryType, + authToken: '', + }) + )) as Registry; assert(newRegistry); assert(newRegistry.type === 'cnpmcore'); }); diff --git a/test/repository/SearchRepository.test.ts b/test/repository/SearchRepository.test.ts index 2a1ff9f7..c8ce34b7 100644 --- a/test/repository/SearchRepository.test.ts +++ b/test/repository/SearchRepository.test.ts @@ -1,7 +1,8 @@ import { strict as assert } from 'node:assert'; import { app, mock } from '@eggjs/mock/bootstrap'; -import { SearchManifestType, SearchRepository } from '../../app/repository/SearchRepository.js'; +import type { SearchManifestType } from '../../app/repository/SearchRepository.js'; +import { SearchRepository } from '../../app/repository/SearchRepository.js'; import { mockES } from '../../config/config.unittest.js'; import { PackageManagerService } from '../../app/core/service/PackageManagerService.js'; import { TestUtil } from '../TestUtil.js'; @@ -34,19 +35,24 @@ describe('test/repository/SearchRepository.test.ts', () => { description: 'example package', }, }; - mockES.add({ - method: 'POST', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, - }, () => { - return { - hits: { - total: { value: 1, relation: 'eq' }, - hits: [{ - _source, - }], - }, - }; - }); + mockES.add( + { + method: 'POST', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, + }, + () => { + return { + hits: { + total: { value: 1, relation: 'eq' }, + hits: [ + { + _source, + }, + ], + }, + }; + } + ); const res = await searchRepository.searchPackage({ body: { query: { @@ -69,7 +75,7 @@ describe('test/repository/SearchRepository.test.ts', () => { scope: 'unscoped', version: '1.0.0', _rev: '243-61f144324ce7cf8f58255946"', - versions: [ '1.0.1' ], + versions: ['1.0.1'], maintainers: [ { name: 'cnpmcore', @@ -88,34 +94,39 @@ describe('test/repository/SearchRepository.test.ts', () => { all: 0, }, }; - mockES.add({ - method: 'PUT', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, - }, () => { - return { - _id: manifest.package.name, - }; - }); + mockES.add( + { + method: 'PUT', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, + }, + () => { + return { + _id: manifest.package.name, + }; + } + ); const id = await searchRepository.upsertPackage(manifest); assert.equal(id, manifest.package.name); }); it('delete work', async () => { const mockedPackageName = 'example'; - mockES.add({ - method: 'DELETE', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, - }, () => { - return { - _id: 'example', - }; - }); + mockES.add( + { + method: 'DELETE', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_doc/:id`, + }, + () => { + return { + _id: 'example', + }; + } + ); const id = await searchRepository.removePackage(mockedPackageName); assert.equal(id, mockedPackageName); }); it('should clear blocked pkg', async () => { - await TestUtil.createPackage({ name: '@cnpm/example', }); @@ -130,20 +141,24 @@ describe('test/repository/SearchRepository.test.ts', () => { }, }; - mockES.add({ - method: 'POST', - path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, - }, () => { - return { - hits: { - total: { value: 1, relation: 'eq' }, - hits: [{ - _source, - }], - }, - }; - }); - + mockES.add( + { + method: 'POST', + path: `/${app.config.cnpmcore.elasticsearchIndex}/_search`, + }, + () => { + return { + hits: { + total: { value: 1, relation: 'eq' }, + hits: [ + { + _source, + }, + ], + }, + }; + } + ); let res = await searchRepository.searchPackage({ body: { @@ -169,15 +184,21 @@ describe('test/repository/SearchRepository.test.ts', () => { let called = false; - mock(PackageSearchService.prototype, 'removePackage', async (fullname: string) => { - if (fullname === '@cnpm/example') { - called = true; + mock( + PackageSearchService.prototype, + 'removePackage', + async (fullname: string) => { + if (fullname === '@cnpm/example') { + called = true; + } } - }); + ); - await packageManagerService.blockPackageByFullname('@cnpm/example', 'test'); + await packageManagerService.blockPackageByFullname( + '@cnpm/example', + 'test' + ); assert(called); - }); }); }); diff --git a/test/repository/TaskRepository.test.ts b/test/repository/TaskRepository.test.ts index 7d795b21..ef6bd420 100644 --- a/test/repository/TaskRepository.test.ts +++ b/test/repository/TaskRepository.test.ts @@ -5,9 +5,14 @@ import { app } from '@eggjs/mock/bootstrap'; import { TaskRepository } from '../../app/repository/TaskRepository.js'; import { Task as TaskModel } from '../../app/repository/model/Task.js'; -import { ChangesStreamTaskData, Task, TaskData } from '../../app/core/entity/Task.js'; +import type { + ChangesStreamTaskData, + TaskData, +} from '../../app/core/entity/Task.js'; +import { Task } from '../../app/core/entity/Task.js'; import { TaskState, TaskType } from '../../app/common/enum/Task.js'; -import { EasyData, EntityUtil } from '../../app/core/util/EntityUtil.js'; +import type { EasyData } from '../../app/core/util/EntityUtil.js'; +import { EntityUtil } from '../../app/core/util/EntityUtil.js'; describe('test/repository/TaskRepository.test.ts', () => { let taskRepository: TaskRepository; @@ -22,7 +27,6 @@ describe('test/repository/TaskRepository.test.ts', () => { }); describe('unique biz id', () => { - it('should save succeed if biz id is equal', async () => { const bizId = 'mock_dup_biz_id'; const data: EasyData, 'taskId'> = { @@ -72,7 +76,7 @@ describe('test/repository/TaskRepository.test.ts', () => { // 持久化保存 task1 await taskRepository.saveTask(task1); // 再取一个 asyncTask ,两者指向相同的数据行 - const asyncTask = await taskRepository.findTask(task1.taskId) as Task; + const asyncTask = (await taskRepository.findTask(task1.taskId)) as Task; // task1 对应的数据被更新了 await setTimeout(1); @@ -140,7 +144,7 @@ describe('test/repository/TaskRepository.test.ts', () => { it('should only save one', async () => { const condition = task.start(); - const [ firstSave, secondSave ] = await Promise.all([ + const [firstSave, secondSave] = await Promise.all([ taskRepository.idempotentSaveTask(task, condition), taskRepository.idempotentSaveTask(task, condition), ]); diff --git a/tsconfig.json b/tsconfig.json index b38938a4..6eaa5a20 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,8 @@ { "extends": "@eggjs/tsconfig", "compilerOptions": { - "strict": true, "target": "ES2021", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "noImplicitAny": true, - "declaration": false, "resolveJsonModule": true, - "useUnknownInCatchVariables": false, + "useUnknownInCatchVariables": false } }