chore(deps): update dependency eslint-plugin-unicorn to v70 - abandoned (#29684)

* chore(deps): update dependency eslint-plugin-unicorn to v70

* fix: linting

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
This commit is contained in:
renovate[bot] 2026-07-20 23:47:14 -04:00 committed by GitHub
parent 8061a2e5ff
commit df970da59e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
266 changed files with 1260 additions and 1212 deletions

View file

@ -3,7 +3,7 @@ import { AuthSharedLink } from 'src/database';
import { AuthDto } from 'src/dtos/auth.dto';
import { AlbumUserRole, Permission } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository';
import { setDifference, setIsEqual, setIsSuperset, setUnion } from 'src/utils/set';
import { areSetsEqual, isSetSuperset, setDifference, setUnion } from 'src/utils/set';
export type GrantedRequest = {
requested: Permission[];
@ -15,7 +15,7 @@ export const isGranted = ({ requested, current }: GrantedRequest) => {
return true;
}
return setIsSuperset(new Set(current), new Set(requested));
return isSetSuperset(new Set(current), new Set(requested));
};
export type AccessRequest = {
@ -36,7 +36,7 @@ export const requireUploadAccess = (auth: AuthDto | null): AuthDto => {
export const requireAccess = async (access: AccessRepository, request: AccessRequest) => {
const allowedIds = await checkAccess(access, request);
if (!setIsEqual(new Set(request.ids), allowedIds)) {
if (!areSetsEqual(new Set(request.ids), allowedIds)) {
throw new BadRequestException(`Not found or no ${request.permission} access`);
}
};

View file

@ -29,10 +29,12 @@ export const getConfig = async (repos: RepoDeps, { withCache }: { withCache: boo
if (!withCache || !config) {
const timestamp = lastUpdated;
await asyncLock.acquire(DatabaseLock[DatabaseLock.GetSystemConfig], async () => {
if (timestamp === lastUpdated) {
config = await buildConfig(repos);
lastUpdated = Date.now();
if (timestamp !== lastUpdated) {
return;
}
config = await buildConfig(repos);
lastUpdated = Date.now();
});
}
@ -45,7 +47,7 @@ export const updateConfig = async (repos: RepoDeps, newConfig: SystemConfig): Pr
const partialConfig: DeepPartial<SystemConfig> = {};
for (const property of getKeysDeep(defaults)) {
const newValue = _.get(newConfig, property);
const isEmpty = newValue === undefined || newValue === null || newValue === '';
const isEmpty = [undefined, null, ''].includes(newValue);
const defaultValue = _.get(defaults, property);
const isEqual = newValue === defaultValue || _.isEqual(newValue, defaultValue);
@ -64,7 +66,7 @@ export const updateConfig = async (repos: RepoDeps, newConfig: SystemConfig): Pr
const loadFromFile = async ({ metadataRepo, logger }: RepoDeps, filepath: string) => {
try {
const file = await metadataRepo.readFile(filepath);
return loadYaml(file.toString()) as unknown;
return loadYaml(file) as unknown;
} catch (error: Error | any) {
logger.error(`Unable to load configuration file: ${filepath}`);
logger.error(error);
@ -107,9 +109,8 @@ const buildConfig = async (repos: RepoDeps) => {
}
if (configFile) {
throw new Error(messages.join('\n'));
} else {
logger.error('Validation error', messages);
}
logger.error('Validation error', messages);
}
const config = (result.success ? result.data : rawConfig) as SystemConfig;
@ -117,10 +118,10 @@ const buildConfig = async (repos: RepoDeps) => {
if (config.server.externalDomain.length > 0) {
const domain = new URL(config.server.externalDomain);
let externalDomain = domain.origin;
if (domain.password && domain.username) {
externalDomain = `${domain.protocol}//${domain.username}:${domain.password}@${domain.host}`;
}
const externalDomain =
domain.password && domain.username
? `${domain.protocol}//${domain.username}:${domain.password}@${domain.host}`
: domain.origin;
config.server.externalDomain = externalDomain;
}

View file

@ -36,18 +36,20 @@ export const getKyselyConfig = (connection: DatabaseConnectionParams): KyselyCon
}),
}),
log(event) {
if (event.level === 'error') {
if (isAssetChecksumConstraint(event.error)) {
return;
}
console.error('Query failed :', {
durationMs: event.queryDurationMillis,
error: event.error,
sql: event.query.sql,
params: event.query.parameters,
});
if (event.level !== 'error') {
return;
}
if (isAssetChecksumConstraint(event.error)) {
return;
}
console.error('Query failed :', {
durationMs: event.queryDurationMillis,
error: event.error,
sql: event.query.sql,
params: event.query.parameters,
});
},
};
};
@ -523,6 +525,6 @@ export function vectorIndexQuery({ vectorExtension, table, indexName, lists }: V
export const updateLockedColumns = <T extends Record<string, unknown> & { lockedProperties?: LockableProperty[] }>(
exif: T,
) => {
exif.lockedProperties = lockableProperties.filter((property) => property in exif);
exif.lockedProperties = lockableProperties.filter((property) => Object.hasOwn(exif, property));
return exif;
};

View file

@ -4,15 +4,14 @@ import { configureUserAgent } from 'src/utils/fetch';
describe('fetch', () => {
it('should set the default user-agent header', async () => {
const spy = vi.fn().mockResolvedValue(new Response());
const original = globalThis.fetch;
globalThis.fetch = spy;
vi.stubGlobal('fetch', spy);
configureUserAgent();
await globalThis.fetch('http://test.local');
await fetch('https://test.local');
const headers: Headers = spy.mock.calls[0][1].headers;
expect(headers.get('User-Agent')).toBe(`immich-server/${serverVersion}`);
globalThis.fetch = original;
vi.unstubAllGlobals();
});
});

View file

@ -1,7 +1,8 @@
import { serverVersion } from 'src/constants';
export function configureUserAgent() {
const originalFetch = globalThis.fetch;
const originalFetch = fetch;
// eslint-disable-next-line unicorn/no-global-object-property-assignment
globalThis.fetch = (input, init) => {
const headers = new Headers(init?.headers);
if (!headers.has('User-Agent')) {

View file

@ -74,7 +74,7 @@ export const sendFile = async (
}
// log non-http errors
if (error instanceof HttpException === false) {
if (!(error instanceof HttpException)) {
logger.error(`Unable to send file: ${error}`, error.stack);
}

View file

@ -2,7 +2,7 @@ import { HttpException } from '@nestjs/common';
import { Request } from 'express';
import { LoggingRepository } from 'src/repositories/logging.repository';
const isRequestAborted = (request: Request) => request.destroyed === true && request.complete === false;
const isRequestAborted = (request: Request) => request.destroyed && !request.complete;
export const isHttpException = (error: Error): error is HttpException => error instanceof HttpException;
export const onRequestError = (req: Request, error: Error, logger: LoggingRepository) => {

View file

@ -38,23 +38,23 @@ export async function detectPriorInstall(
const files = await storageRepository.readdir(path);
const filename = join(StorageCore.getBaseFolder(folder), '.immich');
let readable = false,
writable = false;
let isReadable = false,
isWritable = false;
try {
await storageRepository.readFile(filename);
readable = true;
isReadable = true;
await storageRepository.overwriteFile(filename, Buffer.from(`${Date.now()}`));
writable = true;
await storageRepository.overwriteFile(filename, Buffer.from(Date.now().toString()));
isWritable = true;
} catch {
// no-op
}
return {
folder,
readable,
writable,
readable: isReadable,
writable: isWritable,
files: files.filter((fn) => fn !== '.immich').length,
};
}),

View file

@ -68,9 +68,9 @@ export class BaseConfig implements VideoCodecSWConfig {
static create(config: SystemConfigFFmpegDto, interfaces: VideoInterfaces, tune?: VideoTuning) {
if (config.accel === TranscodeHardwareAcceleration.Disabled) {
return this.getSWCodecConfig(config, tune);
return BaseConfig.getSWCodecConfig(config, tune);
}
return this.getHWCodecConfig(config, interfaces, tune);
return BaseConfig.getHWCodecConfig(config, interfaces, tune);
}
private static getSWCodecConfig(config: SystemConfigFFmpegDto, tune?: VideoTuning): VideoCodecSWConfig {
@ -230,15 +230,15 @@ export class BaseConfig implements VideoCodecSWConfig {
}
}
if (this.getBFrames() > -1) {
options.push('-bf', `${this.getBFrames()}`);
options.push('-bf', String(this.getBFrames()));
}
if (this.getRefs() > 0) {
options.push('-refs', `${this.getRefs()}`);
options.push('-refs', String(this.getRefs()));
}
if (this.getGopSize() > 0) {
options.push('-g', `${this.getGopSize()}`);
options.push('-g', String(this.getGopSize()));
if (this.tune.strictGop) {
options.push('-keyint_min', `${this.getGopSize()}`);
options.push('-keyint_min', String(this.getGopSize()));
}
}
const isHvc =
@ -285,19 +285,19 @@ export class BaseConfig implements VideoCodecSWConfig {
'-maxrate',
`${bitrates.max}${bitrates.unit}`,
];
} else if (bitrates.max > 0) {
}
if (bitrates.max > 0) {
// -bufsize is the peak possible bitrate at any moment, while -maxrate is the max rolling average bitrate
return [
`-${this.useCQP() ? 'q:v' : 'crf'}`,
`${this.config.crf}`,
String(this.config.crf),
'-maxrate',
`${bitrates.max}${bitrates.unit}`,
'-bufsize',
`${bitrates.max * 2}${bitrates.unit}`,
];
} else {
return [`-${this.useCQP() ? 'q:v' : 'crf'}`, `${this.config.crf}`];
}
return [`-${this.useCQP() ? 'q:v' : 'crf'}`, String(this.config.crf)];
}
getInputThreadOptions(): Array<string> {
@ -308,7 +308,7 @@ export class BaseConfig implements VideoCodecSWConfig {
if (this.config.threads <= 0) {
return [];
}
return ['-threads', `${this.config.threads}`];
return ['-threads', String(this.config.threads)];
}
eligibleForTwoPass() {
@ -343,9 +343,9 @@ export class BaseConfig implements VideoCodecSWConfig {
}
shouldScale(videoStream: VideoStreamInfo) {
const oddDimensions = videoStream.height % 2 !== 0 || videoStream.width % 2 !== 0;
const largerThanTarget = Math.min(videoStream.height, videoStream.width) > this.getTargetResolution(videoStream);
return oddDimensions || largerThanTarget;
const isOddDimensions = videoStream.height % 2 !== 0 || videoStream.width % 2 !== 0;
const isLargerThanTarget = Math.min(videoStream.height, videoStream.width) > this.getTargetResolution(videoStream);
return isOddDimensions || isLargerThanTarget;
}
shouldToneMap(videoStream: VideoStreamInfo) {
@ -569,7 +569,7 @@ export class VP9Config extends BaseConfig {
getPresetOptions() {
const speed = Math.min(this.getPresetIndex(), 5); // values over 5 require realtime mode, which is its own can of worms since it overrides -crf and -threads
if (speed >= 0) {
return ['-cpu-used', `${speed}`];
return ['-cpu-used', String(speed)];
}
return [];
}
@ -587,7 +587,7 @@ export class VP9Config extends BaseConfig {
];
}
return [`-${this.useCQP() ? 'q:v' : 'crf'}`, `${this.config.crf}`, '-b:v', `${bitrates.max}${bitrates.unit}`];
return [`-${this.useCQP() ? 'q:v' : 'crf'}`, String(this.config.crf), '-b:v', `${bitrates.max}${bitrates.unit}`];
}
getEncoderOptions(): string[] {
@ -607,13 +607,13 @@ export class AV1Config extends BaseConfig {
getPresetOptions() {
const speed = this.getPresetIndex() + 4; // Use 4 as slowest, giving us an effective range of 4-12 which is far more useful than 0-8
if (speed >= 0) {
return ['-preset', `${speed}`];
return ['-preset', String(speed)];
}
return [];
}
getBitrateOptions() {
return ['-crf', `${this.config.crf}`];
return ['-crf', String(this.config.crf)];
}
getEncoderOptions(): string[] {
@ -699,18 +699,17 @@ export class NvencSwDecodeConfig extends BaseHWConfig {
'-multipass',
'2',
];
} else if (bitrates.max > 0) {
return [
'-cq:v',
`${this.config.crf}`,
'-maxrate',
`${bitrates.max}${bitrates.unit}`,
'-bufsize',
`${bitrates.target}${bitrates.unit}`,
];
} else {
return ['-cq:v', `${this.config.crf}`];
}
return bitrates.max > 0
? [
'-cq:v',
String(this.config.crf),
'-maxrate',
`${bitrates.max}${bitrates.unit}`,
'-bufsize',
`${bitrates.target}${bitrates.unit}`,
]
: ['-cq:v', String(this.config.crf)];
}
getThreadOptions() {
@ -810,11 +809,11 @@ export class QsvSwDecodeConfig extends BaseHWConfig {
return [];
}
presetIndex = Math.min(6, presetIndex) + 1; // 1 to 7
return ['-preset', `${presetIndex}`];
return ['-preset', String(presetIndex)];
}
getBitrateOptions() {
const options = [`-${this.useCQP() ? 'q:v' : 'global_quality:v'}`, `${this.config.crf}`];
const options = [`-${this.useCQP() ? 'q:v' : 'global_quality:v'}`, String(this.config.crf)];
const bitrates = this.getBitrateDistribution();
if (bitrates.max > 0) {
// Workaround for https://github.com/immich-app/immich/issues/29220, to be revisited
@ -939,7 +938,7 @@ export class VaapiSwDecodeConfig extends BaseHWConfig {
return [];
}
presetIndex = Math.min(6, presetIndex) + 1; // 1 to 7
return ['-compression_level', `${presetIndex}`];
return ['-compression_level', String(presetIndex)];
}
getBitrateOptions() {
@ -963,9 +962,9 @@ export class VaapiSwDecodeConfig extends BaseHWConfig {
'3',
); // variable bitrate
} else if (this.useCQP()) {
options.push('-qp:v', `${this.config.crf}`, '-global_quality:v', `${this.config.crf}`, '-rc_mode', '1');
options.push('-qp:v', String(this.config.crf), '-global_quality:v', String(this.config.crf), '-rc_mode', '1');
} else {
options.push('-global_quality:v', `${this.config.crf}`, '-rc_mode', '4');
options.push('-global_quality:v', String(this.config.crf), '-rc_mode', '4');
}
return options;
@ -1072,7 +1071,7 @@ export class RkmppSwDecodeConfig extends BaseHWConfig {
return ['-rc_mode', 'AVBR', '-b:v', `${bitrate}${this.getBitrateUnit()}`];
}
// use CRF value as QP value
return ['-rc_mode', 'CQP', '-qp_init', `${this.config.crf}`];
return ['-rc_mode', 'CQP', '-qp_init', String(this.config.crf)];
}
getVideoCodec(): string {
@ -1106,7 +1105,8 @@ export class RkmppHwDecodeConfig extends RkmppSwDecodeConfig {
`tonemapx=tonemap=${this.config.tonemap}:desat=0:p=${primaries}:t=${transfer}:m=${matrix}:r=pc:peak=100:format=yuv420p`,
'hwupload',
];
} else if (this.shouldScale(videoStream)) {
}
if (this.shouldScale(videoStream)) {
return [`scale_rkrga=${this.getScaling(videoStream)}:format=nv12:afbc=1:async_depth=4`];
}
return [];

View file

@ -132,7 +132,8 @@ const sidecar: Record<string, string[]> = {
const types = { ...image, ...video, ...sidecar };
const isType = (filename: string, r: Record<string, string[]>) => extname(filename).toLowerCase() in r;
const isType = (filename: string, record: Record<string, string[]>) =>
Object.hasOwn(record, extname(filename).toLowerCase());
const lookup = (filename: string) => types[extname(filename).toLowerCase()]?.[0] ?? 'application/octet-stream';
const toExtension = (mimeType: string) => {

View file

@ -152,11 +152,7 @@ export const routeToErrorMessage = (methodName: string) =>
'Failed to ' + methodName.replaceAll(/[A-Z]+/g, (letter) => ` ${letter.toLowerCase()}`);
const isSchema = (schema: string | ReferenceObject | SchemaObject): schema is SchemaObject => {
if (typeof schema === 'string' || '$ref' in schema) {
return false;
}
return true;
return !(typeof schema === 'string' || '$ref' in schema);
};
const patchOpenAPI = (document: OpenAPIObject) => {
@ -195,20 +191,22 @@ const patchOpenAPI = (document: OpenAPIObject) => {
document.components.schemas = sortKeys(schemas);
for (const [schemaName, schema] of Object.entries(schemas)) {
if (schema.properties) {
schema.properties = sortKeys(schema.properties);
for (const [key, value] of Object.entries(schema.properties)) {
if (typeof value === 'string') {
continue;
}
if (isSchema(value) && value.type === 'number' && value.format === 'float') {
throw new Error(`Invalid number format: ${schemaName}.${key}=float (use double instead). `);
}
}
schema.required?.sort();
if (!schema.properties) {
continue;
}
schema.properties = sortKeys(schema.properties);
for (const [key, value] of Object.entries(schema.properties)) {
if (typeof value === 'string') {
continue;
}
if (isSchema(value) && value.type === 'number' && value.format === 'float') {
throw new Error(`Invalid number format: ${schemaName}.${key}=float (use double instead). `);
}
}
schema.required?.sort();
}
}

View file

@ -72,7 +72,7 @@ export const getPreferencesPartial = (newPreferences: UserPreferences) => {
const partial: DeepPartial<UserPreferences> = {};
for (const property of getKeysDeep(defaultPreferences)) {
const newValue = _.get(newPreferences, property);
const isEmpty = newValue === undefined || newValue === null || newValue === '';
const isEmpty = [undefined, null, ''].includes(newValue);
const defaultValue = _.get(defaultPreferences, property);
const isEqual = newValue === defaultValue || _.isEqual(newValue, defaultValue);

View file

@ -22,7 +22,7 @@ export const setDifference = <T>(setA: Set<T>, ...sets: Set<T>[]): Set<T> => {
return difference;
};
export const setIsSuperset = <T>(set: Set<T>, subset: Set<T>): boolean => {
export const isSetSuperset = <T>(set: Set<T>, subset: Set<T>): boolean => {
for (const element of subset) {
if (!set.has(element)) {
return false;
@ -31,6 +31,6 @@ export const setIsSuperset = <T>(set: Set<T>, subset: Set<T>): boolean => {
return true;
};
export const setIsEqual = <T>(setA: Set<T>, setB: Set<T>): boolean => {
return setA.size === setB.size && setIsSuperset(setA, setB);
export const areSetsEqual = <T>(setA: Set<T>, setB: Set<T>): boolean => {
return setA.size === setB.size && isSetSuperset(setA, setB);
};

View file

@ -16,11 +16,13 @@ export const getOutputDimensions = (
}
for (const edit of edits) {
if (edit.action === AssetEditAction.Rotate) {
const angleDegrees = edit.parameters.angle;
if (angleDegrees === 90 || angleDegrees === 270) {
[width, height] = [height, width];
}
if (edit.action !== AssetEditAction.Rotate) {
continue;
}
const angleDegrees = edit.parameters.angle;
if (angleDegrees === 90 || angleDegrees === 270) {
[width, height] = [height, width];
}
}