diff --git a/e2e/package.json b/e2e/package.json index 19ac3f175..6a144725b 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -40,7 +40,7 @@ "eslint": "^10.0.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^75.0.0", + "eslint-plugin-unicorn": "^78.0.0", "exiftool-vendored": "^39.0.0", "globals": "^17.0.0", "luxon": "^3.4.4", diff --git a/e2e/src/ui/generators/timeline/distribution-patterns.ts b/e2e/src/ui/generators/timeline/distribution-patterns.ts index 35f5ead7f..42363e9e1 100644 --- a/e2e/src/ui/generators/timeline/distribution-patterns.ts +++ b/e2e/src/ui/generators/timeline/distribution-patterns.ts @@ -14,9 +14,7 @@ type DayDistributionStrategy = ( rng: SeededRandom, ) => MockTimelineAsset[]; -/** - * Strategies for determining total asset count per month - */ +/** Strategies for determining total asset count per month */ export const ASSET_DISTRIBUTION: Record = { empty: null, // Special case - handled separately sparse: (rng) => rng.nextInt(3, 9), // 3-8 assets @@ -25,9 +23,7 @@ export const ASSET_DISTRIBUTION: Record rng.nextInt(80, 151), // 80-150 assets }; -/** - * Strategies for distributing assets across days within a month - */ +/** Strategies for distributing assets across days within a month */ export const DAY_DISTRIBUTION: Record = { 'single-day': (year, month, daysInMonth, totalAssets, ownerId, rng) => { // All assets on one day in the middle of the month diff --git a/e2e/src/ui/generators/timeline/model-objects.ts b/e2e/src/ui/generators/timeline/model-objects.ts index 1a45fdb6f..4cdc4c1c0 100644 --- a/e2e/src/ui/generators/timeline/model-objects.ts +++ b/e2e/src/ui/generators/timeline/model-objects.ts @@ -1,6 +1,4 @@ -/** - * Generator functions for timeline model objects - */ +/** Generator functions for timeline model objects */ import { faker } from '@faker-js/faker'; import { AssetVisibility } from '@immich/sdk'; @@ -12,9 +10,7 @@ import { ASSET_DISTRIBUTION, DAY_DISTRIBUTION } from './distribution-patterns'; import type { MockTimelineAsset, MockTimelineData, SerializedTimelineData, TimelineConfig } from './timeline-config'; import { ASPECT_RATIO_WEIGHTS, GENERATION_CONSTANTS, validateTimelineConfig } from './timeline-config'; -/** - * Generate a random aspect ratio based on weighted probabilities - */ +/** Generate a random aspect ratio based on weighted probabilities */ export function generateAspectRatio(rng: SeededRandom): string { const random = rng.next(); let cumulative = 0; @@ -89,9 +85,7 @@ export function generateAsset( return asset; } -/** - * Generate assets for a specific day - */ +/** Generate assets for a specific day */ export function generateDayAssets( year: number, month: number, @@ -104,10 +98,10 @@ export function generateDayAssets( } /** - * Distribute assets evenly across consecutive days - * - * @returns Array of generated timeline assets - */ +Distribute assets evenly across consecutive days + +@returns Array of generated timeline assets +*/ export function generateConsecutiveDays( year: number, month: number, @@ -133,9 +127,7 @@ export function generateConsecutiveDays( return assets; } -/** - * Generate assets for a month with specified distribution pattern - */ +/** Generate assets for a month with specified distribution pattern */ export function generateMonthAssets( year: number, month: number, @@ -173,9 +165,7 @@ export function generateMonthAssets( return assets; } -/** - * Main generator function for timeline data - */ +/** Main generator function for timeline data */ export function generateTimelineData(config: TimelineConfig): MockTimelineData { validateTimelineConfig(config); diff --git a/e2e/src/ui/generators/timeline/rest-response.ts b/e2e/src/ui/generators/timeline/rest-response.ts index aecb00920..b904f9da1 100644 --- a/e2e/src/ui/generators/timeline/rest-response.ts +++ b/e2e/src/ui/generators/timeline/rest-response.ts @@ -1,6 +1,4 @@ -/** - * REST API output functions for converting timeline data to API response formats - */ +/** REST API output functions for converting timeline data to API response formats */ import { AlbumUserRole, @@ -19,9 +17,7 @@ import { signupDto } from 'src/fixtures.js'; import { parseTimeBucketKey } from 'src/ui/generators/timeline/utils.js'; import type { MockTimelineAsset, MockTimelineData } from './timeline-config'; -/** - * Convert timeline/asset models to columnar format (parallel arrays) - */ +/** Convert timeline/asset models to columnar format (parallel arrays) */ export function toColumnarFormat(assets: MockTimelineAsset[]): TimeBucketAssetResponseDto { const result: TimeBucketAssetResponseDto = { id: [], @@ -70,11 +66,11 @@ export function toColumnarFormat(assets: MockTimelineAsset[]): TimeBucketAssetRe } /** - * Extract a single bucket from timeline data (mimics getTimeBucket API) - * Automatically handles both ISO timestamp and simple month formats - * Returns data in columnar format matching the actual API - * When albumId is provided, only returns assets from that album - */ +Extract a single bucket from timeline data (mimics getTimeBucket API) +Automatically handles both ISO timestamp and simple month formats +Returns data in columnar format matching the actual API +When albumId is provided, only returns assets from that album +*/ export function getTimeBucket( timelineData: MockTimelineData, timeBucket: string, @@ -141,16 +137,16 @@ export type Changes = { }; /** - * Helper function to determine if an asset should be included based on filter criteria - * @param asset - The asset to check - * @param isTrashed - Filter for trashed status (undefined means no filter) - * @param isArchived - Filter for archived status (undefined means no filter) - * @param isFavorite - Filter for favorite status (undefined means no filter) - * @param deletedAssetIds - Set of IDs for assets that have been deleted - * @param archivedAssetIds - Set of IDs for assets that have been archived - * @param favoritedAssetIds - Set of IDs for assets that have been favorited - * @returns true if the asset matches all filter criteria - */ +Helper function to determine if an asset should be included based on filter criteria +@param asset - The asset to check +@param isTrashed - Filter for trashed status (undefined means no filter) +@param isArchived - Filter for archived status (undefined means no filter) +@param isFavorite - Filter for favorite status (undefined means no filter) +@param deletedAssetIds - Set of IDs for assets that have been deleted +@param archivedAssetIds - Set of IDs for assets that have been archived +@param favoritedAssetIds - Set of IDs for assets that have been favorited +@returns true if the asset matches all filter criteria +*/ function shouldIncludeAsset( asset: MockTimelineAsset, isTrashed: boolean | undefined, @@ -175,9 +171,9 @@ function shouldIncludeAsset( return isFavorite === undefined || actuallyFavorited === isFavorite; } /** - * Get summary for all buckets (mimics getTimeBuckets API) - * When albumId is provided, only includes buckets that contain assets from that album - */ +Get summary for all buckets (mimics getTimeBuckets API) +When albumId is provided, only includes buckets that contain assets from that album +*/ export function getTimeBuckets( timelineData: MockTimelineData, isTrashed: boolean | undefined, @@ -279,9 +275,9 @@ const createDefaultOwner = (ownerId: string) => { }; /** - * Convert a TimelineAssetConfig to a full AssetResponseDto - * This matches the response from GET /api/assets/:id - */ +Convert a TimelineAssetConfig to a full AssetResponseDto +This matches the response from GET /api/assets/:id +*/ export function toAssetResponseDto(asset: MockTimelineAsset, owner?: UserResponseDto): AssetResponseDto { const now = new Date().toISOString(); @@ -292,7 +288,7 @@ export function toAssetResponseDto(asset: MockTimelineAsset, owner?: UserRespons make: null, model: null, exifImageWidth: asset.ratio > 1 ? 4000 : 3000, - exifImageHeight: asset.ratio > 1 ? Math.round(4000 / asset.ratio) : Math.round(3000 * asset.ratio), + exifImageHeight: Math.round(asset.ratio > 1 ? 4000 : 3000 / asset.ratio), fileSizeInByte: asset.fileSizeInByte, orientation: '1', dateTimeOriginal: asset.fileCreatedAt, @@ -348,9 +344,9 @@ export function toAssetResponseDto(asset: MockTimelineAsset, owner?: UserRespons } /** - * Get a single asset by ID from timeline data - * This matches the response from GET /api/assets/:id - */ +Get a single asset by ID from timeline data +This matches the response from GET /api/assets/:id +*/ export function getAsset( timelineData: MockTimelineData, assetId: string, @@ -368,9 +364,9 @@ export function getAsset( } /** - * Get a mock album from timeline data - * This matches the response from GET /api/albums/:id - */ +Get a mock album from timeline data +This matches the response from GET /api/albums/:id +*/ export function getAlbum( timelineData: MockTimelineData, ownerId: string, diff --git a/e2e/src/ui/generators/timeline/timeline-config.ts b/e2e/src/ui/generators/timeline/timeline-config.ts index d28d0c6d6..e5210634f 100644 --- a/e2e/src/ui/generators/timeline/timeline-config.ts +++ b/e2e/src/ui/generators/timeline/timeline-config.ts @@ -57,9 +57,7 @@ export type MonthSpec = { pattern: DayPattern; }; -/** - * Configuration for timeline data generation - */ +/** Configuration for timeline data generation */ export type TimelineConfig = { ownerId?: string; months: MonthSpec[]; @@ -88,9 +86,7 @@ export type SerializedTimelineData = { album: MockAlbum; }; -/** - * Validates a TimelineConfig object to ensure all values are within expected ranges - */ +/** Validates a TimelineConfig object to ensure all values are within expected ranges */ export function validateTimelineConfig(config: TimelineConfig): void { if (!config.months || config.months.length === 0) { throw new Error('TimelineConfig must contain at least one month'); @@ -146,9 +142,7 @@ export function validateTimelineConfig(config: TimelineConfig): void { } } -/** - * Create a default timeline configuration - */ +/** Create a default timeline configuration */ export function createDefaultTimelineConfig(): TimelineConfig { const months: MonthSpec[] = [ // 2024 - Mix of patterns diff --git a/e2e/src/ui/generators/timeline/utils.ts b/e2e/src/ui/generators/timeline/utils.ts index 98ccdc661..5bf11f518 100644 --- a/e2e/src/ui/generators/timeline/utils.ts +++ b/e2e/src/ui/generators/timeline/utils.ts @@ -1,9 +1,7 @@ import { DateTime } from 'luxon'; import { GENERATION_CONSTANTS, MockTimelineAsset } from 'src/ui/generators/timeline/timeline-config.js'; -/** - * Linear Congruential Generator for deterministic pseudo-random numbers - */ +/** Linear Congruential Generator for deterministic pseudo-random numbers */ export class SeededRandom { private seed: number; @@ -11,38 +9,32 @@ export class SeededRandom { this.seed = seed; } - /** - * Generate next random number in range [0, 1) - */ + /** Generate next random number in range [0, 1) */ next(): number { // LCG parameters from Numerical Recipes this.seed = (this.seed * 1_664_525 + 1_013_904_223) % 2_147_483_647; return this.seed / 2_147_483_647; } - /** - * Generate random integer in range [min, max) - */ + /** Generate random integer in range [min, max) */ nextInt(min: number, max: number): number { return Math.floor(this.next() * (max - min)) + min; } - /** - * Generate random boolean with given probability - */ + /** Generate random boolean with given probability */ nextBoolean(probability = 0.5): boolean { return this.next() < probability; } } /** - * Select random days using seed variation to avoid collisions. - * - * @param daysInMonth - Total number of days in the month - * @param numDays - Number of days to select - * @param rng - Random number generator instance - * @returns Array of selected day numbers, sorted in descending order - */ +Select random days using seed variation to avoid collisions. + +@param daysInMonth - Total number of days in the month +@param numDays - Number of days to select +@param rng - Random number generator instance +@returns Array of selected day numbers, sorted in descending order +*/ export function selectRandomDays(daysInMonth: number, numDays: number, rng: SeededRandom): number[] { const selectedDays = new Set(); const maxAttempts = numDays * GENERATION_CONSTANTS.MAX_SELECT_ATTEMPTS; // Safety limit @@ -64,9 +56,7 @@ export function selectRandomDays(daysInMonth: number, numDays: number, rng: Seed return [...selectedDays].toSorted((a, b) => b - a); } -/** - * Select item from array using seeded random - */ +/** Select item from array using seeded random */ export function selectRandom(arr: T[], rng: SeededRandom): T { if (arr.length === 0) { throw new Error('Cannot select from empty array'); @@ -75,9 +65,7 @@ export function selectRandom(arr: T[], rng: SeededRandom): T { return arr[index]; } -/** - * Select multiple random items from array using seeded random without duplicates - */ +/** Select multiple random items from array using seeded random without duplicates */ export function selectRandomMultiple(arr: T[], count: number, rng: SeededRandom): T[] { if (arr.length === 0) { throw new Error('Cannot select from empty array'); @@ -104,11 +92,11 @@ export function selectRandomMultiple(arr: T[], count: number, rng: SeededRand } /** - * Parse timeBucket parameter to extract year-month key - * Handles both formats: - * - ISO timestamp: "2024-12-01T00:00:00.000Z" -> "2024-12-01" - * - Simple format: "2024-12-01" -> "2024-12-01" - */ +Parse timeBucket parameter to extract year-month key +Handles both formats: +- ISO timestamp: "2024-12-01T00:00:00.000Z" -> "2024-12-01" +- Simple format: "2024-12-01" -> "2024-12-01" +*/ export function parseTimeBucketKey(timeBucket: string): string { if (!timeBucket) { throw new Error('timeBucket parameter cannot be empty'); @@ -150,6 +138,7 @@ export function getMockAsset( return null; } + // eslint-disable-next-line unicorn/no-unnecessary-parameters const isInDifferentPeriod = (date1: DateTime, date2: DateTime): boolean => { if (unit === 'day') { return !date1.startOf('day').equals(date2.startOf('day')); diff --git a/e2e/src/ui/specs/asset-viewer/ocr.e2e-spec.ts b/e2e/src/ui/specs/asset-viewer/ocr.e2e-spec.ts index 057c0d925..abfa52d90 100644 --- a/e2e/src/ui/specs/asset-viewer/ocr.e2e-spec.ts +++ b/e2e/src/ui/specs/asset-viewer/ocr.e2e-spec.ts @@ -242,6 +242,7 @@ test.describe('OCR text interaction', () => { return; } + // eslint-disable-next-line unicorn/no-unnecessary-parameters const createTouch = (id: number, x: number, y: number) => { return new Touch({ identifier: id, diff --git a/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts b/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts index 29b008b3c..903336ef0 100644 --- a/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts +++ b/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts @@ -150,10 +150,10 @@ test.describe('Timeline', () => { }); test.describe('keyboard', () => { /** - * This text tests keyboard nativation, and also ensures that the scroll-to-asset behavior - * scrolls the minimum amount. That is, if you are navigating using right arrow (auto scrolling - * as necessary downwards), then the asset should always be at the lowest row of the grid. - */ + This text tests keyboard nativation, and also ensures that the scroll-to-asset behavior + scrolls the minimum amount. That is, if you are navigating using right arrow (auto scrolling + as necessary downwards), then the asset should always be at the lowest row of the grid. + */ test('Next/previous asset - ArrowRight/ArrowLeft', async ({ page }) => { await pageUtils.openPhotosPage(page); await thumbnailUtils.withAssetId(page, assets[0].id).focus(); diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index c68de73cf..02cce0983 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -270,13 +270,13 @@ export const utils = { } }, - waitForWebsocketEvent: ({ event, id, total: count, timeout: ms }: WaitOptions): Promise => { + waitForWebsocketEvent: ({ event, id, total: count, timeout: ms = 10_000 }: WaitOptions): Promise => { return new Promise((resolve, reject) => { if (!id && !count) { reject(new Error('id or count must be provided for waitForWebsocketEvent')); } - const timeout = setTimeout(() => reject(new Error(`Timed out waiting for ${event} event`)), ms || 10_000); + const timeout = setTimeout(() => reject(new Error(`Timed out waiting for ${event} event`)), ms); const type = id ? `id=${id}` : `count=${count}`; console.log(`Waiting for ${event} [${type}]`); const set = events[event]; @@ -550,7 +550,7 @@ export const utils = { { headers: asBearerAuth(accessToken), fetch: (...args: Parameters) => - // eslint-disable-next-line unicorn/no-invalid-argument-count, unicorn/prefer-await + // eslint-disable-next-line unicorn/prefer-await fetch(...args).then((response) => { setCookie = response.headers.getSetCookie(); return response; @@ -660,10 +660,10 @@ export const utils = { return !jobCounts.active && (!jobCounts.waiting || queues[queue].queueStatus.isPaused); }, - waitForQueueFinish: (accessToken: string, queue: keyof QueuesResponseLegacyDto, ms?: number) => { + waitForQueueFinish: (accessToken: string, queue: keyof QueuesResponseLegacyDto, ms: number = 15_000) => { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('Timed out waiting for queue to empty')), ms || 15_000); + const timeout = setTimeout(() => reject(new Error('Timed out waiting for queue to empty')), ms); while (true) { const done = await utils.isQueueEmpty(accessToken, queue); diff --git a/packages/cli/package.json b/packages/cli/package.json index 324349550..98777489b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -33,7 +33,7 @@ "eslint": "^10.0.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^75.0.0", + "eslint-plugin-unicorn": "^78.0.0", "globals": "^17.0.0", "mock-fs": "^5.2.0", "prettier": "^3.7.4", diff --git a/packages/cli/src/queue.ts b/packages/cli/src/queue.ts index 45e1c454f..cfa0b8162 100644 --- a/packages/cli/src/queue.ts +++ b/packages/cli/src/queue.ts @@ -28,11 +28,11 @@ export const defaultQueueOptions = { }; /** - * An in-memory queue that processes tasks in parallel with a given concurrency. - * @see {@link https://www.npmjs.com/package/fastq} - * @template T - The type of the worker task data. - * @template R - The type of the worker output data. - */ +An in-memory queue that processes tasks in parallel with a given concurrency. +@see {@link https://www.npmjs.com/package/fastq} +@template T - The type of the worker task data. +@template R - The type of the worker output data. +*/ export class Queue { private readonly queue: fastq.queueAsPromised>; private readonly store = new Map>(); @@ -40,10 +40,10 @@ export class Queue { readonly worker: (data: T) => Promise; /** - * Create a new queue. - * @param worker - The worker function that processes the task. - * @param options - The queue options. - */ + Create a new queue. + @param worker - The worker function that processes the task. + @param options - The queue options. + */ constructor(worker: (data: T) => Promise, options?: QueueOptions) { this.options = { ...defaultQueueOptions, ...options }; this.worker = worker; @@ -65,21 +65,21 @@ export class Queue { } /** - * Wait for the queue to be empty. - * @returns Promise - The returned Promise will be resolved when all tasks in the queue have been processed by a worker. - * This promise could be ignored as it will not lead to a `unhandledRejection`. - */ + Wait for the queue to be empty. + @returns Promise - The returned Promise will be resolved when all tasks in the queue have been processed by a worker. + This promise could be ignored as it will not lead to a `unhandledRejection`. + */ drained(): Promise { return this.queue.drained(); } /** - * Add a task at the end of the queue. - * @see {@link https://www.npmjs.com/package/fastq} - * @param data - * @returns Promise - A Promise that will be fulfilled (rejected) when the task is completed successfully (unsuccessfully). - * This promise could be ignored as it will not lead to a `unhandledRejection`. - */ + Add a task at the end of the queue. + @see {@link https://www.npmjs.com/package/fastq} + @param data + @returns Promise - A Promise that will be fulfilled (rejected) when the task is completed successfully (unsuccessfully). + This promise could be ignored as it will not lead to a `unhandledRejection`. + */ async push(data: T): Promise> { const id = uniqueId(); const task: Task = { id, status: 'idle', error: undefined, count: 0, data, result: undefined }; diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index a49cede3b..2897a1ae2 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -218,9 +218,9 @@ export const sha1 = (filepath: string) => { }; /** - * Batches items and calls onBatch to process them - * when the batch size is reached or the debounce time has passed. - */ +Batches items and calls onBatch to process them +when the batch size is reached or the debounce time has passed. +*/ export class Batcher { private items: T[] = []; private readonly batchSize: number; diff --git a/server/package.json b/server/package.json index 49f4f5076..7fab6b6cd 100644 --- a/server/package.json +++ b/server/package.json @@ -156,7 +156,7 @@ "eslint-import-resolver-typescript": "^4.4.5", "eslint-plugin-import-x": "^4.17.1", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^75.0.0", + "eslint-plugin-unicorn": "^78.0.0", "globals": "^17.0.0", "mock-fs": "^5.2.0", "pngjs": "^7.0.0", diff --git a/server/src/app.common.ts b/server/src/app.common.ts index bf796efc8..9d1960457 100644 --- a/server/src/app.common.ts +++ b/server/src/app.common.ts @@ -28,14 +28,12 @@ export async function configureExpress( ssr, }: { /** - * Whether to allow swagger module to write to the specs.json - * This is not desirable when the API is not available - * @default true - */ + Whether to allow swagger module to write to the specs.json + This is not desirable when the API is not available + @default true + */ permitSwaggerWrite?: boolean; - /** - * Service to use for server-side rendering - */ + /** Service to use for server-side rendering */ ssr: typeof ApiService | typeof MaintenanceWorkerService; }, ) { diff --git a/server/src/commands/media-location.command.ts b/server/src/commands/media-location.command.ts index 8c39eb0b6..228812a20 100644 --- a/server/src/commands/media-location.command.ts +++ b/server/src/commands/media-location.command.ts @@ -102,7 +102,7 @@ export class PromptConfirmMoveQuestions { message: 'Do you want to proceed? [Y/n]', name: 'value', }) - value(value: string): boolean { - return ['yes', 'y'].includes((value || 'y').toLowerCase()); + value(value: string = 'y'): boolean { + return ['yes', 'y'].includes(value.toLowerCase()); } } diff --git a/server/src/decorators.ts b/server/src/decorators.ts index 36e18f437..926d51ca1 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -32,11 +32,11 @@ export const UpdatedAtTrigger = (name: string) => export const DATABASE_PARAMETER_CHUNK_SIZE = 65_500; /** - * Chunks an array or set into smaller collections of the same type and specified size. - * - * @param collection The collection to chunk. - * @param size The size of each chunk. - */ +Chunks an array or set into smaller collections of the same type and specified size. + +@param collection The collection to chunk. +@param size The size of each chunk. +*/ function chunks(collection: Array, size: number): Array>; function chunks(collection: Set, size: number): Array>; function chunks(collection: Array | Set, size: number): Array> | Array> { @@ -60,12 +60,12 @@ function chunks(collection: Array | Set, size: number): Array> } /** - * Wraps a method that takes a collection of parameters and sequentially calls it with chunks of the collection, - * to overcome the maximum number of parameters allowed by the database driver. - * - * @param options.paramIndex The index of the function parameter to chunk. Defaults to 0. - * @param options.flatten Whether to flatten the results. Defaults to false. - */ +Wraps a method that takes a collection of parameters and sequentially calls it with chunks of the collection, +to overcome the maximum number of parameters allowed by the database driver. + +@param options.paramIndex The index of the function parameter to chunk. Defaults to 0. +@param options.flatten Whether to flatten the results. Defaults to false. +*/ export function Chunked( options: { paramIndex?: number; chunkSize?: number; mergeFn?: (results: any) => any } = {}, ): MethodDecorator { diff --git a/server/src/dtos/asset-media.dto.ts b/server/src/dtos/asset-media.dto.ts index d4eb397be..d8289e70a 100644 --- a/server/src/dtos/asset-media.dto.ts +++ b/server/src/dtos/asset-media.dto.ts @@ -8,9 +8,9 @@ import { JsonParsed, isoDatetimeToDate, stringToBool } from 'src/validation.js'; export enum AssetMediaSize { Original = 'original', /** - * An full-sized image extracted/converted from non-web-friendly formats like RAW/HIF. - * or otherwise the original image itself. - */ + An full-sized image extracted/converted from non-web-friendly formats like RAW/HIF. + or otherwise the original image itself. + */ FULLSIZE = 'fullsize', PREVIEW = 'preview', THUMBNAIL = 'thumbnail', @@ -41,9 +41,9 @@ const AssetMediaBaseSchema = z.object({ duration: z.coerce.number().int().min(0).optional().describe('Duration in milliseconds (for videos)'), filename: z.string().optional().describe('Filename'), /** - * The properties below are added to correctly generate the API docs and client SDKs. Validation should be handled - * in the controller. File parts never reach the request body, so they must be optional here - */ + The properties below are added to correctly generate the API docs and client SDKs. Validation should be handled + in the controller. File parts never reach the request body, so they must be optional here + */ [UploadFieldName.ASSET_DATA]: z .any() .optional() diff --git a/server/src/dtos/env.dto.ts b/server/src/dtos/env.dto.ts index 71658a6bf..f15c58737 100644 --- a/server/src/dtos/env.dto.ts +++ b/server/src/dtos/env.dto.ts @@ -14,10 +14,10 @@ enum DatabaseSslMode { const DatabaseSslModeSchema = z.enum(DatabaseSslMode).describe('Database SSL mode').meta({ id: 'DatabaseSslMode' }); const absolutePath = z.string().regex(/^\//, 'Must be an absolute path').optional(); /** - * Treat certain strings as booleans and coerce them to boolean - * Ideal for environment variables that are strings but should be treated as booleans - * @docs https://zod.dev/api?id=stringbool - */ +Treat certain strings as booleans and coerce them to boolean +Ideal for environment variables that are strings but should be treated as booleans +@docs https://zod.dev/api?id=stringbool +*/ const stringBool = z.stringbool(); const trustedProxiesSchema = z diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index be39fd24a..3f11e01fe 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -323,9 +323,9 @@ export const isFullyAlbumConfined = (filter: SearchFilter): boolean => isAlbumConfined(filter) || (!!filter.or?.length && filter.or.every((branch) => isAlbumConfined(branch))); /** - * The structured shape and the deprecated flat search fields are mutually exclusive - * TODO(v4): remove together with the deprecated flat fields. - */ +The structured shape and the deprecated flat search fields are mutually exclusive +TODO(v4): remove together with the deprecated flat fields. +*/ const withShapeExclusivity = >(schema: T) => { const deprecatedFields = Object.keys(schema.shape).filter( (field) => (schema.shape[field] as z.ZodType).meta()?.deprecated, diff --git a/server/src/enum.ts b/server/src/enum.ts index 0d724ce7b..683f3ca1b 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -59,9 +59,7 @@ export enum ChecksumAlgorithm { } export enum AssetFileType { - /** - * An full/large-size image extracted/converted from RAW photos - */ + /** An full/large-size image extracted/converted from RAW photos */ FullSize = 'fullsize', Preview = 'preview', Thumbnail = 'thumbnail', @@ -362,9 +360,9 @@ export enum SharedLinkType { Album = 'ALBUM', /** - * Individual asset - * or group of assets that are not in an album - */ + Individual asset + or group of assets that are not in an album + */ Individual = 'INDIVIDUAL', } @@ -818,9 +816,9 @@ export enum DvProfile { } /** - * Dolby Vision base-layer signal-compatibility ID from the DOVI configuration record. - * Identifies what the base HEVC/AVC layer renders as on a non-DV decoder. - */ +Dolby Vision base-layer signal-compatibility ID from the DOVI configuration record. +Identifies what the base HEVC/AVC layer renders as on a non-DV decoder. +*/ export enum DvSignalCompatibility { None = 0, Hdr10 = 1, @@ -1227,9 +1225,7 @@ export enum AssetVisibility { Archive = 'archive', Timeline = 'timeline', - /** - * Video part of the LivePhotos and MotionPhotos - */ + /** Video part of the LivePhotos and MotionPhotos */ Hidden = 'hidden', Locked = 'locked', } diff --git a/server/src/main.ts b/server/src/main.ts index f1027b740..540358c66 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -10,23 +10,15 @@ import { SystemMetadataRepository } from 'src/repositories/system-metadata.repos import { type DB } from 'src/schema/index.js'; import { getKyselyConfig } from 'src/utils/database.js'; -/** - * Manages worker lifecycle - */ +/** Manages worker lifecycle */ class Workers { - /** - * Currently running workers - */ + /** Currently running workers */ workers: Partial Promise | void }>> = {}; - /** - * Fail-safe in case anything dies during restart - */ + /** Fail-safe in case anything dies during restart */ restarting = false; - /** - * Boot all enabled workers - */ + /** Boot all enabled workers */ async bootstrap() { const isMaintenanceMode = await this.isMaintenanceMode(); const { workers } = new ConfigRepository().getEnv(); @@ -92,9 +84,9 @@ class Workers { } /** - * Start an individual worker - * @param name Worker - */ + Start an individual worker + @param name Worker + */ private startWorker(name: ImmichWorker) { console.log(`Starting ${name} worker`); diff --git a/server/src/maintenance/maintenance-worker.controller.ts b/server/src/maintenance/maintenance-worker.controller.ts index ac814812f..84eef08ed 100644 --- a/server/src/maintenance/maintenance-worker.controller.ts +++ b/server/src/maintenance/maintenance-worker.controller.ts @@ -43,9 +43,7 @@ export class MaintenanceWorkerController { private databaseBackupService: DatabaseBackupService, ) {} - /** - * {@link _ServerController.getServerConfig } - */ + /** {@link _ServerController.getServerConfig } */ @Get('server/config') getServerConfig(): ServerConfigDto { return this.service.getSystemConfig(); @@ -61,18 +59,14 @@ export class MaintenanceWorkerController { return this.service.getVersion(); } - /** - * {@link _DatabaseBackupController.listDatabaseBackups} - */ + /** {@link _DatabaseBackupController.listDatabaseBackups} */ @Get('admin/database-backups') @MaintenanceRoute() listDatabaseBackups(): Promise { return this.databaseBackupService.listBackups(); } - /** - * {@link _DatabaseBackupController.downloadDatabaseBackup} - */ + /** {@link _DatabaseBackupController.downloadDatabaseBackup} */ @Get('admin/database-backups/:filename') @MaintenanceRoute() async downloadDatabaseBackup( @@ -83,18 +77,14 @@ export class MaintenanceWorkerController { await sendFile(res, next, () => this.databaseBackupService.downloadBackup(filename), this.logger); } - /** - * {@link _DatabaseBackupController.deleteDatabaseBackup} - */ + /** {@link _DatabaseBackupController.deleteDatabaseBackup} */ @Delete('admin/database-backups') @MaintenanceRoute() async deleteDatabaseBackup(@Body() dto: DatabaseBackupDeleteDto): Promise { return this.databaseBackupService.deleteBackup(dto.backups); } - /** - * {@link _DatabaseBackupController.uploadDatabaseBackup} - */ + /** {@link _DatabaseBackupController.uploadDatabaseBackup} */ @Post('admin/database-backups/upload') @MaintenanceRoute() @UseInterceptors(FileInterceptor('file')) diff --git a/server/src/maintenance/maintenance-worker.service.spec.ts b/server/src/maintenance/maintenance-worker.service.spec.ts index e814aafb2..9d131be08 100644 --- a/server/src/maintenance/maintenance-worker.service.spec.ts +++ b/server/src/maintenance/maintenance-worker.service.spec.ts @@ -282,9 +282,7 @@ describe(MaintenanceWorkerService.name, () => { describe.skip('setAction'); // just calls setStatus+runAction - /** - * Actions - */ + /** Actions */ describe('action: start', () => { it('should not do anything', async () => { diff --git a/server/src/maintenance/maintenance-worker.service.ts b/server/src/maintenance/maintenance-worker.service.ts index 415c16f8b..6e51ac61a 100644 --- a/server/src/maintenance/maintenance-worker.service.ts +++ b/server/src/maintenance/maintenance-worker.service.ts @@ -33,9 +33,7 @@ import { getConfig } from 'src/utils/config.js'; import { createMaintenanceLoginUrl, detectPriorInstall } from 'src/utils/maintenance.js'; import { getExternalDomain } from 'src/utils/misc.js'; -/** - * This service is available inside of maintenance mode to manage maintenance mode - */ +/** This service is available inside of maintenance mode to manage maintenance mode */ @Injectable() export class MaintenanceWorkerService { #secret: string | null = null; @@ -87,9 +85,7 @@ export class MaintenanceWorkerService { } } - /** - * {@link _BaseService.configRepos} - */ + /** {@link _BaseService.configRepos} */ private get configRepos() { return { configRepo: this.configRepository, @@ -98,25 +94,19 @@ export class MaintenanceWorkerService { }; } - /** - * {@link _BaseService.prototype.getConfig} - */ + /** {@link _BaseService.prototype.getConfig} */ private getConfig(options: { withCache: boolean }) { return getConfig(this.configRepos, options); } - /** - * {@link _ServerService.getSystemConfig} - */ + /** {@link _ServerService.getSystemConfig} */ getSystemConfig() { return { maintenanceMode: true, } as ServerConfigDto; } - /** - * {@link _VersionService.getVersion} - */ + /** {@link _VersionService.getVersion} */ getVersion() { return ServerVersionResponseDto.fromSemVer(serverVersion); } @@ -125,9 +115,7 @@ export class MaintenanceWorkerService { return { res: 'pong' }; } - /** - * {@link _ApiService.ssr} - */ + /** {@link _ApiService.ssr} */ ssr(excludePaths: string[]) { const { resourcePaths } = this.configRepository.getEnv(); @@ -158,9 +146,7 @@ export class MaintenanceWorkerService { }; } - /** - * {@link _StorageService.detectMediaLocation} - */ + /** {@link _StorageService.detectMediaLocation} */ detectMediaLocation(): string { const envData = this.configRepository.getEnv(); if (envData.storage.mediaLocation) { diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index 6a97043c1..72fb46ed1 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -227,12 +227,12 @@ export class AlbumRepository { } /** - * Get asset IDs for the given album ID. - * - * @param albumId Album ID to get asset IDs for. - * @param assetIds Optional list of asset IDs to filter on. - * @returns Set of Asset IDs for the given album ID. - */ + Get asset IDs for the given album ID. + + @param albumId Album ID to get asset IDs for. + @param assetIds Optional list of asset IDs to filter on. + @returns Set of Asset IDs for the given album ID. + */ @GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] }) @ChunkedSet({ paramIndex: 1 }) async getAssetIds(albumId: string, assetIds: string[]): Promise> { @@ -348,13 +348,13 @@ export class AlbumRepository { } /** - * Makes sure all thumbnails for albums are updated by: - * - Removing thumbnails from albums without assets - * - Removing references of thumbnails to assets outside the album - * - Setting a thumbnail when none is set and the album contains assets - * - * @returns Amount of updated album thumbnails or undefined when unknown - */ + Makes sure all thumbnails for albums are updated by: + - Removing thumbnails from albums without assets + - Removing references of thumbnails to assets outside the album + - Setting a thumbnail when none is set and the album contains assets + + @returns Amount of updated album thumbnails or undefined when unknown + */ async updateThumbnails(): Promise { // Subquery for getting a new thumbnail. @@ -399,9 +399,9 @@ export class AlbumRepository { } /** - * Get per-user asset contribution counts for a single album. - * Excludes deleted assets, orders by count desc. - */ + Get per-user asset contribution counts for a single album. + Excludes deleted assets, orders by count desc. + */ @GenerateSql({ params: [DummyValue.UUID] }) getContributorCounts(id: string) { return this.db diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index 7f3bcfc3a..f46d9b90d 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -73,10 +73,10 @@ export class MediaRepository { } /** - * - * @param input file path to the input image - * @returns ExtractResult if succeeded, or null if failed - */ + + @param input file path to the input image + @returns ExtractResult if succeeded, or null if failed + */ async extract(input: string): Promise { for (const { tag, format } of [ { tag: 'JpgFromRaw2', format: RawExtractedFormat.Jpeg }, @@ -305,9 +305,9 @@ export class MediaRepository { } /** - * Needed for accurate segments, especially when remuxing, seeking and/or VFR is involved. - * Scanning packets for keyframes in JS is much faster than -skip_frame nokey since it avoids decoding the video. - */ + Needed for accurate segments, especially when remuxing, seeking and/or VFR is involved. + Scanning packets for keyframes in JS is much faster than -skip_frame nokey since it avoids decoding the video. + */ probePackets(input: string, streamIndex: number): Promise { const ffprobe = spawn( 'ffprobe', diff --git a/server/src/services/integrity.service.ts b/server/src/services/integrity.service.ts index 501362056..52160e3d2 100644 --- a/server/src/services/integrity.service.ts +++ b/server/src/services/integrity.service.ts @@ -38,28 +38,28 @@ import { ImmichFileResponse } from 'src/utils/file.js'; import { batched, handlePromiseError } from 'src/utils/misc.js'; /** - * Untracked Files: - * Files are detected in /data/encoded-video, /data/library, /data/upload - * Checked against the asset table - * Files are detected in /data/thumbs - * Checked against the asset_file table - * - * * Can perform download or delete of files - * - * Missing Files: - * Paths are queried from asset(originalPath, encodedVideoPath), asset_file(path) - * Check whether files exist on disk - * - * * Reports must include origin (asset or asset_file) & ID for further action - * * Can perform trash (asset) or delete (asset_file) - * - * Checksum Mismatch: - * Paths & checksums are queried from asset(originalPath, checksum) - * Check whether files match checksum, missing files ignored - * - * * Reports must include origin (as above) for further action - * * Can perform download or trash (asset) - */ +Untracked Files: + Files are detected in /data/encoded-video, /data/library, /data/upload + Checked against the asset table + Files are detected in /data/thumbs + Checked against the asset_file table + +Can perform download or delete of files + +Missing Files: + Paths are queried from asset(originalPath, encodedVideoPath), asset_file(path) + Check whether files exist on disk + +Reports must include origin (asset or asset_file) & ID for further action +Can perform trash (asset) or delete (asset_file) + +Checksum Mismatch: + Paths & checksums are queried from asset(originalPath, checksum) + Check whether files match checksum, missing files ignored + +Reports must include origin (as above) for further action +Can perform download or trash (asset) +*/ @Injectable() export class IntegrityService extends BaseService { diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 97f604af0..8c725e07e 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -98,9 +98,7 @@ export class JobService extends BaseService { } } - /** - * Queue follow up jobs - */ + /** Queue follow up jobs */ private async onDone(item: JobItem) { switch (item.name) { case JobName.SidecarCheck: { diff --git a/server/src/services/maintenance.service.ts b/server/src/services/maintenance.service.ts index fbdd3d4f6..31399da7b 100644 --- a/server/src/services/maintenance.service.ts +++ b/server/src/services/maintenance.service.ts @@ -18,9 +18,7 @@ import { } from 'src/utils/maintenance.js'; import { getExternalDomain } from 'src/utils/misc.js'; -/** - * This service is available outside of maintenance mode to manage maintenance mode - */ +/** This service is available outside of maintenance mode to manage maintenance mode */ @Injectable() export class MaintenanceService extends BaseService { getMaintenanceMode(): Promise { diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 07ffb491c..5cfaf29a6 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -169,8 +169,7 @@ export class MediaService extends BaseService { let thumbhash: Buffer | undefined = generated?.thumbhash; if (!thumbhash) { - const extractedImage = await this.extractOriginalImage(asset, config.image); - const { info, data, colorspace } = extractedImage; + const { info, data, colorspace } = await this.extractOriginalImage(asset, config.image); thumbhash = await this.mediaRepository.generateThumbhash( { data, info }, @@ -290,8 +289,8 @@ export class MediaService extends BaseService { private async generateImageThumbnails(asset: ThumbnailAsset, { image }: SystemConfig, useEdits: boolean = false) { // Handle embedded preview extraction for RAW files - const extractedImage = await this.extractOriginalImage(asset, image, useEdits); - const { info, data, colorspace, generateFullsize, convertFullsize, extracted, isTransparent } = extractedImage; + const { info, data, colorspace, generateFullsize, convertFullsize, extracted, isTransparent } = + await this.extractOriginalImage(asset, image, useEdits); const previewFormat = image.preview.format; this.warnOnTransparencyLoss(isTransparent, previewFormat, asset.id); diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 79299ac16..74869a5a1 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -268,7 +268,7 @@ export class StorageTemplateService extends BaseService { private async getTemplatePath( asset: StorageAsset, metadata: MoveAssetMetadata, - stillPhoto?: StorageAsset, + stillPhoto: StorageAsset = asset, ): Promise { const { storageLabel, filename } = metadata; @@ -310,7 +310,7 @@ export class StorageTemplateService extends BaseService { let albumName = null; let albumStartDate = null; let albumEndDate = null; - const assetForMetadata = stillPhoto || asset; + const assetForMetadata = stillPhoto; if (this.template.needsAlbum) { // For motion videos, use the still photo's album information since motion videos @@ -360,19 +360,19 @@ export class StorageTemplateService extends BaseService { } /** - * In case of migrating duplicate filename to a new path, we need to check if it is already migrated - * Due to the mechanism of appending +1, +2, +3, etc to the filename - * - * Example: - * Source = upload/abc/def/FullSizeRender+7.heic - * Expected Destination = upload/abc/def/FullSizeRender.heic - * - * The file is already at the correct location, but since there are other FullSizeRender.heic files in the - * destination, it was renamed to FullSizeRender+7.heic. - * - * The lines below will be used to check if the differences between the source and destination is only the - * +7 suffix, and if so, it will be considered as already migrated. - */ + In case of migrating duplicate filename to a new path, we need to check if it is already migrated + Due to the mechanism of appending +1, +2, +3, etc to the filename + + Example: + Source = upload/abc/def/FullSizeRender+7.heic + Expected Destination = upload/abc/def/FullSizeRender.heic + + The file is already at the correct location, but since there are other FullSizeRender.heic files in the + destination, it was renamed to FullSizeRender+7.heic. + + The lines below will be used to check if the differences between the source and destination is only the + +7 suffix, and if so, it will be considered as already migrated. + */ if (source.startsWith(fullPath) && source.endsWith(`.${extension}`)) { const diff = source.replace(fullPath, '').replace(`.${extension}`, ''); const hasDuplicationAnnotation = /^\+\d+$/.test(diff); diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index f4b72831b..0f7e297f1 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -420,14 +420,14 @@ describe(SystemConfigService.name, () => { }, ]; - for (const { should, externalDomain, result } of externalDomainTests) { + for (const { should, externalDomain, result = 'https://demo.immich.app' } of externalDomainTests) { it(`should normalize an external domain ${should}`, async () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); const partialConfig = { server: { externalDomain } }; mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig)); const config = await sut.getAdminConfig(); - expect(config.server.externalDomain).toEqual(result ?? 'https://demo.immich.app'); + expect(config.server.externalDomain).toEqual(result); }); } diff --git a/server/src/services/workflow-execution.service.ts b/server/src/services/workflow-execution.service.ts index dbfa38359..b8149aa9b 100644 --- a/server/src/services/workflow-execution.service.ts +++ b/server/src/services/workflow-execution.service.ts @@ -98,7 +98,6 @@ export class WorkflowExecutionService extends BaseService { throw new Error('Hostname did not match any listed in methods[].allowedHosts in the plugin manifest'); } - // eslint-disable-next-line unicorn/no-invalid-argument-count const res = await fetch(...args); return { diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index 51aac25b1..2d0b30a97 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -355,8 +355,8 @@ export function withTags(eb: ExpressionBuilder) { ).as('tags'); } -export function truncatedDate(order: AssetOrderBy = AssetOrderBy.TakenAt, size?: 'DAY' | 'MONTH') { - return sql`date_trunc(${sql.lit(size ?? 'MONTH')}, ${sql.ref(order === AssetOrderBy.CreatedAt ? 'asset.createdAt' : 'localDateTime')} AT TIME ZONE 'UTC') AT TIME ZONE 'UTC'`; +export function truncatedDate(order: AssetOrderBy = AssetOrderBy.TakenAt, size: 'DAY' | 'MONTH' = 'MONTH') { + return sql`date_trunc(${sql.lit(size)}, ${sql.ref(order === AssetOrderBy.CreatedAt ? 'asset.createdAt' : 'localDateTime')} AT TIME ZONE 'UTC') AT TIME ZONE 'UTC'`; } export function withTagId(qb: SelectQueryBuilder, tagId: string) { @@ -1106,14 +1106,14 @@ export type ReindexVectorIndexOptions = { indexName: string; lists?: number }; type VectorIndexQueryOptions = { table: string; vectorExtension: VectorExtension } & ReindexVectorIndexOptions; -export function vectorIndexQuery({ vectorExtension, table, indexName, lists }: VectorIndexQueryOptions): string { +export function vectorIndexQuery({ vectorExtension, table, indexName, lists = 1 }: VectorIndexQueryOptions): string { switch (vectorExtension) { case DatabaseExtension.VectorChord: { return ` CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} USING vchordrq (embedding vector_cosine_ops) WITH (options = $$ residual_quantization = false [build.internal] - lists = [${lists ?? 1}] + lists = [${lists}] spherical_centroids = true build_threads = 4 sampling_factor = 1024 diff --git a/server/src/utils/date.ts b/server/src/utils/date.ts index 011605580..d978cd4e6 100644 --- a/server/src/utils/date.ts +++ b/server/src/utils/date.ts @@ -1,23 +1,17 @@ import { DateTime } from 'luxon'; import { isoDateToDate, isoDatetimeToDate } from 'src/validation.js'; -/** - * Convert a date to a ISO 8601 datetime string. - */ +/** Convert a date to a ISO 8601 datetime string. */ export const asDateTimeString = (x: T) => { return x instanceof Date ? isoDatetimeToDate.encode(x) : (x as Exclude); }; -/** - * Convert a date to a date string (yyyy-mm-dd). - */ +/** Convert a date to a date string (yyyy-mm-dd). */ export const asDateString = (x: Date | string | null): string | null => { return x instanceof Date ? isoDateToDate.encode(x) : x; }; -/** - * People born on February 29th are celebrated on February 28th in non-leap years. - */ +/** People born on February 29th are celebrated on February 28th in non-leap years. */ export const isLeapDayObserved = ({ year, month, day }: { year: number; month: number; day: number }) => { return month === 2 && day === 28 && !DateTime.local(year).isInLeapYear; }; diff --git a/server/src/utils/duplicate.ts b/server/src/utils/duplicate.ts index 9bf076657..908dc4fcb 100644 --- a/server/src/utils/duplicate.ts +++ b/server/src/utils/duplicate.ts @@ -1,27 +1,27 @@ import { AssetResponseDto } from 'src/dtos/asset-response.dto.js'; /** - * Counts all truthy values in the exifInfo object. - * This matches the client implementation in web/src/lib/utils/exif-utils.ts - * - * @param asset Asset with optional exifInfo - * @returns Count of truthy EXIF values - */ +Counts all truthy values in the exifInfo object. +This matches the client implementation in web/src/lib/utils/exif-utils.ts + +@param asset Asset with optional exifInfo +@returns Count of truthy EXIF values +*/ export const getExifCount = (asset: AssetResponseDto): number => { return Object.values(asset.exifInfo ?? {}).filter(Boolean).length; }; /** - * Suggests the best duplicate asset to keep from a list of duplicates. - * This is a direct port of the client logic from web/src/lib/utils/duplicate-utils.ts - * - * The best asset is determined by the following criteria: - * 1. Largest image file size in bytes - * 2. Largest count of EXIF data (as tie-breaker) - * - * @param assets List of duplicate assets - * @returns The best asset to keep, or undefined if empty list - */ +Suggests the best duplicate asset to keep from a list of duplicates. +This is a direct port of the client logic from web/src/lib/utils/duplicate-utils.ts + +The best asset is determined by the following criteria: + 1. Largest image file size in bytes + 2. Largest count of EXIF data (as tie-breaker) + +@param assets List of duplicate assets +@returns The best asset to keep, or undefined if empty list +*/ export const suggestDuplicate = (assets: AssetResponseDto[]): AssetResponseDto | undefined => { if (assets.length === 0) { return undefined; @@ -48,12 +48,12 @@ export const suggestDuplicate = (assets: AssetResponseDto[]): AssetResponseDto | }; /** - * Suggests the best duplicate asset IDs to keep from a list of duplicates. - * Returns an array with a single asset ID (the best candidate), or empty if no assets. - * - * @param assets List of duplicate assets - * @returns Array of suggested asset IDs to keep (0 or 1 element) - */ +Suggests the best duplicate asset IDs to keep from a list of duplicates. +Returns an array with a single asset ID (the best candidate), or empty if no assets. + +@param assets List of duplicate assets +@returns Array of suggested asset IDs to keep (0 or 1 element) +*/ export const suggestDuplicateKeepAssetIds = (assets: AssetResponseDto[]): string[] => { const suggested = suggestDuplicate(assets); return suggested ? [suggested.id] : []; diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index 0d92aeb65..6493c2782 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -71,6 +71,7 @@ export class BaseConfig implements VideoCodecSWConfig { readonly presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast']; protected constructor( protected config: ConfigFFmpegDto, + // eslint-disable-next-line unicorn/no-object-as-default-parameter protected tune: VideoTuning = { strictGop: false, lowLatency: false }, ) {} diff --git a/server/src/utils/mime-types.ts b/server/src/utils/mime-types.ts index ce68361f9..a061059ed 100644 --- a/server/src/utils/mime-types.ts +++ b/server/src/utils/mime-types.ts @@ -35,10 +35,10 @@ const raw = { }; /** - * list of supported image extensions from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types excluding svg - * @TODO share with the client - * @see {@link web/src/lib/utils/asset-utils.ts#L329} - **/ +list of supported image extensions from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types excluding svg +@TODO share with the client +@see {@link web/src/lib/utils/asset-utils.ts#L329} +*/ const webSupportedImage = { '.avif': ['image/avif'], '.bmp': ['image/bmp'], diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index dd2b5763b..219706792 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -52,9 +52,7 @@ export const getMethodNames = (instance: any) => { export const getExternalDomain = (server: SystemConfig['server'], defaultDomain = 'https://my.immich.app') => server.externalDomain || defaultDomain; -/** - * @returns a list of strings representing the keys of the object in dot notation - */ +/** @returns a list of strings representing the keys of the object in dot notation */ export const getKeysDeep = (target: unknown, path: string[] = []) => { if (!target || typeof target !== 'object') { return []; diff --git a/server/src/utils/object.ts b/server/src/utils/object.ts index 1d74dec6b..672bb7025 100644 --- a/server/src/utils/object.ts +++ b/server/src/utils/object.ts @@ -1,15 +1,11 @@ import { isEqual, isPlainObject } from 'lodash-es'; -/** - * Deeply clones and converts a class instance to a plain object. - */ +/** Deeply clones and converts a class instance to a plain object. */ export function toPlainObject(obj: T): T { return isPlainObject(obj) ? obj : structuredClone(obj); } -/** - * Performs a deep comparison between objects, converting them to plain objects first if needed. - */ +/** Performs a deep comparison between objects, converting them to plain objects first if needed. */ export function isEqualObject(value: object, other: object): boolean { return isEqual(toPlainObject(value), toPlainObject(other)); } diff --git a/server/src/utils/search-filter.ts b/server/src/utils/search-filter.ts index 674947a08..cd8f7b775 100644 --- a/server/src/utils/search-filter.ts +++ b/server/src/utils/search-filter.ts @@ -25,9 +25,7 @@ const canMatch = (condition: EnumCondition, value: T): boolean => { ); }; -/** - * The conditions that decide which `field` values the filter can return. A top-level condition decides alone, otherwise each branch itself. - */ +/** The conditions that decide which `field` values the filter can return. A top-level condition decides alone, otherwise each branch itself. */ const decidingConditions = (filter: SearchFilter, field: F) => { if (filter[field] !== undefined) { return [filter[field]]; @@ -39,9 +37,9 @@ const decidingConditions = (filter: SearchFilter, field: F) }; /** - * Keeps locked assets out of search results unless the session is elevated: a filter that asks for - * them is rejected with 401, and any other filter gets `visibility != locked` ANDed in. - */ +Keeps locked assets out of search results unless the session is elevated: a filter that asks for +them is rejected with 401, and any other filter gets `visibility != locked` ANDed in. +*/ export const applyLockedVisibilityPolicy = (auth: AuthDto, filter: SearchFilter): SearchFilter => { if (auth.session?.hasElevatedPermission) { return filter; diff --git a/server/src/utils/sync.ts b/server/src/utils/sync.ts index 609eaaa1c..1d9aa6879 100644 --- a/server/src/utils/sync.ts +++ b/server/src/utils/sync.ts @@ -29,6 +29,6 @@ export const serialize = ({ type, data, ids, - ackType, + ackType = type, }: SerializeOptions) => - mapJsonLine({ type, data, ack: toAck({ type: ackType ?? type, updateId: ids[0], extraId: ids[1] }) }); + mapJsonLine({ type, data, ack: toAck({ type: ackType, updateId: ids[0], extraId: ids[1] }) }); diff --git a/server/src/utils/transform.ts b/server/src/utils/transform.ts index be5b9280b..057ecc1e5 100644 --- a/server/src/utils/transform.ts +++ b/server/src/utils/transform.ts @@ -72,9 +72,9 @@ type TransformState = { }; /** - * Transforms an array of points through a series of edit operations (crop, rotate, mirror). - * Points should be in absolute pixel coordinates relative to the starting dimensions. - */ +Transforms an array of points through a series of edit operations (crop, rotate, mirror). +Points should be in absolute pixel coordinates relative to the starting dimensions. +*/ export const transformPoints = ( points: Point[], edits: AssetEditActionItem[], diff --git a/server/src/validation.ts b/server/src/validation.ts index 38a605211..94ec30cd3 100644 --- a/server/src/validation.ts +++ b/server/src/validation.ts @@ -18,14 +18,14 @@ function isIPOrRange(value: string, options?: IsIPRangeOptions): boolean { } /** - * Zod schema that validates an array of strings as IP addresses or IP/CIDR ranges. - * When requireCIDR is true (default), plain IPs are rejected; only CIDR ranges are allowed. - * - * @example - * z.string().optional().transform(...).pipe(IsIPRange()) - * @example - * z.string().optional().transform(...).pipe(IsIPRange({ requireCIDR: false })) - */ +Zod schema that validates an array of strings as IP addresses or IP/CIDR ranges. +When requireCIDR is true (default), plain IPs are rejected; only CIDR ranges are allowed. + +@example +z.string().optional().transform(...).pipe(IsIPRange()) +@example +z.string().optional().transform(...).pipe(IsIPRange({ requireCIDR: false })) +*/ export function IsIPRange(options?: IsIPRangeOptions) { return z .array(z.string()) @@ -33,12 +33,12 @@ export function IsIPRange(options?: IsIPRangeOptions) { } /** - * Like z.object().partial(), but rejects objects where every field is undefined. - * Use for update/patch DTOs where at least one field must be provided. - * - * @example - * nonEmptyPartial({ name: z.string(), bio: z.string() }).meta({ id: 'UpdateDto' }); - */ +Like z.object().partial(), but rejects objects where every field is undefined. +Use for update/patch DTOs where at least one field must be provided. + +@example +nonEmptyPartial({ name: z.string(), bio: z.string() }).meta({ id: 'UpdateDto' }); +*/ export function nonEmptyPartial(shape: T) { return z .object(shape) @@ -49,14 +49,14 @@ export function nonEmptyPartial(shape: T) { } /** - * Zod schema that validates sibling-exclusion for object schemas. - * Validation passes when the target property is missing, or when none of the sibling properties are present. - * Use with .pipe() like IsIPRange. - * - * @example - * const Schema = z.object({ a: z.string().optional(), b: z.string().optional() }); - * Schema.pipe(IsNotSiblingOf(Schema, 'a', ['b'])); - */ +Zod schema that validates sibling-exclusion for object schemas. +Validation passes when the target property is missing, or when none of the sibling properties are present. +Use with .pipe() like IsIPRange. + +@example +const Schema = z.object({ a: z.string().optional(), b: z.string().optional() }); +Schema.pipe(IsNotSiblingOf(Schema, 'a', ['b'])); +*/ export function IsNotSiblingOf< TSchema extends z.ZodObject, TKey extends z.infer> & keyof z.infer, @@ -122,18 +122,18 @@ const FilenameParamSchema = z.object({ export class FilenameParamDto extends createZodDto(FilenameParamSchema) {} /** - * The HTML5 email regex, but with unicode support, so that international email addresses - * (unicode local parts and internationalized domain names) are accepted. - * @see {@link z.regexes.html5Email} - */ +The HTML5 email regex, but with unicode support, so that international email addresses +(unicode local parts and internationalized domain names) are accepted. +@see {@link z.regexes.html5Email} +*/ const unicodeEmail = /^[\p{L}\p{M}\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\p{L}\p{N}](?:[\p{L}\p{M}\p{N}-]{0,61}[\p{L}\p{M}\p{N}])?(?:\.[\p{L}\p{N}](?:[\p{L}\p{M}\p{N}-]{0,61}[\p{L}\p{M}\p{N}])?)*$/u; /** - * Unified email validation - * Converts email strings to lowercase and validates against a unicode aware email regex - * @docs https://zod.dev/api?id=email - */ +Unified email validation +Converts email strings to lowercase and validates against a unicode aware email regex +@docs https://zod.dev/api?id=email +*/ export const toEmail = z .email({ pattern: unicodeEmail, @@ -142,9 +142,9 @@ export const toEmail = z .transform((val) => val.toLowerCase()); /** - * Parse ISO 8601 datetime strings to Date objects - * @docs https://zod.dev/api?id=codec - */ +Parse ISO 8601 datetime strings to Date objects +@docs https://zod.dev/api?id=codec +*/ export const isoDatetimeToDate = z .codec( z.iso.datetime({ @@ -160,9 +160,9 @@ export const isoDatetimeToDate = z .meta({ example: '2024-01-01T00:00:00.000Z' }); /** - * Parse ISO date strings to Date objects - * @docs https://zod.dev/api?id=codec - */ +Parse ISO date strings to Date objects +@docs https://zod.dev/api?id=codec +*/ export const isoDateToDate = z .codec( z.iso.date({ @@ -177,46 +177,44 @@ export const isoDateToDate = z .meta({ example: '2024-01-01' }); /** - * Latitude in range [-90, 90]. Reuse for body or query params. - * - * @example - * // Regular (body): optional coordinates - * latitudeSchema.optional().describe('Latitude coordinate') - * - * @example - * // Pipe (query): coerce string to number then validate range - * z.coerce.number().pipe(latitudeSchema).describe('Latitude (-90 to 90)') - */ +Latitude in range [-90, 90]. Reuse for body or query params. + +@example +// Regular (body): optional coordinates +latitudeSchema.optional().describe('Latitude coordinate') + +@example +// Pipe (query): coerce string to number then validate range +z.coerce.number().pipe(latitudeSchema).describe('Latitude (-90 to 90)') +*/ export const latitudeSchema = z.number().meta({ format: 'double' }).min(-90).max(90); /** - * Longitude in range [-180, 180]. Reuse for body or query params. - * - * @example - * // Regular (body): optional coordinates - * longitudeSchema.optional().describe('Longitude coordinate') - * - * @example - * // Pipe (query): coerce string to number then validate range - * z.coerce.number().pipe(longitudeSchema).describe('Longitude (-180 to 180)') - */ +Longitude in range [-180, 180]. Reuse for body or query params. + +@example +// Regular (body): optional coordinates +longitudeSchema.optional().describe('Longitude coordinate') + +@example +// Pipe (query): coerce string to number then validate range +z.coerce.number().pipe(longitudeSchema).describe('Longitude (-180 to 180)') +*/ export const longitudeSchema = z.number().meta({ format: 'double' }).min(-180).max(180); /** - * Parse string to boolean - * This should be used for boolean query parameters and path parameters, but not for boolean request body parameters, as the first are always string. - * We don't use z.coerce.boolean() as any truthy value is considered true - * z.stringbool() is a more robust way to parse strings to booleans as it lets you specify the truthy and falsy values and the case sensitivity. - * @docs https://zod.dev/api?id=coercion - * @docs https://zod.dev/api?id=stringbool - */ +Parse string to boolean +This should be used for boolean query parameters and path parameters, but not for boolean request body parameters, as the first are always string. +We don't use z.coerce.boolean() as any truthy value is considered true +z.stringbool() is a more robust way to parse strings to booleans as it lets you specify the truthy and falsy values and the case sensitivity. +@docs https://zod.dev/api?id=coercion +@docs https://zod.dev/api?id=stringbool +*/ export const stringToBool = z .stringbool({ truthy: ['true'], falsy: ['false'], case: 'sensitive' }) .meta({ type: 'boolean' }); -/** - * Parse JSON strings from multipart/form-data - */ +/** Parse JSON strings from multipart/form-data */ export const JsonParsed = z.transform((val, ctx) => { if (typeof val === 'string') { try { @@ -234,13 +232,13 @@ export const JsonParsed = z.transform((val, ctx) => { }); /** - * Hex color validation and normalization. - * Accepts formats: #RGB, #RGBA, #RRGGBB, #RRGGBBAA (with or without # prefix). - * Normalizes output to always include the # prefix. - * - * @example - * hexColor.optional() - */ +Hex color validation and normalization. +Accepts formats: #RGB, #RGBA, #RRGGBB, #RRGGBBAA (with or without # prefix). +Normalizes output to always include the # prefix. + +@example +hexColor.optional() +*/ const hexColorRegex = /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/; export const hexColor = z .string() diff --git a/server/test/medium/specs/repositories/asset-edit.repository.spec.ts b/server/test/medium/specs/repositories/asset-edit.repository.spec.ts index 3feddd5b6..f47ffc89c 100644 --- a/server/test/medium/specs/repositories/asset-edit.repository.spec.ts +++ b/server/test/medium/specs/repositories/asset-edit.repository.spec.ts @@ -9,6 +9,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const { ctx } = newMediumService(BaseService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/repositories/asset-job.repository.spec.ts b/server/test/medium/specs/repositories/asset-job.repository.spec.ts index 33c78ea08..6f414b24f 100644 --- a/server/test/medium/specs/repositories/asset-job.repository.spec.ts +++ b/server/test/medium/specs/repositories/asset-job.repository.spec.ts @@ -15,6 +15,7 @@ const consume = async (generator: AsyncIterableIterator) => { let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const { ctx } = newMediumService(BaseService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/repositories/asset.repository.spec.ts b/server/test/medium/specs/repositories/asset.repository.spec.ts index a39b55e0c..5be638597 100644 --- a/server/test/medium/specs/repositories/asset.repository.spec.ts +++ b/server/test/medium/specs/repositories/asset.repository.spec.ts @@ -10,6 +10,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const { ctx } = newMediumService(BaseService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/repositories/memory.repository.spec.ts b/server/test/medium/specs/repositories/memory.repository.spec.ts index 1f5cfb28d..bf268bdfd 100644 --- a/server/test/medium/specs/repositories/memory.repository.spec.ts +++ b/server/test/medium/specs/repositories/memory.repository.spec.ts @@ -9,6 +9,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const { ctx } = newMediumService(BaseService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/repositories/person-user.repository.spec.ts b/server/test/medium/specs/repositories/person-user.repository.spec.ts index 0413a8285..fa4340933 100644 --- a/server/test/medium/specs/repositories/person-user.repository.spec.ts +++ b/server/test/medium/specs/repositories/person-user.repository.spec.ts @@ -13,6 +13,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const { ctx } = newMediumService(BaseService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/repositories/tag.repository.spec.ts b/server/test/medium/specs/repositories/tag.repository.spec.ts index 1923e7eb3..37ce21e02 100644 --- a/server/test/medium/specs/repositories/tag.repository.spec.ts +++ b/server/test/medium/specs/repositories/tag.repository.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const { ctx } = newMediumService(BaseService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/activity.service.spec.ts b/server/test/medium/specs/services/activity.service.spec.ts index bfd4c255e..e5d1596fd 100644 --- a/server/test/medium/specs/services/activity.service.spec.ts +++ b/server/test/medium/specs/services/activity.service.spec.ts @@ -15,6 +15,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(ActivityService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/album.service.spec.ts b/server/test/medium/specs/services/album.service.spec.ts index ca413443b..fe03f3cf2 100644 --- a/server/test/medium/specs/services/album.service.spec.ts +++ b/server/test/medium/specs/services/album.service.spec.ts @@ -12,6 +12,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(AlbumService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/api-key.service.spec.ts b/server/test/medium/specs/services/api-key.service.spec.ts index 21a742e68..c7dd90bc8 100644 --- a/server/test/medium/specs/services/api-key.service.spec.ts +++ b/server/test/medium/specs/services/api-key.service.spec.ts @@ -11,6 +11,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(ApiKeyService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/asset-media.service.spec.ts b/server/test/medium/specs/services/asset-media.service.spec.ts index 5c99f2e29..977d9ef0f 100644 --- a/server/test/medium/specs/services/asset-media.service.spec.ts +++ b/server/test/medium/specs/services/asset-media.service.spec.ts @@ -22,6 +22,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(AssetMediaService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/asset.service.spec.ts b/server/test/medium/specs/services/asset.service.spec.ts index 6d79d58e0..858c62044 100644 --- a/server/test/medium/specs/services/asset.service.spec.ts +++ b/server/test/medium/specs/services/asset.service.spec.ts @@ -23,6 +23,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(AssetService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/auth-admin.service.spec.ts b/server/test/medium/specs/services/auth-admin.service.spec.ts index 623d536b1..22f7065b8 100644 --- a/server/test/medium/specs/services/auth-admin.service.spec.ts +++ b/server/test/medium/specs/services/auth-admin.service.spec.ts @@ -9,6 +9,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(AuthAdminService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/auth.service.spec.ts b/server/test/medium/specs/services/auth.service.spec.ts index 5ba081042..846886af8 100644 --- a/server/test/medium/specs/services/auth.service.spec.ts +++ b/server/test/medium/specs/services/auth.service.spec.ts @@ -22,6 +22,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(AuthService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/duplicate.service.spec.ts b/server/test/medium/specs/services/duplicate.service.spec.ts index bdb0103e7..8f90d0a6f 100644 --- a/server/test/medium/specs/services/duplicate.service.spec.ts +++ b/server/test/medium/specs/services/duplicate.service.spec.ts @@ -20,6 +20,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const { sut, ctx } = newMediumService(DuplicateService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/integrity.service.spec.ts b/server/test/medium/specs/services/integrity.service.spec.ts index ff4ec7810..09b2311d0 100644 --- a/server/test/medium/specs/services/integrity.service.spec.ts +++ b/server/test/medium/specs/services/integrity.service.spec.ts @@ -20,6 +20,7 @@ import { getKyselyDB, makeStream } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(IntegrityService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/library.service.spec.ts b/server/test/medium/specs/services/library.service.spec.ts index eeb0905cd..2861477a7 100644 --- a/server/test/medium/specs/services/library.service.spec.ts +++ b/server/test/medium/specs/services/library.service.spec.ts @@ -30,10 +30,10 @@ let defaultDatabase: Kysely; const fileModifiedAt = new Date(1_700_000_000_000); -const createFile = async (filePath: string, modifiedAt: Date = fileModifiedAt) => { +const createFile = async (filePath: string) => { await mkdir(dirname(filePath), { recursive: true }); await writeFile(filePath, 'test'); - await utimes(filePath, modifiedAt, modifiedAt); + await utimes(filePath, fileModifiedAt, fileModifiedAt); return filePath; }; @@ -103,6 +103,7 @@ class LibraryTestContext extends MediumTestContext { } } +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { const ctx = new LibraryTestContext(db || defaultDatabase); return { sut: ctx.sut, ctx }; diff --git a/server/test/medium/specs/services/memory.service.spec.ts b/server/test/medium/specs/services/memory.service.spec.ts index 75fbff0c0..170d27e0d 100644 --- a/server/test/medium/specs/services/memory.service.spec.ts +++ b/server/test/medium/specs/services/memory.service.spec.ts @@ -19,6 +19,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(MemoryService, { database: db || defaultDatabase, @@ -47,10 +48,14 @@ const create = async (ctx: ReturnType['ctx']) => { const newPersonAsset = async ( ctx: ReturnType['ctx'], - { ownerId, personGroupId, localDateTime }: { ownerId: string; personGroupId: string; localDateTime?: string }, + { + ownerId, + personGroupId, + localDateTime = '2024-06-13T12:00:00.000Z', + }: { ownerId: string; personGroupId: string; localDateTime?: string }, ) => { const assetRepo = ctx.get(AssetRepository); - const { asset } = await ctx.newAsset({ ownerId, localDateTime: localDateTime ?? '2024-06-13T12:00:00.000Z' }); + const { asset } = await ctx.newAsset({ ownerId, localDateTime }); await Promise.all([ ctx.newExif({ assetId: asset.id, make: 'Canon' }), ctx.newJobStatus({ assetId: asset.id }), diff --git a/server/test/medium/specs/services/ocr.service.spec.ts b/server/test/medium/specs/services/ocr.service.spec.ts index 6254d8760..16ae3b9d5 100644 --- a/server/test/medium/specs/services/ocr.service.spec.ts +++ b/server/test/medium/specs/services/ocr.service.spec.ts @@ -15,6 +15,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(OcrService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/partner.service.spec.ts b/server/test/medium/specs/services/partner.service.spec.ts index 7f06e6664..cbcf2f75b 100644 --- a/server/test/medium/specs/services/partner.service.spec.ts +++ b/server/test/medium/specs/services/partner.service.spec.ts @@ -12,6 +12,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(PartnerService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/passkey.service.spec.ts b/server/test/medium/specs/services/passkey.service.spec.ts index 28762bcd7..357d6b51f 100644 --- a/server/test/medium/specs/services/passkey.service.spec.ts +++ b/server/test/medium/specs/services/passkey.service.spec.ts @@ -49,6 +49,7 @@ const authenticationResponse = { }, }; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(PasskeyService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/plugin.service.spec.ts b/server/test/medium/specs/services/plugin.service.spec.ts index 66e6974d2..e3d4a1658 100644 --- a/server/test/medium/specs/services/plugin.service.spec.ts +++ b/server/test/medium/specs/services/plugin.service.spec.ts @@ -13,6 +13,7 @@ let defaultDatabase: Kysely; const wasmBytes = Buffer.from('some-wasm-binary-data'); const sha256hash = Buffer.from('some-manifest-hash'); +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(PluginService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/search.service.spec.ts b/server/test/medium/specs/services/search.service.spec.ts index faab71eed..0da99c318 100644 --- a/server/test/medium/specs/services/search.service.spec.ts +++ b/server/test/medium/specs/services/search.service.spec.ts @@ -18,6 +18,7 @@ let defaultDatabase: Kysely; const unitVector = (index: number) => JSON.stringify(Array.from({ length: 512 }, (_, i) => (i === index ? 1 : 0))); +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(SearchService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/shared-link.service.spec.ts b/server/test/medium/specs/services/shared-link.service.spec.ts index 5bff8f295..b23a07032 100644 --- a/server/test/medium/specs/services/shared-link.service.spec.ts +++ b/server/test/medium/specs/services/shared-link.service.spec.ts @@ -16,6 +16,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(SharedLinkService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/sync.service.spec.ts b/server/test/medium/specs/services/sync.service.spec.ts index edbeacddc..20151053b 100644 --- a/server/test/medium/specs/services/sync.service.spec.ts +++ b/server/test/medium/specs/services/sync.service.spec.ts @@ -13,6 +13,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(SyncService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/tag.service.spec.ts b/server/test/medium/specs/services/tag.service.spec.ts index 2ed6ee063..c2ade2c3f 100644 --- a/server/test/medium/specs/services/tag.service.spec.ts +++ b/server/test/medium/specs/services/tag.service.spec.ts @@ -16,6 +16,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(TagService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/timeline.service.spec.ts b/server/test/medium/specs/services/timeline.service.spec.ts index 030fc45fa..2051f7fdf 100644 --- a/server/test/medium/specs/services/timeline.service.spec.ts +++ b/server/test/medium/specs/services/timeline.service.spec.ts @@ -14,6 +14,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(TimelineService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/version.service.spec.ts b/server/test/medium/specs/services/version.service.spec.ts index 19956d7bb..56ffe2a71 100644 --- a/server/test/medium/specs/services/version.service.spec.ts +++ b/server/test/medium/specs/services/version.service.spec.ts @@ -13,6 +13,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(VersionService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/services/workflow.service.spec.ts b/server/test/medium/specs/services/workflow.service.spec.ts index 7793f55f1..2479d831f 100644 --- a/server/test/medium/specs/services/workflow.service.spec.ts +++ b/server/test/medium/specs/services/workflow.service.spec.ts @@ -13,6 +13,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = (db?: Kysely) => { return newMediumService(WorkflowService, { database: db || defaultDatabase, diff --git a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts index 9961588ef..45a535cb5 100644 --- a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts @@ -9,6 +9,7 @@ import { getKyselyDB, wait } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-album-asset.spec.ts b/server/test/medium/specs/sync/sync-album-asset.spec.ts index 782b16818..0412513bc 100644 --- a/server/test/medium/specs/sync/sync-album-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB, wait } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-album-to-asset.spec.ts b/server/test/medium/specs/sync/sync-album-to-asset.spec.ts index dc3e9250b..bbab79cdc 100644 --- a/server/test/medium/specs/sync/sync-album-to-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-to-asset.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB, wait } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-album-user.spec.ts b/server/test/medium/specs/sync/sync-album-user.spec.ts index b5c9948ad..b8d1f228a 100644 --- a/server/test/medium/specs/sync/sync-album-user.spec.ts +++ b/server/test/medium/specs/sync/sync-album-user.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB, wait } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-album.spec.ts b/server/test/medium/specs/sync/sync-album.spec.ts index 12ecc0854..231c8a667 100644 --- a/server/test/medium/specs/sync/sync-album.spec.ts +++ b/server/test/medium/specs/sync/sync-album.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-asset-edit.spec.ts b/server/test/medium/specs/sync/sync-asset-edit.spec.ts index f916ab809..b75a268a4 100644 --- a/server/test/medium/specs/sync/sync-asset-edit.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-edit.spec.ts @@ -9,6 +9,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-asset-exif.spec.ts index 0cb1ccefa..da2a118b5 100644 --- a/server/test/medium/specs/sync/sync-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-exif.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-asset-face.spec.ts b/server/test/medium/specs/sync/sync-asset-face.spec.ts index 28208a4c8..383ce1304 100644 --- a/server/test/medium/specs/sync/sync-asset-face.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-face.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-asset-metadata.spec.ts b/server/test/medium/specs/sync/sync-asset-metadata.spec.ts index 2526068ef..87cf47f9f 100644 --- a/server/test/medium/specs/sync/sync-asset-metadata.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-metadata.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-asset-ocr.spec.ts b/server/test/medium/specs/sync/sync-asset-ocr.spec.ts index e004c5d54..98a95d2b7 100644 --- a/server/test/medium/specs/sync/sync-asset-ocr.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-ocr.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-asset.spec.ts b/server/test/medium/specs/sync/sync-asset.spec.ts index 4ded43372..0b99375ed 100644 --- a/server/test/medium/specs/sync/sync-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-asset.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-client-disconnect.spec.ts b/server/test/medium/specs/sync/sync-client-disconnect.spec.ts index 23c36d369..f423dbe73 100644 --- a/server/test/medium/specs/sync/sync-client-disconnect.spec.ts +++ b/server/test/medium/specs/sync/sync-client-disconnect.spec.ts @@ -22,6 +22,7 @@ class LineAutoClosingStream extends Writable { private count: number; lines: string[] = []; + // eslint-disable-next-line unicorn/no-unnecessary-parameters constructor({ after }: { after: number }) { super({ highWaterMark: 1 }); this.count = after; diff --git a/server/test/medium/specs/sync/sync-complete.spec.ts b/server/test/medium/specs/sync/sync-complete.spec.ts index 244bbc5d5..dd5a1ec01 100644 --- a/server/test/medium/specs/sync/sync-complete.spec.ts +++ b/server/test/medium/specs/sync/sync-complete.spec.ts @@ -10,6 +10,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-memory-asset.spec.ts b/server/test/medium/specs/sync/sync-memory-asset.spec.ts index 9397a291f..7c25fb88f 100644 --- a/server/test/medium/specs/sync/sync-memory-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-memory-asset.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-memory.spec.ts b/server/test/medium/specs/sync/sync-memory.spec.ts index d872d7f03..cdd887aa5 100644 --- a/server/test/medium/specs/sync/sync-memory.spec.ts +++ b/server/test/medium/specs/sync/sync-memory.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts index b8fdf4c20..0cb0f34b7 100644 --- a/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB, wait } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-partner-asset.spec.ts b/server/test/medium/specs/sync/sync-partner-asset.spec.ts index 0f01d99de..6412b155f 100644 --- a/server/test/medium/specs/sync/sync-partner-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset.spec.ts @@ -10,6 +10,7 @@ import { getKyselyDB, wait } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-partner-stack.spec.ts b/server/test/medium/specs/sync/sync-partner-stack.spec.ts index fc2ebef87..46ec468d2 100644 --- a/server/test/medium/specs/sync/sync-partner-stack.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-stack.spec.ts @@ -10,6 +10,7 @@ import { getKyselyDB, wait } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-partner.spec.ts b/server/test/medium/specs/sync/sync-partner.spec.ts index 1badfb1f9..0e29f5e76 100644 --- a/server/test/medium/specs/sync/sync-partner.spec.ts +++ b/server/test/medium/specs/sync/sync-partner.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-person.spec.ts b/server/test/medium/specs/sync/sync-person.spec.ts index 5a05f9e63..970dea33d 100644 --- a/server/test/medium/specs/sync/sync-person.spec.ts +++ b/server/test/medium/specs/sync/sync-person.spec.ts @@ -8,6 +8,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-reset.spec.ts b/server/test/medium/specs/sync/sync-reset.spec.ts index 2df424145..326202845 100644 --- a/server/test/medium/specs/sync/sync-reset.spec.ts +++ b/server/test/medium/specs/sync/sync-reset.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-stack.spec.ts b/server/test/medium/specs/sync/sync-stack.spec.ts index f4895ad9f..45309ba86 100644 --- a/server/test/medium/specs/sync/sync-stack.spec.ts +++ b/server/test/medium/specs/sync/sync-stack.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/server/test/medium/specs/sync/sync-user-metadata.spec.ts b/server/test/medium/specs/sync/sync-user-metadata.spec.ts index 806f418bc..20c640aba 100644 --- a/server/test/medium/specs/sync/sync-user-metadata.spec.ts +++ b/server/test/medium/specs/sync/sync-user-metadata.spec.ts @@ -7,6 +7,7 @@ import { getKyselyDB } from 'test/utils.js'; let defaultDatabase: Kysely; +// eslint-disable-next-line unicorn/no-unnecessary-parameters const setup = async (db?: Kysely) => { const ctx = new SyncTestContext(db || defaultDatabase); const { auth, user, session } = await ctx.newSyncAuthUser(); diff --git a/web/package.json b/web/package.json index 7f7ff9815..d9c2fc631 100644 --- a/web/package.json +++ b/web/package.json @@ -110,7 +110,7 @@ "eslint-plugin-compat": "^7.0.0", "eslint-plugin-prettier": "^5.5.6", "eslint-plugin-svelte": "^3.23.1", - "eslint-plugin-unicorn": "^75.0.0", + "eslint-plugin-unicorn": "^78.0.0", "factory.ts": "^1.4.1", "globals": "^17.0.0", "happy-dom": "^21.0.0", diff --git a/web/src/lib/actions/click-outside.ts b/web/src/lib/actions/click-outside.ts index 526f238f3..7d11b2418 100644 --- a/web/src/lib/actions/click-outside.ts +++ b/web/src/lib/actions/click-outside.ts @@ -7,11 +7,11 @@ interface Options { } /** - * Calls a function when a click occurs outside of the element, or when the escape key is pressed. - * @param node - * @param options Object containing onOutclick and onEscape functions - * @returns - */ +Calls a function when a click occurs outside of the element, or when the escape key is pressed. +@param node +@param options Object containing onOutclick and onEscape functions +@returns +*/ export function clickOutside(node: HTMLElement, options: Options = {}): ActionReturn { const handleClick = (event: MouseEvent) => { const { onOutclick } = options; diff --git a/web/src/lib/actions/context-menu-navigation.ts b/web/src/lib/actions/context-menu-navigation.ts index ea4cf110a..98f4adfbf 100644 --- a/web/src/lib/actions/context-menu-navigation.ts +++ b/web/src/lib/actions/context-menu-navigation.ts @@ -3,33 +3,19 @@ import { tick } from 'svelte'; import type { Action } from 'svelte/action'; interface Options { - /** - * A function that is called when the dropdown should be closed. - */ + /** A function that is called when the dropdown should be closed. */ closeDropdown: () => void; - /** - * The container element that with direct children that should be navigated. - */ + /** The container element that with direct children that should be navigated. */ container?: HTMLElement; - /** - * Indicates if the dropdown is open. - */ + /** Indicates if the dropdown is open. */ isOpen: boolean; - /** - * Override the default behavior for the escape key. - */ + /** Override the default behavior for the escape key. */ onEscape?: (event: KeyboardEvent) => void; - /** - * A function that is called when the dropdown should be opened. - */ + /** A function that is called when the dropdown should be opened. */ openDropdown?: (event: KeyboardEvent) => void; - /** - * The id of the currently selected element. - */ + /** The id of the currently selected element. */ selectedId: string | undefined; - /** - * A function that is called when the selection changes, to notify consumers of the new selected id. - */ + /** A function that is called when the selection changes, to notify consumers of the new selected id. */ selectionChanged: (id: string | undefined) => void; } diff --git a/web/src/lib/actions/focus-outside.ts b/web/src/lib/actions/focus-outside.ts index 829497ccd..d8b64ce23 100644 --- a/web/src/lib/actions/focus-outside.ts +++ b/web/src/lib/actions/focus-outside.ts @@ -5,10 +5,10 @@ interface Options { } /** - * Calls a function when focus leaves the element. - * @param node - * @param options Object containing onFocusOut function - */ +Calls a function when focus leaves the element. +@param node +@param options Object containing onFocusOut function +*/ export function focusOutside(node: HTMLElement, options: Options = {}) { const { onFocusOut } = options; diff --git a/web/src/lib/actions/focus-trap.ts b/web/src/lib/actions/focus-trap.ts index 607ed2ac4..2ecc5927e 100644 --- a/web/src/lib/actions/focus-trap.ts +++ b/web/src/lib/actions/focus-trap.ts @@ -2,9 +2,7 @@ import { getTabbable } from '#lib/utils/focus-util.ts'; import { tick } from 'svelte'; interface Options { - /** - * Set whether the trap is active or not. - */ + /** Set whether the trap is active or not. */ active?: boolean; } diff --git a/web/src/lib/actions/list-navigation.ts b/web/src/lib/actions/list-navigation.ts index 8c373753a..e60b7d150 100644 --- a/web/src/lib/actions/list-navigation.ts +++ b/web/src/lib/actions/list-navigation.ts @@ -2,10 +2,10 @@ import { shortcuts } from '#lib/actions/shortcut.ts'; import type { Action } from 'svelte/action'; /** - * Enables keyboard navigation (up and down arrows) for a list of elements. - * @param node Element which listens for keyboard events - * @param container Element containing the list of elements - */ +Enables keyboard navigation (up and down arrows) for a list of elements. +@param node Element which listens for keyboard events +@param container Element containing the list of elements +*/ export const listNavigation: Action = ( node: HTMLElement, container?: HTMLElement, diff --git a/web/src/lib/actions/scroll-memory.ts b/web/src/lib/actions/scroll-memory.ts index 2b29caabf..cab78599a 100644 --- a/web/src/lib/actions/scroll-memory.ts +++ b/web/src/lib/actions/scroll-memory.ts @@ -3,30 +3,20 @@ import { handlePromiseError } from '#lib/utils.ts'; import { navigating } from '$app/state'; interface Options { - /** - * This must be kept the same in all subpages of this route for the scroll memory clearer to work. - */ + /** This must be kept the same in all subpages of this route for the scroll memory clearer to work. */ routeStartsWith: string; - /** - * Function to clear additional data/state before scrolling (ex infinite scroll). - */ + /** Function to clear additional data/state before scrolling (ex infinite scroll). */ beforeClear?: () => void; } interface PageOptions extends Options { - /** - * Function to save additional data/state before scrolling (ex infinite scroll). - */ + /** Function to save additional data/state before scrolling (ex infinite scroll). */ beforeSave?: () => void; - /** - * Function to load additional data/state before scrolling (ex infinite scroll). - */ + /** Function to load additional data/state before scrolling (ex infinite scroll). */ beforeScroll?: () => Promise; } -/** - * @param node The scroll slot element, typically from {@link UserPageLayout} - */ +/** @param node The scroll slot element, typically from {@link UserPageLayout} */ export function scrollMemory( node: HTMLElement, { routeStartsWith, beforeSave, beforeClear, beforeScroll }: PageOptions, diff --git a/web/src/lib/actions/use-actions.ts b/web/src/lib/actions/use-actions.ts index 0b09549a0..4f459e685 100644 --- a/web/src/lib/actions/use-actions.ts +++ b/web/src/lib/actions/use-actions.ts @@ -1,7 +1,7 @@ /** - * @license Apache-2.0 - * https://github.com/hperrin/svelte-material-ui/blob/master/packages/common/src/internal/useActions.ts - */ +@license Apache-2.0 +https://github.com/hperrin/svelte-material-ui/blob/master/packages/common/src/internal/useActions.ts +*/ export type SvelteActionReturnType

= void | { update?: (newParams?: P) => void; diff --git a/web/src/lib/components/HeaderActionButton.svelte b/web/src/lib/components/HeaderActionButton.svelte index 9ff90d21c..c1895ba8c 100644 --- a/web/src/lib/components/HeaderActionButton.svelte +++ b/web/src/lib/components/HeaderActionButton.svelte @@ -7,13 +7,13 @@ variant?: Variants; }; - const { action, variant }: Props = $props(); + const { action, variant = 'ghost' }: Props = $props(); const { title, icon, color = 'secondary', onAction } = $derived(action); {#if action.$if?.() ?? true}