@freightos/freightwind 2.1.3 → 2.1.4

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.
@@ -0,0 +1,671 @@
1
+ /**
2
+ * MIME-type → extensions map sourced from the IANA / Apache / Nginx MIME database
3
+ * (https://github.com/jshttp/mime-db). Keys are canonical MIME types
4
+ * (e.g. `image/aces`); values are the extensions that legitimately produce that
5
+ * type, without a leading dot and in lowercase.
6
+ *
7
+ * Used to validate uploaded files: reject when the browser-reported `File.type`
8
+ * is unknown, **and** cross-check that the filename's extension matches the set
9
+ * registered for that type. This catches "extension spoofing" — e.g. a `.exe`
10
+ * renamed to `.jpg` will report `application/x-msdownload` (or empty), which
11
+ * does not list `jpg`, so it fails validation.
12
+ */
13
+ export const VALID_MIME_TYPES = {
14
+ // ── Documents ──────────────────────────────────────────────────────────────
15
+ 'application/pdf': ['pdf'],
16
+ 'application/msword': ['doc', 'dot'],
17
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['docx'],
18
+ 'application/vnd.ms-word.document.macroenabled.12': ['docm'],
19
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.template': ['dotx'],
20
+ 'application/vnd.ms-word.template.macroenabled.12': ['dotm'],
21
+ 'application/vnd.oasis.opendocument.text': ['odt'],
22
+ 'application/vnd.oasis.opendocument.text-template': ['ott'],
23
+ 'application/vnd.oasis.opendocument.text-master': ['odm'],
24
+ 'application/vnd.oasis.opendocument.text-web': ['oth'],
25
+ 'application/rtf': ['rtf'],
26
+ 'text/rtf': ['rtf'],
27
+ 'text/plain': ['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'ini'],
28
+ 'text/markdown': ['md', 'markdown'],
29
+ 'text/x-markdown': ['mkd'],
30
+ 'text/mdx': ['mdx'],
31
+ 'application/x-tex': ['tex'],
32
+ 'application/x-texinfo': ['texinfo', 'texi'],
33
+ 'application/vnd.wordperfect': ['wpd'],
34
+ 'application/vnd.ms-works': ['wps', 'wks', 'wcm', 'wdb'],
35
+ 'application/vnd.apple.pages': ['pages'],
36
+ 'application/x-iwork-pages-sffpages': ['pages'],
37
+ 'application/vnd.google-apps.document': ['gdoc'],
38
+ 'application/fdf': ['fdf'],
39
+ 'application/vnd.fdf': ['fdf'],
40
+ 'application/xfdf': ['xfdf'],
41
+ 'application/vnd.adobe.xfdf': ['xfdf'],
42
+ 'application/epub+zip': ['epub'],
43
+ 'application/x-mobipocket-ebook': ['mobi', 'prc'],
44
+ 'application/vnd.amazon.ebook': ['azw'],
45
+ 'application/x-abiword': ['abw'],
46
+ 'application/x-gnumeric': ['gnumeric'],
47
+ 'text/richtext': ['rtx'],
48
+ 'text/sgml': ['sgml', 'sgm'],
49
+ 'text/troff': ['t', 'tr', 'roff', 'man', 'me', 'ms'],
50
+ 'application/vnd.software602.filler.form+xml': ['fo'],
51
+ 'text/shex': ['shex'],
52
+ 'text/spdx': ['spdx'],
53
+ 'text/turtle': ['ttl'],
54
+ 'text/n3': ['n3'],
55
+ 'text/vnd.sun.j2me.app-descriptor': ['jad'],
56
+ 'text/x-org': ['org'],
57
+ // ── Spreadsheets ───────────────────────────────────────────────────────────
58
+ 'application/vnd.ms-excel': ['xls', 'xlm', 'xla', 'xlc', 'xlt', 'xlw'],
59
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['xlsx'],
60
+ 'application/vnd.ms-excel.sheet.macroenabled.12': ['xlsm'],
61
+ 'application/vnd.ms-excel.sheet.binary.macroenabled.12': ['xlsb'],
62
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.template': ['xltx'],
63
+ 'application/vnd.ms-excel.template.macroenabled.12': ['xltm'],
64
+ 'application/vnd.ms-excel.addin.macroenabled.12': ['xlam'],
65
+ 'application/vnd.oasis.opendocument.spreadsheet': ['ods'],
66
+ 'application/vnd.oasis.opendocument.spreadsheet-template': ['ots'],
67
+ 'text/csv': ['csv'],
68
+ 'text/tab-separated-values': ['tsv'],
69
+ 'application/vnd.apple.numbers': ['numbers'],
70
+ 'application/x-iwork-numbers-sffnumbers': ['numbers'],
71
+ 'application/vnd.google-apps.spreadsheet': ['gsheet'],
72
+ // ── Presentations ──────────────────────────────────────────────────────────
73
+ 'application/vnd.ms-powerpoint': ['ppt', 'pps', 'pot'],
74
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['pptx'],
75
+ 'application/vnd.ms-powerpoint.presentation.macroenabled.12': ['pptm'],
76
+ 'application/vnd.openxmlformats-officedocument.presentationml.slideshow': ['ppsx'],
77
+ 'application/vnd.ms-powerpoint.slideshow.macroenabled.12': ['ppsm'],
78
+ 'application/vnd.openxmlformats-officedocument.presentationml.template': ['potx'],
79
+ 'application/vnd.ms-powerpoint.template.macroenabled.12': ['potm'],
80
+ 'application/vnd.ms-powerpoint.addin.macroenabled.12': ['ppam'],
81
+ 'application/vnd.openxmlformats-officedocument.presentationml.slide': ['sldx'],
82
+ 'application/vnd.ms-powerpoint.slide.macroenabled.12': ['sldm'],
83
+ 'application/vnd.oasis.opendocument.presentation': ['odp'],
84
+ 'application/vnd.oasis.opendocument.presentation-template': ['otp'],
85
+ 'application/vnd.apple.keynote': ['key'],
86
+ 'application/x-iwork-keynote-sffkey': ['key'],
87
+ 'application/vnd.google-apps.presentation': ['gslides'],
88
+ 'application/vnd.google-apps.form': ['gform'],
89
+ 'application/vnd.google-apps.drawing': ['gdraw'],
90
+ 'application/vnd.google-apps.jam': ['gjam'],
91
+ 'application/vnd.google-apps.map': ['gmap'],
92
+ 'application/vnd.google-apps.script': ['gscript'],
93
+ 'application/vnd.google-apps.site': ['gsite'],
94
+ // ── Images ─────────────────────────────────────────────────────────────────
95
+ 'image/png': ['png'],
96
+ 'image/jpeg': ['jpg', 'jpeg', 'jpe'],
97
+ 'image/pjpeg': ['jfif'],
98
+ 'image/gif': ['gif'],
99
+ 'image/webp': ['webp'],
100
+ 'image/avif': ['avif'],
101
+ 'image/apng': ['apng'],
102
+ 'image/bmp': ['bmp', 'dib'],
103
+ 'image/x-ms-bmp': ['bmp'],
104
+ 'image/tiff': ['tif', 'tiff'],
105
+ 'image/svg+xml': ['svg', 'svgz'],
106
+ 'image/x-icon': ['ico'],
107
+ 'image/vnd.microsoft.icon': ['ico'],
108
+ 'image/heic': ['heic'],
109
+ 'image/heic-sequence': ['heics'],
110
+ 'image/heif': ['heif'],
111
+ 'image/heif-sequence': ['heifs'],
112
+ 'image/cgm': ['cgm'],
113
+ 'image/jp2': ['jp2', 'jpg2'],
114
+ 'image/jpx': ['jpx', 'jpf'],
115
+ 'image/jpm': ['jpm', 'jpgm'],
116
+ 'image/jxl': ['jxl'],
117
+ 'image/jxr': ['jxr'],
118
+ 'image/jxra': ['jxra'],
119
+ 'image/jxrs': ['jxrs'],
120
+ 'image/jxs': ['jxs'],
121
+ 'image/jxsc': ['jxsc'],
122
+ 'image/jxsi': ['jxsi'],
123
+ 'image/jxss': ['jxss'],
124
+ 'image/jph': ['jph'],
125
+ 'image/jphc': ['jhc'],
126
+ 'image/hej2k': ['hej2'],
127
+ 'image/jls': ['jls'],
128
+ 'image/jaii': ['jaii'],
129
+ 'image/jais': ['jais'],
130
+ 'image/ktx': ['ktx'],
131
+ 'image/ktx2': ['ktx2'],
132
+ 'image/aces': ['exr'],
133
+ 'image/avci': ['avci'],
134
+ 'image/avcs': ['avcs'],
135
+ 'image/dicom-rle': ['drle'],
136
+ 'image/dpx': ['dpx'],
137
+ 'image/fits': ['fits'],
138
+ 'image/g3fax': ['g3'],
139
+ 'image/ief': ['ief'],
140
+ 'image/t38': ['t38'],
141
+ 'image/tiff-fx': ['tfx'],
142
+ 'image/emf': ['emf'],
143
+ 'image/wmf': ['wmf'],
144
+ 'image/vnd.adobe.photoshop': ['psd'],
145
+ 'application/postscript': ['ai', 'eps', 'ps'],
146
+ 'image/x-adobe-dng': ['dng'],
147
+ 'image/prs.btif': ['btif', 'btf'],
148
+ 'image/prs.pti': ['pti'],
149
+ 'image/vnd.airzip.accelerator.azv': ['azv'],
150
+ 'image/vnd.blockfact.facti': ['facti'],
151
+ 'image/vnd.dece.graphic': ['uvi', 'uvvi', 'uvg', 'uvvg'],
152
+ 'image/vnd.djvu': ['djvu', 'djv'],
153
+ 'image/vnd.dvb.subtitle': ['sub'],
154
+ 'image/vnd.dwg': ['dwg'],
155
+ 'image/vnd.dxf': ['dxf'],
156
+ 'image/vnd.fastbidsheet': ['fbs'],
157
+ 'image/vnd.fpx': ['fpx'],
158
+ 'image/vnd.fst': ['fst'],
159
+ 'image/vnd.fujixerox.edmics-mmr': ['mmr'],
160
+ 'image/vnd.fujixerox.edmics-rlc': ['rlc'],
161
+ 'image/vnd.ms-dds': ['dds'],
162
+ 'image/vnd.ms-modi': ['mdi'],
163
+ 'image/vnd.ms-photo': ['wdp'],
164
+ 'image/vnd.net-fpx': ['npx'],
165
+ 'image/vnd.pco.b16': ['b16'],
166
+ 'image/vnd.tencent.tap': ['tap'],
167
+ 'image/vnd.valve.source.texture': ['vtf'],
168
+ 'image/vnd.wap.wbmp': ['wbmp'],
169
+ 'image/vnd.xiff': ['xif'],
170
+ 'image/vnd.zbrush.pcx': ['pcx'],
171
+ 'image/x-3ds': ['3ds'],
172
+ 'image/x-cmu-raster': ['ras'],
173
+ 'image/x-cmx': ['cmx'],
174
+ 'image/x-freehand': ['fh', 'fhc', 'fh4', 'fh5', 'fh7'],
175
+ 'image/x-jng': ['jng'],
176
+ 'image/x-mrsid-image': ['sid'],
177
+ 'image/sgi': ['sgi'],
178
+ 'image/x-pcx': ['pcx'],
179
+ 'image/x-pict': ['pic', 'pct'],
180
+ 'image/x-portable-anymap': ['pnm'],
181
+ 'image/x-portable-bitmap': ['pbm'],
182
+ 'image/x-portable-graymap': ['pgm'],
183
+ 'image/x-portable-pixmap': ['ppm'],
184
+ 'image/x-rgb': ['rgb'],
185
+ 'image/x-tga': ['tga'],
186
+ 'image/x-xbitmap': ['xbm'],
187
+ 'image/x-xpixmap': ['xpm'],
188
+ 'image/x-xwindowdump': ['xwd'],
189
+ // ── Audio ──────────────────────────────────────────────────────────────────
190
+ 'audio/mpeg': ['mpga', 'mp2', 'mp2a', 'mp3', 'm2a', 'm3a'],
191
+ 'audio/mp3': ['mp3'],
192
+ 'audio/wav': ['wav'],
193
+ 'audio/wave': ['wav'],
194
+ 'audio/x-wav': ['wav'],
195
+ 'audio/ogg': ['oga', 'ogg', 'spx', 'opus'],
196
+ 'audio/mp4': ['m4a', 'mp4a', 'm4b'],
197
+ 'audio/x-m4a': ['m4a'],
198
+ 'audio/aac': ['adts', 'aac'],
199
+ 'audio/x-aac': ['aac'],
200
+ 'audio/x-aiff': ['aif', 'aiff', 'aifc'],
201
+ 'audio/x-flac': ['flac'],
202
+ 'audio/x-ms-wma': ['wma'],
203
+ 'audio/amr': ['amr'],
204
+ 'audio/midi': ['mid', 'midi', 'kar', 'rmi'],
205
+ 'audio/matroska': ['mka'],
206
+ 'audio/x-matroska': ['mka'],
207
+ 'audio/mobile-xmf': ['mxmf'],
208
+ 'audio/basic': ['au', 'snd'],
209
+ 'audio/x-pn-realaudio': ['ram', 'ra'],
210
+ 'audio/x-realaudio': ['ra'],
211
+ 'audio/x-pn-realaudio-plugin': ['rmp'],
212
+ 'audio/webm': ['weba'],
213
+ 'audio/x-caf': ['caf'],
214
+ 'audio/s3m': ['s3m'],
215
+ 'audio/silk': ['sil'],
216
+ 'audio/xm': ['xm'],
217
+ 'audio/x-ms-wax': ['wax'],
218
+ 'audio/x-mpegurl': ['m3u'],
219
+ 'audio/adpcm': ['adp'],
220
+ 'audio/vnd.dra': ['dra'],
221
+ 'audio/vnd.dts': ['dts'],
222
+ 'audio/vnd.dts.hd': ['dtshd'],
223
+ 'audio/vnd.lucent.voice': ['lvp'],
224
+ 'audio/vnd.ms-playready.media.pya': ['pya'],
225
+ 'audio/vnd.digital-winds': ['eol'],
226
+ 'audio/vnd.rip': ['rip'],
227
+ 'audio/vnd.nuera.ecelp4800': ['ecelp4800'],
228
+ 'audio/vnd.nuera.ecelp7470': ['ecelp7470'],
229
+ 'audio/vnd.nuera.ecelp9600': ['ecelp9600'],
230
+ // ── Video ──────────────────────────────────────────────────────────────────
231
+ 'video/mp4': ['mp4', 'mp4v', 'mpg4'],
232
+ 'video/mpeg': ['mpeg', 'mpg', 'mpe', 'm1v', 'm2v'],
233
+ 'video/x-m4v': ['m4v'],
234
+ 'video/quicktime': ['qt', 'mov'],
235
+ 'video/x-msvideo': ['avi'],
236
+ 'video/x-matroska': ['mkv', 'mk3d', 'mks'],
237
+ 'video/matroska': ['mkv'],
238
+ 'video/matroska-3d': ['mk3d'],
239
+ 'video/webm': ['webm'],
240
+ 'video/x-flv': ['flv'],
241
+ 'video/x-f4v': ['f4v'],
242
+ 'video/x-ms-wmv': ['wmv'],
243
+ 'video/x-ms-wm': ['wm'],
244
+ 'video/x-ms-wmx': ['wmx'],
245
+ 'video/x-ms-wvx': ['wvx'],
246
+ 'video/x-ms-asf': ['asf', 'asx'],
247
+ 'video/x-ms-vob': ['vob'],
248
+ 'video/mp2t': ['ts', 'm2t', 'm2ts', 'mts'],
249
+ 'video/ogg': ['ogv'],
250
+ 'video/3gpp': ['3gp', '3gpp'],
251
+ 'video/3gpp2': ['3g2'],
252
+ 'video/vnd.mpegurl': ['mxu', 'm4u'],
253
+ 'video/x-mng': ['mng'],
254
+ 'video/x-fli': ['fli'],
255
+ 'video/x-sgi-movie': ['movie'],
256
+ 'video/x-smv': ['smv'],
257
+ 'video/iso.segment': ['m4s'],
258
+ 'video/mj2': ['mj2', 'mjp2'],
259
+ 'video/h261': ['h261'],
260
+ 'video/h263': ['h263'],
261
+ 'video/h264': ['h264'],
262
+ 'video/jpeg': ['jpgv'],
263
+ 'video/jpm': ['jpm', 'jpgm'],
264
+ 'video/vnd.dvb.file': ['dvb'],
265
+ 'video/vnd.fvt': ['fvt'],
266
+ 'video/vnd.vivo': ['viv'],
267
+ 'video/vnd.ms-playready.media.pyv': ['pyv'],
268
+ 'video/vnd.dece.hd': ['uvh', 'uvvh'],
269
+ 'video/vnd.dece.mobile': ['uvm', 'uvvm'],
270
+ 'video/vnd.dece.pd': ['uvp', 'uvvp'],
271
+ 'video/vnd.dece.sd': ['uvs', 'uvvs'],
272
+ 'video/vnd.dece.video': ['uvv', 'uvvv'],
273
+ 'video/vnd.uvvu.mp4': ['uvu', 'uvvu'],
274
+ // ── Archives & Compression ─────────────────────────────────────────────────
275
+ 'application/zip': ['zip'],
276
+ 'application/x-zip-compressed': ['zip'],
277
+ 'application/vnd.rar': ['rar'],
278
+ 'application/x-rar-compressed': ['rar'],
279
+ 'application/x-compressed': ['rar'],
280
+ 'application/gzip': ['gz'],
281
+ 'application/x-bzip': ['bz'],
282
+ 'application/x-bzip2': ['bz2', 'boz'],
283
+ 'application/x-7z-compressed': ['7z'],
284
+ 'application/x-tar': ['tar'],
285
+ 'application/x-xz': ['xz'],
286
+ 'application/x-lzh-compressed': ['lzh', 'lha'],
287
+ 'application/vnd.ms-cab-compressed': ['cab'],
288
+ 'application/x-arj': ['arj'],
289
+ 'application/x-freearc': ['arc'],
290
+ 'application/x-ace-compressed': ['ace'],
291
+ 'application/x-stuffit': ['sit'],
292
+ 'application/x-stuffitx': ['sitx'],
293
+ 'application/x-sea': ['sea'],
294
+ 'application/x-shar': ['shar'],
295
+ 'application/x-iso9660-image': ['iso'],
296
+ 'application/x-apple-diskimage': ['dmg'],
297
+ 'application/x-cpio': ['cpio'],
298
+ 'application/vnd.android.package-archive': ['apk'],
299
+ 'application/java-archive': ['jar', 'war', 'ear'],
300
+ 'application/x-debian-package': ['deb', 'udeb'],
301
+ 'application/x-redhat-package-manager': ['rpm'],
302
+ 'application/x-msdownload': ['exe', 'dll', 'com', 'bat', 'msi'],
303
+ 'application/octet-stream': ['bin', 'dms', 'lrf', 'mar', 'so', 'dist', 'distz', 'pkg', 'bpk', 'dump', 'elc', 'deploy', 'exe', 'dll', 'deb', 'dmg', 'iso', 'img', 'msi', 'msp', 'msm', 'buffer'],
304
+ 'application/msix': ['msix'],
305
+ 'application/msixbundle': ['msixbundle'],
306
+ 'application/appx': ['appx'],
307
+ 'application/appxbundle': ['appxbundle'],
308
+ 'application/appinstaller': ['appinstaller'],
309
+ 'application/x-chrome-extension': ['crx'],
310
+ 'application/x-xpinstall': ['xpi'],
311
+ 'application/zip+dotlottie': ['lottie'],
312
+ 'application/x-cbr': ['cbr', 'cba', 'cbt', 'cbz', 'cb7'],
313
+ 'application/x-gca-compressed': ['gca'],
314
+ 'application/x-dgc-compressed': ['dgc'],
315
+ 'application/x-virtualbox-ova': ['ova'],
316
+ 'application/x-virtualbox-vbox': ['vbox'],
317
+ 'application/x-virtualbox-vbox-extpack': ['vbox-extpack'],
318
+ 'application/x-sv4cpio': ['sv4cpio'],
319
+ 'application/x-sv4crc': ['sv4crc'],
320
+ 'application/x-ustar': ['ustar'],
321
+ 'application/x-gtar': ['gtar'],
322
+ 'application/x-cfs-compressed': ['cfs'],
323
+ 'application/x-nzb': ['nzb'],
324
+ 'application/x-bittorrent': ['torrent'],
325
+ 'application/vnd.ms-pki.stl': ['stl'],
326
+ // ── Code & Markup ──────────────────────────────────────────────────────────
327
+ 'application/javascript': ['js'],
328
+ 'text/javascript': ['js', 'mjs'],
329
+ 'application/node': ['cjs'],
330
+ 'text/jsx': ['jsx'],
331
+ 'application/json': ['json', 'map'],
332
+ 'application/json5': ['json5'],
333
+ 'application/ld+json': ['jsonld'],
334
+ 'application/jsonml+json': ['jsonml'],
335
+ 'application/wasm': ['wasm'],
336
+ 'text/html': ['html', 'htm', 'shtml'],
337
+ 'application/xhtml+xml': ['xhtml', 'xht'],
338
+ 'text/css': ['css'],
339
+ 'text/less': ['less'],
340
+ 'text/x-sass': ['sass'],
341
+ 'text/x-scss': ['scss'],
342
+ 'text/stylus': ['stylus', 'styl'],
343
+ 'application/xml': ['xml', 'xsl', 'xsd', 'rng'],
344
+ 'text/xml': ['xml'],
345
+ 'application/xslt+xml': ['xsl', 'xslt'],
346
+ 'application/xml-dtd': ['dtd'],
347
+ 'text/x-c': ['c', 'cc', 'cxx', 'cpp', 'h', 'hh', 'dic'],
348
+ 'text/x-java-source': ['java'],
349
+ 'application/java-vm': ['class'],
350
+ 'text/x-php': ['php'],
351
+ 'application/x-httpd-php': ['php'],
352
+ 'text/x-pascal': ['p', 'pas'],
353
+ 'text/x-fortran': ['f', 'for', 'f77', 'f90'],
354
+ 'text/x-asm': ['s', 'asm'],
355
+ 'application/x-perl': ['pl', 'pm'],
356
+ 'application/x-sh': ['sh'],
357
+ 'application/x-csh': ['csh'],
358
+ 'application/x-tcl': ['tcl', 'tk'],
359
+ 'application/x-lua-bytecode': ['luac'],
360
+ 'text/x-lua': ['lua'],
361
+ 'application/x-sql': ['sql'],
362
+ 'application/sql': ['sql'],
363
+ 'application/dart': ['dart'],
364
+ 'application/vnd.dart': ['dart'],
365
+ 'text/coffeescript': ['coffee', 'litcoffee'],
366
+ 'text/jade': ['jade'],
367
+ 'text/x-handlebars-template': ['hbs'],
368
+ 'text/x-processing': ['pde'],
369
+ 'text/mathml': ['mml'],
370
+ 'application/mathml+xml': ['mathml'],
371
+ 'application/yaml': ['yaml', 'yml'],
372
+ 'text/yaml': ['yaml', 'yml'],
373
+ 'application/toml': ['toml'],
374
+ 'text/x-component': ['htc'],
375
+ 'text/x-nfo': ['nfo'],
376
+ 'text/x-opml': ['opml'],
377
+ 'text/uri-list': ['uri', 'uris', 'urls'],
378
+ 'text/x-suse-ymp': ['ymp'],
379
+ 'text/x-uuencode': ['uu'],
380
+ 'text/vnd.curl': ['curl'],
381
+ 'text/vnd.curl.dcurl': ['dcurl'],
382
+ 'text/vnd.curl.mcurl': ['mcurl'],
383
+ 'text/vnd.curl.scurl': ['scurl'],
384
+ 'text/x-sfv': ['sfv'],
385
+ 'text/x-setext': ['etx'],
386
+ 'text/wgsl': ['wgsl'],
387
+ 'application/x-ipynb+json': ['ipynb'],
388
+ 'text/prs.lines.tag': ['dsc'],
389
+ 'text/vnd.fly': ['fly'],
390
+ 'text/vnd.fmi.flexstor': ['flx'],
391
+ 'text/vnd.graphviz': ['gv'],
392
+ 'text/vnd.in3d.3dml': ['3dml'],
393
+ 'text/vnd.in3d.spot': ['spot'],
394
+ 'text/vnd.wap.wml': ['wml'],
395
+ 'text/vnd.wap.wmlscript': ['wmls'],
396
+ // ── Fonts ──────────────────────────────────────────────────────────────────
397
+ 'font/ttf': ['ttf'],
398
+ 'font/otf': ['otf'],
399
+ 'font/collection': ['ttc'],
400
+ 'font/woff': ['woff'],
401
+ 'font/woff2': ['woff2'],
402
+ 'application/vnd.ms-fontobject': ['eot'],
403
+ 'application/x-font-type1': ['pfa', 'pfb', 'pfm', 'afm'],
404
+ 'application/x-font-bdf': ['bdf'],
405
+ 'application/x-font-ghostscript': ['gsf'],
406
+ 'application/x-font-linux-psf': ['psf'],
407
+ 'application/x-font-pcf': ['pcf'],
408
+ 'application/x-font-snf': ['snf'],
409
+ // ── Data & Database ────────────────────────────────────────────────────────
410
+ 'application/vnd.sqlite3': ['sqlite', 'sqlite3'],
411
+ 'application/x-msaccess': ['mdb'],
412
+ 'application/vnd.dbf': ['dbf'],
413
+ 'application/vnd.apache.parquet': ['parquet'],
414
+ 'application/x-netcdf': ['nc', 'cdf'],
415
+ 'application/x-hdf': ['hdf'],
416
+ // ── Office (other) ─────────────────────────────────────────────────────────
417
+ 'application/onenote': ['onetoc', 'onetoc2', 'onetmp', 'onepkg', 'one', 'onea'],
418
+ 'application/x-mspublisher': ['pub'],
419
+ 'application/vnd.ms-project': ['mpp', 'mpt'],
420
+ 'application/vnd.visio': ['vsd', 'vst', 'vss', 'vsw', 'vsdx', 'vtx'],
421
+ 'application/vnd.ms-visio.viewer': ['vdx'],
422
+ 'application/vnd.ms-officetheme': ['thmx'],
423
+ 'application/vnd.ms-htmlhelp': ['chm'],
424
+ 'application/x-msmoney': ['mny'],
425
+ 'application/vnd.ms-wpl': ['wpl'],
426
+ 'application/vnd.ms-xpsdocument': ['xps'],
427
+ 'application/x-msbinder': ['obd'],
428
+ 'application/x-mscardfile': ['crd'],
429
+ 'application/x-msclip': ['clp'],
430
+ 'application/x-msschedule': ['scd'],
431
+ 'application/x-msterminal': ['trm'],
432
+ 'application/x-mswrite': ['wri'],
433
+ 'application/winhlp': ['hlp'],
434
+ // ── CAD & 3D ───────────────────────────────────────────────────────────────
435
+ 'model/vnd.dwf': ['dwf'],
436
+ 'model/step': ['step', 'stp', 'stpnc', 'p21', '210'],
437
+ 'model/step+xml': ['stpx'],
438
+ 'model/step+zip': ['stpz'],
439
+ 'model/step-xml+zip': ['stpxz'],
440
+ 'model/stl': ['stl'],
441
+ 'model/iges': ['iges', 'igs'],
442
+ 'model/3mf': ['3mf'],
443
+ 'model/obj': ['obj'],
444
+ 'model/mtl': ['mtl'],
445
+ 'application/vnd.autodesk.fbx': ['fbx'],
446
+ 'model/gltf+json': ['gltf'],
447
+ 'model/gltf-binary': ['glb'],
448
+ 'model/vnd.usdz+zip': ['usdz'],
449
+ 'model/vnd.usda': ['usda'],
450
+ 'application/x-blender': ['blend'],
451
+ 'model/vnd.collada+xml': ['dae'],
452
+ 'model/x3d+xml': ['x3d', 'x3dz'],
453
+ 'model/x3d+binary': ['x3db', 'x3dbz'],
454
+ 'model/x3d+vrml': ['x3dv', 'x3dvz'],
455
+ 'model/vrml': ['wrl', 'vrml'],
456
+ 'model/mesh': ['msh', 'mesh', 'silo'],
457
+ 'model/u3d': ['u3d'],
458
+ 'model/jt': ['jt'],
459
+ 'model/prc': ['prc'],
460
+ 'model/vnd.bary': ['bary'],
461
+ 'model/vnd.cld': ['cld'],
462
+ 'model/vnd.gdl': ['gdl'],
463
+ 'model/vnd.gtw': ['gtw'],
464
+ 'model/vnd.opengex': ['ogex'],
465
+ 'model/vnd.parasolid.transmit.binary': ['x_b'],
466
+ 'model/vnd.parasolid.transmit.text': ['x_t'],
467
+ 'model/vnd.pytha.pyox': ['pyo', 'pyox'],
468
+ 'model/vnd.sap.vds': ['vds'],
469
+ 'model/vnd.valve.source.compiled-map': ['bsp'],
470
+ 'model/vnd.vtu': ['vtu'],
471
+ // ── Calendar / Contact / Mail ──────────────────────────────────────────────
472
+ 'text/calendar': ['ics', 'ifb'],
473
+ 'text/x-vcalendar': ['vcs'],
474
+ 'text/vcard': ['vcard'],
475
+ 'text/x-vcard': ['vcf'],
476
+ 'message/rfc822': ['eml', 'mime', 'mht', 'mhtml'],
477
+ 'application/vnd.ms-outlook': ['msg'],
478
+ 'application/mbox': ['mbox'],
479
+ 'message/global': ['u8msg'],
480
+ 'message/global-delivery-status': ['u8dsn'],
481
+ 'message/global-disposition-notification': ['u8mdn'],
482
+ 'message/global-headers': ['u8hdr'],
483
+ 'message/vnd.wfa.wsc': ['wsc'],
484
+ 'text/vnd.familysearch.gedcom': ['ged'],
485
+ // ── Certificates / Keys / Crypto ───────────────────────────────────────────
486
+ 'application/x-x509-ca-cert': ['der', 'crt', 'pem'],
487
+ 'application/x-pkcs12': ['p12', 'pfx'],
488
+ 'application/x-pkcs7-certificates': ['p7b', 'spc'],
489
+ 'application/pkcs7-mime': ['p7m', 'p7c'],
490
+ 'application/x-pkcs7-certreqresp': ['p7r'],
491
+ 'application/pkcs7-signature': ['p7s'],
492
+ 'application/pkcs8': ['p8'],
493
+ 'application/pkcs10': ['p10'],
494
+ 'application/pkix-cert': ['cer'],
495
+ 'application/pkix-crl': ['crl'],
496
+ 'application/pkix-pkipath': ['pkipath'],
497
+ 'application/pkixcmp': ['pki'],
498
+ 'application/pgp-encrypted': ['pgp'],
499
+ 'application/pgp-keys': ['asc'],
500
+ 'application/pgp-signature': ['sig', 'asc'],
501
+ 'application/x-keepass2': ['kdbx'],
502
+ // ── Subtitles / Captions ───────────────────────────────────────────────────
503
+ 'application/x-subrip': ['srt'],
504
+ 'text/vtt': ['vtt'],
505
+ 'text/vnd.dvb.subtitle': ['sub'],
506
+ // ── Disk Images / Virtualization ───────────────────────────────────────────
507
+ 'application/x-virtualbox-vhd': ['vhd'],
508
+ 'application/x-virtualbox-vmdk': ['vmdk'],
509
+ 'application/x-virtualbox-vdi': ['vdi'],
510
+ 'application/x-virtualbox-ovf': ['ovf'],
511
+ 'application/x-virtualbox-hdd': ['hdd'],
512
+ // ── GIS / Geospatial ───────────────────────────────────────────────────────
513
+ 'application/geo+json': ['geojson'],
514
+ 'application/vnd.google-earth.kml+xml': ['kml'],
515
+ 'application/vnd.google-earth.kmz': ['kmz'],
516
+ 'application/gpx+xml': ['gpx'],
517
+ 'application/gml+xml': ['gml'],
518
+ 'application/vnd.mapbox-vector-tile': ['mvt'],
519
+ 'application/vnd.openstreetmap.data+xml': ['osm'],
520
+ // ── Web / Manifests / Feeds / RDF ──────────────────────────────────────────
521
+ 'application/manifest+json': ['webmanifest'],
522
+ 'application/x-web-app-manifest+json': ['webapp'],
523
+ 'application/atom+xml': ['atom'],
524
+ 'application/atomcat+xml': ['atomcat'],
525
+ 'application/atomdeleted+xml': ['atomdeleted'],
526
+ 'application/atomsvc+xml': ['atomsvc'],
527
+ 'application/rss+xml': ['rss'],
528
+ 'application/rsd+xml': ['rsd'],
529
+ 'application/rdf+xml': ['rdf', 'owl'],
530
+ 'application/wsdl+xml': ['wsdl'],
531
+ 'application/wspolicy+xml': ['wspolicy'],
532
+ 'application/widget': ['wgt'],
533
+ 'application/yang': ['yang'],
534
+ 'application/yin+xml': ['yin'],
535
+ 'application/xv+xml': ['mxml', 'xhvml', 'xvml', 'xvm'],
536
+ 'application/xaml+xml': ['xaml'],
537
+ 'application/xproc+xml': ['xpl'],
538
+ 'application/xop+xml': ['xop'],
539
+ 'application/xenc+xml': ['xenc'],
540
+ 'application/xliff+xml': ['xlf'],
541
+ 'application/xspf+xml': ['xspf'],
542
+ 'application/vnd.xfdl': ['xfdl'],
543
+ 'application/vnd.xara': ['xar'],
544
+ 'application/voicexml+xml': ['vxml'],
545
+ 'application/watcherinfo+xml': ['wif'],
546
+ // ── Installers / Specialty containers ──────────────────────────────────────
547
+ 'application/x-shockwave-flash': ['swf'],
548
+ 'application/x-silverlight-app': ['xap'],
549
+ 'application/x-ms-application': ['application'],
550
+ 'application/x-ms-shortcut': ['lnk'],
551
+ 'application/x-java-jnlp-file': ['jnlp'],
552
+ 'application/x-install-instructions': ['install'],
553
+ 'application/x-makeself': ['run'],
554
+ 'application/x-java-archive-diff': ['jardiff'],
555
+ 'application/x-mie': ['mie'],
556
+ 'application/vnd.apple.installer+xml': ['mpkg'],
557
+ 'application/vnd.apple.pkpass': ['pkpass'],
558
+ 'application/vnd.apple.mpegurl': ['m3u8'],
559
+ // ── Z-machine / Game data ──────────────────────────────────────────────────
560
+ 'application/x-zmachine': ['z1', 'z2', 'z3', 'z4', 'z5', 'z6', 'z7', 'z8'],
561
+ 'application/x-tads': ['gam'],
562
+ 'application/x-glulx': ['ulx'],
563
+ 'application/x-doom': ['wad'],
564
+ 'application/x-conference': ['nsc'],
565
+ 'application/x-cdlink': ['vcd'],
566
+ 'application/x-chess-pgn': ['pgn'],
567
+ 'application/x-chat': ['chat'],
568
+ 'application/x-cocoa': ['cco'],
569
+ // ── Adobe AIR / Misc app packages ──────────────────────────────────────────
570
+ 'application/vnd.adobe.air-application-installer-package+zip': ['air'],
571
+ 'application/vnd.adobe.fxp': ['fxp', 'fxpl'],
572
+ 'application/vnd.adobe.xdp+xml': ['xdp'],
573
+ 'application/vnd.adobe.formscentral.fcdt': ['fcdt'],
574
+ // ── Other (preserved from prior list) ──────────────────────────────────────
575
+ 'application/x-research-info-systems': ['ris'],
576
+ 'application/x-tex-tfm': ['tfm'],
577
+ 'application/x-t3vm-image': ['t3'],
578
+ 'application/x-eva': ['eva'],
579
+ 'application/x-envoy': ['evy'],
580
+ 'application/x-dvi': ['dvi'],
581
+ 'application/x-dtbresource+xml': ['res'],
582
+ 'application/x-dtbook+xml': ['dtb'],
583
+ 'application/x-dtbncx+xml': ['ncx'],
584
+ 'application/x-director': ['dir', 'dcr', 'dxr', 'cst', 'cct', 'cxt', 'w3d', 'fgd', 'swa'],
585
+ 'application/x-bcpio': ['bcpio'],
586
+ 'application/x-blorb': ['blb', 'blorb'],
587
+ 'application/x-authorware-bin': ['aab', 'x32', 'u32', 'vox'],
588
+ 'application/x-authorware-map': ['aam'],
589
+ 'application/x-authorware-seg': ['aas'],
590
+ 'application/x-futuresplash': ['spl'],
591
+ 'application/x-gramps-xml': ['gramps'],
592
+ 'application/x-wais-source': ['src'],
593
+ 'application/x-pilot': ['prc', 'pdb'],
594
+ 'application/x-pki-message': [],
595
+ 'application/x-ns-proxy-autoconfig': ['pac'],
596
+ 'application/x-xfig': ['fig'],
597
+ 'application/x-msmediaview': ['mvb', 'm13', 'm14'],
598
+ 'application/x-ms-wmd': ['wmd'],
599
+ 'application/x-ms-wmz': ['wmz'],
600
+ 'application/x-ms-xbap': ['xbap'],
601
+ 'application/x-msmetafile': ['wmf', 'wmz', 'emf', 'emz'],
602
+ 'x-conference/x-cooltalk': ['ice'],
603
+ };
604
+ /**
605
+ * Flat set of every extension known to {@link VALID_MIME_TYPES}, used by the
606
+ * extension-only fast path ({@link hasValidExtension}). Derived once at module
607
+ * load — kept in sync with the map automatically.
608
+ */
609
+ export const VALID_FILE_EXTENSIONS = new Set(Object.values(VALID_MIME_TYPES).flat());
610
+ /**
611
+ * Normalize a filename or raw extension to a lowercase no-dot extension token.
612
+ * Returns null if no extension is present.
613
+ */
614
+ export function normalizeExtension(input) {
615
+ const dot = input.lastIndexOf('.');
616
+ if (dot < 0 || dot === input.length - 1)
617
+ return null;
618
+ return input.slice(dot + 1).toLowerCase();
619
+ }
620
+ /**
621
+ * Returns true when the given filename ends in a recognized extension.
622
+ * Files with no extension, or extensions that aren't in {@link VALID_FILE_EXTENSIONS},
623
+ * are rejected as "fake" / nonsense.
624
+ *
625
+ * Prefer {@link validateFileType} when a `File` object is available — it catches
626
+ * extension spoofing that this function cannot.
627
+ */
628
+ export function hasValidExtension(filename) {
629
+ const ext = normalizeExtension(filename);
630
+ if (!ext)
631
+ return false;
632
+ return VALID_FILE_EXTENSIONS.has(ext);
633
+ }
634
+ /**
635
+ * Strict file validation: the browser-reported MIME type must be known, AND the
636
+ * filename's extension must be one of the extensions registered for that type.
637
+ *
638
+ * Rejects:
639
+ * - files with no extension
640
+ * - files whose `type` is empty or unknown to {@link VALID_MIME_TYPES}
641
+ * - files whose extension does not match the registered list for `file.type`
642
+ * (extension-spoofing guard, e.g. `.exe` renamed to `.jpg`)
643
+ *
644
+ * `file.type` comparison is case-insensitive; some browsers lowercase but a few
645
+ * legacy ones don't.
646
+ */
647
+ export function validateFileType(file) {
648
+ const mimeType = file.type.toLowerCase();
649
+ if (!mimeType)
650
+ return false;
651
+ const allowedExtensions = VALID_MIME_TYPES[mimeType];
652
+ if (!allowedExtensions)
653
+ return false;
654
+ const ext = normalizeExtension(file.name);
655
+ if (!ext)
656
+ return false;
657
+ return allowedExtensions.includes(ext);
658
+ }
659
+ /**
660
+ * Look up every MIME type that legitimately produces the given extension.
661
+ * Useful for `accept`-attribute construction or reverse diagnostics.
662
+ */
663
+ export function getMimeTypesForExtension(extension) {
664
+ const ext = extension.startsWith('.') ? extension.slice(1).toLowerCase() : extension.toLowerCase();
665
+ const matches = [];
666
+ for (const [mimeType, exts] of Object.entries(VALID_MIME_TYPES)) {
667
+ if (exts.includes(ext))
668
+ matches.push(mimeType);
669
+ }
670
+ return matches;
671
+ }