mime-bytes 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +398 -0
- package/esm/file-type-detector.js +374 -0
- package/esm/file-types-registry.js +1208 -0
- package/esm/index.js +11 -0
- package/esm/peak.js +90 -0
- package/esm/utils/extensions.js +114 -0
- package/esm/utils/magic-bytes.js +61 -0
- package/esm/utils/mime-types.js +90 -0
- package/file-type-detector.d.ts +101 -0
- package/file-type-detector.js +381 -0
- package/file-types-registry.d.ts +28 -0
- package/file-types-registry.js +1217 -0
- package/index.d.ts +6 -0
- package/index.js +42 -0
- package/package.json +38 -0
- package/peak.d.ts +20 -0
- package/peak.js +95 -0
- package/utils/extensions.d.ts +9 -0
- package/utils/extensions.js +125 -0
- package/utils/magic-bytes.d.ts +9 -0
- package/utils/magic-bytes.js +69 -0
- package/utils/mime-types.d.ts +14 -0
- package/utils/mime-types.js +98 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { FileTypeDetector, FileTypeDetectorOptions, defaultDetector, detectFromStream, detectFromBuffer, detectFromExtension } from './file-type-detector';
|
|
2
|
+
export { FileTypeDefinition, DetectionResult, ContentTypeMapping, FILE_TYPES, CONTENT_TYPE_MAPPINGS, getFileTypeByMagicBytes, getFileTypeByExtension, getFileTypesByCategory, getContentTypeByExtension, detectCharset } from './file-types-registry';
|
|
3
|
+
export { peek, BufferPeekStream, PeekStreamOptions, PeekCallback, PeekPromise } from './peak';
|
|
4
|
+
export * from './utils/magic-bytes';
|
|
5
|
+
export * from './utils/mime-types';
|
|
6
|
+
export * from './utils/extensions';
|
package/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Main export file for mime-bytes package
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
15
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.BufferPeekStream = exports.peek = exports.detectCharset = exports.getContentTypeByExtension = exports.getFileTypesByCategory = exports.getFileTypeByExtension = exports.getFileTypeByMagicBytes = exports.CONTENT_TYPE_MAPPINGS = exports.FILE_TYPES = exports.detectFromExtension = exports.detectFromBuffer = exports.detectFromStream = exports.defaultDetector = exports.FileTypeDetector = void 0;
|
|
19
|
+
// Export the main detector class and convenience functions
|
|
20
|
+
var file_type_detector_1 = require("./file-type-detector");
|
|
21
|
+
Object.defineProperty(exports, "FileTypeDetector", { enumerable: true, get: function () { return file_type_detector_1.FileTypeDetector; } });
|
|
22
|
+
Object.defineProperty(exports, "defaultDetector", { enumerable: true, get: function () { return file_type_detector_1.defaultDetector; } });
|
|
23
|
+
Object.defineProperty(exports, "detectFromStream", { enumerable: true, get: function () { return file_type_detector_1.detectFromStream; } });
|
|
24
|
+
Object.defineProperty(exports, "detectFromBuffer", { enumerable: true, get: function () { return file_type_detector_1.detectFromBuffer; } });
|
|
25
|
+
Object.defineProperty(exports, "detectFromExtension", { enumerable: true, get: function () { return file_type_detector_1.detectFromExtension; } });
|
|
26
|
+
// Export registry types and functions
|
|
27
|
+
var file_types_registry_1 = require("./file-types-registry");
|
|
28
|
+
Object.defineProperty(exports, "FILE_TYPES", { enumerable: true, get: function () { return file_types_registry_1.FILE_TYPES; } });
|
|
29
|
+
Object.defineProperty(exports, "CONTENT_TYPE_MAPPINGS", { enumerable: true, get: function () { return file_types_registry_1.CONTENT_TYPE_MAPPINGS; } });
|
|
30
|
+
Object.defineProperty(exports, "getFileTypeByMagicBytes", { enumerable: true, get: function () { return file_types_registry_1.getFileTypeByMagicBytes; } });
|
|
31
|
+
Object.defineProperty(exports, "getFileTypeByExtension", { enumerable: true, get: function () { return file_types_registry_1.getFileTypeByExtension; } });
|
|
32
|
+
Object.defineProperty(exports, "getFileTypesByCategory", { enumerable: true, get: function () { return file_types_registry_1.getFileTypesByCategory; } });
|
|
33
|
+
Object.defineProperty(exports, "getContentTypeByExtension", { enumerable: true, get: function () { return file_types_registry_1.getContentTypeByExtension; } });
|
|
34
|
+
Object.defineProperty(exports, "detectCharset", { enumerable: true, get: function () { return file_types_registry_1.detectCharset; } });
|
|
35
|
+
// Export peek stream functionality
|
|
36
|
+
var peak_1 = require("./peak");
|
|
37
|
+
Object.defineProperty(exports, "peek", { enumerable: true, get: function () { return peak_1.peek; } });
|
|
38
|
+
Object.defineProperty(exports, "BufferPeekStream", { enumerable: true, get: function () { return peak_1.BufferPeekStream; } });
|
|
39
|
+
// Export utility functions
|
|
40
|
+
__exportStar(require("./utils/magic-bytes"), exports);
|
|
41
|
+
__exportStar(require("./utils/mime-types"), exports);
|
|
42
|
+
__exportStar(require("./utils/extensions"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mime-bytes",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
5
|
+
"description": "mime-bytes",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"module": "esm/index.js",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"homepage": "https://github.com/launchql/launchql",
|
|
10
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"directory": "dist"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/launchql/launchql"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/launchql/launchql/issues"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"copy": "copyfiles -f ../../LICENSE README.md package.json dist",
|
|
24
|
+
"clean": "rimraf dist/**",
|
|
25
|
+
"prepare": "npm run build",
|
|
26
|
+
"build": "npm run clean; tsc; tsc -p tsconfig.esm.json; npm run copy",
|
|
27
|
+
"build:dev": "npm run clean; tsc --declarationMap; tsc -p tsconfig.esm.json; npm run copy",
|
|
28
|
+
"lint": "eslint . --fix",
|
|
29
|
+
"test": "jest",
|
|
30
|
+
"test:watch": "jest --watch"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [],
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/glob": "^8.1.0",
|
|
35
|
+
"glob": "^11.0.2"
|
|
36
|
+
},
|
|
37
|
+
"gitHead": "52bbc49baf1ed0e4a1b1f349bf1817b3f71f72b0"
|
|
38
|
+
}
|
package/peak.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Transform, TransformOptions, Readable } from 'stream';
|
|
2
|
+
export interface PeekStreamOptions extends TransformOptions {
|
|
3
|
+
peekBytes: number;
|
|
4
|
+
}
|
|
5
|
+
export declare class BufferPeekStream extends Transform {
|
|
6
|
+
private peekBytes;
|
|
7
|
+
private buffer;
|
|
8
|
+
private bufferLength;
|
|
9
|
+
private peeked;
|
|
10
|
+
constructor(options: PeekStreamOptions);
|
|
11
|
+
_transform(chunk: any, encoding: BufferEncoding, callback: Function): void;
|
|
12
|
+
_flush(callback: Function): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function peek(source: Readable, bytes: number, callback?: (err: Error | null, buffer: Buffer, dest: BufferPeekStream) => void): BufferPeekStream;
|
|
15
|
+
export declare function peek(source: Readable, callback: (err: Error | null, buffer: Buffer, dest: BufferPeekStream) => void): BufferPeekStream;
|
|
16
|
+
export declare namespace peek {
|
|
17
|
+
var promise: (source: Readable, bytes?: number) => Promise<[Buffer, BufferPeekStream]>;
|
|
18
|
+
}
|
|
19
|
+
export type PeekCallback = (err: Error | null, buffer: Buffer, dest: BufferPeekStream) => void;
|
|
20
|
+
export type PeekPromise = (source: Readable, bytes?: number) => Promise<[Buffer, BufferPeekStream]>;
|
package/peak.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TypeScript implementation of peek stream for efficient file type detection
|
|
3
|
+
// Based on reference-packages/buffer-peak/peak.js
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.BufferPeekStream = void 0;
|
|
6
|
+
exports.peek = peek;
|
|
7
|
+
const stream_1 = require("stream");
|
|
8
|
+
class BufferPeekStream extends stream_1.Transform {
|
|
9
|
+
peekBytes;
|
|
10
|
+
buffer;
|
|
11
|
+
bufferLength;
|
|
12
|
+
peeked;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
super(options);
|
|
15
|
+
this.peekBytes = options.peekBytes || 16;
|
|
16
|
+
this.buffer = Buffer.alloc(0);
|
|
17
|
+
this.bufferLength = 0;
|
|
18
|
+
this.peeked = false;
|
|
19
|
+
}
|
|
20
|
+
_transform(chunk, encoding, callback) {
|
|
21
|
+
if (this.peeked) {
|
|
22
|
+
// After peeking, just pass through
|
|
23
|
+
this.push(chunk);
|
|
24
|
+
callback();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Accumulate data until we have enough to peek
|
|
28
|
+
const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
|
|
29
|
+
this.buffer = Buffer.concat([this.buffer, chunkBuffer]);
|
|
30
|
+
this.bufferLength += chunkBuffer.length;
|
|
31
|
+
if (this.bufferLength >= this.peekBytes) {
|
|
32
|
+
// We have enough data to peek
|
|
33
|
+
this.peeked = true;
|
|
34
|
+
// Emit the peek event with the requested bytes
|
|
35
|
+
const peekBuffer = this.buffer.slice(0, this.peekBytes);
|
|
36
|
+
this.emit('peek', peekBuffer);
|
|
37
|
+
// Push all accumulated data
|
|
38
|
+
this.push(this.buffer);
|
|
39
|
+
this.buffer = Buffer.alloc(0);
|
|
40
|
+
this.bufferLength = 0;
|
|
41
|
+
callback();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Need more data
|
|
45
|
+
callback();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
_flush(callback) {
|
|
49
|
+
if (!this.peeked && this.bufferLength > 0) {
|
|
50
|
+
// Not enough data was received, emit what we have
|
|
51
|
+
this.peeked = true;
|
|
52
|
+
this.emit('peek', this.buffer);
|
|
53
|
+
this.push(this.buffer);
|
|
54
|
+
}
|
|
55
|
+
callback();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.BufferPeekStream = BufferPeekStream;
|
|
59
|
+
function peek(source, bytesOrCallback, callback) {
|
|
60
|
+
let bytes;
|
|
61
|
+
let cb;
|
|
62
|
+
if (typeof bytesOrCallback === 'function') {
|
|
63
|
+
bytes = 16; // Default peek bytes
|
|
64
|
+
cb = bytesOrCallback;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
bytes = bytesOrCallback;
|
|
68
|
+
cb = callback;
|
|
69
|
+
}
|
|
70
|
+
const dest = new BufferPeekStream({ peekBytes: bytes });
|
|
71
|
+
if (cb) {
|
|
72
|
+
dest.once('peek', (buffer) => {
|
|
73
|
+
cb(null, buffer, dest);
|
|
74
|
+
});
|
|
75
|
+
dest.once('error', (err) => {
|
|
76
|
+
cb(err, Buffer.alloc(0), dest);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return source.pipe(dest);
|
|
80
|
+
}
|
|
81
|
+
// Promise-based version
|
|
82
|
+
peek.promise = function (source, bytes = 16) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
const dest = peek(source, bytes, (err, buffer, stream) => {
|
|
85
|
+
if (err) {
|
|
86
|
+
reject(err);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
resolve([buffer, stream]);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// Handle source errors
|
|
93
|
+
source.once('error', reject);
|
|
94
|
+
});
|
|
95
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function normalizeExtension(extension: string): string;
|
|
2
|
+
export declare function getExtension(filename: string): string;
|
|
3
|
+
export declare function isCompressedExtension(extension: string): boolean;
|
|
4
|
+
export declare function isDocumentExtension(extension: string): boolean;
|
|
5
|
+
export declare function isMediaExtension(extension: string): boolean;
|
|
6
|
+
export declare function isImageExtension(extension: string): boolean;
|
|
7
|
+
export declare function isExecutableExtension(extension: string): boolean;
|
|
8
|
+
export declare function getCategoryFromExtension(extension: string): string;
|
|
9
|
+
export declare function getDoubleExtension(filename: string): string | null;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Extension utility functions
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.normalizeExtension = normalizeExtension;
|
|
5
|
+
exports.getExtension = getExtension;
|
|
6
|
+
exports.isCompressedExtension = isCompressedExtension;
|
|
7
|
+
exports.isDocumentExtension = isDocumentExtension;
|
|
8
|
+
exports.isMediaExtension = isMediaExtension;
|
|
9
|
+
exports.isImageExtension = isImageExtension;
|
|
10
|
+
exports.isExecutableExtension = isExecutableExtension;
|
|
11
|
+
exports.getCategoryFromExtension = getCategoryFromExtension;
|
|
12
|
+
exports.getDoubleExtension = getDoubleExtension;
|
|
13
|
+
// Normalize file extension
|
|
14
|
+
function normalizeExtension(extension) {
|
|
15
|
+
return extension.toLowerCase().replace(/^\./, '');
|
|
16
|
+
}
|
|
17
|
+
// Extract extension from filename
|
|
18
|
+
function getExtension(filename) {
|
|
19
|
+
const lastDot = filename.lastIndexOf('.');
|
|
20
|
+
if (lastDot === -1 || lastDot === filename.length - 1) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
return normalizeExtension(filename.substring(lastDot + 1));
|
|
24
|
+
}
|
|
25
|
+
// Check if extension is commonly associated with compressed files
|
|
26
|
+
function isCompressedExtension(extension) {
|
|
27
|
+
const compressed = [
|
|
28
|
+
'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'lz', 'lzma', 'z',
|
|
29
|
+
'tgz', 'tbz', 'tbz2', 'txz', 'tlz', 'arc', 'arj', 'cab', 'dmg',
|
|
30
|
+
'iso', 'lha', 'lzh', 'pkg', 'deb', 'rpm', 'msi', 'jar', 'war',
|
|
31
|
+
'ear', 'sar', 'aar', 'apk', 'ipa', 'xpi', 'egg', 'whl', 'gem'
|
|
32
|
+
];
|
|
33
|
+
return compressed.includes(normalizeExtension(extension));
|
|
34
|
+
}
|
|
35
|
+
// Check if extension is commonly associated with document files
|
|
36
|
+
function isDocumentExtension(extension) {
|
|
37
|
+
const documents = [
|
|
38
|
+
'pdf', 'doc', 'docx', 'odt', 'rtf', 'tex', 'wpd', 'txt', 'md',
|
|
39
|
+
'xls', 'xlsx', 'ods', 'csv', 'ppt', 'pptx', 'odp', 'epub', 'mobi',
|
|
40
|
+
'azw', 'azw3', 'fb2', 'lit', 'pdb', 'ps', 'eps', 'indd', 'xps'
|
|
41
|
+
];
|
|
42
|
+
return documents.includes(normalizeExtension(extension));
|
|
43
|
+
}
|
|
44
|
+
// Check if extension is commonly associated with media files
|
|
45
|
+
function isMediaExtension(extension) {
|
|
46
|
+
const media = [
|
|
47
|
+
// Video
|
|
48
|
+
'mp4', 'avi', 'mkv', 'mov', 'wmv', 'flv', 'webm', 'vob', 'ogv',
|
|
49
|
+
'ogg', 'm4v', '3gp', '3g2', 'mpg', 'mpeg', 'mp2', 'mpe', 'mpv',
|
|
50
|
+
'm2v', 'svi', 'mxf', 'roq', 'nsv', 'f4v', 'f4p', 'f4a', 'f4b',
|
|
51
|
+
// Audio
|
|
52
|
+
'mp3', 'wav', 'flac', 'aac', 'ogg', 'oga', 'wma', 'm4a', 'opus',
|
|
53
|
+
'ape', 'wv', 'amr', 'ac3', 'dts', 'spx', 'mid', 'midi', 'kar',
|
|
54
|
+
'aiff', 'aif', 'aifc', 'au', 'snd', 'voc', 'ra', 'rm', 'ram'
|
|
55
|
+
];
|
|
56
|
+
return media.includes(normalizeExtension(extension));
|
|
57
|
+
}
|
|
58
|
+
// Check if extension is commonly associated with image files
|
|
59
|
+
function isImageExtension(extension) {
|
|
60
|
+
const images = [
|
|
61
|
+
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'tif',
|
|
62
|
+
'tiff', 'psd', 'raw', 'heif', 'heic', 'indd', 'ai', 'eps', 'ps',
|
|
63
|
+
'xcf', 'cdr', 'cmx', 'dib', 'jxr', 'hdp', 'wdp', 'cur', 'icns',
|
|
64
|
+
'pbm', 'pgm', 'ppm', 'pnm', 'pcx', 'dcx', 'dds', 'dng', 'cr2',
|
|
65
|
+
'cr3', 'crw', 'nef', 'nrw', 'orf', 'raf', 'rw2', 'rwl', 'srw',
|
|
66
|
+
'arw', 'srf', 'sr2', 'bay', 'cap', 'iiq', 'eip', 'dcs', 'dcr',
|
|
67
|
+
'drf', 'k25', 'kdc', 'mdc', 'mef', 'mos', 'mrw', 'pef', 'ptx',
|
|
68
|
+
'pxn', 'r3d', 'x3f', 'qoi'
|
|
69
|
+
];
|
|
70
|
+
return images.includes(normalizeExtension(extension));
|
|
71
|
+
}
|
|
72
|
+
// Check if extension is commonly associated with executable files
|
|
73
|
+
function isExecutableExtension(extension) {
|
|
74
|
+
const executables = [
|
|
75
|
+
'exe', 'dll', 'so', 'dylib', 'app', 'deb', 'rpm', 'dmg', 'pkg',
|
|
76
|
+
'msi', 'bat', 'cmd', 'sh', 'ps1', 'vbs', 'js', 'jar', 'class',
|
|
77
|
+
'pyc', 'pyo', 'elf', 'o', 'out', 'bin', 'run', 'com', 'scr',
|
|
78
|
+
'cpl', 'ocx', 'sys', 'drv', 'efi', 'mui', 'ax', 'ime', 'rs',
|
|
79
|
+
'tsp', 'fon', 'wasm', 'ko', 'mod', 'prx', 'puff', 'axf', 'dex'
|
|
80
|
+
];
|
|
81
|
+
return executables.includes(normalizeExtension(extension));
|
|
82
|
+
}
|
|
83
|
+
// Get category from extension
|
|
84
|
+
function getCategoryFromExtension(extension) {
|
|
85
|
+
const ext = normalizeExtension(extension);
|
|
86
|
+
if (isImageExtension(ext))
|
|
87
|
+
return 'image';
|
|
88
|
+
if (isMediaExtension(ext))
|
|
89
|
+
return 'media';
|
|
90
|
+
if (isDocumentExtension(ext))
|
|
91
|
+
return 'document';
|
|
92
|
+
if (isCompressedExtension(ext))
|
|
93
|
+
return 'archive';
|
|
94
|
+
if (isExecutableExtension(ext))
|
|
95
|
+
return 'executable';
|
|
96
|
+
// Check for specific categories
|
|
97
|
+
const categories = {
|
|
98
|
+
font: ['ttf', 'otf', 'woff', 'woff2', 'eot', 'fon', 'fnt'],
|
|
99
|
+
database: ['db', 'db3', 'sqlite', 'sqlite3', 'mdb', 'accdb', 'dbf'],
|
|
100
|
+
code: ['js', 'ts', 'jsx', 'tsx', 'py', 'java', 'c', 'cpp', 'h', 'hpp', 'cs', 'php', 'rb', 'go', 'rs', 'swift', 'kt', 'scala', 'r', 'lua', 'pl', 'sh', 'bash', 'zsh', 'fish', 'ps1', 'psm1', 'psd1', 'bat', 'cmd'],
|
|
101
|
+
config: ['json', 'xml', 'yaml', 'yml', 'toml', 'ini', 'cfg', 'conf', 'properties', 'env'],
|
|
102
|
+
text: ['txt', 'md', 'markdown', 'rst', 'asciidoc', 'adoc', 'org', 'tex', 'log']
|
|
103
|
+
};
|
|
104
|
+
for (const [category, extensions] of Object.entries(categories)) {
|
|
105
|
+
if (extensions.includes(ext)) {
|
|
106
|
+
return category;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return 'other';
|
|
110
|
+
}
|
|
111
|
+
// Common double extensions (e.g., .tar.gz)
|
|
112
|
+
const DOUBLE_EXTENSIONS = [
|
|
113
|
+
'tar.gz', 'tar.bz2', 'tar.xz', 'tar.lz', 'tar.lzma', 'tar.Z',
|
|
114
|
+
'tar.br', 'tar.zst', 'user.js', 'min.js', 'min.css', 'd.ts'
|
|
115
|
+
];
|
|
116
|
+
// Get double extension if applicable
|
|
117
|
+
function getDoubleExtension(filename) {
|
|
118
|
+
const lower = filename.toLowerCase();
|
|
119
|
+
for (const doubleExt of DOUBLE_EXTENSIONS) {
|
|
120
|
+
if (lower.endsWith('.' + doubleExt)) {
|
|
121
|
+
return doubleExt;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function hexToBuffer(hexArray: string[]): Buffer;
|
|
2
|
+
export declare function bufferToHex(buffer: Buffer): string;
|
|
3
|
+
export declare function compareBytes(buffer: Buffer, pattern: string[], offset?: number): boolean;
|
|
4
|
+
export declare function findMagicBytes(buffer: Buffer, patterns: Array<{
|
|
5
|
+
pattern: string[];
|
|
6
|
+
offset?: number;
|
|
7
|
+
}>): number;
|
|
8
|
+
export declare function extractBytes(buffer: Buffer, offset: number, length: number): Buffer;
|
|
9
|
+
export declare function isTextLike(buffer: Buffer): boolean;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Magic bytes utility functions
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.hexToBuffer = hexToBuffer;
|
|
5
|
+
exports.bufferToHex = bufferToHex;
|
|
6
|
+
exports.compareBytes = compareBytes;
|
|
7
|
+
exports.findMagicBytes = findMagicBytes;
|
|
8
|
+
exports.extractBytes = extractBytes;
|
|
9
|
+
exports.isTextLike = isTextLike;
|
|
10
|
+
function hexToBuffer(hexArray) {
|
|
11
|
+
const bytes = hexArray.map(hex => {
|
|
12
|
+
if (hex === '?')
|
|
13
|
+
return 0; // Wildcard placeholder
|
|
14
|
+
return parseInt(hex.replace(/0x/i, ''), 16);
|
|
15
|
+
});
|
|
16
|
+
return Buffer.from(bytes);
|
|
17
|
+
}
|
|
18
|
+
function bufferToHex(buffer) {
|
|
19
|
+
return buffer.toString('hex');
|
|
20
|
+
}
|
|
21
|
+
function compareBytes(buffer, pattern, offset = 0) {
|
|
22
|
+
// Empty patterns should not match
|
|
23
|
+
if (!pattern || pattern.length === 0) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (offset + pattern.length > buffer.length) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
30
|
+
if (pattern[i] === '?')
|
|
31
|
+
continue; // Skip wildcards
|
|
32
|
+
const expectedByte = parseInt(pattern[i].replace(/0x/i, ''), 16);
|
|
33
|
+
const actualByte = buffer[offset + i];
|
|
34
|
+
if (expectedByte !== actualByte) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function findMagicBytes(buffer, patterns) {
|
|
41
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
42
|
+
const { pattern, offset = 0 } = patterns[i];
|
|
43
|
+
if (compareBytes(buffer, pattern, offset)) {
|
|
44
|
+
return i;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return -1;
|
|
48
|
+
}
|
|
49
|
+
// Extract a specific number of bytes from buffer at offset
|
|
50
|
+
function extractBytes(buffer, offset, length) {
|
|
51
|
+
if (offset + length > buffer.length) {
|
|
52
|
+
return buffer.slice(offset);
|
|
53
|
+
}
|
|
54
|
+
return buffer.slice(offset, offset + length);
|
|
55
|
+
}
|
|
56
|
+
// Check if buffer contains text-like content
|
|
57
|
+
function isTextLike(buffer) {
|
|
58
|
+
const sampleSize = Math.min(buffer.length, 512);
|
|
59
|
+
let printableCount = 0;
|
|
60
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
61
|
+
const byte = buffer[i];
|
|
62
|
+
// Check for printable ASCII characters, tabs, newlines, carriage returns
|
|
63
|
+
if ((byte >= 32 && byte <= 126) || byte === 9 || byte === 10 || byte === 13) {
|
|
64
|
+
printableCount++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// If more than 85% are printable characters, likely text
|
|
68
|
+
return (printableCount / sampleSize) > 0.85;
|
|
69
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const MIME_CATEGORIES: {
|
|
2
|
+
readonly IMAGE: "image";
|
|
3
|
+
readonly VIDEO: "video";
|
|
4
|
+
readonly AUDIO: "audio";
|
|
5
|
+
readonly APPLICATION: "application";
|
|
6
|
+
readonly TEXT: "text";
|
|
7
|
+
readonly FONT: "font";
|
|
8
|
+
};
|
|
9
|
+
export type MimeCategory = typeof MIME_CATEGORIES[keyof typeof MIME_CATEGORIES];
|
|
10
|
+
export declare function getMimeCategory(mimeType: string): MimeCategory | null;
|
|
11
|
+
export declare function isBinaryMimeType(mimeType: string): boolean;
|
|
12
|
+
export declare function normalizeMimeType(mimeType: string): string;
|
|
13
|
+
export declare function getFileCategoryFromMime(mimeType: string): string;
|
|
14
|
+
export declare function resolveMimeAlias(mimeType: string): string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// MIME type utility functions
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.MIME_CATEGORIES = void 0;
|
|
5
|
+
exports.getMimeCategory = getMimeCategory;
|
|
6
|
+
exports.isBinaryMimeType = isBinaryMimeType;
|
|
7
|
+
exports.normalizeMimeType = normalizeMimeType;
|
|
8
|
+
exports.getFileCategoryFromMime = getFileCategoryFromMime;
|
|
9
|
+
exports.resolveMimeAlias = resolveMimeAlias;
|
|
10
|
+
// Common MIME type categories
|
|
11
|
+
exports.MIME_CATEGORIES = {
|
|
12
|
+
IMAGE: 'image',
|
|
13
|
+
VIDEO: 'video',
|
|
14
|
+
AUDIO: 'audio',
|
|
15
|
+
APPLICATION: 'application',
|
|
16
|
+
TEXT: 'text',
|
|
17
|
+
FONT: 'font'
|
|
18
|
+
};
|
|
19
|
+
// Extract category from MIME type
|
|
20
|
+
function getMimeCategory(mimeType) {
|
|
21
|
+
const category = mimeType.split('/')[0];
|
|
22
|
+
if (Object.values(exports.MIME_CATEGORIES).includes(category)) {
|
|
23
|
+
return category;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
// Check if MIME type is binary
|
|
28
|
+
function isBinaryMimeType(mimeType) {
|
|
29
|
+
const textTypes = [
|
|
30
|
+
'text/',
|
|
31
|
+
'application/json',
|
|
32
|
+
'application/xml',
|
|
33
|
+
'application/javascript',
|
|
34
|
+
'application/typescript',
|
|
35
|
+
'application/x-sh',
|
|
36
|
+
'application/x-csh',
|
|
37
|
+
'application/x-python',
|
|
38
|
+
'application/x-ruby',
|
|
39
|
+
'application/x-perl'
|
|
40
|
+
];
|
|
41
|
+
return !textTypes.some(type => mimeType.startsWith(type));
|
|
42
|
+
}
|
|
43
|
+
// Normalize MIME type (remove parameters)
|
|
44
|
+
function normalizeMimeType(mimeType) {
|
|
45
|
+
return mimeType.split(';')[0].trim().toLowerCase();
|
|
46
|
+
}
|
|
47
|
+
// Get file category from MIME type
|
|
48
|
+
function getFileCategoryFromMime(mimeType) {
|
|
49
|
+
const normalized = normalizeMimeType(mimeType);
|
|
50
|
+
if (normalized.startsWith('image/'))
|
|
51
|
+
return 'image';
|
|
52
|
+
if (normalized.startsWith('video/'))
|
|
53
|
+
return 'video';
|
|
54
|
+
if (normalized.startsWith('audio/'))
|
|
55
|
+
return 'audio';
|
|
56
|
+
if (normalized.startsWith('font/'))
|
|
57
|
+
return 'font';
|
|
58
|
+
if (normalized.startsWith('text/'))
|
|
59
|
+
return 'text';
|
|
60
|
+
// Special cases for application types
|
|
61
|
+
if (normalized.includes('zip') || normalized.includes('compressed') || normalized.includes('archive')) {
|
|
62
|
+
return 'archive';
|
|
63
|
+
}
|
|
64
|
+
if (normalized.includes('pdf') || normalized.includes('document') || normalized.includes('msword') || normalized.includes('officedocument')) {
|
|
65
|
+
return 'document';
|
|
66
|
+
}
|
|
67
|
+
if (normalized.includes('executable') || normalized.includes('x-msdownload') || normalized.includes('x-elf') || normalized.includes('x-mach')) {
|
|
68
|
+
return 'executable';
|
|
69
|
+
}
|
|
70
|
+
if (normalized.includes('sqlite') || normalized.includes('database')) {
|
|
71
|
+
return 'database';
|
|
72
|
+
}
|
|
73
|
+
return 'other';
|
|
74
|
+
}
|
|
75
|
+
// Common MIME type aliases
|
|
76
|
+
const MIME_ALIASES = {
|
|
77
|
+
'application/x-javascript': 'application/javascript',
|
|
78
|
+
'text/javascript': 'application/javascript',
|
|
79
|
+
'application/x-mpegURL': 'application/vnd.apple.mpegurl',
|
|
80
|
+
'audio/mp3': 'audio/mpeg',
|
|
81
|
+
'audio/x-mp3': 'audio/mpeg',
|
|
82
|
+
'audio/x-mpeg': 'audio/mpeg',
|
|
83
|
+
'video/x-m4v': 'video/mp4',
|
|
84
|
+
'audio/x-m4a': 'audio/mp4',
|
|
85
|
+
'image/jpg': 'image/jpeg',
|
|
86
|
+
'image/x-png': 'image/png',
|
|
87
|
+
'image/x-icon': 'image/vnd.microsoft.icon',
|
|
88
|
+
'text/xml': 'application/xml',
|
|
89
|
+
'application/x-compressed': 'application/x-compress',
|
|
90
|
+
'application/x-gzip': 'application/gzip',
|
|
91
|
+
'application/x-bzip': 'application/x-bzip2',
|
|
92
|
+
'application/x-tar': 'application/tar'
|
|
93
|
+
};
|
|
94
|
+
// Resolve MIME type aliases
|
|
95
|
+
function resolveMimeAlias(mimeType) {
|
|
96
|
+
const normalized = normalizeMimeType(mimeType);
|
|
97
|
+
return MIME_ALIASES[normalized] || normalized;
|
|
98
|
+
}
|