diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1627b4439b..cae6b1a125 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -436,12 +436,6 @@ importers: chokidar: specifier: ^4.0.3 version: 4.0.3 - class-transformer: - specifier: ^0.5.1 - version: 0.5.1 - class-validator: - specifier: ^0.14.0 - version: 0.14.4 compression: specifier: ^1.8.0 version: 1.8.1 @@ -18257,13 +18251,15 @@ snapshots: cjs-module-lexer@2.2.0: {} - class-transformer@0.5.1: {} + class-transformer@0.5.1: + optional: true class-validator@0.14.4: dependencies: '@types/validator': 13.15.10 libphonenumber-js: 1.12.38 validator: 13.15.26 + optional: true clean-css@5.3.3: dependencies: @@ -21104,7 +21100,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libphonenumber-js@1.12.38: {} + libphonenumber-js@1.12.38: + optional: true lightningcss-android-arm64@1.31.1: optional: true diff --git a/server/package.json b/server/package.json index 339f3ed409..d4fff077bf 100644 --- a/server/package.json +++ b/server/package.json @@ -65,8 +65,6 @@ "body-parser": "^2.2.0", "bullmq": "^5.51.0", "chokidar": "^4.0.3", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", "compression": "^1.8.0", "cookie": "^1.0.2", "cookie-parser": "^1.4.7", diff --git a/server/src/bin/sync-sql.ts b/server/src/bin/sync-sql.ts index b632332069..5be9ae29b9 100644 --- a/server/src/bin/sync-sql.ts +++ b/server/src/bin/sync-sql.ts @@ -3,7 +3,6 @@ import { INestApplication } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { SchedulerRegistry } from '@nestjs/schedule'; import { Test } from '@nestjs/testing'; -import { ClassConstructor } from 'class-transformer'; import { ClsModule } from 'nestjs-cls'; import { KyselyModule } from 'nestjs-kysely'; import { OpenTelemetryModule } from 'nestjs-otel'; @@ -44,7 +43,7 @@ export class SqlLogger { const reflector = new Reflector(); -type Repository = ClassConstructor; +type Repository = new (...args: any[]) => any; type SqlGeneratorOptions = { targetDir: string }; class SqlGenerator { diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index fbc281ccb3..c505dd3fb3 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; import { ModuleRef, Reflector } from '@nestjs/core'; -import { ClassConstructor } from 'class-transformer'; import _ from 'lodash'; import { Socket } from 'socket.io'; import { SystemConfig } from 'src/config'; @@ -152,7 +151,7 @@ export class EventRepository { this.logger.setContext(EventRepository.name); } - setup({ services }: { services: ClassConstructor[] }) { + setup({ services }: { services: (new (...args: any[]) => unknown)[] }) { const reflector = this.moduleRef.get(Reflector, { strict: false }); const items: Item[] = []; const worker = this.configRepository.getWorker(); diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index b12accb68e..4ea19f9cc7 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -2,7 +2,6 @@ import { getQueueToken } from '@nestjs/bullmq'; import { Injectable } from '@nestjs/common'; import { ModuleRef, Reflector } from '@nestjs/core'; import { JobsOptions, Queue, Worker } from 'bullmq'; -import { ClassConstructor } from 'class-transformer'; import { setTimeout } from 'node:timers/promises'; import { JobConfig } from 'src/decorators'; import { QueueJobResponseDto, QueueJobSearchDto } from 'src/dtos/queue.dto'; @@ -34,7 +33,7 @@ export class JobRepository { this.logger.setContext(JobRepository.name); } - setup(services: ClassConstructor[]) { + setup(services: (new (...args: any[]) => unknown)[]) { const reflector = this.moduleRef.get(Reflector, { strict: false }); // discovery diff --git a/server/src/repositories/telemetry.repository.ts b/server/src/repositories/telemetry.repository.ts index 5fbbb76cf7..d87c0acf5a 100644 --- a/server/src/repositories/telemetry.repository.ts +++ b/server/src/repositories/telemetry.repository.ts @@ -11,7 +11,6 @@ import { resourceFromAttributes } from '@opentelemetry/resources'; import { AggregationType } from '@opentelemetry/sdk-metrics'; import { NodeSDK, contextBase } from '@opentelemetry/sdk-node'; import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'; -import { ClassConstructor } from 'class-transformer'; import { snakeCase, startCase } from 'lodash'; import { MetricService } from 'nestjs-otel'; import { copyMetadataFromFunctionToFunction } from 'nestjs-otel/lib/opentelemetry.utils'; @@ -118,7 +117,7 @@ export class TelemetryRepository { this.repo = new MetricGroupRepository(metricService).configure({ enabled: metrics.has(ImmichTelemetry.Repo) }); } - setup({ repositories }: { repositories: ClassConstructor[] }) { + setup({ repositories }: { repositories: (new (...args: any[]) => unknown)[] }) { const { telemetry } = this.configRepository.getEnv(); const { metrics } = telemetry; if (!metrics.has(ImmichTelemetry.Repo)) { @@ -136,7 +135,7 @@ export class TelemetryRepository { } } - private wrap(Repository: ClassConstructor) { + private wrap(Repository: new (...args: any[]) => unknown) { const className = Repository.name; const descriptors = Object.getOwnPropertyDescriptors(Repository.prototype); const unit = 'ms'; diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index b8e1b78107..e87d605602 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -1,5 +1,4 @@ import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; -import { isString } from 'class-validator'; import { parse } from 'cookie'; import { DateTime } from 'luxon'; import { IncomingHttpHeaders } from 'node:http'; @@ -307,7 +306,7 @@ export class AuthService extends BaseService { const storageLabel = this.getClaim(profile, { key: storageLabelClaim, default: '', - isValid: isString, + isValid: (value: unknown): value is string => typeof value === 'string', }); const storageQuota = this.getClaim(profile, { key: storageQuotaClaim, @@ -317,7 +316,7 @@ export class AuthService extends BaseService { const role = this.getClaim<'admin' | 'user'>(profile, { key: roleClaim, default: 'user', - isValid: (value: unknown) => isString(value) && ['admin', 'user'].includes(value), + isValid: (value: unknown) => typeof value === 'string' && ['admin', 'user'].includes(value), }); const userName = profile.name ?? `${profile.given_name || ''} ${profile.family_name || ''}`; diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index ee4b4ec05f..55f45156cc 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -1,4 +1,3 @@ -import { plainToInstance } from 'class-transformer'; import { defaults, SystemConfig } from 'src/config'; import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AssetFileType, JobName, JobStatus, UserMetadataKey } from 'src/enum'; @@ -101,7 +100,7 @@ describe(NotificationService.name, () => { it('skips smtp validation with DTO when there are no changes', async () => { const oldConfig = { ...configs.smtpEnabled }; - const newConfig = plainToInstance(SystemConfigDto, configs.smtpEnabled); + const newConfig = configs.smtpEnabled as SystemConfigDto; await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); diff --git a/server/src/services/queue.service.ts b/server/src/services/queue.service.ts index cdfa2ad2ed..662ccbe618 100644 --- a/server/src/services/queue.service.ts +++ b/server/src/services/queue.service.ts @@ -1,5 +1,4 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { ClassConstructor } from 'class-transformer'; import { SystemConfig } from 'src/config'; import { OnEvent } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -39,7 +38,7 @@ const asNightlyTasksCron = (config: SystemConfig) => { @Injectable() export class QueueService extends BaseService { - private services: ClassConstructor[] = []; + private services: (new (...args: any[]) => unknown)[] = []; private nightlyJobsLock = false; @OnEvent({ name: 'ConfigInit' }) @@ -96,7 +95,7 @@ export class QueueService extends BaseService { } } - setServices(services: ClassConstructor[]) { + setServices(services: (new (...args: any[]) => unknown)[]) { this.services = services; } diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index ea95b4df24..981141b02e 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -1,5 +1,4 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { instanceToPlain } from 'class-transformer'; import _ from 'lodash'; import { defaults } from 'src/config'; import { OnEvent } from 'src/decorators'; @@ -61,7 +60,7 @@ export class SystemConfigService extends BaseService { @OnEvent({ name: 'ConfigValidate' }) onConfigValidate({ newConfig, oldConfig }: ArgOf<'ConfigValidate'>) { const { logLevel } = this.configRepository.getEnv(); - if (!_.isEqual(instanceToPlain(newConfig.logging), oldConfig.logging) && logLevel) { + if (!_.isEqual(toPlainObject(newConfig.logging), oldConfig.logging) && logLevel) { throw new Error('Logging cannot be changed while the environment variable IMMICH_LOG_LEVEL is set.'); } } diff --git a/server/test/utils.ts b/server/test/utils.ts index 20410ae701..aa9a9735bf 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -3,7 +3,6 @@ import { CallHandler, ExecutionContext, Provider } from '@nestjs/common'; import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { transformException } from '@nestjs/platform-express/multer/multer/multer.utils'; import { Test } from '@nestjs/testing'; -import { ClassConstructor } from 'class-transformer'; import { NextFunction } from 'express'; import { Kysely } from 'kysely'; import multer from 'multer'; @@ -93,7 +92,7 @@ export type ControllerContext = { close: () => Promise; }; -export const controllerSetup = async (controller: ClassConstructor, providers: Provider[]) => { +export const controllerSetup = async (controller: new (...args: any[]) => unknown, providers: Provider[]) => { const noopInterceptor = { intercept: (ctx: never, next: CallHandler) => next.handle() }; const upload = multer({ storage: multer.memoryStorage() }); const memoryFileInterceptor = { @@ -164,14 +163,14 @@ const mockFn = (label: string, { strict }: { strict: boolean }) => { }); }; -export const mockBaseService = (service: ClassConstructor) => { +export const mockBaseService = (service: new (...args: any[]) => T) => { return automock(service, { args: [{ setContext: () => {} }], strict: false }); }; export const automock = ( - Dependency: ClassConstructor, + Dependency: new (...args: any[]) => T, options?: { - args?: ConstructorParameters>; + args?: ConstructorParameters T>; strict?: boolean; }, ): AutoMocked => {