@juspay/neurolink 10.10.12 → 10.11.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/CHANGELOG.md +6 -0
- package/dist/adapters/imageFormatSupport.d.ts +75 -0
- package/dist/adapters/imageFormatSupport.js +283 -0
- package/dist/adapters/video/ffmpegAdapter.d.ts +6 -0
- package/dist/adapters/video/ffmpegAdapter.js +1 -1
- package/dist/browser/neurolink.min.js +399 -398
- package/dist/lib/adapters/imageFormatSupport.d.ts +75 -0
- package/dist/lib/adapters/imageFormatSupport.js +284 -0
- package/dist/lib/adapters/video/ffmpegAdapter.d.ts +6 -0
- package/dist/lib/adapters/video/ffmpegAdapter.js +1 -1
- package/dist/lib/processors/config/fileExtensions.d.ts +32 -15
- package/dist/lib/processors/config/fileExtensions.js +27 -66
- package/dist/lib/processors/config/fileTypeRegistry.d.ts +106 -0
- package/dist/lib/processors/config/fileTypeRegistry.js +702 -0
- package/dist/lib/processors/config/index.d.ts +2 -1
- package/dist/lib/processors/config/index.js +5 -1
- package/dist/lib/processors/config/mimeConstants.d.ts +22 -7
- package/dist/lib/processors/config/mimeConstants.js +45 -66
- package/dist/lib/processors/media/AudioProcessor.js +16 -38
- package/dist/lib/processors/media/VideoProcessor.js +11 -32
- package/dist/lib/providers/googleAiStudio/client.js +12 -1
- package/dist/lib/providers/googleVertex/client.js +123 -66
- package/dist/lib/types/file.d.ts +41 -0
- package/dist/lib/utils/fileDetector.js +367 -251
- package/dist/lib/utils/imageProcessor.js +14 -17
- package/dist/lib/utils/markupSniff.d.ts +37 -0
- package/dist/lib/utils/markupSniff.js +125 -0
- package/dist/lib/utils/messageBuilder.d.ts +14 -0
- package/dist/lib/utils/messageBuilder.js +172 -92
- package/dist/processors/config/fileExtensions.d.ts +32 -15
- package/dist/processors/config/fileExtensions.js +27 -66
- package/dist/processors/config/fileTypeRegistry.d.ts +106 -0
- package/dist/processors/config/fileTypeRegistry.js +701 -0
- package/dist/processors/config/index.d.ts +2 -1
- package/dist/processors/config/index.js +5 -1
- package/dist/processors/config/mimeConstants.d.ts +22 -7
- package/dist/processors/config/mimeConstants.js +45 -66
- package/dist/processors/media/AudioProcessor.js +16 -38
- package/dist/processors/media/VideoProcessor.js +11 -32
- package/dist/providers/googleAiStudio/client.js +12 -1
- package/dist/providers/googleVertex/client.js +123 -66
- package/dist/types/file.d.ts +41 -0
- package/dist/utils/fileDetector.js +367 -251
- package/dist/utils/imageProcessor.js +14 -17
- package/dist/utils/markupSniff.d.ts +37 -0
- package/dist/utils/markupSniff.js +124 -0
- package/dist/utils/messageBuilder.d.ts +14 -0
- package/dist/utils/messageBuilder.js +172 -92
- package/package.json +3 -2
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical file-format registry — the single source of truth for
|
|
3
|
+
* extension ↔ MIME ↔ FileType ↔ modality.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists
|
|
6
|
+
*
|
|
7
|
+
* The same knowledge used to live in five hand-maintained tables that had no
|
|
8
|
+
* way of agreeing with each other:
|
|
9
|
+
*
|
|
10
|
+
* 1. `EXTENSION_MIME_MAP` (config/mimeConstants.ts)
|
|
11
|
+
* 2. the `*_EXTENSIONS` category arrays (config/fileExtensions.ts)
|
|
12
|
+
* 3. `ExtensionStrategy.typeMap` + `.mimeMap` (utils/fileDetector.ts)
|
|
13
|
+
* 4. `EXTENSION_TYPE_MAP` (utils/messageBuilder.ts)
|
|
14
|
+
* 5. each processor's `supportedExtensions` / `supportedMimeTypes`
|
|
15
|
+
*
|
|
16
|
+
* They drifted, and the drift was not cosmetic — a format that a processor
|
|
17
|
+
* declared it supported was routinely invisible to the detector standing in
|
|
18
|
+
* front of it, so the file never reached the processor at all. Measured on real
|
|
19
|
+
* ffmpeg-generated media before this registry existed:
|
|
20
|
+
*
|
|
21
|
+
* .aiff .m2ts .mts .vob .3g2 .3gp → "unknown" (downgraded to metadata text)
|
|
22
|
+
* .mpg .mpeg → "csv" (binary parsed as a spreadsheet)
|
|
23
|
+
* .ts → "text" (MPEG-TS inlined as source code)
|
|
24
|
+
*
|
|
25
|
+
* Deriving all five consumers from one table makes that class of drift
|
|
26
|
+
* structurally impossible rather than merely fixed once.
|
|
27
|
+
*
|
|
28
|
+
* ## Invariants
|
|
29
|
+
*
|
|
30
|
+
* - `extensions[0]` is canonical for the format; the rest are accepted aliases.
|
|
31
|
+
* - `mimeTypes[0]` is canonical; the rest are aliases seen in the wild
|
|
32
|
+
* (`audio/x-wav`, `image/x-icon`, vendor prefixes, …).
|
|
33
|
+
* - Every extension and every MIME type appears in exactly one entry. The
|
|
34
|
+
* `assertRegistryIsUnambiguous()` check below is executed at module load, so
|
|
35
|
+
* a duplicate is a startup failure rather than a silent last-write-wins.
|
|
36
|
+
* - `fileType` is the *routing* type the detector emits (what processor to
|
|
37
|
+
* use); `modality` is the *category* a human means. They deliberately differ:
|
|
38
|
+
* an .odt is `fileType: "docx"` (the Word processor handles it) but
|
|
39
|
+
* `modality: "document"`, and .svg is `fileType: "svg"` (sanitised as markup,
|
|
40
|
+
* never sent to a vision API) but `modality: "image"`.
|
|
41
|
+
*
|
|
42
|
+
* @module processors/config/fileTypeRegistry
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* Every file format NeuroLink can identify, grouped by modality.
|
|
46
|
+
*
|
|
47
|
+
* Adding a format here is all that is required — the extension map, the MIME
|
|
48
|
+
* map, the detector's lookup tables, the category arrays and the media
|
|
49
|
+
* processors' supported-format lists are all derived from this array.
|
|
50
|
+
*/
|
|
51
|
+
export const FILE_TYPE_REGISTRY = [
|
|
52
|
+
// ===========================================================================
|
|
53
|
+
// IMAGE
|
|
54
|
+
// ===========================================================================
|
|
55
|
+
{
|
|
56
|
+
label: "JPEG image",
|
|
57
|
+
extensions: [".jpg", ".jpeg", ".jpe", ".jfif"],
|
|
58
|
+
mimeTypes: ["image/jpeg", "image/jpg"],
|
|
59
|
+
fileType: "image",
|
|
60
|
+
modality: "image",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: "PNG image",
|
|
64
|
+
extensions: [".png"],
|
|
65
|
+
mimeTypes: ["image/png"],
|
|
66
|
+
fileType: "image",
|
|
67
|
+
modality: "image",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
label: "Animated PNG",
|
|
71
|
+
extensions: [".apng"],
|
|
72
|
+
mimeTypes: ["image/apng"],
|
|
73
|
+
fileType: "image",
|
|
74
|
+
modality: "image",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: "GIF image",
|
|
78
|
+
extensions: [".gif"],
|
|
79
|
+
mimeTypes: ["image/gif"],
|
|
80
|
+
fileType: "image",
|
|
81
|
+
modality: "image",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: "WebP image",
|
|
85
|
+
extensions: [".webp"],
|
|
86
|
+
mimeTypes: ["image/webp"],
|
|
87
|
+
fileType: "image",
|
|
88
|
+
modality: "image",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: "BMP image",
|
|
92
|
+
extensions: [".bmp", ".dib"],
|
|
93
|
+
mimeTypes: ["image/bmp", "image/x-ms-bmp"],
|
|
94
|
+
fileType: "image",
|
|
95
|
+
modality: "image",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
label: "TIFF image",
|
|
99
|
+
extensions: [".tiff", ".tif"],
|
|
100
|
+
mimeTypes: ["image/tiff", "image/x-tiff"],
|
|
101
|
+
fileType: "image",
|
|
102
|
+
modality: "image",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
label: "AVIF image",
|
|
106
|
+
extensions: [".avif"],
|
|
107
|
+
mimeTypes: ["image/avif"],
|
|
108
|
+
fileType: "image",
|
|
109
|
+
modality: "image",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: "HEIC image",
|
|
113
|
+
extensions: [".heic", ".heics"],
|
|
114
|
+
mimeTypes: ["image/heic", "image/heic-sequence"],
|
|
115
|
+
fileType: "image",
|
|
116
|
+
modality: "image",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: "HEIF image",
|
|
120
|
+
extensions: [".heif", ".heifs"],
|
|
121
|
+
mimeTypes: ["image/heif", "image/heif-sequence"],
|
|
122
|
+
fileType: "image",
|
|
123
|
+
modality: "image",
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
label: "JPEG 2000 image",
|
|
127
|
+
extensions: [".jp2", ".j2k", ".jpf", ".jpx"],
|
|
128
|
+
mimeTypes: ["image/jp2", "image/jpx"],
|
|
129
|
+
fileType: "image",
|
|
130
|
+
modality: "image",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
label: "Icon",
|
|
134
|
+
extensions: [".ico"],
|
|
135
|
+
mimeTypes: ["image/x-icon", "image/vnd.microsoft.icon"],
|
|
136
|
+
fileType: "image",
|
|
137
|
+
modality: "image",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
// SVG is markup, not raster. It is sanitised and passed as text — no vision
|
|
141
|
+
// provider accepts image/svg+xml — hence fileType "svg" rather than "image".
|
|
142
|
+
label: "SVG image",
|
|
143
|
+
extensions: [".svg", ".svgz"],
|
|
144
|
+
mimeTypes: ["image/svg+xml"],
|
|
145
|
+
fileType: "svg",
|
|
146
|
+
modality: "image",
|
|
147
|
+
},
|
|
148
|
+
// ===========================================================================
|
|
149
|
+
// VIDEO
|
|
150
|
+
// ===========================================================================
|
|
151
|
+
{
|
|
152
|
+
label: "MP4 video",
|
|
153
|
+
extensions: [".mp4", ".m4v"],
|
|
154
|
+
mimeTypes: ["video/mp4", "video/x-m4v"],
|
|
155
|
+
fileType: "video",
|
|
156
|
+
modality: "video",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
label: "QuickTime video",
|
|
160
|
+
extensions: [".mov", ".qt"],
|
|
161
|
+
mimeTypes: ["video/quicktime"],
|
|
162
|
+
fileType: "video",
|
|
163
|
+
modality: "video",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
label: "Matroska video",
|
|
167
|
+
extensions: [".mkv"],
|
|
168
|
+
mimeTypes: ["video/x-matroska"],
|
|
169
|
+
fileType: "video",
|
|
170
|
+
modality: "video",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
label: "WebM video",
|
|
174
|
+
extensions: [".webm"],
|
|
175
|
+
mimeTypes: ["video/webm"],
|
|
176
|
+
fileType: "video",
|
|
177
|
+
modality: "video",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
label: "AVI video",
|
|
181
|
+
extensions: [".avi"],
|
|
182
|
+
mimeTypes: ["video/x-msvideo", "video/avi", "video/msvideo"],
|
|
183
|
+
fileType: "video",
|
|
184
|
+
modality: "video",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
label: "Windows Media video",
|
|
188
|
+
extensions: [".wmv", ".asf"],
|
|
189
|
+
mimeTypes: ["video/x-ms-wmv", "video/x-ms-asf"],
|
|
190
|
+
fileType: "video",
|
|
191
|
+
modality: "video",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
label: "Flash video",
|
|
195
|
+
extensions: [".flv", ".f4v"],
|
|
196
|
+
mimeTypes: ["video/x-flv"],
|
|
197
|
+
fileType: "video",
|
|
198
|
+
modality: "video",
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
// MPEG-1/2 program stream. Detected as CSV before this registry existed,
|
|
202
|
+
// because the content heuristics saw consistent delimiters in the binary.
|
|
203
|
+
label: "MPEG video",
|
|
204
|
+
extensions: [".mpg", ".mpeg", ".mpe", ".m1v", ".m2v", ".vob"],
|
|
205
|
+
mimeTypes: ["video/mpeg", "video/x-mpeg"],
|
|
206
|
+
fileType: "video",
|
|
207
|
+
modality: "video",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
// MPEG-2 transport stream. `.ts` collides with TypeScript source; the
|
|
211
|
+
// extension resolves to TypeScript (overwhelmingly the common case in a
|
|
212
|
+
// TypeScript SDK) and content detection reclaims genuine transport streams
|
|
213
|
+
// via their 0x47 sync pattern. See TYPESCRIPT_AMBIGUOUS_EXTENSIONS.
|
|
214
|
+
label: "MPEG transport stream",
|
|
215
|
+
extensions: [".m2ts", ".mts", ".ts", ".m2t"],
|
|
216
|
+
// Lookups lowercase before matching, so the IANA-registered "video/MP2T"
|
|
217
|
+
// spelling resolves through the same entry without a separate alias.
|
|
218
|
+
mimeTypes: ["video/mp2t"],
|
|
219
|
+
fileType: "video",
|
|
220
|
+
modality: "video",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
label: "3GPP video",
|
|
224
|
+
extensions: [".3gp", ".3gpp"],
|
|
225
|
+
mimeTypes: ["video/3gpp"],
|
|
226
|
+
fileType: "video",
|
|
227
|
+
modality: "video",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
label: "3GPP2 video",
|
|
231
|
+
extensions: [".3g2", ".3gpp2"],
|
|
232
|
+
mimeTypes: ["video/3gpp2"],
|
|
233
|
+
fileType: "video",
|
|
234
|
+
modality: "video",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
label: "Ogg video",
|
|
238
|
+
extensions: [".ogv"],
|
|
239
|
+
mimeTypes: ["video/ogg"],
|
|
240
|
+
fileType: "video",
|
|
241
|
+
modality: "video",
|
|
242
|
+
},
|
|
243
|
+
// ===========================================================================
|
|
244
|
+
// AUDIO
|
|
245
|
+
// ===========================================================================
|
|
246
|
+
{
|
|
247
|
+
label: "MP3 audio",
|
|
248
|
+
extensions: [".mp3", ".mp2", ".mpga"],
|
|
249
|
+
mimeTypes: ["audio/mpeg", "audio/mp3", "audio/x-mpeg"],
|
|
250
|
+
fileType: "audio",
|
|
251
|
+
modality: "audio",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
label: "WAV audio",
|
|
255
|
+
extensions: [".wav", ".wave"],
|
|
256
|
+
mimeTypes: ["audio/wav", "audio/x-wav", "audio/wave", "audio/vnd.wave"],
|
|
257
|
+
fileType: "audio",
|
|
258
|
+
modality: "audio",
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
label: "FLAC audio",
|
|
262
|
+
extensions: [".flac"],
|
|
263
|
+
mimeTypes: ["audio/flac", "audio/x-flac"],
|
|
264
|
+
fileType: "audio",
|
|
265
|
+
modality: "audio",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
label: "Ogg audio",
|
|
269
|
+
extensions: [".ogg", ".oga"],
|
|
270
|
+
mimeTypes: ["audio/ogg", "audio/vorbis", "audio/x-vorbis+ogg"],
|
|
271
|
+
fileType: "audio",
|
|
272
|
+
modality: "audio",
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
label: "Opus audio",
|
|
276
|
+
extensions: [".opus"],
|
|
277
|
+
mimeTypes: ["audio/opus"],
|
|
278
|
+
fileType: "audio",
|
|
279
|
+
modality: "audio",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
label: "MPEG-4 audio",
|
|
283
|
+
extensions: [".m4a", ".m4b", ".m4p"],
|
|
284
|
+
mimeTypes: ["audio/mp4", "audio/x-m4a", "audio/m4a"],
|
|
285
|
+
fileType: "audio",
|
|
286
|
+
modality: "audio",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
label: "AAC audio",
|
|
290
|
+
extensions: [".aac", ".adts"],
|
|
291
|
+
mimeTypes: ["audio/aac", "audio/aacp", "audio/x-aac"],
|
|
292
|
+
fileType: "audio",
|
|
293
|
+
modality: "audio",
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
label: "Windows Media audio",
|
|
297
|
+
extensions: [".wma"],
|
|
298
|
+
mimeTypes: ["audio/x-ms-wma"],
|
|
299
|
+
fileType: "audio",
|
|
300
|
+
modality: "audio",
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
label: "AIFF audio",
|
|
304
|
+
extensions: [".aiff", ".aif", ".aifc"],
|
|
305
|
+
mimeTypes: ["audio/aiff", "audio/x-aiff"],
|
|
306
|
+
fileType: "audio",
|
|
307
|
+
modality: "audio",
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
label: "AMR audio",
|
|
311
|
+
extensions: [".amr"],
|
|
312
|
+
mimeTypes: ["audio/amr", "audio/3gpp"],
|
|
313
|
+
fileType: "audio",
|
|
314
|
+
modality: "audio",
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
label: "Monkey's Audio",
|
|
318
|
+
extensions: [".ape"],
|
|
319
|
+
mimeTypes: ["audio/x-ape", "audio/ape"],
|
|
320
|
+
fileType: "audio",
|
|
321
|
+
modality: "audio",
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
label: "WavPack audio",
|
|
325
|
+
extensions: [".wv"],
|
|
326
|
+
mimeTypes: ["audio/x-wavpack", "audio/wavpack"],
|
|
327
|
+
fileType: "audio",
|
|
328
|
+
modality: "audio",
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
label: "MIDI",
|
|
332
|
+
extensions: [".mid", ".midi"],
|
|
333
|
+
mimeTypes: ["audio/midi", "audio/x-midi"],
|
|
334
|
+
fileType: "audio",
|
|
335
|
+
modality: "audio",
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
label: "Core Audio",
|
|
339
|
+
extensions: [".caf"],
|
|
340
|
+
mimeTypes: ["audio/x-caf"],
|
|
341
|
+
fileType: "audio",
|
|
342
|
+
modality: "audio",
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
label: "Sun audio",
|
|
346
|
+
extensions: [".au", ".snd"],
|
|
347
|
+
mimeTypes: ["audio/basic"],
|
|
348
|
+
fileType: "audio",
|
|
349
|
+
modality: "audio",
|
|
350
|
+
},
|
|
351
|
+
// ===========================================================================
|
|
352
|
+
// DOCUMENTS
|
|
353
|
+
// ===========================================================================
|
|
354
|
+
{
|
|
355
|
+
label: "PDF document",
|
|
356
|
+
extensions: [".pdf"],
|
|
357
|
+
mimeTypes: ["application/pdf", "application/x-pdf"],
|
|
358
|
+
fileType: "pdf",
|
|
359
|
+
modality: "document",
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
label: "Word document",
|
|
363
|
+
extensions: [".docx"],
|
|
364
|
+
mimeTypes: [
|
|
365
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
366
|
+
],
|
|
367
|
+
fileType: "docx",
|
|
368
|
+
modality: "document",
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
label: "Legacy Word document",
|
|
372
|
+
extensions: [".doc"],
|
|
373
|
+
mimeTypes: ["application/msword"],
|
|
374
|
+
fileType: "docx",
|
|
375
|
+
modality: "document",
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
label: "Excel spreadsheet",
|
|
379
|
+
extensions: [".xlsx"],
|
|
380
|
+
mimeTypes: [
|
|
381
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
382
|
+
],
|
|
383
|
+
fileType: "xlsx",
|
|
384
|
+
modality: "document",
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
label: "Legacy Excel spreadsheet",
|
|
388
|
+
extensions: [".xls"],
|
|
389
|
+
mimeTypes: ["application/vnd.ms-excel"],
|
|
390
|
+
fileType: "xlsx",
|
|
391
|
+
modality: "document",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
label: "PowerPoint presentation",
|
|
395
|
+
extensions: [".pptx"],
|
|
396
|
+
mimeTypes: [
|
|
397
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
398
|
+
],
|
|
399
|
+
fileType: "pptx",
|
|
400
|
+
modality: "document",
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
label: "Legacy PowerPoint presentation",
|
|
404
|
+
extensions: [".ppt"],
|
|
405
|
+
mimeTypes: ["application/vnd.ms-powerpoint"],
|
|
406
|
+
fileType: "pptx",
|
|
407
|
+
modality: "document",
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
label: "OpenDocument text",
|
|
411
|
+
extensions: [".odt"],
|
|
412
|
+
mimeTypes: ["application/vnd.oasis.opendocument.text"],
|
|
413
|
+
fileType: "docx",
|
|
414
|
+
modality: "document",
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
label: "OpenDocument spreadsheet",
|
|
418
|
+
extensions: [".ods"],
|
|
419
|
+
mimeTypes: ["application/vnd.oasis.opendocument.spreadsheet"],
|
|
420
|
+
fileType: "xlsx",
|
|
421
|
+
modality: "document",
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
label: "OpenDocument presentation",
|
|
425
|
+
extensions: [".odp"],
|
|
426
|
+
mimeTypes: ["application/vnd.oasis.opendocument.presentation"],
|
|
427
|
+
fileType: "pptx",
|
|
428
|
+
modality: "document",
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
label: "Rich Text Format",
|
|
432
|
+
extensions: [".rtf"],
|
|
433
|
+
mimeTypes: ["application/rtf", "text/rtf"],
|
|
434
|
+
fileType: "docx",
|
|
435
|
+
modality: "document",
|
|
436
|
+
},
|
|
437
|
+
// ===========================================================================
|
|
438
|
+
// TABULAR DATA
|
|
439
|
+
// ===========================================================================
|
|
440
|
+
{
|
|
441
|
+
label: "CSV data",
|
|
442
|
+
extensions: [".csv"],
|
|
443
|
+
mimeTypes: ["text/csv", "application/csv"],
|
|
444
|
+
fileType: "csv",
|
|
445
|
+
modality: "data",
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
label: "TSV data",
|
|
449
|
+
extensions: [".tsv", ".tab"],
|
|
450
|
+
mimeTypes: ["text/tab-separated-values"],
|
|
451
|
+
fileType: "csv",
|
|
452
|
+
modality: "data",
|
|
453
|
+
},
|
|
454
|
+
// ===========================================================================
|
|
455
|
+
// ARCHIVES
|
|
456
|
+
// ===========================================================================
|
|
457
|
+
{
|
|
458
|
+
label: "ZIP archive",
|
|
459
|
+
extensions: [".zip"],
|
|
460
|
+
mimeTypes: ["application/zip", "application/x-zip-compressed"],
|
|
461
|
+
fileType: "archive",
|
|
462
|
+
modality: "archive",
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
label: "TAR archive",
|
|
466
|
+
extensions: [".tar"],
|
|
467
|
+
mimeTypes: ["application/x-tar"],
|
|
468
|
+
fileType: "archive",
|
|
469
|
+
modality: "archive",
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
label: "GZIP archive",
|
|
473
|
+
extensions: [".gz", ".tgz", ".tar.gz"],
|
|
474
|
+
mimeTypes: ["application/gzip", "application/x-gzip"],
|
|
475
|
+
fileType: "archive",
|
|
476
|
+
modality: "archive",
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
label: "BZIP2 archive",
|
|
480
|
+
extensions: [".bz2", ".tbz2", ".tar.bz2"],
|
|
481
|
+
mimeTypes: ["application/x-bzip2"],
|
|
482
|
+
fileType: "archive",
|
|
483
|
+
modality: "archive",
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
label: "XZ archive",
|
|
487
|
+
extensions: [".xz", ".txz", ".tar.xz"],
|
|
488
|
+
mimeTypes: ["application/x-xz"],
|
|
489
|
+
fileType: "archive",
|
|
490
|
+
modality: "archive",
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
label: "Zstandard archive",
|
|
494
|
+
extensions: [".zst"],
|
|
495
|
+
mimeTypes: ["application/zstd"],
|
|
496
|
+
fileType: "archive",
|
|
497
|
+
modality: "archive",
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
label: "RAR archive",
|
|
501
|
+
extensions: [".rar"],
|
|
502
|
+
mimeTypes: ["application/x-rar-compressed", "application/vnd.rar"],
|
|
503
|
+
fileType: "archive",
|
|
504
|
+
modality: "archive",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
label: "7-Zip archive",
|
|
508
|
+
extensions: [".7z"],
|
|
509
|
+
mimeTypes: ["application/x-7z-compressed"],
|
|
510
|
+
fileType: "archive",
|
|
511
|
+
modality: "archive",
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
label: "Java archive",
|
|
515
|
+
extensions: [".jar"],
|
|
516
|
+
mimeTypes: ["application/java-archive"],
|
|
517
|
+
fileType: "archive",
|
|
518
|
+
modality: "archive",
|
|
519
|
+
},
|
|
520
|
+
];
|
|
521
|
+
// =============================================================================
|
|
522
|
+
// DERIVED LOOKUPS
|
|
523
|
+
// =============================================================================
|
|
524
|
+
/**
|
|
525
|
+
* Extensions whose format is genuinely ambiguous and whose non-media reading
|
|
526
|
+
* wins when only the filename is available.
|
|
527
|
+
*
|
|
528
|
+
* `.ts` is both MPEG-2 transport stream and TypeScript source. In a TypeScript
|
|
529
|
+
* SDK the source reading is overwhelmingly more likely, so the extension
|
|
530
|
+
* resolves to TypeScript; a real transport stream is still recognised by
|
|
531
|
+
* content (its 0x47 sync byte repeating at a 188- or 192-byte stride), and
|
|
532
|
+
* content detection runs first, so the correct answer wins whenever the bytes
|
|
533
|
+
* are available.
|
|
534
|
+
*
|
|
535
|
+
* The override carries the MIME type as well as the routing type. Overriding
|
|
536
|
+
* only the routing type produced a self-contradictory answer — a .ts file came
|
|
537
|
+
* back as `type: "text"` with `mimeType: "video/mp2t"`, because the derived
|
|
538
|
+
* `EXTENSION_MIME_MAP` still took its MIME from the transport-stream entry.
|
|
539
|
+
* That value is public API and reaches telemetry, so both halves must agree.
|
|
540
|
+
*/
|
|
541
|
+
const EXTENSION_OVERRIDES = {
|
|
542
|
+
".ts": {
|
|
543
|
+
fileType: "text",
|
|
544
|
+
mimeType: "text/typescript",
|
|
545
|
+
// `null`, not "document": TypeScript source belongs to no media modality at
|
|
546
|
+
// all. Naming one would be wrong in both directions — it would put .ts into
|
|
547
|
+
// DOCUMENT_EXTENSIONS, and it could never be honoured symmetrically anyway,
|
|
548
|
+
// because extensionsForModality() iterates registry entries and .ts lives
|
|
549
|
+
// in the transport-stream entry. An override can remove an extension from
|
|
550
|
+
// its entry's modality; it cannot move it into another entry's. `null`
|
|
551
|
+
// makes both helpers agree by construction: listed under no modality, and
|
|
552
|
+
// never reported as belonging to one.
|
|
553
|
+
modality: null,
|
|
554
|
+
},
|
|
555
|
+
};
|
|
556
|
+
/**
|
|
557
|
+
* Extensions that must NOT be treated as belonging to their registry entry's
|
|
558
|
+
* modality when only a filename is available.
|
|
559
|
+
*
|
|
560
|
+
* Derived from {@link EXTENSION_OVERRIDES}: `extensionsForModality("video")`
|
|
561
|
+
* feeds both `VIDEO_EXTENSIONS` and `VideoProcessor`'s supported list, and
|
|
562
|
+
* `BaseFileProcessor.isFileSupported` accepts a file when the *extension*
|
|
563
|
+
* matches even if the supplied mimetype says otherwise. Leaving `.ts` in that
|
|
564
|
+
* list made `isVideoFile("text/plain", "app.ts")` return true.
|
|
565
|
+
*/
|
|
566
|
+
/**
|
|
567
|
+
* Extension → MIME for the overridden extensions only.
|
|
568
|
+
*
|
|
569
|
+
* Exported so `EXTENSION_MIME_MAP` can apply the override as its FINAL layer.
|
|
570
|
+
* That map is built by spreading the registry over the text map, so without
|
|
571
|
+
* this the registry's `.ts → video/mp2t` won again and `getMimeTypeForExtension`
|
|
572
|
+
* disagreed with `mimeTypeForExtension` about the very same file.
|
|
573
|
+
*/
|
|
574
|
+
export const EXTENSION_MIME_OVERRIDES = Object.fromEntries(Object.entries(EXTENSION_OVERRIDES).map(([ext, o]) => [ext, o.mimeType]));
|
|
575
|
+
const extensionToEntry = new Map();
|
|
576
|
+
const mimeTypeToEntry = new Map();
|
|
577
|
+
/**
|
|
578
|
+
* Fail loudly at module load if two entries claim the same extension or MIME
|
|
579
|
+
* type. Silently letting the later entry win is exactly how the five legacy
|
|
580
|
+
* tables drifted apart in the first place.
|
|
581
|
+
*/
|
|
582
|
+
function assertRegistryIsUnambiguous() {
|
|
583
|
+
for (const entry of FILE_TYPE_REGISTRY) {
|
|
584
|
+
for (const ext of entry.extensions) {
|
|
585
|
+
const existing = extensionToEntry.get(ext);
|
|
586
|
+
if (existing) {
|
|
587
|
+
throw new Error(`File-type registry: extension "${ext}" is claimed by both ` +
|
|
588
|
+
`"${existing.label}" and "${entry.label}".`);
|
|
589
|
+
}
|
|
590
|
+
extensionToEntry.set(ext, entry);
|
|
591
|
+
}
|
|
592
|
+
for (const mime of entry.mimeTypes) {
|
|
593
|
+
const key = mime.toLowerCase();
|
|
594
|
+
const existing = mimeTypeToEntry.get(key);
|
|
595
|
+
if (existing) {
|
|
596
|
+
throw new Error(`File-type registry: MIME type "${mime}" is claimed by both ` +
|
|
597
|
+
`"${existing.label}" and "${entry.label}".`);
|
|
598
|
+
}
|
|
599
|
+
mimeTypeToEntry.set(key, entry);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
assertRegistryIsUnambiguous();
|
|
604
|
+
/**
|
|
605
|
+
* Normalize an extension to the registry's key form: lowercased, with a leading
|
|
606
|
+
* dot. Accepts `"png"`, `".PNG"`, `"image.png"` and `"/tmp/a.png"` alike.
|
|
607
|
+
*
|
|
608
|
+
* Compound extensions (`.tar.gz`) are matched before the single-segment form so
|
|
609
|
+
* a `.tar.gz` resolves to the gzip entry rather than to `.gz` by accident —
|
|
610
|
+
* they happen to agree today, but the lookup should not depend on that.
|
|
611
|
+
*/
|
|
612
|
+
export function normalizeExtension(raw) {
|
|
613
|
+
const lower = raw.trim().toLowerCase();
|
|
614
|
+
if (!lower) {
|
|
615
|
+
return "";
|
|
616
|
+
}
|
|
617
|
+
const withoutPath = lower.slice(Math.max(lower.lastIndexOf("/"), lower.lastIndexOf("\\")) + 1);
|
|
618
|
+
const firstDot = withoutPath.indexOf(".");
|
|
619
|
+
if (firstDot === -1) {
|
|
620
|
+
return `.${withoutPath}`;
|
|
621
|
+
}
|
|
622
|
+
// Walk every suffix longest-first so a registered compound extension wins over
|
|
623
|
+
// its last segment: "release.tar.gz" must resolve to ".tar.gz", and starting
|
|
624
|
+
// at the *first* dot alone would miss it for "v1.0.tar.gz".
|
|
625
|
+
for (let i = firstDot; i !== -1; i = withoutPath.indexOf(".", i + 1)) {
|
|
626
|
+
const suffix = withoutPath.slice(i);
|
|
627
|
+
if (extensionToEntry.has(suffix)) {
|
|
628
|
+
return suffix;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return withoutPath.slice(withoutPath.lastIndexOf("."));
|
|
632
|
+
}
|
|
633
|
+
/** Look up the registry entry for a file extension, or undefined. */
|
|
634
|
+
export function lookupByExtension(ext) {
|
|
635
|
+
return extensionToEntry.get(normalizeExtension(ext));
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Look up the registry entry for a MIME type, or undefined. Any `;charset=…`
|
|
639
|
+
* parameter is stripped, matching how Content-Type headers arrive.
|
|
640
|
+
*/
|
|
641
|
+
export function lookupByMimeType(mime) {
|
|
642
|
+
return mimeTypeToEntry.get(mime.split(";")[0].trim().toLowerCase());
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Routing {@link FileType} for an extension, honouring {@link EXTENSION_OVERRIDES}.
|
|
646
|
+
* Returns undefined when the extension is not in the registry.
|
|
647
|
+
*/
|
|
648
|
+
export function fileTypeForExtension(ext) {
|
|
649
|
+
const normalized = normalizeExtension(ext);
|
|
650
|
+
const override = EXTENSION_OVERRIDES[normalized];
|
|
651
|
+
if (override) {
|
|
652
|
+
return override.fileType;
|
|
653
|
+
}
|
|
654
|
+
return extensionToEntry.get(normalized)?.fileType;
|
|
655
|
+
}
|
|
656
|
+
/** Canonical MIME type for an extension, or undefined when unknown. */
|
|
657
|
+
export function mimeTypeForExtension(ext) {
|
|
658
|
+
const normalized = normalizeExtension(ext);
|
|
659
|
+
// Honour the override here too — see EXTENSION_OVERRIDES for why the routing
|
|
660
|
+
// type and the MIME type must not be allowed to disagree.
|
|
661
|
+
return (EXTENSION_OVERRIDES[normalized]?.mimeType ??
|
|
662
|
+
extensionToEntry.get(normalized)?.mimeTypes[0]);
|
|
663
|
+
}
|
|
664
|
+
/** Canonical extension (with leading dot) for a MIME type, or undefined. */
|
|
665
|
+
export function extensionForMimeType(mime) {
|
|
666
|
+
return lookupByMimeType(mime)?.extensions[0];
|
|
667
|
+
}
|
|
668
|
+
/** Every extension registered for a modality, in registry order. */
|
|
669
|
+
export function extensionsForModality(modality) {
|
|
670
|
+
return FILE_TYPE_REGISTRY.filter((e) => e.modality === modality)
|
|
671
|
+
.flatMap((e) => [...e.extensions])
|
|
672
|
+
.filter((ext) => {
|
|
673
|
+
const override = EXTENSION_OVERRIDES[ext];
|
|
674
|
+
// An overridden extension only belongs to the modality it was overridden
|
|
675
|
+
// TO, so `.ts` leaves the video lists and cannot be matched as video on
|
|
676
|
+
// filename alone. Content detection still reclaims real streams.
|
|
677
|
+
return !override || override.modality === modality;
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
/** Every MIME type registered for a modality, in registry order. */
|
|
681
|
+
export function mimeTypesForModality(modality) {
|
|
682
|
+
return FILE_TYPE_REGISTRY.filter((e) => e.modality === modality).flatMap((e) => [...e.mimeTypes]);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* True when the extension belongs to the given modality. Uses the entry's
|
|
686
|
+
* modality, not its routing `fileType`, so `.svg` counts as an image and `.odt`
|
|
687
|
+
* counts as a document.
|
|
688
|
+
*/
|
|
689
|
+
export function isModalityExtension(ext, modality) {
|
|
690
|
+
// Must consult EXTENSION_OVERRIDES for the same reason extensionsForModality
|
|
691
|
+
// does — otherwise the two disagree about the same extension, and `.ts` is
|
|
692
|
+
// absent from the video list yet still answers true to "is this video?".
|
|
693
|
+
const override = EXTENSION_OVERRIDES[normalizeExtension(ext)];
|
|
694
|
+
if (override) {
|
|
695
|
+
// A null override modality means "no media modality", so this is false for
|
|
696
|
+
// every modality — matching extensionsForModality(), which lists it under
|
|
697
|
+
// none. See EXTENSION_OVERRIDES for why the two must agree.
|
|
698
|
+
return override.modality === modality;
|
|
699
|
+
}
|
|
700
|
+
return lookupByExtension(ext)?.modality === modality;
|
|
701
|
+
}
|
|
702
|
+
//# sourceMappingURL=fileTypeRegistry.js.map
|