mirror of
https://github.com/immich-app/immich
synced 2026-08-29 13:15:45 +00:00
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:
parent
8061a2e5ff
commit
df970da59e
266 changed files with 1260 additions and 1212 deletions
|
|
@ -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`);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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')) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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 [];
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue