Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"/oclif.manifest.json"
],
"dependencies": {
"@inquirer/prompts": "8.1.0",
"@inquirer/prompts": "8.2.0",
"@internxt/inxt-js": "2.2.9",
"@internxt/lib": "1.4.1",
"@internxt/sdk": "1.12.0",
Expand All @@ -57,7 +57,7 @@
"otpauth": "9.4.1",
"pm2": "6.0.14",
"range-parser": "1.2.1",
"selfsigned": "5.4.0",
"selfsigned": "5.5.0",
"tty-table": "5.0.0",
"winston": "3.19.0"
},
Expand All @@ -68,20 +68,20 @@
"@types/cli-progress": "3.11.6",
"@types/express": "5.0.6",
"@types/mime-types": "3.0.1",
"@types/node": "25.0.5",
"@types/node": "25.0.7",
"@types/range-parser": "1.2.7",
"@vitest/coverage-istanbul": "4.0.16",
"@vitest/spy": "4.0.16",
"@vitest/coverage-istanbul": "4.0.17",
"@vitest/spy": "4.0.17",
"eslint": "9.39.2",
"husky": "9.1.7",
"lint-staged": "16.2.7",
"nodemon": "3.1.11",
"oclif": "4.22.63",
"oclif": "4.22.65",
"prettier": "3.7.4",
"rimraf": "6.1.2",
"ts-node": "10.9.2",
"typescript": "5.9.3",
"vitest": "4.0.16",
"vitest": "4.0.17",
"vitest-mock-express": "2.2.0"
},
"optionalDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export class AuthService {
public logout = async (): Promise<void> => {
try {
const user = await ConfigService.instance.readUser();
if (!user || !user.token) {
if (!user?.token) {
return;
}
const authClient = SdkManager.instance.getAuth();
Expand Down
2 changes: 1 addition & 1 deletion src/services/config.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'node:path';
import os from 'os';
import os from 'node:os';
import fs from 'node:fs/promises';
import { ConfigKeys } from '../types/config.types';
import { LoginCredentials, WebdavConfig } from '../types/command.types';
Expand Down
6 changes: 2 additions & 4 deletions src/services/sdk-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ export class SdkManager {
* @throws {Error} When throwErrorOnMissingCredentials is setted to true and there is not apiSecurity defined
* @returns The SDK Manager api security details
**/
public static readonly getApiSecurity = (
config = { throwErrorOnMissingCredentials: true },
): SdkManagerApiSecurity => {
if (!SdkManager.apiSecurity && config.throwErrorOnMissingCredentials)
public static readonly getApiSecurity = ({ throwErrorOnMissingCredentials = true } = {}): SdkManagerApiSecurity => {
if (!SdkManager.apiSecurity && throwErrorOnMissingCredentials)
throw new Error('Api security properties not found in SdkManager');

return SdkManager.apiSecurity as SdkManagerApiSecurity;
Expand Down
6 changes: 3 additions & 3 deletions src/utils/cli.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ export class CLIUtils {
}
} while (!isValid && currentAttempts < maxAttempts);

if (!isValid) {
throw validation.error;
} else {
if (isValid) {
return promptValue;
} else {
throw validation.error;
}
};

Expand Down
6 changes: 4 additions & 2 deletions src/utils/errors.utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { logger } from './logger.utils';
import { types } from 'node:util';

export function isError(error: unknown): error is Error {
return types.isNativeError(error);
return typeof Error.isError === 'function'
? Error.isError(error)
: error instanceof Error ||
(typeof error === 'object' && error !== null && 'message' in error && ('stack' in error || 'name' in error));
}

export function isAlreadyExistsError(error: unknown): error is Error {
Expand Down
8 changes: 5 additions & 3 deletions src/utils/network.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,18 @@ export class NetworkUtils {
throw new Error(`Multi Range-Requests functionality is not implemented. ${JSON.stringify(rangeOptions)}`);
} else if (parsed.length <= 0) {
throw new Error(`Empty Range-Request. ${JSON.stringify(rangeOptions)}`);
} else if (parsed.type !== 'bytes') {
throw new Error(`Unkwnown Range-Request type "${parsed.type}". ${JSON.stringify(rangeOptions)}`);
} else {
}

if (parsed.type === 'bytes') {
const rangeSize = parsed[0].end - parsed[0].start + 1;
return {
range: rangeOptions.range,
rangeSize: rangeSize,
totalFileSize: rangeOptions.totalFileSize,
parsed: parsed[0],
};
} else {
throw new Error(`Unkwnown Range-Request type "${parsed.type}". ${JSON.stringify(rangeOptions)}`);
}
} else if (parsed === -1) {
throw new Error(`Malformed Range-Request. ${JSON.stringify(rangeOptions)}`);
Expand Down
14 changes: 7 additions & 7 deletions src/utils/thumbnail.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@ const imageExtensions: FileExtensionMap = {
const pdfExtensions: FileExtensionMap = {
pdf: ['pdf'],
};
const thumbnailableImageExtension: string[] = [
const thumbnailableImageExtension: Set<string> = new Set([
...imageExtensions['jpg'],
...imageExtensions['png'],
...imageExtensions['webp'],
...imageExtensions['gif'],
...imageExtensions['tiff'],
];
const thumbnailablePdfExtension: string[] = pdfExtensions['pdf'];
const thumbnailableExtension: string[] = [...thumbnailableImageExtension];
]);
const thumbnailablePdfExtension: Set<string> = new Set(pdfExtensions['pdf']);
const thumbnailableExtension: Set<string> = new Set(thumbnailableImageExtension);

export const isFileThumbnailable = (fileType: string) => {
return fileType.trim().length > 0 && thumbnailableExtension.includes(fileType.trim().toLowerCase());
return fileType.trim().length > 0 && thumbnailableExtension.has(fileType.trim().toLowerCase());
};

export const isPDFThumbnailable = (fileType: string) => {
return fileType.trim().length > 0 && thumbnailablePdfExtension.includes(fileType.trim().toLowerCase());
return fileType.trim().length > 0 && thumbnailablePdfExtension.has(fileType.trim().toLowerCase());
};

export const isImageThumbnailable = (fileType: string) => {
return fileType.trim().length > 0 && thumbnailableImageExtension.includes(fileType.trim().toLowerCase());
return fileType.trim().length > 0 && thumbnailableImageExtension.has(fileType.trim().toLowerCase());
};

export const tryUploadThumbnail = async ({
Expand Down
2 changes: 1 addition & 1 deletion src/utils/xml.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class XMLUtils {
return parser.parse(xml);
}

static toXML(object: object, options: XmlBuilderOptions = { format: true }) {
static toXML(object: object, options: XmlBuilderOptions) {
const builder = new XMLBuilder(options);
return builder.build(object);
}
Expand Down
2 changes: 1 addition & 1 deletion src/webdav/handlers/PUT.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class PUTRequestHandler implements WebDavMethodHandler {

handle = async (req: Request, res: Response) => {
let contentLength = Number(req.headers['content-length']);
if (!contentLength || isNaN(contentLength) || contentLength <= 0) {
if (!contentLength || Number.isNaN(contentLength) || contentLength <= 0) {
contentLength = 0;
}

Expand Down
2 changes: 1 addition & 1 deletion src/webdav/middewares/errors.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const ErrorHandlingMiddleware: ErrorRequestHandler = (err, req, res, _) =
);

let statusCode = 500;
if ('statusCode' in err && !isNaN(err.statusCode)) {
if ('statusCode' in err && !Number.isNaN(err.statusCode)) {
statusCode = err.statusCode;
}

Expand Down
4 changes: 2 additions & 2 deletions src/webdav/webdav-server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Express } from 'express';
import https from 'https';
import http from 'http';
import https from 'node:https';
import http from 'node:http';
import { ConfigService } from '../services/config.service';
import { OPTIONSRequestHandler } from './handlers/OPTIONS.handler';
import { PROPFINDRequestHandler } from './handlers/PROPFIND.handler';
Expand Down
169 changes: 156 additions & 13 deletions test/utils/thumbnail.utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,173 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { createFileStreamWithBuffer } from '../../src/utils/thumbnail.utils';
import {
createFileStreamWithBuffer,
isFileThumbnailable,
isImageThumbnailable,
isPDFThumbnailable,
} from '../../src/utils/thumbnail.utils';
import { BufferStream } from '../../src/utils/stream.utils';
import path from 'node:path';
import { Readable } from 'node:stream';

describe('createFileStreamWithBuffer', () => {
describe('Thumbnail Utils tests', () => {
const testFilePath = path.join(process.cwd(), 'test/fixtures/test-content.fixture.txt');

beforeEach(() => {
vi.restoreAllMocks();
});

it('should create BufferStream and pipe stream when file type is thumbnailable', () => {
const result = createFileStreamWithBuffer({ path: testFilePath, fileType: 'png' });
describe('createFileStreamWithBuffer', () => {
it('should create BufferStream and pipe stream when file type is thumbnailable', () => {
const result = createFileStreamWithBuffer({ path: testFilePath, fileType: 'png' });

expect(result.bufferStream).toBeDefined();
expect(result.bufferStream).toBeInstanceOf(BufferStream);
expect(result.fileStream).toBeDefined();
expect(result.fileStream).toBeInstanceOf(Readable);
expect(result.bufferStream).toBeDefined();
expect(result.bufferStream).toBeInstanceOf(BufferStream);
expect(result.fileStream).toBeDefined();
expect(result.fileStream).toBeInstanceOf(Readable);
});

it('should not create BufferStream when file type is not thumbnailable', () => {
const result = createFileStreamWithBuffer({ path: testFilePath, fileType: 'txt' });

expect(result.bufferStream).toBeUndefined();
expect(result.fileStream).toBeDefined();
expect(result.fileStream).toBeInstanceOf(Readable);
});
});

it('should not create BufferStream when file type is not thumbnailable', () => {
const result = createFileStreamWithBuffer({ path: testFilePath, fileType: 'txt' });
describe('isFileThumbnailable', () => {
it('should return true for valid image extensions', () => {
expect(isFileThumbnailable('jpg')).toBe(true);
expect(isFileThumbnailable('jpeg')).toBe(true);
expect(isFileThumbnailable('png')).toBe(true);
expect(isFileThumbnailable('webp')).toBe(true);
expect(isFileThumbnailable('gif')).toBe(true);
expect(isFileThumbnailable('tif')).toBe(true);
expect(isFileThumbnailable('tiff')).toBe(true);
});

it('should return true regardless of case', () => {
expect(isFileThumbnailable('JPG')).toBe(true);
expect(isFileThumbnailable('PNG')).toBe(true);
expect(isFileThumbnailable('Webp')).toBe(true);
expect(isFileThumbnailable('GIF')).toBe(true);
});

it('should handle whitespace correctly', () => {
expect(isFileThumbnailable(' jpg ')).toBe(true);
expect(isFileThumbnailable(' png ')).toBe(true);
expect(isFileThumbnailable('\tgif\t')).toBe(true);
});

it('should return false for non-thumbnailable extensions', () => {
expect(isFileThumbnailable('pdf')).toBe(false);
expect(isFileThumbnailable('doc')).toBe(false);
expect(isFileThumbnailable('txt')).toBe(false);
expect(isFileThumbnailable('mp4')).toBe(false);
expect(isFileThumbnailable('bmp')).toBe(false);
expect(isFileThumbnailable('raw')).toBe(false);
expect(isFileThumbnailable('heic')).toBe(false);
});

it('should return false for empty strings', () => {
expect(isFileThumbnailable('')).toBe(false);
expect(isFileThumbnailable(' ')).toBe(false);
expect(isFileThumbnailable('\t\n')).toBe(false);
});

it('should return false for invalid input', () => {
expect(isFileThumbnailable('unknown')).toBe(false);
expect(isFileThumbnailable('jpgg')).toBe(false);
});
});

describe('isPDFThumbnailable', () => {
it('should return true for pdf extension', () => {
expect(isPDFThumbnailable('pdf')).toBe(true);
});

it('should return true regardless of case', () => {
expect(isPDFThumbnailable('PDF')).toBe(true);
expect(isPDFThumbnailable('Pdf')).toBe(true);
expect(isPDFThumbnailable('pDf')).toBe(true);
});

it('should handle whitespace correctly', () => {
expect(isPDFThumbnailable(' pdf ')).toBe(true);
expect(isPDFThumbnailable(' PDF ')).toBe(true);
expect(isPDFThumbnailable('\tpdf\n')).toBe(true);
});

it('should return false for non-pdf extensions', () => {
expect(isPDFThumbnailable('jpg')).toBe(false);
expect(isPDFThumbnailable('png')).toBe(false);
expect(isPDFThumbnailable('doc')).toBe(false);
expect(isPDFThumbnailable('docx')).toBe(false);
expect(isPDFThumbnailable('txt')).toBe(false);
});

it('should return false for empty strings', () => {
expect(isPDFThumbnailable('')).toBe(false);
expect(isPDFThumbnailable(' ')).toBe(false);
expect(isPDFThumbnailable('\t\n')).toBe(false);
});

it('should return false for invalid input', () => {
expect(isPDFThumbnailable('pdff')).toBe(false);
expect(isPDFThumbnailable('pd')).toBe(false);
});
});

describe('isImageThumbnailable', () => {
it('should return true for all thumbnailable image extensions', () => {
expect(isImageThumbnailable('jpg')).toBe(true);
expect(isImageThumbnailable('jpeg')).toBe(true);
expect(isImageThumbnailable('png')).toBe(true);
expect(isImageThumbnailable('webp')).toBe(true);
expect(isImageThumbnailable('gif')).toBe(true);
expect(isImageThumbnailable('tif')).toBe(true);
expect(isImageThumbnailable('tiff')).toBe(true);
});

it('should return true regardless of case', () => {
expect(isImageThumbnailable('JPG')).toBe(true);
expect(isImageThumbnailable('PNG')).toBe(true);
expect(isImageThumbnailable('GIF')).toBe(true);
expect(isImageThumbnailable('Jpeg')).toBe(true);
});

it('should handle whitespace correctly', () => {
expect(isImageThumbnailable(' jpg ')).toBe(true);
expect(isImageThumbnailable(' png ')).toBe(true);
expect(isImageThumbnailable('\twebp\n')).toBe(true);
});

it('should return false for non-thumbnailable image formats', () => {
expect(isImageThumbnailable('bmp')).toBe(false);
expect(isImageThumbnailable('heic')).toBe(false);
expect(isImageThumbnailable('raw')).toBe(false);
expect(isImageThumbnailable('cr2')).toBe(false);
expect(isImageThumbnailable('nef')).toBe(false);
expect(isImageThumbnailable('eps')).toBe(false);
});

it('should return false for non-image extensions', () => {
expect(isImageThumbnailable('pdf')).toBe(false);
expect(isImageThumbnailable('doc')).toBe(false);
expect(isImageThumbnailable('txt')).toBe(false);
expect(isImageThumbnailable('mp4')).toBe(false);
expect(isImageThumbnailable('mp3')).toBe(false);
});

it('should return false for empty strings', () => {
expect(isImageThumbnailable('')).toBe(false);
expect(isImageThumbnailable(' ')).toBe(false);
expect(isImageThumbnailable('\t\n')).toBe(false);
});

expect(result.bufferStream).toBeUndefined();
expect(result.fileStream).toBeDefined();
expect(result.fileStream).toBeInstanceOf(Readable);
it('should return false for invalid input', () => {
expect(isImageThumbnailable('jpgg')).toBe(false);
expect(isImageThumbnailable('unknown')).toBe(false);
});
});
});
Loading