fix: noImplicitAny ts (#568)

> Attempted to apply the `noImplicitAny`, parameter types should be
specified unless any is manually declared.
* 🐞 Fixed an issue in bugVersionAdvice where AbbreviatedManifest was
being set abnormally.
* 🤖 Added index.d.ts to store declarations for dependencies without
types.
* 🤔 skipLibCheck has no effect on leoric for now, so it cannot be
enabled temporarily.
--------
> 尝试应用 `noImplicitAny` 配置,除非手动声明 any,否则需要指定参数类型
* 🐞 修复 bugVersionAdvice 中,AbbreviatedManifest 设置异常
* 🤖 添加 index.d.ts 存放无类型依赖声明
* 🤔 skipLibCheck 对 leoric 失效,暂时无法开启 



![image](https://github.com/cnpm/cnpmcore/assets/5574625/7ed9d22e-cac8-4202-ba3c-d4c26eb7dc00)
This commit is contained in:
elrrrrrrr
2023-08-08 20:42:57 +08:00
committed by GitHub
parent 3297121b9f
commit 1932bb9713
33 changed files with 239 additions and 79 deletions

View File

@@ -105,13 +105,13 @@ const WHITE_FILENAME_CONTENT_TYPES = {
'.eslintignore': PLAIN_TEXT,
'.jshintrc': 'application/json',
'.eslintrc': 'application/json',
};
} as const;
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] ||
WHITE_FILENAME_CONTENT_TYPES[filename as keyof typeof WHITE_FILENAME_CONTENT_TYPES] ||
DEFAULT_CONTENT_TYPE;
}

View File

@@ -34,7 +34,7 @@ export function integrity(plain: string): string {
}
export function checkIntegrity(plain: string, expectedIntegrity: string): boolean {
return ssri.checkData(plain, expectedIntegrity);
return !!ssri.checkData(plain, expectedIntegrity);
}
export function sha512(plain: string): string {

View File

@@ -11,6 +11,7 @@ import {
HttpClientRequestOptions,
HttpClientResponse,
} from 'egg';
import { PackageManifestType } from '../../repository/PackageRepository';
type HttpMethod = HttpClientRequestOptions['method'];
@@ -40,7 +41,7 @@ export class NPMRegistry {
this.registryHost = registryHost;
}
public async getFullManifests(fullname: string, optionalConfig?: {retries?:number, remoteAuthToken?:string}): Promise<RegistryResponse> {
public async getFullManifests(fullname: string, optionalConfig?: { retries?: number, remoteAuthToken?: string }): Promise<{ method: HttpMethod } & HttpClientResponse<PackageManifestType>> {
let retries = optionalConfig?.retries || 3;
// set query t=timestamp, make sure CDN cache disable
// cache=0 is sync worker request flag

View File

@@ -17,6 +17,8 @@ export type FetchResult = {
nextParams?: any;
};
const platforms = [ 'darwin', 'linux', 'win32' ] as const;
export const BINARY_ADAPTER_ATTRIBUTE = Symbol('BINARY_ADAPTER_ATTRIBUTE');
export abstract class AbstractBinary {
@@ -74,7 +76,7 @@ export abstract class AbstractBinary {
protected listNodePlatforms() {
// https://nodejs.org/api/os.html#osplatform
return [ 'darwin', 'linux', 'win32' ];
return platforms;
}
protected listNodeArchs(binaryConfig?: BinaryTaskConfig) {
@@ -87,11 +89,11 @@ export abstract class AbstractBinary {
};
}
protected listNodeLibcs() {
protected listNodeLibcs(): Record<typeof platforms[number], string[]> {
// https://github.com/lovell/detect-libc/blob/master/lib/detect-libc.js#L42
return {
linux: [ 'glibc', 'musl' ],
darwin: [ 'unknown' ],
linux: [ 'glibc', 'musl' ],
win32: [ 'unknown' ],
};
}

View File

@@ -189,7 +189,7 @@ const DOWNLOAD_PATHS = {
'android': {
'<unknown>': 'builds/android/%s/android.zip',
},
};
} as const;
@SingletonProto()
@BinaryAdapter(BinaryType.Playwright)
@@ -215,7 +215,7 @@ export class PlaywrightBinary extends AbstractBinary {
.filter(version => version.match(/^(?:\d+\.\d+\.\d+)(?:-beta-\d+)?$/))
// select recently update 20 items
.slice(-20);
const browsers: { name: string; revision: string; browserVersion: string; revisionOverrides?: Record<string, string> }[] = [];
const browsers: { name: keyof typeof DOWNLOAD_PATHS; revision: string; browserVersion: string; revisionOverrides?: Record<string, string> }[] = [];
await Promise.all(
packageVersions.map(version =>
this.requestJSON(

View File

@@ -6,6 +6,16 @@ import { AbstractChangeStream, RegistryChangesStream } from './AbstractChangesSt
const MAX_LIMIT = 10000;
type FetchResults = {
results: {
seq: number;
type: string;
id: string;
changes: Record<string, string>[];
gmt_modified: Date,
}[];
};
@SingletonProto()
@RegistryChangesStream(RegistryType.Cnpmjsorg)
export class CnpmjsorgChangesStream extends AbstractChangeStream {
@@ -18,13 +28,13 @@ export class CnpmjsorgChangesStream extends AbstractChangeStream {
return since;
}
private async tryFetch(registry: Registry, since: string, limit = 1000) {
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}`);
}
const db = this.getChangesStreamUrl(registry, since, limit);
// json mode
const res = await this.httpclient.request(db, {
const res = await this.httpclient.request<FetchResults>(db, {
followRedirect: true,
timeout: 30000,
dataType: 'json',

View File

@@ -8,7 +8,7 @@ export type BugVersionPackages = Record<string, BugVersionPackage>;
export class BugVersion {
private readonly data: BugVersionPackages;
constructor(data) {
constructor(data: BugVersionPackages) {
this.data = data;
}

View File

@@ -48,7 +48,7 @@ export class Hook extends Entity {
}
// payload 可能会特别大,如果做多次 stringify 浪费太多 cpu
signPayload(payload: object): { digest, payloadStr } {
signPayload(payload: object) {
const payloadStr = JSON.stringify(payload);
const digest = crypto.createHmac('sha256', this.secret)
.update(JSON.stringify(payload))

View File

@@ -38,7 +38,7 @@ export class SqlRange {
};
}
const paddingSemver = new PaddingSemVer(comparator.semver);
const operator = OPERATOR_MAP[comparator.operator];
const operator = OPERATOR_MAP[comparator.operator as keyof typeof OPERATOR_MAP];
if (!operator) {
throw new Error(`unknown operator ${comparator.operator}`);
}

View File

@@ -40,7 +40,7 @@ export class BugVersionService {
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);
bugVersion = new BugVersion(data || {});
this.bugVersionStore.setBugVersion(bugVersion, tag!.version);
}
return bugVersion;

View File

@@ -425,7 +425,7 @@ export class PackageManagerService extends AbstractService {
public plusPackageVersionCounter(fullname: string, version: string) {
// set counter + 1, schedule will store them into database
const counters = PackageManagerService.downloadCounters;
const counters: Record<string, Record<string, number>> = PackageManagerService.downloadCounters;
if (!counters[fullname]) counters[fullname] = {};
counters[fullname][version] = (counters[fullname][version] || 0) + 1;
// Total
@@ -444,7 +444,7 @@ export class PackageManagerService extends AbstractService {
// will be call by schedule/SavePackageVersionDownloadCounter.ts
async savePackageVersionCounters() {
// { [fullname]: { [version]: number } }
const counters = PackageManagerService.downloadCounters;
const counters: Record<string, Record<string, number>> = PackageManagerService.downloadCounters;
const fullnames = Object.keys(counters);
if (fullnames.length === 0) return;
@@ -724,13 +724,16 @@ export class PackageManagerService extends AbstractService {
const fieldsFromLatestManifest = [
'author', 'bugs', 'contributors', 'description', 'homepage', 'keywords', 'license',
'readmeFilename', 'repository',
];
] as const;
// the latest version metas
for (const field of fieldsFromLatestManifest) {
fullManifests[field] = latestManifest[field];
if (latestManifest[field]) {
(fullManifests as Record<string, unknown>)[field] = latestManifest[field];
}
}
}
private async _setPackageDistTagsAndLatestInfos(pkg: Package, fullManifests: PackageManifestType, abbreviatedManifests: AbbreviatedPackageManifestType) {
const distTags = await this._listPackageDistTags(pkg);
if (distTags.latest) {

View File

@@ -18,7 +18,7 @@ import { downloadToTempfile } from '../../common/FileUtil';
import { TaskState, TaskType } from '../../common/enum/Task';
import { AbstractService } from '../../common/AbstractService';
import { TaskRepository } from '../../repository/TaskRepository';
import { PackageJSONType, PackageRepository } from '../../repository/PackageRepository';
import { PackageJSONType, PackageManifestType, PackageRepository } from '../../repository/PackageRepository';
import { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository';
import { UserRepository } from '../../repository/UserRepository';
import { Task, SyncPackageTaskOptions, CreateSyncPackageTask } from '../entity/Task';
@@ -484,7 +484,7 @@ export class PackageSyncerService extends AbstractService {
// { name: 'jasonlaster11', email: 'jason.laster.11@gmail.com' }
// ],
let maintainers = data.maintainers;
const maintainersMap = {};
const maintainersMap: Record<string, PackageManifestType['maintainers']> = {};
const users: User[] = [];
let changedUserCount = 0;
if (!Array.isArray(maintainers) || maintainers.length === 0) {
@@ -619,7 +619,7 @@ export class PackageSyncerService extends AbstractService {
}
if (!isEqual(remoteItemValue, existsItem[key])) {
diffMeta[key] = remoteItemValue;
} else if (!ignoreInAbbreviated.includes(key) && existsAbbreviatedItem && !isEqual(remoteItemValue, existsAbbreviatedItem[key])) {
} else if (!ignoreInAbbreviated.includes(key) && existsAbbreviatedItem && !isEqual(remoteItemValue, (existsAbbreviatedItem as Record<string, unknown>)[key])) {
// should diff exists abbreviated item too
diffMeta[key] = remoteItemValue;
}

View File

@@ -49,7 +49,7 @@ export class PackageVersionService {
if (isFullManifests) {
manifest = await this.distRepository.findPackageVersionManifest(pkgId, version);
} else {
manifest = 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}`;

View File

@@ -199,14 +199,14 @@ export class UserService extends AbstractService {
await this.userRepository.removeToken(token.tokenId);
}
async findWebauthnCredential(userId: string, browserType?: string) {
async findWebauthnCredential(userId: string, browserType: string | undefined | null) {
const credential = await this.userRepository.findCredentialByUserIdAndBrowserType(userId, browserType || null);
return credential;
}
async createWebauthnCredential(userId: string, options: CreateWebauthnCredentialOptions) {
async createWebauthnCredential(userId: string | undefined, options: CreateWebauthnCredentialOptions) {
const credentialEntity = WebauthnCredentialEntity.create({
userId,
userId: userId as string,
credentialId: options.credentialId,
publicKey: options.publicKey,
browserType: options.browserType,
@@ -215,7 +215,7 @@ export class UserService extends AbstractService {
return credentialEntity;
}
async removeWebauthnCredential(userId: string, browserType?: string) {
async removeWebauthnCredential(userId?: string, browserType?: string) {
const credential = await this.userRepository.findCredentialByUserIdAndBrowserType(userId, browserType || null);
if (credential) {
await this.userRepository.removeCredential(credential.wancId);

View File

@@ -28,15 +28,15 @@ export class DownloadController extends AbstractController {
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 days = {};
const versions = {};
const days: Record<string, number> = {};
const versions: Record<string, { day: string, downloads: number }[]> = {};
for (const entity of entities) {
const yearMonth = String(entity.yearMonth);
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}`;
const counter = entity[field];
const field = `d${day}` as keyof typeof entity;
const counter = entity[field] as number;
if (!counter) continue;
const date = `${prefix}-${day}`;
days[date] = (days[date] || 0) + counter;
@@ -66,14 +66,14 @@ export class DownloadController extends AbstractController {
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 = {};
const days: Record<string, number> = {};
for (const entity of entities) {
const yearMonth = String(entity.yearMonth);
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}`;
const counter = entity[field];
const field = `d${day}` as keyof typeof entity;
const counter = entity[field] as number;
if (!counter) continue;
const date = `${prefix}-${day}`;
days[date] = (days[date] || 0) + counter;
@@ -115,4 +115,3 @@ export class DownloadController extends AbstractController {
return [ startDate, endDate ];
}
}

View File

@@ -29,7 +29,7 @@ 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 tags = {};
const tags: Record<string, string> = {};
for (const entity of tagEntities) {
tags[entity.tag] = entity.version;
}

View File

@@ -21,6 +21,7 @@ import { Static, Type } from '@sinclair/typebox';
import { AbstractController } from '../AbstractController';
import { getScopeAndName, FULLNAME_REG_STRING, extractPackageJSON } from '../../../common/PackageUtil';
import { PackageManagerService } from '../../../core/service/PackageManagerService';
import { PackageVersion as PackageVersionEntity } from '../../../core/entity/PackageVersion';
import {
VersionRule,
TagWithVersionRule,
@@ -103,7 +104,7 @@ export class SavePackageVersionController extends AbstractController {
const [ scope, name ] = getScopeAndName(fullname);
const pkg = await this.packageRepository.findPackage(scope, name);
if (!pkg) {
const errors = (validateResult.errors || validateResult.warnings).join(', ');
const errors = (validateResult.errors || validateResult.warnings || []).join(', ');
throw new UnprocessableEntityError(`package.name invalid, errors: ${errors}`);
}
}
@@ -185,7 +186,8 @@ export class SavePackageVersionController extends AbstractController {
const tarballPkg = await extractPackageJSON(tarballBytes);
const versionManifest = pkg.versions[tarballPkg.version];
const diffKeys = STRICT_CHECK_TARBALL_FIELDS.filter(key => {
return !isEqual(tarballPkg[key], versionManifest[key]);
const targetKey = key as unknown as keyof typeof versionManifest;
return !isEqual(tarballPkg[key], versionManifest[targetKey]);
});
if (diffKeys.length > 0) {
throw new UnprocessableEntityError(`${diffKeys} mismatch between tarball and manifest`);
@@ -205,7 +207,7 @@ export class SavePackageVersionController extends AbstractController {
const registry = await this.registryManagerService.ensureSelfRegistry();
let packageVersionEntity;
let packageVersionEntity: PackageVersionEntity | undefined;
const lockRes = await this.cacheAdapter.usingLock(`${pkg.name}:publish`, 60, async () => {
packageVersionEntity = await this.packageManagerService.publish({
scope,
@@ -230,12 +232,12 @@ export class SavePackageVersionController extends AbstractController {
}
this.logger.info('[package:version:add] %s@%s, packageVersionId: %s, tag: %s, userId: %s',
packageVersion.name, packageVersion.version, packageVersionEntity.packageVersionId,
tagWithVersion.tag, user.userId);
packageVersion.name, packageVersion.version, packageVersionEntity?.packageVersionId,
tagWithVersion.tag, user?.userId);
ctx.status = 201;
return {
ok: true,
rev: `${packageVersionEntity.id}-${packageVersionEntity.packageVersionId}`,
rev: `${packageVersionEntity?.id}-${packageVersionEntity?.packageVersionId}`,
};
}

View File

@@ -79,8 +79,8 @@ export class UpdateTotalData {
for (const row of rows) {
for (let i = 1; i <= 31; i++) {
const day = String(i).padStart(2, '0');
const field = `d${day}`;
const counter = row[field];
const field = `d${day}` as keyof typeof row;
const counter = row[field] as number;
if (!counter) continue;
const dayInt = row.yearMonth * 100 + i;
if (dayInt === todayInt) download.today += counter;

View File

@@ -3,7 +3,7 @@ import { RegistryType } from '../common/enum/Registry';
import semver from 'semver';
import npa from 'npm-package-arg';
import { HookType } from '../common/enum/Hook';
import binaryConfig from '../../config/binaries';
import binaryConfig, { BinaryName } from '../../config/binaries';
export const Name = Type.String({
transform: [ 'trim' ],
@@ -151,8 +151,8 @@ export function patchAjv(ajv: any) {
});
ajv.addFormat('binary-name', {
type: 'string',
validate: (binaryName: string) => {
return !!binaryConfig[binaryName];
validate: (binaryName: BinaryName) => {
return binaryConfig[binaryName];
},
});
ajv.addFormat('semver-version-array', {

View File

@@ -22,6 +22,8 @@ import {
verifyRegistrationResponse,
generateAuthenticationOptions,
verifyAuthenticationResponse,
VerifyRegistrationResponseOpts,
VerifyAuthenticationResponseOpts,
} from '@simplewebauthn/server';
import type { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/typescript-types';
import { LoginResultCode, WanStatusCode } from '../../common/enum/User';
@@ -44,6 +46,17 @@ type LoginPrepareResult = {
wanCredentialAuthOption?: PublicKeyCredentialRequestOptionsJSON;
};
type LoginImplementRequest = {
accData: {
username: string;
password: string;
};
wanCredentialRegiData: unknown;
wanCredentialAuthData: unknown;
needUnbindWan: boolean;
};
const UserRule = Type.Object({
name: Type.String({ minLength: 1, maxLength: 100 }),
password: Type.String({ minLength: 8, maxLength: 100 }),
@@ -102,7 +115,7 @@ export class WebauthController extends MiddlewareController {
path: '/-/v1/login/request/session/:sessionId',
method: HTTPMethodEnum.POST,
})
async loginImplement(@Context() ctx: EggContext, @HTTPParam() sessionId: string, @HTTPBody() 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') {
@@ -123,7 +136,7 @@ export class WebauthController extends MiddlewareController {
}
}
const browserType = getBrowserTypeForWebauthn(ctx.headers['user-agent']);
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;
@@ -139,7 +152,7 @@ export class WebauthController extends MiddlewareController {
}
try {
const verification = await verifyAuthenticationResponse({
response: wanCredentialAuthData,
response: wanCredentialAuthData as VerifyAuthenticationResponseOpts['response'],
expectedChallenge,
expectedOrigin,
expectedRPID,
@@ -193,7 +206,7 @@ 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
@@ -215,7 +228,7 @@ export class WebauthController extends MiddlewareController {
if (enableWebAuthn && isSupportWebAuthn && wanCredentialRegiData) {
try {
const verification = await verifyRegistrationResponse({
response: wanCredentialRegiData,
response: wanCredentialRegiData as VerifyRegistrationResponseOpts['response'],
expectedChallenge,
expectedOrigin,
expectedRPID,
@@ -225,7 +238,7 @@ export class WebauthController extends MiddlewareController {
const { credentialPublicKey, credentialID } = registrationInfo;
const base64CredentialPublicKey = base64url.encode(Buffer.from(new Uint8Array(credentialPublicKey)));
const base64CredentialID = base64url.encode(Buffer.from(new Uint8Array(credentialID)));
this.userService.createWebauthnCredential(user.userId, {
this.userService.createWebauthnCredential(user?.userId, {
credentialId: base64CredentialID,
publicKey: base64CredentialPublicKey,
browserType,

View File

@@ -15,9 +15,9 @@ export class BinaryRepository extends AbstractRepository {
if (binary.id) {
const model = await this.Binary.findOne({ id: binary.id });
if (!model) return;
await ModelConvertor.saveEntityToModel(binary, model);
await ModelConvertor.saveEntityToModel<BinaryModel>(binary as unknown as Record<string, unknown>, model);
} else {
const model = await ModelConvertor.convertEntityToModel(binary, this.Binary);
const model = await ModelConvertor.convertEntityToModel(binary as unknown as Record<string, unknown>, this.Binary);
this.logger.info('[BinaryRepository:saveBinary:new] id: %s, binaryId: %s', model.id, model.binaryId);
}
}

View File

@@ -17,6 +17,7 @@ import type { Maintainer as MaintainerModel } from './model/Maintainer';
import type { User as UserModel } from './model/User';
import { User as UserEntity } from '../core/entity/User';
import { AbstractRepository } from './AbstractRepository';
import { BugVersionPackages } from '../core/entity/BugVersion';
export type PackageManifestType = Pick<PackageJSONType, PackageJSONPickKey> & {
_id: string;
@@ -63,7 +64,9 @@ export type PackageJSONType = CnpmcorePatchInfo & {
directories?: DirectoriesType;
repository?: RepositoryType;
scripts?: Record<string, string>;
config?: Record<string, unknown>;
config?: {
'bug-versions'?: BugVersionPackages;
};
dependencies?: DepInfo;
devDependencies?: DepInfo;
peerDependencies?: DepInfo;
@@ -102,7 +105,7 @@ export type PackageJSONType = CnpmcorePatchInfo & {
[key: string]: unknown;
};
type PackageJSONPickKey = 'name' | 'author' | 'bugs' | 'description' | 'homepage' | 'keywords' | 'license' | 'readme' | 'readmeFilename' | 'repository' | 'versions';
type PackageJSONPickKey = 'name' | 'author' | 'bugs' | 'description' | 'homepage' | 'keywords' | 'license' | 'readme' | 'readmeFilename' | 'repository' | 'versions' | 'contributors';
type CnpmcorePatchInfo = {
_cnpmcore_publish_time?: Date;

View File

@@ -65,7 +65,7 @@ export class PackageVersionDownloadRepository extends AbstractRepository {
}
for (const [ date, counter ] of counters) {
const field = `d${date}`;
model[field] = counter;
(model as unknown as Record<string, number>)[field] = counter;
}
await model.save();
}

View File

@@ -47,7 +47,7 @@ export class PackageVersionRepository {
scope,
name,
tag,
} as object);
} as object) as { version: string }[];
const tagModel = tags && tags[0];
return tagModel?.version;
}
@@ -64,7 +64,7 @@ export class PackageVersionRepository {
'packages.name': name,
...sqlRange.condition,
} as object)
.order('packageVersions.paddingVersion', 'desc');
.order('packageVersions.paddingVersion', 'desc') as { version: string }[];
return versions?.[0]?.version;
}
@@ -78,6 +78,6 @@ export class PackageVersionRepository {
...sqlRange.condition,
} as object);
return (versions as any).toObject()
.map(t => t.version);
.map((t: { version: string }) => t.version);
}
}

View File

@@ -146,7 +146,7 @@ export class UserRepository extends AbstractRepository {
}
}
async findCredentialByUserIdAndBrowserType(userId: string, browserType: string | null) {
async findCredentialByUserIdAndBrowserType(userId: string | undefined, browserType: string | null) {
const model = await this.WebauthnCredential.findOne({
userId,
browserType,

View File

@@ -55,7 +55,7 @@ export class PackageVersion extends Bone {
@Attribute(DataTypes.BOOLEAN)
isPreRelease: boolean;
static beforeCreate(instance) {
static beforeCreate(instance: { version: string; paddingVersion: string; isPreRelease: boolean }) {
if (!instance.paddingVersion) {
const paddingSemVer = new PaddingSemVer(instance.version);
instance.paddingVersion = paddingSemVer.paddingVersion;

View File

@@ -2,5 +2,8 @@
"name": "cnpmcore-repository",
"eggModule": {
"name": "cnpmcoreRepository"
},
"devDependencies": {
"@types/lodash": "^4.14.196"
}
}

View File

@@ -8,13 +8,16 @@ const CREATED_AT = 'createdAt';
const UPDATED_AT = 'updatedAt';
const ID = 'id';
type BonePatchInfo = { id?: bigint, updatedAt?: Date, createdAt?: Date };
type PatchedBone = Bone & BonePatchInfo;
export class ModelConvertor {
static async convertEntityToModel<T extends Bone>(entity: object, ModelClazz: EggProtoImplClass<T>, options?): Promise<T> {
static async convertEntityToModel<T extends(PatchedBone)>(entity: object, ModelClazz: EggProtoImplClass<T>, options?: object): Promise<T> {
const metadata = ModelMetadataUtil.getModelMetadata(ModelClazz);
if (!metadata) {
throw new Error(`Model ${ModelClazz.name} has no metadata`);
}
const attributes = {};
const attributes: Record<string, unknown> = {};
for (const attributeMeta of metadata.attributes) {
const modelPropertyName = attributeMeta.propertyName;
const entityPropertyName = ModelConvertorUtil.getEntityPropertyName(ModelClazz, modelPropertyName);
@@ -22,17 +25,17 @@ export class ModelConvertor {
const attributeValue = _.get(entity, entityPropertyName);
attributes[modelPropertyName] = attributeValue;
}
const model = await (ModelClazz as unknown as typeof Bone).create(attributes, options);
const model = await (ModelClazz as unknown as typeof Bone).create(attributes, options) as PatchedBone;
// auto set entity id to model id
entity[ID] = model[ID];
(entity as Record<string, unknown>)[ID] = model[ID];
// use model dates
entity[UPDATED_AT] = model[UPDATED_AT];
entity[CREATED_AT] = model[CREATED_AT];
(entity as Record<string, unknown>)[UPDATED_AT] = model[UPDATED_AT];
(entity as Record<string, unknown>)[CREATED_AT] = model[CREATED_AT];
return model as T;
}
static convertEntityToChanges<T extends Bone>(entity: object, ModelClazz: EggProtoImplClass<T>) {
const changes = {};
const changes: Record<string, unknown> = {};
const metadata = ModelMetadataUtil.getModelMetadata(ModelClazz);
if (!metadata) {
throw new Error(`Model ${ModelClazz.name} has no metadata`);
@@ -45,13 +48,13 @@ export class ModelConvertor {
changes[modelPropertyName] = attributeValue;
}
changes[UPDATED_AT] = new Date();
entity[UPDATED_AT] = changes[UPDATED_AT];
(entity as Record<string, unknown>)[UPDATED_AT] = changes[UPDATED_AT];
return changes;
}
// TODO: options is QueryOptions, should let leoric export it to use
// Find out which attributes changed and set `updatedAt` to now
static async saveEntityToModel<T extends Bone>(entity: object, model: T, options?): Promise<boolean> {
static async saveEntityToModel<T extends Bone>(entity: object, model: T & PatchedBone, options?: object): Promise<boolean> {
const ModelClazz = model.constructor as EggProtoImplClass<T>;
const metadata = ModelMetadataUtil.getModelMetadata(ModelClazz);
if (!metadata) {
@@ -64,14 +67,14 @@ export class ModelConvertor {
// Restricted updates to the primary key
if (entityPropertyName === ID && model[ID]) continue;
const attributeValue = _.get(entity, entityPropertyName);
model[modelPropertyName] = attributeValue;
(model as unknown as Record<string, unknown>)[modelPropertyName] = attributeValue;
}
// Restricted updates to the UPDATED_AT
// Leoric will set by default
model[UPDATED_AT] = undefined;
await model.save(options);
entity[UPDATED_AT] = model[UPDATED_AT];
(entity as Record<string, unknown>)[UPDATED_AT] = model[UPDATED_AT];
return true;
}
@@ -85,7 +88,7 @@ export class ModelConvertor {
for (const attributeMeta of metadata.attributes) {
const modelPropertyName = attributeMeta.propertyName;
const entityPropertyName = ModelConvertorUtil.getEntityPropertyName(ModelClazz as EggProtoImplClass, modelPropertyName);
const attributeValue = bone[attributeMeta.propertyName];
const attributeValue = bone[attributeMeta.propertyName as keyof Bone];
_.set(data, entityPropertyName, attributeValue);
}
const model = Reflect.construct(entityClazz, [ data ]);

113
index.d.ts vendored Normal file
View File

@@ -0,0 +1,113 @@
declare module 'fs-cnpm' {
export default class FSClient extends NFSClient {
constructor(options: {
dir: string;
});
}
}
declare module 'ssri' {
export interface Integrity {
algorithm: string;
digest: string;
options?: string[];
}
export interface HashLike {
digest: string;
algorithm: string;
options?: string[];
sha1: {
hexDigest(): string;
}[];
sha512: { toString(): string }[];
}
export interface HashOptions {
algorithms?: string[];
options?: string[];
}
export interface IntegrityOptions {
algorithms?: string[];
options?: string[];
single?: boolean;
}
export interface CreateRes {
update(v: string): { digest: () => { toString() }; };
}
export function fromHex(hexDigest: string, algorithm: string, options?: string[]): Integrity;
export function fromData(data: Buffer | string | Uint8Array, options?: HashOptions): HashLike;
export function fromStream(stream: NodeJS.ReadableStream, options?: HashOptions): Promise<HashLike>;
export function checkData(data: Buffer | string, sri: string | Integrity, options?: IntegrityOptions): boolean;
export function checkStream(stream: NodeJS.ReadableStream, sri: string | Integrity, options?: IntegrityOptions): Promise<boolean>;
export function parse(sri: string): Integrity;
export function create(): CreateRes;
export function stringify(integrity: Integrity, options?: { strict?: boolean }): string;
}
declare module 'oss-cnpm' {
import { Readable } from 'stream';
export interface AppendResult {
name: string;
url: string;
etag: string;
size: number;
}
export interface UploadOptions {
key: string;
content: Readable;
size: number;
}
export interface UploadResult {
name: string;
url: string;
etag: string;
size: number;
}
export interface DownloadOptions {
key: string;
}
export default class OSSClient {
constructor(options: {
cdnBaseUrl?: string;
accessKeyId: string;
accessKeySecret: string;
bucket: string;
internal?: boolean;
secure?: boolean;
timeout?: number;
cname?: boolean;
endpoint?: string;
defaultHeaders?: Record<string, string>;
});
append(options: UploadOptions): Promise<AppendResult>;
upload(options: UploadOptions): Promise<UploadResult>;
download(options: DownloadOptions): Promise<Readable>;
delete(key: string): Promise<void>;
exists(key: string): Promise<boolean>;
stat(key: string): Promise<{ size: number }>;
url(key: string): string;
}
}

2
module.d.ts vendored
View File

@@ -4,4 +4,4 @@ declare module "egg" {
export interface EggContextModule {
cnpmcoreCore: ContextCnpmcore;
}
}
};

View File

@@ -117,6 +117,7 @@
"devDependencies": {
"@cnpmjs/npm-cli-login": "^1.1.0",
"@simplewebauthn/typescript-types": "^7.0.0",
"@types/mime-types": "^2.1.1",
"@types/mocha": "^10.0.1",
"@types/mysql": "^2.15.21",
"@types/semver": "^7.3.12",
@@ -127,7 +128,11 @@
"eslint": "^8.29.0",
"eslint-config-egg": "^12.1.0",
"git-contributor": "2",
"typescript": "^5.0.4"
"typescript": "^5.0.4",
"@types/ua-parser-js": "^0.7.36",
"@types/lodash": "^4.14.196",
"@types/npm-package-arg": "^6.1.1",
"@types/validate-npm-package-name": "^4.0.0"
},
"author": "killagu",
"license": "MIT",

View File

@@ -1,14 +1,16 @@
{
"extends": "@eggjs/tsconfig",
"compilerOptions": {
"strict": true,
"target": "ES2021",
"module": "Node16",
"moduleResolution": "Node",
"declaration": false,
"resolveJsonModule": true,
"useUnknownInCatchVariables": false
"useUnknownInCatchVariables": false,
},
"exclude": [
"test",
"node_modules"
],
}

View File

@@ -14,6 +14,7 @@
"config/**/*.ts",
"typings/**/*.ts",
"app.ts",
"module.d.ts"
"module.d.ts",
"index.d.ts"
]
}