@rezwanul7/storage 0.0.0-stage → 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,464 @@
1
+ // src/shared/fileSize.ts
2
+ var SIZE_UNITS = {
3
+ b: 1,
4
+ kb: 1024,
5
+ mb: 1024 ** 2,
6
+ gb: 1024 ** 3,
7
+ tb: 1024 ** 4
8
+ };
9
+ function parseFileSize(input) {
10
+ if (input == null) return void 0;
11
+ if (typeof input === "number" && Number.isFinite(input)) return input;
12
+ if (typeof input !== "string") return void 0;
13
+ const match = input.trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb|tb)?$/);
14
+ if (!match) return void 0;
15
+ return parseFloat(match[1]) * (SIZE_UNITS[match[2] || "b"] ?? 1);
16
+ }
17
+ function formatFileSize(bytes) {
18
+ if (bytes === 0) return "0 B";
19
+ const units = ["B", "KB", "MB", "GB", "TB"];
20
+ const index = Math.floor(Math.log(bytes) / Math.log(1024));
21
+ return `${(bytes / Math.pow(1024, index)).toFixed(1)} ${units[index]}`;
22
+ }
23
+
24
+ // src/shared/mimeTypes.ts
25
+ var DANGEROUS_EXTENSIONS = /* @__PURE__ */ new Set([
26
+ "sh",
27
+ "bash",
28
+ "zsh",
29
+ "exe",
30
+ "dll",
31
+ "com",
32
+ "scr",
33
+ "bat",
34
+ "cmd",
35
+ "ps1",
36
+ "jar",
37
+ "class"
38
+ ]);
39
+ var DANGEROUS_MIME = /* @__PURE__ */ new Set([
40
+ "application/x-sh",
41
+ "application/x-shellscript",
42
+ "text/x-shellscript",
43
+ "text/x-sh",
44
+ "application/x-dosexec",
45
+ "application/java-archive"
46
+ ]);
47
+ var MIME_BY_EXT = {
48
+ // Image
49
+ png: "image/png",
50
+ jpg: "image/jpeg",
51
+ jpeg: "image/jpeg",
52
+ gif: "image/gif",
53
+ bmp: "image/bmp",
54
+ webp: "image/webp",
55
+ tiff: "image/tiff",
56
+ tif: "image/tiff",
57
+ svg: "image/svg+xml",
58
+ ico: "image/vnd.microsoft.icon",
59
+ avif: "image/avif",
60
+ heic: "image/heic",
61
+ // Audio
62
+ mp3: "audio/mpeg",
63
+ wav: "audio/wav",
64
+ ogg: "audio/ogg",
65
+ flac: "audio/x-flac",
66
+ m4a: "audio/mp4",
67
+ // Video
68
+ mp4: "video/mp4",
69
+ mkv: "video/x-matroska",
70
+ mov: "video/quicktime",
71
+ avi: "video/x-msvideo",
72
+ webm: "video/webm",
73
+ // Documents
74
+ pdf: "application/pdf",
75
+ xls: "application/vnd.ms-excel",
76
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
77
+ ods: "application/vnd.oasis.opendocument.spreadsheet",
78
+ ppt: "application/vnd.ms-powerpoint",
79
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
80
+ odp: "application/vnd.oasis.opendocument.presentation",
81
+ doc: "application/msword",
82
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
83
+ odt: "application/vnd.oasis.opendocument.text",
84
+ rtf: "application/rtf",
85
+ // Datasets (JSON Lines has no registered MIME, so it is served as JSON)
86
+ csv: "text/csv",
87
+ tsv: "text/tab-separated-values",
88
+ json: "application/json",
89
+ jsonl: "application/json",
90
+ ndjson: "application/json",
91
+ // Archives
92
+ zip: "application/zip",
93
+ rar: "application/x-rar-compressed",
94
+ "7z": "application/x-7z-compressed",
95
+ tar: "application/x-tar",
96
+ gz: "application/gzip",
97
+ // Code
98
+ js: "text/javascript",
99
+ jsx: "text/jsx",
100
+ ts: "text/typescript",
101
+ tsx: "text/tsx",
102
+ py: "text/x-python",
103
+ rb: "text/x-ruby",
104
+ php: "application/x-httpd-php",
105
+ java: "text/x-java-source",
106
+ kt: "text/x-kotlin",
107
+ c: "text/x-c",
108
+ cpp: "text/x-c",
109
+ h: "text/x-c",
110
+ hpp: "text/x-c",
111
+ cs: "text/x-csharp",
112
+ go: "text/x-go",
113
+ rs: "text/x-rust",
114
+ html: "text/html",
115
+ htm: "text/html",
116
+ css: "text/css",
117
+ scss: "text/x-scss",
118
+ yaml: "text/yaml",
119
+ yml: "text/yaml",
120
+ sql: "application/sql",
121
+ xml: "application/xml",
122
+ // Text
123
+ txt: "text/plain",
124
+ md: "text/markdown",
125
+ markdown: "text/markdown",
126
+ log: "text/plain",
127
+ ini: "text/plain",
128
+ cfg: "text/plain",
129
+ conf: "text/plain",
130
+ env: "text/plain",
131
+ // Executables (blocked by DANGEROUS_EXTENSIONS, mapped so they report a real MIME)
132
+ sh: "application/x-sh",
133
+ bash: "application/x-sh",
134
+ zsh: "application/x-sh",
135
+ exe: "application/x-msdos-program",
136
+ dll: "application/x-msdownload",
137
+ com: "application/x-msdownload",
138
+ bat: "application/x-msdownload",
139
+ jar: "application/java-archive",
140
+ class: "application/java-vm"
141
+ };
142
+ var EXT_BY_MIME = {};
143
+ for (const [ext, mime] of Object.entries(MIME_BY_EXT)) {
144
+ EXT_BY_MIME[mime] ??= ext;
145
+ }
146
+ function mimeFromExtension(ext) {
147
+ if (!ext) return void 0;
148
+ return MIME_BY_EXT[ext.toLowerCase()];
149
+ }
150
+ function extensionFromMime(mime) {
151
+ if (!mime) return void 0;
152
+ return EXT_BY_MIME[mime.toLowerCase()];
153
+ }
154
+
155
+ // src/shared/fileExtension.ts
156
+ function getFileExtension(name) {
157
+ const dot = name.lastIndexOf(".");
158
+ if (dot === -1) return void 0;
159
+ return name.slice(dot + 1).toLowerCase() || void 0;
160
+ }
161
+
162
+ // src/shared/detectFileTypeFromName.ts
163
+ function detectFileTypeFromName(input) {
164
+ const name = input.name;
165
+ const declaredMime = input.type || void 0;
166
+ const declaredExt = getFileExtension(name);
167
+ const finalExt = declaredExt;
168
+ const finalMime = resolveMime(declaredExt, declaredMime);
169
+ if (declaredExt && DANGEROUS_EXTENSIONS.has(declaredExt)) {
170
+ return {
171
+ ok: false,
172
+ reason: `Extension .${declaredExt} is blocked.`,
173
+ mime: finalMime,
174
+ ext: finalExt
175
+ };
176
+ }
177
+ if (declaredMime && DANGEROUS_MIME.has(declaredMime)) {
178
+ return {
179
+ ok: false,
180
+ reason: `MIME type ${declaredMime} is unsafe.`,
181
+ mime: finalMime,
182
+ ext: finalExt
183
+ };
184
+ }
185
+ if (declaredExt) {
186
+ return {
187
+ ok: true,
188
+ mime: finalMime,
189
+ ext: declaredExt
190
+ };
191
+ }
192
+ if (declaredMime) {
193
+ return {
194
+ ok: true,
195
+ mime: declaredMime,
196
+ ext: extensionFromMime(declaredMime)
197
+ };
198
+ }
199
+ return {
200
+ ok: false,
201
+ reason: "Unable to determine file type.",
202
+ mime: finalMime,
203
+ ext: void 0
204
+ };
205
+ }
206
+ function resolveMime(ext, declaredMime) {
207
+ const lookup = mimeFromExtension(ext);
208
+ if (lookup) {
209
+ return lookup;
210
+ }
211
+ if (declaredMime) {
212
+ return declaredMime;
213
+ }
214
+ return "application/octet-stream";
215
+ }
216
+
217
+ // src/shared/fileCategories/fileCategories.detect.ts
218
+ var FILE_CATEGORY_DETECTORS = [
219
+ // ---- IMAGE ---------------------------------------------------------------
220
+ {
221
+ id: "Image",
222
+ parent: "Image",
223
+ detect: {
224
+ mimeStartsWith: ["image/"],
225
+ extensions: ["png", "jpg", "jpeg", "gif", "bmp", "webp", "tiff", "svg"]
226
+ }
227
+ },
228
+ // ---- AUDIO ---------------------------------------------------------------
229
+ {
230
+ id: "Audio",
231
+ parent: "Audio",
232
+ detect: {
233
+ mimeStartsWith: ["audio/"],
234
+ extensions: ["mp3", "wav", "ogg", "flac", "m4a"]
235
+ }
236
+ },
237
+ // ---- VIDEO ---------------------------------------------------------------
238
+ {
239
+ id: "Video",
240
+ parent: "Video",
241
+ detect: {
242
+ mimeStartsWith: ["video/"],
243
+ extensions: ["mp4", "mkv", "mov", "avi", "webm"]
244
+ }
245
+ },
246
+ // ---- PDF -----------------------------------------------------------------
247
+ {
248
+ id: "Pdf",
249
+ parent: "Document",
250
+ detect: {
251
+ mimeEquals: ["application/pdf"],
252
+ extensions: ["pdf"]
253
+ }
254
+ },
255
+ // ---- SPREADSHEET ---------------------------------------------------------
256
+ {
257
+ id: "Spreadsheet",
258
+ parent: "Document",
259
+ detect: {
260
+ mimeEquals: [
261
+ "application/vnd.ms-excel",
262
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
263
+ "application/vnd.oasis.opendocument.spreadsheet"
264
+ ],
265
+ extensions: ["xls", "xlsx", "ods"]
266
+ }
267
+ },
268
+ // ---- PRESENTATION --------------------------------------------------------
269
+ {
270
+ id: "Presentation",
271
+ parent: "Document",
272
+ detect: {
273
+ mimeEquals: [
274
+ "application/vnd.ms-powerpoint",
275
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
276
+ "application/vnd.oasis.opendocument.presentation"
277
+ ],
278
+ extensions: ["ppt", "pptx", "odp"]
279
+ }
280
+ },
281
+ // ---- DOCUMENT (WORD / RICH DOCS) ----------------------------------------
282
+ {
283
+ id: "Document",
284
+ parent: "Document",
285
+ detect: {
286
+ mimeEquals: [
287
+ "application/msword",
288
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
289
+ "application/vnd.oasis.opendocument.text"
290
+ ],
291
+ extensions: ["doc", "docx", "odt", "rtf"]
292
+ }
293
+ },
294
+ // ---- DATASET (CSV, JSONL, NDJSON, TSV) -----------------------------------
295
+ {
296
+ id: "Dataset",
297
+ parent: "Document",
298
+ detect: {
299
+ mimeEquals: ["text/csv", "application/json", "text/tab-separated-values"],
300
+ extensions: ["csv", "tsv", "jsonl", "ndjson"]
301
+ }
302
+ },
303
+ // ---- ARCHIVE -------------------------------------------------------------
304
+ {
305
+ id: "Archive",
306
+ parent: "Binary",
307
+ detect: {
308
+ mimeEquals: [
309
+ "application/zip",
310
+ "application/x-7z-compressed",
311
+ "application/x-rar-compressed",
312
+ "application/x-tar",
313
+ "application/gzip"
314
+ ],
315
+ extensions: ["zip", "rar", "7z", "tar", "gz"]
316
+ }
317
+ },
318
+ // ---- CODE ----------------------------------------------------------------
319
+ {
320
+ id: "Code",
321
+ parent: "Text",
322
+ detect: {
323
+ // Exact MIMEs (from MIME_BY_EXT) so source files match Code before Text's "text/" prefix
324
+ mimeEquals: [
325
+ "text/javascript",
326
+ "text/jsx",
327
+ "text/typescript",
328
+ "text/tsx",
329
+ "text/x-python",
330
+ "text/x-ruby",
331
+ "text/x-java-source",
332
+ "text/x-kotlin",
333
+ "text/x-c",
334
+ "text/x-csharp",
335
+ "text/x-go",
336
+ "text/x-rust",
337
+ "text/html",
338
+ "text/css",
339
+ "text/x-scss",
340
+ "text/yaml"
341
+ ],
342
+ extensions: [
343
+ "js",
344
+ "ts",
345
+ "tsx",
346
+ "jsx",
347
+ "py",
348
+ "rb",
349
+ "php",
350
+ "java",
351
+ "kt",
352
+ "c",
353
+ "cpp",
354
+ "h",
355
+ "hpp",
356
+ "cs",
357
+ "go",
358
+ "rs",
359
+ "sh",
360
+ "bash",
361
+ "zsh",
362
+ "html",
363
+ "css",
364
+ "scss",
365
+ "json",
366
+ "yml",
367
+ "yaml",
368
+ "sql"
369
+ ]
370
+ }
371
+ },
372
+ // ---- TEXT (PLAIN, MARKDOWN, LOGS, ETC.) ----------------------------------
373
+ {
374
+ id: "Text",
375
+ parent: "Text",
376
+ detect: {
377
+ mimeStartsWith: ["text/"],
378
+ extensions: ["txt", "md", "markdown", "log", "ini", "cfg", "conf", "env"]
379
+ }
380
+ },
381
+ // ---- BINARY (FALLBACK FOR NON-TEXT, NON-ARCHIVE) -------------------------
382
+ {
383
+ id: "Binary",
384
+ parent: "Binary",
385
+ detect: {
386
+ mimeEquals: ["application/octet-stream"]
387
+ }
388
+ },
389
+ // ---- UNKNOWN -------------------------------------------------------------
390
+ {
391
+ id: "Unknown",
392
+ parent: "Unknown"
393
+ }
394
+ ];
395
+
396
+ // src/shared/fileCategories/getFileCategory.ts
397
+ function getFileCategory(input) {
398
+ const mime = normalize(input.mime);
399
+ const ext = normalize(input.ext);
400
+ if (mime) {
401
+ for (const def of FILE_CATEGORY_DETECTORS) {
402
+ const eq = def.detect?.mimeEquals;
403
+ if (eq && eq.includes(mime)) {
404
+ return def.id;
405
+ }
406
+ }
407
+ }
408
+ if (mime) {
409
+ for (const def of FILE_CATEGORY_DETECTORS) {
410
+ const prefixes = def.detect?.mimeStartsWith;
411
+ if (prefixes && prefixes.some((p) => mime.startsWith(p))) {
412
+ return def.id;
413
+ }
414
+ }
415
+ }
416
+ if (ext) {
417
+ for (const def of FILE_CATEGORY_DETECTORS) {
418
+ const exts = def.detect?.extensions;
419
+ if (exts && exts.includes(ext)) {
420
+ return def.id;
421
+ }
422
+ }
423
+ }
424
+ return "Unknown";
425
+ }
426
+ function normalize(value) {
427
+ if (!value) return void 0;
428
+ const trimmed = value.trim().toLowerCase();
429
+ return trimmed || void 0;
430
+ }
431
+
432
+ // src/shared/fileCategories/getExtensionsByCategory.ts
433
+ function getExtensionsByCategory(category) {
434
+ const extensions = FILE_CATEGORY_DETECTORS.find((c) => c.id === category)?.detect?.extensions;
435
+ if (!extensions?.length) {
436
+ throw new Error(
437
+ `File category '${category}' does not define extensions. Please add the configuration in fileCategories.detect.ts.`
438
+ );
439
+ }
440
+ return [...extensions];
441
+ }
442
+ function getExtensionsByCategories(categories) {
443
+ return Array.from(
444
+ new Set(
445
+ categories.flatMap((category) => getExtensionsByCategory(category))
446
+ )
447
+ );
448
+ }
449
+
450
+ // src/shared/fileCategories/getMimeTypesByCategory.ts
451
+ function getMimeTypesByCategory(category) {
452
+ const mimeEquals = FILE_CATEGORY_DETECTORS.find((c) => c.id === category)?.detect?.mimeEquals;
453
+ if (!mimeEquals?.length) {
454
+ throw new Error(
455
+ `File category '${category}' does not define 'mimeEquals'. Please add the configuration in fileCategories.detect.ts.`
456
+ );
457
+ }
458
+ return [...mimeEquals];
459
+ }
460
+ function getMimeTypesByCategories(categories) {
461
+ return Array.from(new Set(categories.flatMap(getMimeTypesByCategory)));
462
+ }
463
+
464
+ export { FILE_CATEGORY_DETECTORS, detectFileTypeFromName, formatFileSize, getExtensionsByCategories, getExtensionsByCategory, getFileCategory, getFileExtension, getMimeTypesByCategories, getMimeTypesByCategory, parseFileSize };