@better-media/plugin-validation 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,2957 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs/promises');
4
+ var crypto = require('crypto');
5
+ var core = require('@better-media/core');
6
+ require('stream/web');
7
+ require('stream');
8
+ var path = require('path');
9
+ var imageSize = require('image-size');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
14
+ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
15
+ var path__default = /*#__PURE__*/_interopDefault(path);
16
+
17
+ var __create = Object.create;
18
+ var __defProp = Object.defineProperty;
19
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
20
+ var __getOwnPropNames = Object.getOwnPropertyNames;
21
+ var __getProtoOf = Object.getPrototypeOf;
22
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
23
+ var __commonJS = (cb, mod) => function __require() {
24
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
25
+ };
26
+ var __copyProps = (to, from, except, desc) => {
27
+ if (from && typeof from === "object" || typeof from === "function") {
28
+ for (let key of __getOwnPropNames(from))
29
+ if (!__hasOwnProp.call(to, key) && key !== except)
30
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
31
+ }
32
+ return to;
33
+ };
34
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
35
+ // If the importer is in node compatibility mode or this is not an ESM
36
+ // file that has been converted to a CommonJS file using a Babel-
37
+ // compatible transform (i.e. "__esModule" has not been set), then set
38
+ // "default" to the CommonJS "module.exports" for node compatibility.
39
+ __defProp(target, "default", { value: mod, enumerable: true }) ,
40
+ mod
41
+ ));
42
+
43
+ // ../../../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
44
+ var require_ieee754 = __commonJS({
45
+ "../../../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js"(exports$1) {
46
+ exports$1.read = function(buffer, offset, isLE, mLen, nBytes) {
47
+ var e, m;
48
+ var eLen = nBytes * 8 - mLen - 1;
49
+ var eMax = (1 << eLen) - 1;
50
+ var eBias = eMax >> 1;
51
+ var nBits = -7;
52
+ var i = isLE ? nBytes - 1 : 0;
53
+ var d = isLE ? -1 : 1;
54
+ var s = buffer[offset + i];
55
+ i += d;
56
+ e = s & (1 << -nBits) - 1;
57
+ s >>= -nBits;
58
+ nBits += eLen;
59
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {
60
+ }
61
+ m = e & (1 << -nBits) - 1;
62
+ e >>= -nBits;
63
+ nBits += mLen;
64
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {
65
+ }
66
+ if (e === 0) {
67
+ e = 1 - eBias;
68
+ } else if (e === eMax) {
69
+ return m ? NaN : (s ? -1 : 1) * Infinity;
70
+ } else {
71
+ m = m + Math.pow(2, mLen);
72
+ e = e - eBias;
73
+ }
74
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
75
+ };
76
+ exports$1.write = function(buffer, value, offset, isLE, mLen, nBytes) {
77
+ var e, m, c;
78
+ var eLen = nBytes * 8 - mLen - 1;
79
+ var eMax = (1 << eLen) - 1;
80
+ var eBias = eMax >> 1;
81
+ var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
82
+ var i = isLE ? 0 : nBytes - 1;
83
+ var d = isLE ? 1 : -1;
84
+ var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
85
+ value = Math.abs(value);
86
+ if (isNaN(value) || value === Infinity) {
87
+ m = isNaN(value) ? 1 : 0;
88
+ e = eMax;
89
+ } else {
90
+ e = Math.floor(Math.log(value) / Math.LN2);
91
+ if (value * (c = Math.pow(2, -e)) < 1) {
92
+ e--;
93
+ c *= 2;
94
+ }
95
+ if (e + eBias >= 1) {
96
+ value += rt / c;
97
+ } else {
98
+ value += rt * Math.pow(2, 1 - eBias);
99
+ }
100
+ if (value * c >= 2) {
101
+ e++;
102
+ c /= 2;
103
+ }
104
+ if (e + eBias >= eMax) {
105
+ m = 0;
106
+ e = eMax;
107
+ } else if (e + eBias >= 1) {
108
+ m = (value * c - 1) * Math.pow(2, mLen);
109
+ e = e + eBias;
110
+ } else {
111
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
112
+ e = 0;
113
+ }
114
+ }
115
+ for (; mLen >= 8; buffer[offset + i] = m & 255, i += d, m /= 256, mLen -= 8) {
116
+ }
117
+ e = e << mLen | m;
118
+ eLen += mLen;
119
+ for (; eLen > 0; buffer[offset + i] = e & 255, i += d, e /= 256, eLen -= 8) {
120
+ }
121
+ buffer[offset + i - d] |= s * 128;
122
+ };
123
+ }
124
+ });
125
+
126
+ // ../../../node_modules/.pnpm/peek-readable@5.4.2/node_modules/peek-readable/lib/EndOfStreamError.js
127
+ var defaultMessages = "End-Of-Stream";
128
+ var EndOfStreamError = class extends Error {
129
+ constructor() {
130
+ super(defaultMessages);
131
+ }
132
+ };
133
+
134
+ // ../../../node_modules/.pnpm/peek-readable@5.4.2/node_modules/peek-readable/lib/AbstractStreamReader.js
135
+ var AbstractStreamReader = class {
136
+ constructor() {
137
+ this.maxStreamReadSize = 1 * 1024 * 1024;
138
+ this.endOfStream = false;
139
+ this.peekQueue = [];
140
+ }
141
+ async peek(uint8Array, offset, length) {
142
+ const bytesRead = await this.read(uint8Array, offset, length);
143
+ this.peekQueue.push(uint8Array.subarray(offset, offset + bytesRead));
144
+ return bytesRead;
145
+ }
146
+ async read(buffer, offset, length) {
147
+ if (length === 0) {
148
+ return 0;
149
+ }
150
+ let bytesRead = this.readFromPeekBuffer(buffer, offset, length);
151
+ bytesRead += await this.readRemainderFromStream(buffer, offset + bytesRead, length - bytesRead);
152
+ if (bytesRead === 0) {
153
+ throw new EndOfStreamError();
154
+ }
155
+ return bytesRead;
156
+ }
157
+ /**
158
+ * Read chunk from stream
159
+ * @param buffer - Target Uint8Array (or Buffer) to store data read from stream in
160
+ * @param offset - Offset target
161
+ * @param length - Number of bytes to read
162
+ * @returns Number of bytes read
163
+ */
164
+ readFromPeekBuffer(buffer, offset, length) {
165
+ let remaining = length;
166
+ let bytesRead = 0;
167
+ while (this.peekQueue.length > 0 && remaining > 0) {
168
+ const peekData = this.peekQueue.pop();
169
+ if (!peekData)
170
+ throw new Error("peekData should be defined");
171
+ const lenCopy = Math.min(peekData.length, remaining);
172
+ buffer.set(peekData.subarray(0, lenCopy), offset + bytesRead);
173
+ bytesRead += lenCopy;
174
+ remaining -= lenCopy;
175
+ if (lenCopy < peekData.length) {
176
+ this.peekQueue.push(peekData.subarray(lenCopy));
177
+ }
178
+ }
179
+ return bytesRead;
180
+ }
181
+ async readRemainderFromStream(buffer, offset, initialRemaining) {
182
+ let remaining = initialRemaining;
183
+ let bytesRead = 0;
184
+ while (remaining > 0 && !this.endOfStream) {
185
+ const reqLen = Math.min(remaining, this.maxStreamReadSize);
186
+ const chunkLen = await this.readFromStream(buffer, offset + bytesRead, reqLen);
187
+ if (chunkLen === 0)
188
+ break;
189
+ bytesRead += chunkLen;
190
+ remaining -= chunkLen;
191
+ }
192
+ return bytesRead;
193
+ }
194
+ };
195
+
196
+ // ../../../node_modules/.pnpm/peek-readable@5.4.2/node_modules/peek-readable/lib/WebStreamReader.js
197
+ var WebStreamReader = class extends AbstractStreamReader {
198
+ constructor(stream) {
199
+ super();
200
+ this.reader = stream.getReader({ mode: "byob" });
201
+ }
202
+ async readFromStream(buffer, offset, length) {
203
+ if (this.endOfStream) {
204
+ throw new EndOfStreamError();
205
+ }
206
+ const result = await this.reader.read(new Uint8Array(length));
207
+ if (result.done) {
208
+ this.endOfStream = result.done;
209
+ }
210
+ if (result.value) {
211
+ buffer.set(result.value, offset);
212
+ return result.value.byteLength;
213
+ }
214
+ return 0;
215
+ }
216
+ abort() {
217
+ return this.reader.cancel();
218
+ }
219
+ async close() {
220
+ await this.abort();
221
+ this.reader.releaseLock();
222
+ }
223
+ };
224
+
225
+ // ../../../node_modules/.pnpm/strtok3@9.1.1/node_modules/strtok3/lib/AbstractTokenizer.js
226
+ var AbstractTokenizer = class {
227
+ /**
228
+ * Constructor
229
+ * @param options Tokenizer options
230
+ * @protected
231
+ */
232
+ constructor(options) {
233
+ this.numBuffer = new Uint8Array(8);
234
+ this.position = 0;
235
+ this.onClose = options?.onClose;
236
+ if (options?.abortSignal) {
237
+ options.abortSignal.addEventListener("abort", () => {
238
+ this.abort();
239
+ });
240
+ }
241
+ }
242
+ /**
243
+ * Read a token from the tokenizer-stream
244
+ * @param token - The token to read
245
+ * @param position - If provided, the desired position in the tokenizer-stream
246
+ * @returns Promise with token data
247
+ */
248
+ async readToken(token, position = this.position) {
249
+ const uint8Array = new Uint8Array(token.len);
250
+ const len = await this.readBuffer(uint8Array, { position });
251
+ if (len < token.len)
252
+ throw new EndOfStreamError();
253
+ return token.get(uint8Array, 0);
254
+ }
255
+ /**
256
+ * Peek a token from the tokenizer-stream.
257
+ * @param token - Token to peek from the tokenizer-stream.
258
+ * @param position - Offset where to begin reading within the file. If position is null, data will be read from the current file position.
259
+ * @returns Promise with token data
260
+ */
261
+ async peekToken(token, position = this.position) {
262
+ const uint8Array = new Uint8Array(token.len);
263
+ const len = await this.peekBuffer(uint8Array, { position });
264
+ if (len < token.len)
265
+ throw new EndOfStreamError();
266
+ return token.get(uint8Array, 0);
267
+ }
268
+ /**
269
+ * Read a numeric token from the stream
270
+ * @param token - Numeric token
271
+ * @returns Promise with number
272
+ */
273
+ async readNumber(token) {
274
+ const len = await this.readBuffer(this.numBuffer, { length: token.len });
275
+ if (len < token.len)
276
+ throw new EndOfStreamError();
277
+ return token.get(this.numBuffer, 0);
278
+ }
279
+ /**
280
+ * Read a numeric token from the stream
281
+ * @param token - Numeric token
282
+ * @returns Promise with number
283
+ */
284
+ async peekNumber(token) {
285
+ const len = await this.peekBuffer(this.numBuffer, { length: token.len });
286
+ if (len < token.len)
287
+ throw new EndOfStreamError();
288
+ return token.get(this.numBuffer, 0);
289
+ }
290
+ /**
291
+ * Ignore number of bytes, advances the pointer in under tokenizer-stream.
292
+ * @param length - Number of bytes to ignore
293
+ * @return resolves the number of bytes ignored, equals length if this available, otherwise the number of bytes available
294
+ */
295
+ async ignore(length) {
296
+ if (this.fileInfo.size !== void 0) {
297
+ const bytesLeft = this.fileInfo.size - this.position;
298
+ if (length > bytesLeft) {
299
+ this.position += bytesLeft;
300
+ return bytesLeft;
301
+ }
302
+ }
303
+ this.position += length;
304
+ return length;
305
+ }
306
+ async close() {
307
+ await this.abort();
308
+ await this.onClose?.();
309
+ }
310
+ normalizeOptions(uint8Array, options) {
311
+ if (options && options.position !== void 0 && options.position < this.position) {
312
+ throw new Error("`options.position` must be equal or greater than `tokenizer.position`");
313
+ }
314
+ if (options) {
315
+ return {
316
+ mayBeLess: options.mayBeLess === true,
317
+ offset: options.offset ? options.offset : 0,
318
+ length: options.length ? options.length : uint8Array.length - (options.offset ? options.offset : 0),
319
+ position: options.position ? options.position : this.position
320
+ };
321
+ }
322
+ return {
323
+ mayBeLess: false,
324
+ offset: 0,
325
+ length: uint8Array.length,
326
+ position: this.position
327
+ };
328
+ }
329
+ abort() {
330
+ return Promise.resolve();
331
+ }
332
+ };
333
+
334
+ // ../../../node_modules/.pnpm/strtok3@9.1.1/node_modules/strtok3/lib/ReadStreamTokenizer.js
335
+ var maxBufferSize = 256e3;
336
+ var ReadStreamTokenizer = class extends AbstractTokenizer {
337
+ /**
338
+ * Constructor
339
+ * @param streamReader stream-reader to read from
340
+ * @param options Tokenizer options
341
+ */
342
+ constructor(streamReader, options) {
343
+ super(options);
344
+ this.streamReader = streamReader;
345
+ this.fileInfo = options?.fileInfo ?? {};
346
+ }
347
+ /**
348
+ * Read buffer from tokenizer
349
+ * @param uint8Array - Target Uint8Array to fill with data read from the tokenizer-stream
350
+ * @param options - Read behaviour options
351
+ * @returns Promise with number of bytes read
352
+ */
353
+ async readBuffer(uint8Array, options) {
354
+ const normOptions = this.normalizeOptions(uint8Array, options);
355
+ const skipBytes = normOptions.position - this.position;
356
+ if (skipBytes > 0) {
357
+ await this.ignore(skipBytes);
358
+ return this.readBuffer(uint8Array, options);
359
+ }
360
+ if (skipBytes < 0) {
361
+ throw new Error("`options.position` must be equal or greater than `tokenizer.position`");
362
+ }
363
+ if (normOptions.length === 0) {
364
+ return 0;
365
+ }
366
+ const bytesRead = await this.streamReader.read(uint8Array, normOptions.offset, normOptions.length);
367
+ this.position += bytesRead;
368
+ if ((!options || !options.mayBeLess) && bytesRead < normOptions.length) {
369
+ throw new EndOfStreamError();
370
+ }
371
+ return bytesRead;
372
+ }
373
+ /**
374
+ * Peek (read ahead) buffer from tokenizer
375
+ * @param uint8Array - Uint8Array (or Buffer) to write data to
376
+ * @param options - Read behaviour options
377
+ * @returns Promise with number of bytes peeked
378
+ */
379
+ async peekBuffer(uint8Array, options) {
380
+ const normOptions = this.normalizeOptions(uint8Array, options);
381
+ let bytesRead = 0;
382
+ if (normOptions.position) {
383
+ const skipBytes = normOptions.position - this.position;
384
+ if (skipBytes > 0) {
385
+ const skipBuffer = new Uint8Array(normOptions.length + skipBytes);
386
+ bytesRead = await this.peekBuffer(skipBuffer, { mayBeLess: normOptions.mayBeLess });
387
+ uint8Array.set(skipBuffer.subarray(skipBytes), normOptions.offset);
388
+ return bytesRead - skipBytes;
389
+ }
390
+ if (skipBytes < 0) {
391
+ throw new Error("Cannot peek from a negative offset in a stream");
392
+ }
393
+ }
394
+ if (normOptions.length > 0) {
395
+ try {
396
+ bytesRead = await this.streamReader.peek(uint8Array, normOptions.offset, normOptions.length);
397
+ } catch (err) {
398
+ if (options?.mayBeLess && err instanceof EndOfStreamError) {
399
+ return 0;
400
+ }
401
+ throw err;
402
+ }
403
+ if (!normOptions.mayBeLess && bytesRead < normOptions.length) {
404
+ throw new EndOfStreamError();
405
+ }
406
+ }
407
+ return bytesRead;
408
+ }
409
+ async ignore(length) {
410
+ const bufSize = Math.min(maxBufferSize, length);
411
+ const buf = new Uint8Array(bufSize);
412
+ let totBytesRead = 0;
413
+ while (totBytesRead < length) {
414
+ const remaining = length - totBytesRead;
415
+ const bytesRead = await this.readBuffer(buf, { length: Math.min(bufSize, remaining) });
416
+ if (bytesRead < 0) {
417
+ return bytesRead;
418
+ }
419
+ totBytesRead += bytesRead;
420
+ }
421
+ return totBytesRead;
422
+ }
423
+ abort() {
424
+ return this.streamReader.abort();
425
+ }
426
+ supportsRandomAccess() {
427
+ return false;
428
+ }
429
+ };
430
+
431
+ // ../../../node_modules/.pnpm/strtok3@9.1.1/node_modules/strtok3/lib/BufferTokenizer.js
432
+ var BufferTokenizer = class extends AbstractTokenizer {
433
+ /**
434
+ * Construct BufferTokenizer
435
+ * @param uint8Array - Uint8Array to tokenize
436
+ * @param options Tokenizer options
437
+ */
438
+ constructor(uint8Array, options) {
439
+ super(options);
440
+ this.uint8Array = uint8Array;
441
+ this.fileInfo = { ...options?.fileInfo ?? {}, ...{ size: uint8Array.length } };
442
+ }
443
+ /**
444
+ * Read buffer from tokenizer
445
+ * @param uint8Array - Uint8Array to tokenize
446
+ * @param options - Read behaviour options
447
+ * @returns {Promise<number>}
448
+ */
449
+ async readBuffer(uint8Array, options) {
450
+ if (options?.position) {
451
+ if (options.position < this.position) {
452
+ throw new Error("`options.position` must be equal or greater than `tokenizer.position`");
453
+ }
454
+ this.position = options.position;
455
+ }
456
+ const bytesRead = await this.peekBuffer(uint8Array, options);
457
+ this.position += bytesRead;
458
+ return bytesRead;
459
+ }
460
+ /**
461
+ * Peek (read ahead) buffer from tokenizer
462
+ * @param uint8Array
463
+ * @param options - Read behaviour options
464
+ * @returns {Promise<number>}
465
+ */
466
+ async peekBuffer(uint8Array, options) {
467
+ const normOptions = this.normalizeOptions(uint8Array, options);
468
+ const bytes2read = Math.min(this.uint8Array.length - normOptions.position, normOptions.length);
469
+ if (!normOptions.mayBeLess && bytes2read < normOptions.length) {
470
+ throw new EndOfStreamError();
471
+ }
472
+ uint8Array.set(this.uint8Array.subarray(normOptions.position, normOptions.position + bytes2read), normOptions.offset);
473
+ return bytes2read;
474
+ }
475
+ close() {
476
+ return super.close();
477
+ }
478
+ supportsRandomAccess() {
479
+ return true;
480
+ }
481
+ setPosition(position) {
482
+ this.position = position;
483
+ }
484
+ };
485
+
486
+ // ../../../node_modules/.pnpm/strtok3@9.1.1/node_modules/strtok3/lib/core.js
487
+ function fromWebStream(webStream, options) {
488
+ return new ReadStreamTokenizer(new WebStreamReader(webStream), options);
489
+ }
490
+ function fromBuffer(uint8Array, options) {
491
+ return new BufferTokenizer(uint8Array, options);
492
+ }
493
+
494
+ // ../../../node_modules/.pnpm/token-types@6.1.2/node_modules/token-types/lib/index.js
495
+ __toESM(require_ieee754());
496
+
497
+ // ../../../node_modules/.pnpm/@borewit+text-codec@0.2.2/node_modules/@borewit/text-codec/lib/index.js
498
+ var WINDOWS_1252_EXTRA = {
499
+ 128: "\u20AC",
500
+ 130: "\u201A",
501
+ 131: "\u0192",
502
+ 132: "\u201E",
503
+ 133: "\u2026",
504
+ 134: "\u2020",
505
+ 135: "\u2021",
506
+ 136: "\u02C6",
507
+ 137: "\u2030",
508
+ 138: "\u0160",
509
+ 139: "\u2039",
510
+ 140: "\u0152",
511
+ 142: "\u017D",
512
+ 145: "\u2018",
513
+ 146: "\u2019",
514
+ 147: "\u201C",
515
+ 148: "\u201D",
516
+ 149: "\u2022",
517
+ 150: "\u2013",
518
+ 151: "\u2014",
519
+ 152: "\u02DC",
520
+ 153: "\u2122",
521
+ 154: "\u0161",
522
+ 155: "\u203A",
523
+ 156: "\u0153",
524
+ 158: "\u017E",
525
+ 159: "\u0178"
526
+ };
527
+ for (const [code, char] of Object.entries(WINDOWS_1252_EXTRA)) {
528
+ }
529
+ var _utf8Decoder;
530
+ function utf8Decoder() {
531
+ if (typeof globalThis.TextDecoder === "undefined")
532
+ return void 0;
533
+ return _utf8Decoder !== null && _utf8Decoder !== void 0 ? _utf8Decoder : _utf8Decoder = new globalThis.TextDecoder("utf-8");
534
+ }
535
+ var CHUNK = 32 * 1024;
536
+ var REPLACEMENT = 65533;
537
+ function textDecode(bytes, encoding = "utf-8") {
538
+ switch (encoding.toLowerCase()) {
539
+ case "utf-8":
540
+ case "utf8": {
541
+ const dec = utf8Decoder();
542
+ return dec ? dec.decode(bytes) : decodeUTF8(bytes);
543
+ }
544
+ case "utf-16le":
545
+ return decodeUTF16LE(bytes);
546
+ case "us-ascii":
547
+ case "ascii":
548
+ return decodeASCII(bytes);
549
+ case "latin1":
550
+ case "iso-8859-1":
551
+ return decodeLatin1(bytes);
552
+ case "windows-1252":
553
+ return decodeWindows1252(bytes);
554
+ default:
555
+ throw new RangeError(`Encoding '${encoding}' not supported`);
556
+ }
557
+ }
558
+ function flushChunk(parts, chunk) {
559
+ if (chunk.length === 0)
560
+ return;
561
+ parts.push(String.fromCharCode.apply(null, chunk));
562
+ chunk.length = 0;
563
+ }
564
+ function pushCodeUnit(parts, chunk, codeUnit) {
565
+ chunk.push(codeUnit);
566
+ if (chunk.length >= CHUNK)
567
+ flushChunk(parts, chunk);
568
+ }
569
+ function pushCodePoint(parts, chunk, cp) {
570
+ if (cp <= 65535) {
571
+ pushCodeUnit(parts, chunk, cp);
572
+ return;
573
+ }
574
+ cp -= 65536;
575
+ pushCodeUnit(parts, chunk, 55296 + (cp >> 10));
576
+ pushCodeUnit(parts, chunk, 56320 + (cp & 1023));
577
+ }
578
+ function decodeUTF8(bytes) {
579
+ const parts = [];
580
+ const chunk = [];
581
+ let i = 0;
582
+ if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
583
+ i = 3;
584
+ }
585
+ while (i < bytes.length) {
586
+ const b1 = bytes[i];
587
+ if (b1 <= 127) {
588
+ pushCodeUnit(parts, chunk, b1);
589
+ i++;
590
+ continue;
591
+ }
592
+ if (b1 < 194 || b1 > 244) {
593
+ pushCodeUnit(parts, chunk, REPLACEMENT);
594
+ i++;
595
+ continue;
596
+ }
597
+ if (b1 <= 223) {
598
+ if (i + 1 >= bytes.length) {
599
+ pushCodeUnit(parts, chunk, REPLACEMENT);
600
+ i++;
601
+ continue;
602
+ }
603
+ const b22 = bytes[i + 1];
604
+ if ((b22 & 192) !== 128) {
605
+ pushCodeUnit(parts, chunk, REPLACEMENT);
606
+ i++;
607
+ continue;
608
+ }
609
+ const cp2 = (b1 & 31) << 6 | b22 & 63;
610
+ pushCodeUnit(parts, chunk, cp2);
611
+ i += 2;
612
+ continue;
613
+ }
614
+ if (b1 <= 239) {
615
+ if (i + 2 >= bytes.length) {
616
+ pushCodeUnit(parts, chunk, REPLACEMENT);
617
+ i++;
618
+ continue;
619
+ }
620
+ const b22 = bytes[i + 1];
621
+ const b32 = bytes[i + 2];
622
+ const valid2 = (b22 & 192) === 128 && (b32 & 192) === 128 && !(b1 === 224 && b22 < 160) && // overlong
623
+ !(b1 === 237 && b22 >= 160);
624
+ if (!valid2) {
625
+ pushCodeUnit(parts, chunk, REPLACEMENT);
626
+ i++;
627
+ continue;
628
+ }
629
+ const cp2 = (b1 & 15) << 12 | (b22 & 63) << 6 | b32 & 63;
630
+ pushCodeUnit(parts, chunk, cp2);
631
+ i += 3;
632
+ continue;
633
+ }
634
+ if (i + 3 >= bytes.length) {
635
+ pushCodeUnit(parts, chunk, REPLACEMENT);
636
+ i++;
637
+ continue;
638
+ }
639
+ const b2 = bytes[i + 1];
640
+ const b3 = bytes[i + 2];
641
+ const b4 = bytes[i + 3];
642
+ const valid = (b2 & 192) === 128 && (b3 & 192) === 128 && (b4 & 192) === 128 && !(b1 === 240 && b2 < 144) && // overlong
643
+ !(b1 === 244 && b2 > 143);
644
+ if (!valid) {
645
+ pushCodeUnit(parts, chunk, REPLACEMENT);
646
+ i++;
647
+ continue;
648
+ }
649
+ const cp = (b1 & 7) << 18 | (b2 & 63) << 12 | (b3 & 63) << 6 | b4 & 63;
650
+ pushCodePoint(parts, chunk, cp);
651
+ i += 4;
652
+ }
653
+ flushChunk(parts, chunk);
654
+ return parts.join("");
655
+ }
656
+ function decodeUTF16LE(bytes) {
657
+ const parts = [];
658
+ const chunk = [];
659
+ const len = bytes.length;
660
+ let i = 0;
661
+ while (i + 1 < len) {
662
+ const u1 = bytes[i] | bytes[i + 1] << 8;
663
+ i += 2;
664
+ if (u1 >= 55296 && u1 <= 56319) {
665
+ if (i + 1 < len) {
666
+ const u2 = bytes[i] | bytes[i + 1] << 8;
667
+ if (u2 >= 56320 && u2 <= 57343) {
668
+ pushCodeUnit(parts, chunk, u1);
669
+ pushCodeUnit(parts, chunk, u2);
670
+ i += 2;
671
+ } else {
672
+ pushCodeUnit(parts, chunk, REPLACEMENT);
673
+ }
674
+ } else {
675
+ pushCodeUnit(parts, chunk, REPLACEMENT);
676
+ }
677
+ continue;
678
+ }
679
+ if (u1 >= 56320 && u1 <= 57343) {
680
+ pushCodeUnit(parts, chunk, REPLACEMENT);
681
+ continue;
682
+ }
683
+ pushCodeUnit(parts, chunk, u1);
684
+ }
685
+ if (i < len) {
686
+ pushCodeUnit(parts, chunk, REPLACEMENT);
687
+ }
688
+ flushChunk(parts, chunk);
689
+ return parts.join("");
690
+ }
691
+ function decodeASCII(bytes) {
692
+ const parts = [];
693
+ for (let i = 0; i < bytes.length; i += CHUNK) {
694
+ const end = Math.min(bytes.length, i + CHUNK);
695
+ const codes = new Array(end - i);
696
+ for (let j = i, k = 0; j < end; j++, k++) {
697
+ codes[k] = bytes[j] & 127;
698
+ }
699
+ parts.push(String.fromCharCode.apply(null, codes));
700
+ }
701
+ return parts.join("");
702
+ }
703
+ function decodeLatin1(bytes) {
704
+ const parts = [];
705
+ for (let i = 0; i < bytes.length; i += CHUNK) {
706
+ const end = Math.min(bytes.length, i + CHUNK);
707
+ const codes = new Array(end - i);
708
+ for (let j = i, k = 0; j < end; j++, k++) {
709
+ codes[k] = bytes[j];
710
+ }
711
+ parts.push(String.fromCharCode.apply(null, codes));
712
+ }
713
+ return parts.join("");
714
+ }
715
+ function decodeWindows1252(bytes) {
716
+ const parts = [];
717
+ let out = "";
718
+ for (let i = 0; i < bytes.length; i++) {
719
+ const b = bytes[i];
720
+ const extra = b >= 128 && b <= 159 ? WINDOWS_1252_EXTRA[b] : void 0;
721
+ out += extra !== null && extra !== void 0 ? extra : String.fromCharCode(b);
722
+ if (out.length >= CHUNK) {
723
+ parts.push(out);
724
+ out = "";
725
+ }
726
+ }
727
+ if (out)
728
+ parts.push(out);
729
+ return parts.join("");
730
+ }
731
+
732
+ // ../../../node_modules/.pnpm/token-types@6.1.2/node_modules/token-types/lib/index.js
733
+ function dv(array) {
734
+ return new DataView(array.buffer, array.byteOffset);
735
+ }
736
+ var UINT8 = {
737
+ len: 1,
738
+ get(array, offset) {
739
+ return dv(array).getUint8(offset);
740
+ },
741
+ put(array, offset, value) {
742
+ dv(array).setUint8(offset, value);
743
+ return offset + 1;
744
+ }
745
+ };
746
+ var UINT16_LE = {
747
+ len: 2,
748
+ get(array, offset) {
749
+ return dv(array).getUint16(offset, true);
750
+ },
751
+ put(array, offset, value) {
752
+ dv(array).setUint16(offset, value, true);
753
+ return offset + 2;
754
+ }
755
+ };
756
+ var UINT16_BE = {
757
+ len: 2,
758
+ get(array, offset) {
759
+ return dv(array).getUint16(offset);
760
+ },
761
+ put(array, offset, value) {
762
+ dv(array).setUint16(offset, value);
763
+ return offset + 2;
764
+ }
765
+ };
766
+ var UINT32_LE = {
767
+ len: 4,
768
+ get(array, offset) {
769
+ return dv(array).getUint32(offset, true);
770
+ },
771
+ put(array, offset, value) {
772
+ dv(array).setUint32(offset, value, true);
773
+ return offset + 4;
774
+ }
775
+ };
776
+ var UINT32_BE = {
777
+ len: 4,
778
+ get(array, offset) {
779
+ return dv(array).getUint32(offset);
780
+ },
781
+ put(array, offset, value) {
782
+ dv(array).setUint32(offset, value);
783
+ return offset + 4;
784
+ }
785
+ };
786
+ var INT32_BE = {
787
+ len: 4,
788
+ get(array, offset) {
789
+ return dv(array).getInt32(offset);
790
+ },
791
+ put(array, offset, value) {
792
+ dv(array).setInt32(offset, value);
793
+ return offset + 4;
794
+ }
795
+ };
796
+ var UINT64_LE = {
797
+ len: 8,
798
+ get(array, offset) {
799
+ return dv(array).getBigUint64(offset, true);
800
+ },
801
+ put(array, offset, value) {
802
+ dv(array).setBigUint64(offset, value, true);
803
+ return offset + 8;
804
+ }
805
+ };
806
+ var StringType = class {
807
+ constructor(len, encoding) {
808
+ this.len = len;
809
+ this.encoding = encoding;
810
+ }
811
+ get(data, offset = 0) {
812
+ const bytes = data.subarray(offset, offset + this.len);
813
+ return textDecode(bytes, this.encoding);
814
+ }
815
+ };
816
+
817
+ // ../../../node_modules/.pnpm/uint8array-extras@1.5.0/node_modules/uint8array-extras/index.js
818
+ ({
819
+ utf8: new globalThis.TextDecoder("utf8")
820
+ });
821
+ new globalThis.TextEncoder();
822
+ Array.from({ length: 256 }, (_, index) => index.toString(16).padStart(2, "0"));
823
+ function getUintBE(view) {
824
+ const { byteLength } = view;
825
+ if (byteLength === 6) {
826
+ return view.getUint16(0) * 2 ** 32 + view.getUint32(2);
827
+ }
828
+ if (byteLength === 5) {
829
+ return view.getUint8(0) * 2 ** 32 + view.getUint32(1);
830
+ }
831
+ if (byteLength === 4) {
832
+ return view.getUint32(0);
833
+ }
834
+ if (byteLength === 3) {
835
+ return view.getUint8(0) * 2 ** 16 + view.getUint16(1);
836
+ }
837
+ if (byteLength === 2) {
838
+ return view.getUint16(0);
839
+ }
840
+ if (byteLength === 1) {
841
+ return view.getUint8(0);
842
+ }
843
+ }
844
+ function indexOf(array, value) {
845
+ const arrayLength = array.length;
846
+ const valueLength = value.length;
847
+ if (valueLength === 0) {
848
+ return -1;
849
+ }
850
+ if (valueLength > arrayLength) {
851
+ return -1;
852
+ }
853
+ const validOffsetLength = arrayLength - valueLength;
854
+ for (let index = 0; index <= validOffsetLength; index++) {
855
+ let isMatch = true;
856
+ for (let index2 = 0; index2 < valueLength; index2++) {
857
+ if (array[index + index2] !== value[index2]) {
858
+ isMatch = false;
859
+ break;
860
+ }
861
+ }
862
+ if (isMatch) {
863
+ return index;
864
+ }
865
+ }
866
+ return -1;
867
+ }
868
+ function includes(array, value) {
869
+ return indexOf(array, value) !== -1;
870
+ }
871
+
872
+ // ../../../node_modules/.pnpm/file-type@19.6.0/node_modules/file-type/util.js
873
+ function stringToBytes(string) {
874
+ return [...string].map((character) => character.charCodeAt(0));
875
+ }
876
+ function tarHeaderChecksumMatches(arrayBuffer, offset = 0) {
877
+ const readSum = Number.parseInt(new StringType(6).get(arrayBuffer, 148).replace(/\0.*$/, "").trim(), 8);
878
+ if (Number.isNaN(readSum)) {
879
+ return false;
880
+ }
881
+ let sum = 8 * 32;
882
+ for (let index = offset; index < offset + 148; index++) {
883
+ sum += arrayBuffer[index];
884
+ }
885
+ for (let index = offset + 156; index < offset + 512; index++) {
886
+ sum += arrayBuffer[index];
887
+ }
888
+ return readSum === sum;
889
+ }
890
+ var uint32SyncSafeToken = {
891
+ get: (buffer, offset) => buffer[offset + 3] & 127 | buffer[offset + 2] << 7 | buffer[offset + 1] << 14 | buffer[offset] << 21,
892
+ len: 4
893
+ };
894
+
895
+ // ../../../node_modules/.pnpm/file-type@19.6.0/node_modules/file-type/supported.js
896
+ var extensions = [
897
+ "jpg",
898
+ "png",
899
+ "apng",
900
+ "gif",
901
+ "webp",
902
+ "flif",
903
+ "xcf",
904
+ "cr2",
905
+ "cr3",
906
+ "orf",
907
+ "arw",
908
+ "dng",
909
+ "nef",
910
+ "rw2",
911
+ "raf",
912
+ "tif",
913
+ "bmp",
914
+ "icns",
915
+ "jxr",
916
+ "psd",
917
+ "indd",
918
+ "zip",
919
+ "tar",
920
+ "rar",
921
+ "gz",
922
+ "bz2",
923
+ "7z",
924
+ "dmg",
925
+ "mp4",
926
+ "mid",
927
+ "mkv",
928
+ "webm",
929
+ "mov",
930
+ "avi",
931
+ "mpg",
932
+ "mp2",
933
+ "mp3",
934
+ "m4a",
935
+ "oga",
936
+ "ogg",
937
+ "ogv",
938
+ "opus",
939
+ "flac",
940
+ "wav",
941
+ "spx",
942
+ "amr",
943
+ "pdf",
944
+ "epub",
945
+ "elf",
946
+ "macho",
947
+ "exe",
948
+ "swf",
949
+ "rtf",
950
+ "wasm",
951
+ "woff",
952
+ "woff2",
953
+ "eot",
954
+ "ttf",
955
+ "otf",
956
+ "ico",
957
+ "flv",
958
+ "ps",
959
+ "xz",
960
+ "sqlite",
961
+ "nes",
962
+ "crx",
963
+ "xpi",
964
+ "cab",
965
+ "deb",
966
+ "ar",
967
+ "rpm",
968
+ "Z",
969
+ "lz",
970
+ "cfb",
971
+ "mxf",
972
+ "mts",
973
+ "blend",
974
+ "bpg",
975
+ "docx",
976
+ "pptx",
977
+ "xlsx",
978
+ "3gp",
979
+ "3g2",
980
+ "j2c",
981
+ "jp2",
982
+ "jpm",
983
+ "jpx",
984
+ "mj2",
985
+ "aif",
986
+ "qcp",
987
+ "odt",
988
+ "ods",
989
+ "odp",
990
+ "xml",
991
+ "mobi",
992
+ "heic",
993
+ "cur",
994
+ "ktx",
995
+ "ape",
996
+ "wv",
997
+ "dcm",
998
+ "ics",
999
+ "glb",
1000
+ "pcap",
1001
+ "dsf",
1002
+ "lnk",
1003
+ "alias",
1004
+ "voc",
1005
+ "ac3",
1006
+ "m4v",
1007
+ "m4p",
1008
+ "m4b",
1009
+ "f4v",
1010
+ "f4p",
1011
+ "f4b",
1012
+ "f4a",
1013
+ "mie",
1014
+ "asf",
1015
+ "ogm",
1016
+ "ogx",
1017
+ "mpc",
1018
+ "arrow",
1019
+ "shp",
1020
+ "aac",
1021
+ "mp1",
1022
+ "it",
1023
+ "s3m",
1024
+ "xm",
1025
+ "ai",
1026
+ "skp",
1027
+ "avif",
1028
+ "eps",
1029
+ "lzh",
1030
+ "pgp",
1031
+ "asar",
1032
+ "stl",
1033
+ "chm",
1034
+ "3mf",
1035
+ "zst",
1036
+ "jxl",
1037
+ "vcf",
1038
+ "jls",
1039
+ "pst",
1040
+ "dwg",
1041
+ "parquet",
1042
+ "class",
1043
+ "arj",
1044
+ "cpio",
1045
+ "ace",
1046
+ "avro",
1047
+ "icc",
1048
+ "fbx",
1049
+ "vsdx",
1050
+ "vtt",
1051
+ "apk"
1052
+ ];
1053
+ var mimeTypes = [
1054
+ "image/jpeg",
1055
+ "image/png",
1056
+ "image/gif",
1057
+ "image/webp",
1058
+ "image/flif",
1059
+ "image/x-xcf",
1060
+ "image/x-canon-cr2",
1061
+ "image/x-canon-cr3",
1062
+ "image/tiff",
1063
+ "image/bmp",
1064
+ "image/vnd.ms-photo",
1065
+ "image/vnd.adobe.photoshop",
1066
+ "application/x-indesign",
1067
+ "application/epub+zip",
1068
+ "application/x-xpinstall",
1069
+ "application/vnd.oasis.opendocument.text",
1070
+ "application/vnd.oasis.opendocument.spreadsheet",
1071
+ "application/vnd.oasis.opendocument.presentation",
1072
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1073
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1074
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1075
+ "application/zip",
1076
+ "application/x-tar",
1077
+ "application/x-rar-compressed",
1078
+ "application/gzip",
1079
+ "application/x-bzip2",
1080
+ "application/x-7z-compressed",
1081
+ "application/x-apple-diskimage",
1082
+ "application/x-apache-arrow",
1083
+ "video/mp4",
1084
+ "audio/midi",
1085
+ "video/x-matroska",
1086
+ "video/webm",
1087
+ "video/quicktime",
1088
+ "video/vnd.avi",
1089
+ "audio/wav",
1090
+ "audio/qcelp",
1091
+ "audio/x-ms-asf",
1092
+ "video/x-ms-asf",
1093
+ "application/vnd.ms-asf",
1094
+ "video/mpeg",
1095
+ "video/3gpp",
1096
+ "audio/mpeg",
1097
+ "audio/mp4",
1098
+ // RFC 4337
1099
+ "video/ogg",
1100
+ "audio/ogg",
1101
+ "audio/ogg; codecs=opus",
1102
+ "application/ogg",
1103
+ "audio/x-flac",
1104
+ "audio/ape",
1105
+ "audio/wavpack",
1106
+ "audio/amr",
1107
+ "application/pdf",
1108
+ "application/x-elf",
1109
+ "application/x-mach-binary",
1110
+ "application/x-msdownload",
1111
+ "application/x-shockwave-flash",
1112
+ "application/rtf",
1113
+ "application/wasm",
1114
+ "font/woff",
1115
+ "font/woff2",
1116
+ "application/vnd.ms-fontobject",
1117
+ "font/ttf",
1118
+ "font/otf",
1119
+ "image/x-icon",
1120
+ "video/x-flv",
1121
+ "application/postscript",
1122
+ "application/eps",
1123
+ "application/x-xz",
1124
+ "application/x-sqlite3",
1125
+ "application/x-nintendo-nes-rom",
1126
+ "application/x-google-chrome-extension",
1127
+ "application/vnd.ms-cab-compressed",
1128
+ "application/x-deb",
1129
+ "application/x-unix-archive",
1130
+ "application/x-rpm",
1131
+ "application/x-compress",
1132
+ "application/x-lzip",
1133
+ "application/x-cfb",
1134
+ "application/x-mie",
1135
+ "application/mxf",
1136
+ "video/mp2t",
1137
+ "application/x-blender",
1138
+ "image/bpg",
1139
+ "image/j2c",
1140
+ "image/jp2",
1141
+ "image/jpx",
1142
+ "image/jpm",
1143
+ "image/mj2",
1144
+ "audio/aiff",
1145
+ "application/xml",
1146
+ "application/x-mobipocket-ebook",
1147
+ "image/heif",
1148
+ "image/heif-sequence",
1149
+ "image/heic",
1150
+ "image/heic-sequence",
1151
+ "image/icns",
1152
+ "image/ktx",
1153
+ "application/dicom",
1154
+ "audio/x-musepack",
1155
+ "text/calendar",
1156
+ "text/vcard",
1157
+ "text/vtt",
1158
+ "model/gltf-binary",
1159
+ "application/vnd.tcpdump.pcap",
1160
+ "audio/x-dsf",
1161
+ // Non-standard
1162
+ "application/x.ms.shortcut",
1163
+ // Invented by us
1164
+ "application/x.apple.alias",
1165
+ // Invented by us
1166
+ "audio/x-voc",
1167
+ "audio/vnd.dolby.dd-raw",
1168
+ "audio/x-m4a",
1169
+ "image/apng",
1170
+ "image/x-olympus-orf",
1171
+ "image/x-sony-arw",
1172
+ "image/x-adobe-dng",
1173
+ "image/x-nikon-nef",
1174
+ "image/x-panasonic-rw2",
1175
+ "image/x-fujifilm-raf",
1176
+ "video/x-m4v",
1177
+ "video/3gpp2",
1178
+ "application/x-esri-shape",
1179
+ "audio/aac",
1180
+ "audio/x-it",
1181
+ "audio/x-s3m",
1182
+ "audio/x-xm",
1183
+ "video/MP1S",
1184
+ "video/MP2P",
1185
+ "application/vnd.sketchup.skp",
1186
+ "image/avif",
1187
+ "application/x-lzh-compressed",
1188
+ "application/pgp-encrypted",
1189
+ "application/x-asar",
1190
+ "model/stl",
1191
+ "application/vnd.ms-htmlhelp",
1192
+ "model/3mf",
1193
+ "image/jxl",
1194
+ "application/zstd",
1195
+ "image/jls",
1196
+ "application/vnd.ms-outlook",
1197
+ "image/vnd.dwg",
1198
+ "application/x-parquet",
1199
+ "application/java-vm",
1200
+ "application/x-arj",
1201
+ "application/x-cpio",
1202
+ "application/x-ace-compressed",
1203
+ "application/avro",
1204
+ "application/vnd.iccprofile",
1205
+ "application/x.autodesk.fbx",
1206
+ // Invented by us
1207
+ "application/vnd.visio",
1208
+ "application/vnd.android.package-archive"
1209
+ ];
1210
+
1211
+ // ../../../node_modules/.pnpm/file-type@19.6.0/node_modules/file-type/core.js
1212
+ var reasonableDetectionSizeInBytes = 4100;
1213
+ async function fileTypeFromBuffer(input) {
1214
+ return new FileTypeParser().fromBuffer(input);
1215
+ }
1216
+ function _check(buffer, headers, options) {
1217
+ options = {
1218
+ offset: 0,
1219
+ ...options
1220
+ };
1221
+ for (const [index, header] of headers.entries()) {
1222
+ if (options.mask) {
1223
+ if (header !== (options.mask[index] & buffer[index + options.offset])) {
1224
+ return false;
1225
+ }
1226
+ } else if (header !== buffer[index + options.offset]) {
1227
+ return false;
1228
+ }
1229
+ }
1230
+ return true;
1231
+ }
1232
+ var FileTypeParser = class {
1233
+ constructor(options) {
1234
+ this.detectors = options?.customDetectors;
1235
+ this.tokenizerOptions = {
1236
+ abortSignal: options?.signal
1237
+ };
1238
+ this.fromTokenizer = this.fromTokenizer.bind(this);
1239
+ this.fromBuffer = this.fromBuffer.bind(this);
1240
+ this.parse = this.parse.bind(this);
1241
+ }
1242
+ async fromTokenizer(tokenizer) {
1243
+ const initialPosition = tokenizer.position;
1244
+ for (const detector of this.detectors || []) {
1245
+ const fileType = await detector(tokenizer);
1246
+ if (fileType) {
1247
+ return fileType;
1248
+ }
1249
+ if (initialPosition !== tokenizer.position) {
1250
+ return void 0;
1251
+ }
1252
+ }
1253
+ return this.parse(tokenizer);
1254
+ }
1255
+ async fromBuffer(input) {
1256
+ if (!(input instanceof Uint8Array || input instanceof ArrayBuffer)) {
1257
+ throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`ArrayBuffer\`, got \`${typeof input}\``);
1258
+ }
1259
+ const buffer = input instanceof Uint8Array ? input : new Uint8Array(input);
1260
+ if (!(buffer?.length > 1)) {
1261
+ return;
1262
+ }
1263
+ return this.fromTokenizer(fromBuffer(buffer, this.tokenizerOptions));
1264
+ }
1265
+ async fromBlob(blob) {
1266
+ return this.fromStream(blob.stream());
1267
+ }
1268
+ async fromStream(stream) {
1269
+ const tokenizer = await fromWebStream(stream, this.tokenizerOptions);
1270
+ try {
1271
+ return await this.fromTokenizer(tokenizer);
1272
+ } finally {
1273
+ await tokenizer.close();
1274
+ }
1275
+ }
1276
+ async toDetectionStream(stream, options) {
1277
+ const { sampleSize = reasonableDetectionSizeInBytes } = options;
1278
+ let detectedFileType;
1279
+ let firstChunk;
1280
+ const reader = stream.getReader({ mode: "byob" });
1281
+ try {
1282
+ const { value: chunk, done } = await reader.read(new Uint8Array(sampleSize));
1283
+ firstChunk = chunk;
1284
+ if (!done && chunk) {
1285
+ try {
1286
+ detectedFileType = await this.fromBuffer(chunk.slice(0, sampleSize));
1287
+ } catch (error) {
1288
+ if (!(error instanceof EndOfStreamError)) {
1289
+ throw error;
1290
+ }
1291
+ detectedFileType = void 0;
1292
+ }
1293
+ }
1294
+ firstChunk = chunk;
1295
+ } finally {
1296
+ reader.releaseLock();
1297
+ }
1298
+ const transformStream = new TransformStream({
1299
+ async start(controller) {
1300
+ controller.enqueue(firstChunk);
1301
+ },
1302
+ transform(chunk, controller) {
1303
+ controller.enqueue(chunk);
1304
+ }
1305
+ });
1306
+ const newStream = stream.pipeThrough(transformStream);
1307
+ newStream.fileType = detectedFileType;
1308
+ return newStream;
1309
+ }
1310
+ check(header, options) {
1311
+ return _check(this.buffer, header, options);
1312
+ }
1313
+ checkString(header, options) {
1314
+ return this.check(stringToBytes(header), options);
1315
+ }
1316
+ async parse(tokenizer) {
1317
+ this.buffer = new Uint8Array(reasonableDetectionSizeInBytes);
1318
+ if (tokenizer.fileInfo.size === void 0) {
1319
+ tokenizer.fileInfo.size = Number.MAX_SAFE_INTEGER;
1320
+ }
1321
+ this.tokenizer = tokenizer;
1322
+ await tokenizer.peekBuffer(this.buffer, { length: 12, mayBeLess: true });
1323
+ if (this.check([66, 77])) {
1324
+ return {
1325
+ ext: "bmp",
1326
+ mime: "image/bmp"
1327
+ };
1328
+ }
1329
+ if (this.check([11, 119])) {
1330
+ return {
1331
+ ext: "ac3",
1332
+ mime: "audio/vnd.dolby.dd-raw"
1333
+ };
1334
+ }
1335
+ if (this.check([120, 1])) {
1336
+ return {
1337
+ ext: "dmg",
1338
+ mime: "application/x-apple-diskimage"
1339
+ };
1340
+ }
1341
+ if (this.check([77, 90])) {
1342
+ return {
1343
+ ext: "exe",
1344
+ mime: "application/x-msdownload"
1345
+ };
1346
+ }
1347
+ if (this.check([37, 33])) {
1348
+ await tokenizer.peekBuffer(this.buffer, { length: 24, mayBeLess: true });
1349
+ if (this.checkString("PS-Adobe-", { offset: 2 }) && this.checkString(" EPSF-", { offset: 14 })) {
1350
+ return {
1351
+ ext: "eps",
1352
+ mime: "application/eps"
1353
+ };
1354
+ }
1355
+ return {
1356
+ ext: "ps",
1357
+ mime: "application/postscript"
1358
+ };
1359
+ }
1360
+ if (this.check([31, 160]) || this.check([31, 157])) {
1361
+ return {
1362
+ ext: "Z",
1363
+ mime: "application/x-compress"
1364
+ };
1365
+ }
1366
+ if (this.check([199, 113])) {
1367
+ return {
1368
+ ext: "cpio",
1369
+ mime: "application/x-cpio"
1370
+ };
1371
+ }
1372
+ if (this.check([96, 234])) {
1373
+ return {
1374
+ ext: "arj",
1375
+ mime: "application/x-arj"
1376
+ };
1377
+ }
1378
+ if (this.check([239, 187, 191])) {
1379
+ this.tokenizer.ignore(3);
1380
+ return this.parse(tokenizer);
1381
+ }
1382
+ if (this.check([71, 73, 70])) {
1383
+ return {
1384
+ ext: "gif",
1385
+ mime: "image/gif"
1386
+ };
1387
+ }
1388
+ if (this.check([73, 73, 188])) {
1389
+ return {
1390
+ ext: "jxr",
1391
+ mime: "image/vnd.ms-photo"
1392
+ };
1393
+ }
1394
+ if (this.check([31, 139, 8])) {
1395
+ return {
1396
+ ext: "gz",
1397
+ mime: "application/gzip"
1398
+ };
1399
+ }
1400
+ if (this.check([66, 90, 104])) {
1401
+ return {
1402
+ ext: "bz2",
1403
+ mime: "application/x-bzip2"
1404
+ };
1405
+ }
1406
+ if (this.checkString("ID3")) {
1407
+ await tokenizer.ignore(6);
1408
+ const id3HeaderLength = await tokenizer.readToken(uint32SyncSafeToken);
1409
+ if (tokenizer.position + id3HeaderLength > tokenizer.fileInfo.size) {
1410
+ return {
1411
+ ext: "mp3",
1412
+ mime: "audio/mpeg"
1413
+ };
1414
+ }
1415
+ await tokenizer.ignore(id3HeaderLength);
1416
+ return this.fromTokenizer(tokenizer);
1417
+ }
1418
+ if (this.checkString("MP+")) {
1419
+ return {
1420
+ ext: "mpc",
1421
+ mime: "audio/x-musepack"
1422
+ };
1423
+ }
1424
+ if ((this.buffer[0] === 67 || this.buffer[0] === 70) && this.check([87, 83], { offset: 1 })) {
1425
+ return {
1426
+ ext: "swf",
1427
+ mime: "application/x-shockwave-flash"
1428
+ };
1429
+ }
1430
+ if (this.check([255, 216, 255])) {
1431
+ if (this.check([247], { offset: 3 })) {
1432
+ return {
1433
+ ext: "jls",
1434
+ mime: "image/jls"
1435
+ };
1436
+ }
1437
+ return {
1438
+ ext: "jpg",
1439
+ mime: "image/jpeg"
1440
+ };
1441
+ }
1442
+ if (this.check([79, 98, 106, 1])) {
1443
+ return {
1444
+ ext: "avro",
1445
+ mime: "application/avro"
1446
+ };
1447
+ }
1448
+ if (this.checkString("FLIF")) {
1449
+ return {
1450
+ ext: "flif",
1451
+ mime: "image/flif"
1452
+ };
1453
+ }
1454
+ if (this.checkString("8BPS")) {
1455
+ return {
1456
+ ext: "psd",
1457
+ mime: "image/vnd.adobe.photoshop"
1458
+ };
1459
+ }
1460
+ if (this.checkString("WEBP", { offset: 8 })) {
1461
+ return {
1462
+ ext: "webp",
1463
+ mime: "image/webp"
1464
+ };
1465
+ }
1466
+ if (this.checkString("MPCK")) {
1467
+ return {
1468
+ ext: "mpc",
1469
+ mime: "audio/x-musepack"
1470
+ };
1471
+ }
1472
+ if (this.checkString("FORM")) {
1473
+ return {
1474
+ ext: "aif",
1475
+ mime: "audio/aiff"
1476
+ };
1477
+ }
1478
+ if (this.checkString("icns", { offset: 0 })) {
1479
+ return {
1480
+ ext: "icns",
1481
+ mime: "image/icns"
1482
+ };
1483
+ }
1484
+ if (this.check([80, 75, 3, 4])) {
1485
+ try {
1486
+ while (tokenizer.position + 30 < tokenizer.fileInfo.size) {
1487
+ await tokenizer.readBuffer(this.buffer, { length: 30 });
1488
+ const view = new DataView(this.buffer.buffer);
1489
+ const zipHeader = {
1490
+ compressedSize: view.getUint32(18, true),
1491
+ uncompressedSize: view.getUint32(22, true),
1492
+ filenameLength: view.getUint16(26, true),
1493
+ extraFieldLength: view.getUint16(28, true)
1494
+ };
1495
+ zipHeader.filename = await tokenizer.readToken(new StringType(zipHeader.filenameLength, "utf-8"));
1496
+ await tokenizer.ignore(zipHeader.extraFieldLength);
1497
+ if (/classes\d*\.dex/.test(zipHeader.filename)) {
1498
+ return {
1499
+ ext: "apk",
1500
+ mime: "application/vnd.android.package-archive"
1501
+ };
1502
+ }
1503
+ if (zipHeader.filename === "META-INF/mozilla.rsa") {
1504
+ return {
1505
+ ext: "xpi",
1506
+ mime: "application/x-xpinstall"
1507
+ };
1508
+ }
1509
+ if (zipHeader.filename.endsWith(".rels") || zipHeader.filename.endsWith(".xml")) {
1510
+ const type = zipHeader.filename.split("/")[0];
1511
+ switch (type) {
1512
+ case "_rels":
1513
+ break;
1514
+ case "word":
1515
+ return {
1516
+ ext: "docx",
1517
+ mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
1518
+ };
1519
+ case "ppt":
1520
+ return {
1521
+ ext: "pptx",
1522
+ mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
1523
+ };
1524
+ case "xl":
1525
+ return {
1526
+ ext: "xlsx",
1527
+ mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1528
+ };
1529
+ case "visio":
1530
+ return {
1531
+ ext: "vsdx",
1532
+ mime: "application/vnd.visio"
1533
+ };
1534
+ default:
1535
+ break;
1536
+ }
1537
+ }
1538
+ if (zipHeader.filename.startsWith("xl/")) {
1539
+ return {
1540
+ ext: "xlsx",
1541
+ mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1542
+ };
1543
+ }
1544
+ if (zipHeader.filename.startsWith("3D/") && zipHeader.filename.endsWith(".model")) {
1545
+ return {
1546
+ ext: "3mf",
1547
+ mime: "model/3mf"
1548
+ };
1549
+ }
1550
+ if (zipHeader.filename === "mimetype" && zipHeader.compressedSize === zipHeader.uncompressedSize) {
1551
+ let mimeType = await tokenizer.readToken(new StringType(zipHeader.compressedSize, "utf-8"));
1552
+ mimeType = mimeType.trim();
1553
+ switch (mimeType) {
1554
+ case "application/epub+zip":
1555
+ return {
1556
+ ext: "epub",
1557
+ mime: "application/epub+zip"
1558
+ };
1559
+ case "application/vnd.oasis.opendocument.text":
1560
+ return {
1561
+ ext: "odt",
1562
+ mime: "application/vnd.oasis.opendocument.text"
1563
+ };
1564
+ case "application/vnd.oasis.opendocument.spreadsheet":
1565
+ return {
1566
+ ext: "ods",
1567
+ mime: "application/vnd.oasis.opendocument.spreadsheet"
1568
+ };
1569
+ case "application/vnd.oasis.opendocument.presentation":
1570
+ return {
1571
+ ext: "odp",
1572
+ mime: "application/vnd.oasis.opendocument.presentation"
1573
+ };
1574
+ default:
1575
+ }
1576
+ }
1577
+ if (zipHeader.compressedSize === 0) {
1578
+ let nextHeaderIndex = -1;
1579
+ while (nextHeaderIndex < 0 && tokenizer.position < tokenizer.fileInfo.size) {
1580
+ await tokenizer.peekBuffer(this.buffer, { mayBeLess: true });
1581
+ nextHeaderIndex = indexOf(this.buffer, new Uint8Array([80, 75, 3, 4]));
1582
+ await tokenizer.ignore(nextHeaderIndex >= 0 ? nextHeaderIndex : this.buffer.length);
1583
+ }
1584
+ } else {
1585
+ await tokenizer.ignore(zipHeader.compressedSize);
1586
+ }
1587
+ }
1588
+ } catch (error) {
1589
+ if (!(error instanceof EndOfStreamError)) {
1590
+ throw error;
1591
+ }
1592
+ }
1593
+ return {
1594
+ ext: "zip",
1595
+ mime: "application/zip"
1596
+ };
1597
+ }
1598
+ if (this.checkString("OggS")) {
1599
+ await tokenizer.ignore(28);
1600
+ const type = new Uint8Array(8);
1601
+ await tokenizer.readBuffer(type);
1602
+ if (_check(type, [79, 112, 117, 115, 72, 101, 97, 100])) {
1603
+ return {
1604
+ ext: "opus",
1605
+ mime: "audio/ogg; codecs=opus"
1606
+ };
1607
+ }
1608
+ if (_check(type, [128, 116, 104, 101, 111, 114, 97])) {
1609
+ return {
1610
+ ext: "ogv",
1611
+ mime: "video/ogg"
1612
+ };
1613
+ }
1614
+ if (_check(type, [1, 118, 105, 100, 101, 111, 0])) {
1615
+ return {
1616
+ ext: "ogm",
1617
+ mime: "video/ogg"
1618
+ };
1619
+ }
1620
+ if (_check(type, [127, 70, 76, 65, 67])) {
1621
+ return {
1622
+ ext: "oga",
1623
+ mime: "audio/ogg"
1624
+ };
1625
+ }
1626
+ if (_check(type, [83, 112, 101, 101, 120, 32, 32])) {
1627
+ return {
1628
+ ext: "spx",
1629
+ mime: "audio/ogg"
1630
+ };
1631
+ }
1632
+ if (_check(type, [1, 118, 111, 114, 98, 105, 115])) {
1633
+ return {
1634
+ ext: "ogg",
1635
+ mime: "audio/ogg"
1636
+ };
1637
+ }
1638
+ return {
1639
+ ext: "ogx",
1640
+ mime: "application/ogg"
1641
+ };
1642
+ }
1643
+ if (this.check([80, 75]) && (this.buffer[2] === 3 || this.buffer[2] === 5 || this.buffer[2] === 7) && (this.buffer[3] === 4 || this.buffer[3] === 6 || this.buffer[3] === 8)) {
1644
+ return {
1645
+ ext: "zip",
1646
+ mime: "application/zip"
1647
+ };
1648
+ }
1649
+ if (this.checkString("ftyp", { offset: 4 }) && (this.buffer[8] & 96) !== 0) {
1650
+ const brandMajor = new StringType(4, "latin1").get(this.buffer, 8).replace("\0", " ").trim();
1651
+ switch (brandMajor) {
1652
+ case "avif":
1653
+ case "avis":
1654
+ return { ext: "avif", mime: "image/avif" };
1655
+ case "mif1":
1656
+ return { ext: "heic", mime: "image/heif" };
1657
+ case "msf1":
1658
+ return { ext: "heic", mime: "image/heif-sequence" };
1659
+ case "heic":
1660
+ case "heix":
1661
+ return { ext: "heic", mime: "image/heic" };
1662
+ case "hevc":
1663
+ case "hevx":
1664
+ return { ext: "heic", mime: "image/heic-sequence" };
1665
+ case "qt":
1666
+ return { ext: "mov", mime: "video/quicktime" };
1667
+ case "M4V":
1668
+ case "M4VH":
1669
+ case "M4VP":
1670
+ return { ext: "m4v", mime: "video/x-m4v" };
1671
+ case "M4P":
1672
+ return { ext: "m4p", mime: "video/mp4" };
1673
+ case "M4B":
1674
+ return { ext: "m4b", mime: "audio/mp4" };
1675
+ case "M4A":
1676
+ return { ext: "m4a", mime: "audio/x-m4a" };
1677
+ case "F4V":
1678
+ return { ext: "f4v", mime: "video/mp4" };
1679
+ case "F4P":
1680
+ return { ext: "f4p", mime: "video/mp4" };
1681
+ case "F4A":
1682
+ return { ext: "f4a", mime: "audio/mp4" };
1683
+ case "F4B":
1684
+ return { ext: "f4b", mime: "audio/mp4" };
1685
+ case "crx":
1686
+ return { ext: "cr3", mime: "image/x-canon-cr3" };
1687
+ default:
1688
+ if (brandMajor.startsWith("3g")) {
1689
+ if (brandMajor.startsWith("3g2")) {
1690
+ return { ext: "3g2", mime: "video/3gpp2" };
1691
+ }
1692
+ return { ext: "3gp", mime: "video/3gpp" };
1693
+ }
1694
+ return { ext: "mp4", mime: "video/mp4" };
1695
+ }
1696
+ }
1697
+ if (this.checkString("MThd")) {
1698
+ return {
1699
+ ext: "mid",
1700
+ mime: "audio/midi"
1701
+ };
1702
+ }
1703
+ if (this.checkString("wOFF") && (this.check([0, 1, 0, 0], { offset: 4 }) || this.checkString("OTTO", { offset: 4 }))) {
1704
+ return {
1705
+ ext: "woff",
1706
+ mime: "font/woff"
1707
+ };
1708
+ }
1709
+ if (this.checkString("wOF2") && (this.check([0, 1, 0, 0], { offset: 4 }) || this.checkString("OTTO", { offset: 4 }))) {
1710
+ return {
1711
+ ext: "woff2",
1712
+ mime: "font/woff2"
1713
+ };
1714
+ }
1715
+ if (this.check([212, 195, 178, 161]) || this.check([161, 178, 195, 212])) {
1716
+ return {
1717
+ ext: "pcap",
1718
+ mime: "application/vnd.tcpdump.pcap"
1719
+ };
1720
+ }
1721
+ if (this.checkString("DSD ")) {
1722
+ return {
1723
+ ext: "dsf",
1724
+ mime: "audio/x-dsf"
1725
+ // Non-standard
1726
+ };
1727
+ }
1728
+ if (this.checkString("LZIP")) {
1729
+ return {
1730
+ ext: "lz",
1731
+ mime: "application/x-lzip"
1732
+ };
1733
+ }
1734
+ if (this.checkString("fLaC")) {
1735
+ return {
1736
+ ext: "flac",
1737
+ mime: "audio/x-flac"
1738
+ };
1739
+ }
1740
+ if (this.check([66, 80, 71, 251])) {
1741
+ return {
1742
+ ext: "bpg",
1743
+ mime: "image/bpg"
1744
+ };
1745
+ }
1746
+ if (this.checkString("wvpk")) {
1747
+ return {
1748
+ ext: "wv",
1749
+ mime: "audio/wavpack"
1750
+ };
1751
+ }
1752
+ if (this.checkString("%PDF")) {
1753
+ try {
1754
+ await tokenizer.ignore(1350);
1755
+ const maxBufferSize2 = 10 * 1024 * 1024;
1756
+ const buffer = new Uint8Array(Math.min(maxBufferSize2, tokenizer.fileInfo.size));
1757
+ await tokenizer.readBuffer(buffer, { mayBeLess: true });
1758
+ if (includes(buffer, new TextEncoder().encode("AIPrivateData"))) {
1759
+ return {
1760
+ ext: "ai",
1761
+ mime: "application/postscript"
1762
+ };
1763
+ }
1764
+ } catch (error) {
1765
+ if (!(error instanceof EndOfStreamError)) {
1766
+ throw error;
1767
+ }
1768
+ }
1769
+ return {
1770
+ ext: "pdf",
1771
+ mime: "application/pdf"
1772
+ };
1773
+ }
1774
+ if (this.check([0, 97, 115, 109])) {
1775
+ return {
1776
+ ext: "wasm",
1777
+ mime: "application/wasm"
1778
+ };
1779
+ }
1780
+ if (this.check([73, 73])) {
1781
+ const fileType = await this.readTiffHeader(false);
1782
+ if (fileType) {
1783
+ return fileType;
1784
+ }
1785
+ }
1786
+ if (this.check([77, 77])) {
1787
+ const fileType = await this.readTiffHeader(true);
1788
+ if (fileType) {
1789
+ return fileType;
1790
+ }
1791
+ }
1792
+ if (this.checkString("MAC ")) {
1793
+ return {
1794
+ ext: "ape",
1795
+ mime: "audio/ape"
1796
+ };
1797
+ }
1798
+ if (this.check([26, 69, 223, 163])) {
1799
+ async function readField() {
1800
+ const msb = await tokenizer.peekNumber(UINT8);
1801
+ let mask = 128;
1802
+ let ic = 0;
1803
+ while ((msb & mask) === 0 && mask !== 0) {
1804
+ ++ic;
1805
+ mask >>= 1;
1806
+ }
1807
+ const id = new Uint8Array(ic + 1);
1808
+ await tokenizer.readBuffer(id);
1809
+ return id;
1810
+ }
1811
+ async function readElement() {
1812
+ const idField = await readField();
1813
+ const lengthField = await readField();
1814
+ lengthField[0] ^= 128 >> lengthField.length - 1;
1815
+ const nrLength = Math.min(6, lengthField.length);
1816
+ const idView = new DataView(idField.buffer);
1817
+ const lengthView = new DataView(lengthField.buffer, lengthField.length - nrLength, nrLength);
1818
+ return {
1819
+ id: getUintBE(idView),
1820
+ len: getUintBE(lengthView)
1821
+ };
1822
+ }
1823
+ async function readChildren(children) {
1824
+ while (children > 0) {
1825
+ const element = await readElement();
1826
+ if (element.id === 17026) {
1827
+ const rawValue = await tokenizer.readToken(new StringType(element.len));
1828
+ return rawValue.replaceAll(/\00.*$/g, "");
1829
+ }
1830
+ await tokenizer.ignore(element.len);
1831
+ --children;
1832
+ }
1833
+ }
1834
+ const re = await readElement();
1835
+ const docType = await readChildren(re.len);
1836
+ switch (docType) {
1837
+ case "webm":
1838
+ return {
1839
+ ext: "webm",
1840
+ mime: "video/webm"
1841
+ };
1842
+ case "matroska":
1843
+ return {
1844
+ ext: "mkv",
1845
+ mime: "video/x-matroska"
1846
+ };
1847
+ default:
1848
+ return;
1849
+ }
1850
+ }
1851
+ if (this.check([82, 73, 70, 70])) {
1852
+ if (this.check([65, 86, 73], { offset: 8 })) {
1853
+ return {
1854
+ ext: "avi",
1855
+ mime: "video/vnd.avi"
1856
+ };
1857
+ }
1858
+ if (this.check([87, 65, 86, 69], { offset: 8 })) {
1859
+ return {
1860
+ ext: "wav",
1861
+ mime: "audio/wav"
1862
+ };
1863
+ }
1864
+ if (this.check([81, 76, 67, 77], { offset: 8 })) {
1865
+ return {
1866
+ ext: "qcp",
1867
+ mime: "audio/qcelp"
1868
+ };
1869
+ }
1870
+ }
1871
+ if (this.checkString("SQLi")) {
1872
+ return {
1873
+ ext: "sqlite",
1874
+ mime: "application/x-sqlite3"
1875
+ };
1876
+ }
1877
+ if (this.check([78, 69, 83, 26])) {
1878
+ return {
1879
+ ext: "nes",
1880
+ mime: "application/x-nintendo-nes-rom"
1881
+ };
1882
+ }
1883
+ if (this.checkString("Cr24")) {
1884
+ return {
1885
+ ext: "crx",
1886
+ mime: "application/x-google-chrome-extension"
1887
+ };
1888
+ }
1889
+ if (this.checkString("MSCF") || this.checkString("ISc(")) {
1890
+ return {
1891
+ ext: "cab",
1892
+ mime: "application/vnd.ms-cab-compressed"
1893
+ };
1894
+ }
1895
+ if (this.check([237, 171, 238, 219])) {
1896
+ return {
1897
+ ext: "rpm",
1898
+ mime: "application/x-rpm"
1899
+ };
1900
+ }
1901
+ if (this.check([197, 208, 211, 198])) {
1902
+ return {
1903
+ ext: "eps",
1904
+ mime: "application/eps"
1905
+ };
1906
+ }
1907
+ if (this.check([40, 181, 47, 253])) {
1908
+ return {
1909
+ ext: "zst",
1910
+ mime: "application/zstd"
1911
+ };
1912
+ }
1913
+ if (this.check([127, 69, 76, 70])) {
1914
+ return {
1915
+ ext: "elf",
1916
+ mime: "application/x-elf"
1917
+ };
1918
+ }
1919
+ if (this.check([33, 66, 68, 78])) {
1920
+ return {
1921
+ ext: "pst",
1922
+ mime: "application/vnd.ms-outlook"
1923
+ };
1924
+ }
1925
+ if (this.checkString("PAR1")) {
1926
+ return {
1927
+ ext: "parquet",
1928
+ mime: "application/x-parquet"
1929
+ };
1930
+ }
1931
+ if (this.check([207, 250, 237, 254])) {
1932
+ return {
1933
+ ext: "macho",
1934
+ mime: "application/x-mach-binary"
1935
+ };
1936
+ }
1937
+ if (this.check([79, 84, 84, 79, 0])) {
1938
+ return {
1939
+ ext: "otf",
1940
+ mime: "font/otf"
1941
+ };
1942
+ }
1943
+ if (this.checkString("#!AMR")) {
1944
+ return {
1945
+ ext: "amr",
1946
+ mime: "audio/amr"
1947
+ };
1948
+ }
1949
+ if (this.checkString("{\\rtf")) {
1950
+ return {
1951
+ ext: "rtf",
1952
+ mime: "application/rtf"
1953
+ };
1954
+ }
1955
+ if (this.check([70, 76, 86, 1])) {
1956
+ return {
1957
+ ext: "flv",
1958
+ mime: "video/x-flv"
1959
+ };
1960
+ }
1961
+ if (this.checkString("IMPM")) {
1962
+ return {
1963
+ ext: "it",
1964
+ mime: "audio/x-it"
1965
+ };
1966
+ }
1967
+ if (this.checkString("-lh0-", { offset: 2 }) || this.checkString("-lh1-", { offset: 2 }) || this.checkString("-lh2-", { offset: 2 }) || this.checkString("-lh3-", { offset: 2 }) || this.checkString("-lh4-", { offset: 2 }) || this.checkString("-lh5-", { offset: 2 }) || this.checkString("-lh6-", { offset: 2 }) || this.checkString("-lh7-", { offset: 2 }) || this.checkString("-lzs-", { offset: 2 }) || this.checkString("-lz4-", { offset: 2 }) || this.checkString("-lz5-", { offset: 2 }) || this.checkString("-lhd-", { offset: 2 })) {
1968
+ return {
1969
+ ext: "lzh",
1970
+ mime: "application/x-lzh-compressed"
1971
+ };
1972
+ }
1973
+ if (this.check([0, 0, 1, 186])) {
1974
+ if (this.check([33], { offset: 4, mask: [241] })) {
1975
+ return {
1976
+ ext: "mpg",
1977
+ // May also be .ps, .mpeg
1978
+ mime: "video/MP1S"
1979
+ };
1980
+ }
1981
+ if (this.check([68], { offset: 4, mask: [196] })) {
1982
+ return {
1983
+ ext: "mpg",
1984
+ // May also be .mpg, .m2p, .vob or .sub
1985
+ mime: "video/MP2P"
1986
+ };
1987
+ }
1988
+ }
1989
+ if (this.checkString("ITSF")) {
1990
+ return {
1991
+ ext: "chm",
1992
+ mime: "application/vnd.ms-htmlhelp"
1993
+ };
1994
+ }
1995
+ if (this.check([202, 254, 186, 190])) {
1996
+ return {
1997
+ ext: "class",
1998
+ mime: "application/java-vm"
1999
+ };
2000
+ }
2001
+ if (this.check([253, 55, 122, 88, 90, 0])) {
2002
+ return {
2003
+ ext: "xz",
2004
+ mime: "application/x-xz"
2005
+ };
2006
+ }
2007
+ if (this.checkString("<?xml ")) {
2008
+ return {
2009
+ ext: "xml",
2010
+ mime: "application/xml"
2011
+ };
2012
+ }
2013
+ if (this.check([55, 122, 188, 175, 39, 28])) {
2014
+ return {
2015
+ ext: "7z",
2016
+ mime: "application/x-7z-compressed"
2017
+ };
2018
+ }
2019
+ if (this.check([82, 97, 114, 33, 26, 7]) && (this.buffer[6] === 0 || this.buffer[6] === 1)) {
2020
+ return {
2021
+ ext: "rar",
2022
+ mime: "application/x-rar-compressed"
2023
+ };
2024
+ }
2025
+ if (this.checkString("solid ")) {
2026
+ return {
2027
+ ext: "stl",
2028
+ mime: "model/stl"
2029
+ };
2030
+ }
2031
+ if (this.checkString("AC")) {
2032
+ const version = new StringType(4, "latin1").get(this.buffer, 2);
2033
+ if (version.match("^d*") && version >= 1e3 && version <= 1050) {
2034
+ return {
2035
+ ext: "dwg",
2036
+ mime: "image/vnd.dwg"
2037
+ };
2038
+ }
2039
+ }
2040
+ if (this.checkString("070707")) {
2041
+ return {
2042
+ ext: "cpio",
2043
+ mime: "application/x-cpio"
2044
+ };
2045
+ }
2046
+ if (this.checkString("BLENDER")) {
2047
+ return {
2048
+ ext: "blend",
2049
+ mime: "application/x-blender"
2050
+ };
2051
+ }
2052
+ if (this.checkString("!<arch>")) {
2053
+ await tokenizer.ignore(8);
2054
+ const string = await tokenizer.readToken(new StringType(13, "ascii"));
2055
+ if (string === "debian-binary") {
2056
+ return {
2057
+ ext: "deb",
2058
+ mime: "application/x-deb"
2059
+ };
2060
+ }
2061
+ return {
2062
+ ext: "ar",
2063
+ mime: "application/x-unix-archive"
2064
+ };
2065
+ }
2066
+ if (this.checkString("WEBVTT") && // One of LF, CR, tab, space, or end of file must follow "WEBVTT" per the spec (see `fixture/fixture-vtt-*.vtt` for examples). Note that `\0` is technically the null character (there is no such thing as an EOF character). However, checking for `\0` gives us the same result as checking for the end of the stream.
2067
+ ["\n", "\r", " ", " ", "\0"].some((char7) => this.checkString(char7, { offset: 6 }))) {
2068
+ return {
2069
+ ext: "vtt",
2070
+ mime: "text/vtt"
2071
+ };
2072
+ }
2073
+ if (this.check([137, 80, 78, 71, 13, 10, 26, 10])) {
2074
+ await tokenizer.ignore(8);
2075
+ async function readChunkHeader() {
2076
+ return {
2077
+ length: await tokenizer.readToken(INT32_BE),
2078
+ type: await tokenizer.readToken(new StringType(4, "latin1"))
2079
+ };
2080
+ }
2081
+ do {
2082
+ const chunk = await readChunkHeader();
2083
+ if (chunk.length < 0) {
2084
+ return;
2085
+ }
2086
+ switch (chunk.type) {
2087
+ case "IDAT":
2088
+ return {
2089
+ ext: "png",
2090
+ mime: "image/png"
2091
+ };
2092
+ case "acTL":
2093
+ return {
2094
+ ext: "apng",
2095
+ mime: "image/apng"
2096
+ };
2097
+ default:
2098
+ await tokenizer.ignore(chunk.length + 4);
2099
+ }
2100
+ } while (tokenizer.position + 8 < tokenizer.fileInfo.size);
2101
+ return {
2102
+ ext: "png",
2103
+ mime: "image/png"
2104
+ };
2105
+ }
2106
+ if (this.check([65, 82, 82, 79, 87, 49, 0, 0])) {
2107
+ return {
2108
+ ext: "arrow",
2109
+ mime: "application/x-apache-arrow"
2110
+ };
2111
+ }
2112
+ if (this.check([103, 108, 84, 70, 2, 0, 0, 0])) {
2113
+ return {
2114
+ ext: "glb",
2115
+ mime: "model/gltf-binary"
2116
+ };
2117
+ }
2118
+ if (this.check([102, 114, 101, 101], { offset: 4 }) || this.check([109, 100, 97, 116], { offset: 4 }) || this.check([109, 111, 111, 118], { offset: 4 }) || this.check([119, 105, 100, 101], { offset: 4 })) {
2119
+ return {
2120
+ ext: "mov",
2121
+ mime: "video/quicktime"
2122
+ };
2123
+ }
2124
+ if (this.check([73, 73, 82, 79, 8, 0, 0, 0, 24])) {
2125
+ return {
2126
+ ext: "orf",
2127
+ mime: "image/x-olympus-orf"
2128
+ };
2129
+ }
2130
+ if (this.checkString("gimp xcf ")) {
2131
+ return {
2132
+ ext: "xcf",
2133
+ mime: "image/x-xcf"
2134
+ };
2135
+ }
2136
+ if (this.check([73, 73, 85, 0, 24, 0, 0, 0, 136, 231, 116, 216])) {
2137
+ return {
2138
+ ext: "rw2",
2139
+ mime: "image/x-panasonic-rw2"
2140
+ };
2141
+ }
2142
+ if (this.check([48, 38, 178, 117, 142, 102, 207, 17, 166, 217])) {
2143
+ async function readHeader() {
2144
+ const guid = new Uint8Array(16);
2145
+ await tokenizer.readBuffer(guid);
2146
+ return {
2147
+ id: guid,
2148
+ size: Number(await tokenizer.readToken(UINT64_LE))
2149
+ };
2150
+ }
2151
+ await tokenizer.ignore(30);
2152
+ while (tokenizer.position + 24 < tokenizer.fileInfo.size) {
2153
+ const header = await readHeader();
2154
+ let payload = header.size - 24;
2155
+ if (_check(header.id, [145, 7, 220, 183, 183, 169, 207, 17, 142, 230, 0, 192, 12, 32, 83, 101])) {
2156
+ const typeId = new Uint8Array(16);
2157
+ payload -= await tokenizer.readBuffer(typeId);
2158
+ if (_check(typeId, [64, 158, 105, 248, 77, 91, 207, 17, 168, 253, 0, 128, 95, 92, 68, 43])) {
2159
+ return {
2160
+ ext: "asf",
2161
+ mime: "audio/x-ms-asf"
2162
+ };
2163
+ }
2164
+ if (_check(typeId, [192, 239, 25, 188, 77, 91, 207, 17, 168, 253, 0, 128, 95, 92, 68, 43])) {
2165
+ return {
2166
+ ext: "asf",
2167
+ mime: "video/x-ms-asf"
2168
+ };
2169
+ }
2170
+ break;
2171
+ }
2172
+ await tokenizer.ignore(payload);
2173
+ }
2174
+ return {
2175
+ ext: "asf",
2176
+ mime: "application/vnd.ms-asf"
2177
+ };
2178
+ }
2179
+ if (this.check([171, 75, 84, 88, 32, 49, 49, 187, 13, 10, 26, 10])) {
2180
+ return {
2181
+ ext: "ktx",
2182
+ mime: "image/ktx"
2183
+ };
2184
+ }
2185
+ if ((this.check([126, 16, 4]) || this.check([126, 24, 4])) && this.check([48, 77, 73, 69], { offset: 4 })) {
2186
+ return {
2187
+ ext: "mie",
2188
+ mime: "application/x-mie"
2189
+ };
2190
+ }
2191
+ if (this.check([39, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], { offset: 2 })) {
2192
+ return {
2193
+ ext: "shp",
2194
+ mime: "application/x-esri-shape"
2195
+ };
2196
+ }
2197
+ if (this.check([255, 79, 255, 81])) {
2198
+ return {
2199
+ ext: "j2c",
2200
+ mime: "image/j2c"
2201
+ };
2202
+ }
2203
+ if (this.check([0, 0, 0, 12, 106, 80, 32, 32, 13, 10, 135, 10])) {
2204
+ await tokenizer.ignore(20);
2205
+ const type = await tokenizer.readToken(new StringType(4, "ascii"));
2206
+ switch (type) {
2207
+ case "jp2 ":
2208
+ return {
2209
+ ext: "jp2",
2210
+ mime: "image/jp2"
2211
+ };
2212
+ case "jpx ":
2213
+ return {
2214
+ ext: "jpx",
2215
+ mime: "image/jpx"
2216
+ };
2217
+ case "jpm ":
2218
+ return {
2219
+ ext: "jpm",
2220
+ mime: "image/jpm"
2221
+ };
2222
+ case "mjp2":
2223
+ return {
2224
+ ext: "mj2",
2225
+ mime: "image/mj2"
2226
+ };
2227
+ default:
2228
+ return;
2229
+ }
2230
+ }
2231
+ if (this.check([255, 10]) || this.check([0, 0, 0, 12, 74, 88, 76, 32, 13, 10, 135, 10])) {
2232
+ return {
2233
+ ext: "jxl",
2234
+ mime: "image/jxl"
2235
+ };
2236
+ }
2237
+ if (this.check([254, 255])) {
2238
+ if (this.check([0, 60, 0, 63, 0, 120, 0, 109, 0, 108], { offset: 2 })) {
2239
+ return {
2240
+ ext: "xml",
2241
+ mime: "application/xml"
2242
+ };
2243
+ }
2244
+ return void 0;
2245
+ }
2246
+ if (this.check([0, 0, 1, 186]) || this.check([0, 0, 1, 179])) {
2247
+ return {
2248
+ ext: "mpg",
2249
+ mime: "video/mpeg"
2250
+ };
2251
+ }
2252
+ if (this.check([0, 1, 0, 0, 0])) {
2253
+ return {
2254
+ ext: "ttf",
2255
+ mime: "font/ttf"
2256
+ };
2257
+ }
2258
+ if (this.check([0, 0, 1, 0])) {
2259
+ return {
2260
+ ext: "ico",
2261
+ mime: "image/x-icon"
2262
+ };
2263
+ }
2264
+ if (this.check([0, 0, 2, 0])) {
2265
+ return {
2266
+ ext: "cur",
2267
+ mime: "image/x-icon"
2268
+ };
2269
+ }
2270
+ if (this.check([208, 207, 17, 224, 161, 177, 26, 225])) {
2271
+ return {
2272
+ ext: "cfb",
2273
+ mime: "application/x-cfb"
2274
+ };
2275
+ }
2276
+ await tokenizer.peekBuffer(this.buffer, { length: Math.min(256, tokenizer.fileInfo.size), mayBeLess: true });
2277
+ if (this.check([97, 99, 115, 112], { offset: 36 })) {
2278
+ return {
2279
+ ext: "icc",
2280
+ mime: "application/vnd.iccprofile"
2281
+ };
2282
+ }
2283
+ if (this.checkString("**ACE", { offset: 7 }) && this.checkString("**", { offset: 12 })) {
2284
+ return {
2285
+ ext: "ace",
2286
+ mime: "application/x-ace-compressed"
2287
+ };
2288
+ }
2289
+ if (this.checkString("BEGIN:")) {
2290
+ if (this.checkString("VCARD", { offset: 6 })) {
2291
+ return {
2292
+ ext: "vcf",
2293
+ mime: "text/vcard"
2294
+ };
2295
+ }
2296
+ if (this.checkString("VCALENDAR", { offset: 6 })) {
2297
+ return {
2298
+ ext: "ics",
2299
+ mime: "text/calendar"
2300
+ };
2301
+ }
2302
+ }
2303
+ if (this.checkString("FUJIFILMCCD-RAW")) {
2304
+ return {
2305
+ ext: "raf",
2306
+ mime: "image/x-fujifilm-raf"
2307
+ };
2308
+ }
2309
+ if (this.checkString("Extended Module:")) {
2310
+ return {
2311
+ ext: "xm",
2312
+ mime: "audio/x-xm"
2313
+ };
2314
+ }
2315
+ if (this.checkString("Creative Voice File")) {
2316
+ return {
2317
+ ext: "voc",
2318
+ mime: "audio/x-voc"
2319
+ };
2320
+ }
2321
+ if (this.check([4, 0, 0, 0]) && this.buffer.length >= 16) {
2322
+ const jsonSize = new DataView(this.buffer.buffer).getUint32(12, true);
2323
+ if (jsonSize > 12 && this.buffer.length >= jsonSize + 16) {
2324
+ try {
2325
+ const header = new TextDecoder().decode(this.buffer.slice(16, jsonSize + 16));
2326
+ const json = JSON.parse(header);
2327
+ if (json.files) {
2328
+ return {
2329
+ ext: "asar",
2330
+ mime: "application/x-asar"
2331
+ };
2332
+ }
2333
+ } catch {
2334
+ }
2335
+ }
2336
+ }
2337
+ if (this.check([6, 14, 43, 52, 2, 5, 1, 1, 13, 1, 2, 1, 1, 2])) {
2338
+ return {
2339
+ ext: "mxf",
2340
+ mime: "application/mxf"
2341
+ };
2342
+ }
2343
+ if (this.checkString("SCRM", { offset: 44 })) {
2344
+ return {
2345
+ ext: "s3m",
2346
+ mime: "audio/x-s3m"
2347
+ };
2348
+ }
2349
+ if (this.check([71]) && this.check([71], { offset: 188 })) {
2350
+ return {
2351
+ ext: "mts",
2352
+ mime: "video/mp2t"
2353
+ };
2354
+ }
2355
+ if (this.check([71], { offset: 4 }) && this.check([71], { offset: 196 })) {
2356
+ return {
2357
+ ext: "mts",
2358
+ mime: "video/mp2t"
2359
+ };
2360
+ }
2361
+ if (this.check([66, 79, 79, 75, 77, 79, 66, 73], { offset: 60 })) {
2362
+ return {
2363
+ ext: "mobi",
2364
+ mime: "application/x-mobipocket-ebook"
2365
+ };
2366
+ }
2367
+ if (this.check([68, 73, 67, 77], { offset: 128 })) {
2368
+ return {
2369
+ ext: "dcm",
2370
+ mime: "application/dicom"
2371
+ };
2372
+ }
2373
+ if (this.check([76, 0, 0, 0, 1, 20, 2, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 70])) {
2374
+ return {
2375
+ ext: "lnk",
2376
+ mime: "application/x.ms.shortcut"
2377
+ // Invented by us
2378
+ };
2379
+ }
2380
+ if (this.check([98, 111, 111, 107, 0, 0, 0, 0, 109, 97, 114, 107, 0, 0, 0, 0])) {
2381
+ return {
2382
+ ext: "alias",
2383
+ mime: "application/x.apple.alias"
2384
+ // Invented by us
2385
+ };
2386
+ }
2387
+ if (this.checkString("Kaydara FBX Binary \0")) {
2388
+ return {
2389
+ ext: "fbx",
2390
+ mime: "application/x.autodesk.fbx"
2391
+ // Invented by us
2392
+ };
2393
+ }
2394
+ if (this.check([76, 80], { offset: 34 }) && (this.check([0, 0, 1], { offset: 8 }) || this.check([1, 0, 2], { offset: 8 }) || this.check([2, 0, 2], { offset: 8 }))) {
2395
+ return {
2396
+ ext: "eot",
2397
+ mime: "application/vnd.ms-fontobject"
2398
+ };
2399
+ }
2400
+ if (this.check([6, 6, 237, 245, 216, 29, 70, 229, 189, 49, 239, 231, 254, 116, 183, 29])) {
2401
+ return {
2402
+ ext: "indd",
2403
+ mime: "application/x-indesign"
2404
+ };
2405
+ }
2406
+ await tokenizer.peekBuffer(this.buffer, { length: Math.min(512, tokenizer.fileInfo.size), mayBeLess: true });
2407
+ if (tarHeaderChecksumMatches(this.buffer)) {
2408
+ return {
2409
+ ext: "tar",
2410
+ mime: "application/x-tar"
2411
+ };
2412
+ }
2413
+ if (this.check([255, 254])) {
2414
+ if (this.check([60, 0, 63, 0, 120, 0, 109, 0, 108, 0], { offset: 2 })) {
2415
+ return {
2416
+ ext: "xml",
2417
+ mime: "application/xml"
2418
+ };
2419
+ }
2420
+ if (this.check([255, 14, 83, 0, 107, 0, 101, 0, 116, 0, 99, 0, 104, 0, 85, 0, 112, 0, 32, 0, 77, 0, 111, 0, 100, 0, 101, 0, 108, 0], { offset: 2 })) {
2421
+ return {
2422
+ ext: "skp",
2423
+ mime: "application/vnd.sketchup.skp"
2424
+ };
2425
+ }
2426
+ return void 0;
2427
+ }
2428
+ if (this.checkString("-----BEGIN PGP MESSAGE-----")) {
2429
+ return {
2430
+ ext: "pgp",
2431
+ mime: "application/pgp-encrypted"
2432
+ };
2433
+ }
2434
+ if (this.buffer.length >= 2 && this.check([255, 224], { offset: 0, mask: [255, 224] })) {
2435
+ if (this.check([16], { offset: 1, mask: [22] })) {
2436
+ if (this.check([8], { offset: 1, mask: [8] })) {
2437
+ return {
2438
+ ext: "aac",
2439
+ mime: "audio/aac"
2440
+ };
2441
+ }
2442
+ return {
2443
+ ext: "aac",
2444
+ mime: "audio/aac"
2445
+ };
2446
+ }
2447
+ if (this.check([2], { offset: 1, mask: [6] })) {
2448
+ return {
2449
+ ext: "mp3",
2450
+ mime: "audio/mpeg"
2451
+ };
2452
+ }
2453
+ if (this.check([4], { offset: 1, mask: [6] })) {
2454
+ return {
2455
+ ext: "mp2",
2456
+ mime: "audio/mpeg"
2457
+ };
2458
+ }
2459
+ if (this.check([6], { offset: 1, mask: [6] })) {
2460
+ return {
2461
+ ext: "mp1",
2462
+ mime: "audio/mpeg"
2463
+ };
2464
+ }
2465
+ }
2466
+ }
2467
+ async readTiffTag(bigEndian) {
2468
+ const tagId = await this.tokenizer.readToken(bigEndian ? UINT16_BE : UINT16_LE);
2469
+ this.tokenizer.ignore(10);
2470
+ switch (tagId) {
2471
+ case 50341:
2472
+ return {
2473
+ ext: "arw",
2474
+ mime: "image/x-sony-arw"
2475
+ };
2476
+ case 50706:
2477
+ return {
2478
+ ext: "dng",
2479
+ mime: "image/x-adobe-dng"
2480
+ };
2481
+ }
2482
+ }
2483
+ async readTiffIFD(bigEndian) {
2484
+ const numberOfTags = await this.tokenizer.readToken(bigEndian ? UINT16_BE : UINT16_LE);
2485
+ for (let n = 0; n < numberOfTags; ++n) {
2486
+ const fileType = await this.readTiffTag(bigEndian);
2487
+ if (fileType) {
2488
+ return fileType;
2489
+ }
2490
+ }
2491
+ }
2492
+ async readTiffHeader(bigEndian) {
2493
+ const version = (bigEndian ? UINT16_BE : UINT16_LE).get(this.buffer, 2);
2494
+ const ifdOffset = (bigEndian ? UINT32_BE : UINT32_LE).get(this.buffer, 4);
2495
+ if (version === 42) {
2496
+ if (ifdOffset >= 6) {
2497
+ if (this.checkString("CR", { offset: 8 })) {
2498
+ return {
2499
+ ext: "cr2",
2500
+ mime: "image/x-canon-cr2"
2501
+ };
2502
+ }
2503
+ if (ifdOffset >= 8 && (this.check([28, 0, 254, 0], { offset: 8 }) || this.check([31, 0, 11, 0], { offset: 8 }))) {
2504
+ return {
2505
+ ext: "nef",
2506
+ mime: "image/x-nikon-nef"
2507
+ };
2508
+ }
2509
+ }
2510
+ await this.tokenizer.ignore(ifdOffset);
2511
+ const fileType = await this.readTiffIFD(bigEndian);
2512
+ return fileType ?? {
2513
+ ext: "tif",
2514
+ mime: "image/tiff"
2515
+ };
2516
+ }
2517
+ if (version === 43) {
2518
+ return {
2519
+ ext: "tif",
2520
+ mime: "image/tiff"
2521
+ };
2522
+ }
2523
+ }
2524
+ };
2525
+ new Set(extensions);
2526
+ new Set(mimeTypes);
2527
+
2528
+ // src/extract-metadata.ts
2529
+ async function extractMetadataFromBuffer(buffer, context, opts) {
2530
+ const extract = opts.extractMetadata !== false;
2531
+ if (!extract) {
2532
+ return {};
2533
+ }
2534
+ const patch = {
2535
+ file: {},
2536
+ checksums: {}
2537
+ };
2538
+ const { trusted } = context;
2539
+ if (trusted.file?.size == null) {
2540
+ patch.file.size = buffer.length;
2541
+ }
2542
+ if (trusted.file?.mimeType == null || trusted.file?.extension == null) {
2543
+ const ft = await fileTypeFromBuffer(buffer);
2544
+ if (ft) {
2545
+ if (trusted.file?.mimeType == null) patch.file.mimeType = ft.mime;
2546
+ if (trusted.file?.extension == null) {
2547
+ patch.file.extension = ft.ext.startsWith(".") ? ft.ext : `.${ft.ext}`;
2548
+ }
2549
+ }
2550
+ }
2551
+ const algorithms = opts.extractChecksums ?? ["sha256"];
2552
+ for (const algo of algorithms) {
2553
+ if (algo === "sha256" && trusted.checksums?.sha256 == null) {
2554
+ patch.checksums.sha256 = crypto__default.default.createHash("sha256").update(buffer).digest("hex");
2555
+ } else if (algo === "md5" && trusted.checksums?.md5 == null) {
2556
+ patch.checksums.md5 = crypto__default.default.createHash("md5").update(buffer).digest("hex");
2557
+ }
2558
+ }
2559
+ return patch;
2560
+ }
2561
+ async function validateFileType(buffer, file, metadata, opts) {
2562
+ const errors = [];
2563
+ const ext = path__default.default.extname(file.key).toLowerCase();
2564
+ if (opts.allowedExtensions && opts.allowedExtensions.length > 0) {
2565
+ const allowed = opts.allowedExtensions.map((e) => e.toLowerCase().replace(/^\.?/, "."));
2566
+ if (!allowed.includes(ext)) {
2567
+ errors.push({
2568
+ rule: "extension",
2569
+ message: `Extension ${ext} not allowed. Allowed: ${allowed.join(", ")}`,
2570
+ details: { extension: ext, allowedExtensions: opts.allowedExtensions }
2571
+ });
2572
+ }
2573
+ }
2574
+ let detectedMime;
2575
+ let magicMime;
2576
+ const useMagicBytes = opts.useMagicBytes !== false;
2577
+ if (useMagicBytes) {
2578
+ const ft = await fileTypeFromBuffer(buffer);
2579
+ magicMime = ft?.mime;
2580
+ if (!magicMime && opts.allowedMimeTypes && opts.allowedMimeTypes.length > 0) {
2581
+ errors.push({
2582
+ rule: "magic-bytes",
2583
+ message: "Could not detect MIME type from file content (magic bytes)",
2584
+ details: { fileKey: file.key }
2585
+ });
2586
+ }
2587
+ if (ext) {
2588
+ const allowedMimes = core.EXTENSION_TO_MIME_MAP[ext];
2589
+ if (allowedMimes) {
2590
+ if (!magicMime) {
2591
+ const isGeneric = allowedMimes.some(
2592
+ (m) => m === "application/octet-stream" || m === "text/plain"
2593
+ );
2594
+ if (!isGeneric) {
2595
+ errors.push({
2596
+ rule: "mime-spoof",
2597
+ message: `MIME spoofing detected: file content does not match expected ${ext} format`,
2598
+ details: { extension: ext, magicMime: "none" }
2599
+ });
2600
+ }
2601
+ } else if (!allowedMimes.includes(magicMime)) {
2602
+ errors.push({
2603
+ rule: "mime-spoof",
2604
+ message: `MIME spoofing detected: content is ${magicMime} but extension is ${ext}`,
2605
+ details: { extension: ext, magicMime }
2606
+ });
2607
+ }
2608
+ }
2609
+ }
2610
+ detectedMime = magicMime;
2611
+ }
2612
+ if (!detectedMime) {
2613
+ const metaMime = file.mimeType ?? metadata.contentType ?? metadata.mimeType ?? metadata["content-type"];
2614
+ detectedMime = typeof metaMime === "string" ? metaMime : void 0;
2615
+ }
2616
+ if (opts.allowedMimeTypes && opts.allowedMimeTypes.length > 0) {
2617
+ if (detectedMime && !opts.allowedMimeTypes.includes(detectedMime)) {
2618
+ errors.push({
2619
+ rule: "mime-type",
2620
+ message: `MIME type ${detectedMime} not allowed. Allowed: ${opts.allowedMimeTypes.join(", ")}`,
2621
+ details: { mime: detectedMime, allowedMimeTypes: opts.allowedMimeTypes }
2622
+ });
2623
+ } else if (!detectedMime) {
2624
+ errors.push({
2625
+ rule: "mime-type-missing",
2626
+ message: "MIME type could not be determined for validation",
2627
+ details: { fileKey: file.key }
2628
+ });
2629
+ }
2630
+ }
2631
+ return errors;
2632
+ }
2633
+
2634
+ // src/validators/file-size.ts
2635
+ function validateFileSize(buffer, opts) {
2636
+ const errors = [];
2637
+ const size = buffer.length;
2638
+ if (opts.minBytes != null && size < opts.minBytes) {
2639
+ errors.push({
2640
+ rule: "min-size",
2641
+ message: `File size ${size} bytes is below minimum ${opts.minBytes} bytes`,
2642
+ details: { size, minBytes: opts.minBytes }
2643
+ });
2644
+ }
2645
+ if (opts.maxBytes != null && size > opts.maxBytes) {
2646
+ errors.push({
2647
+ rule: "max-size",
2648
+ message: `File size ${size} bytes exceeds maximum ${opts.maxBytes} bytes`,
2649
+ details: { size, maxBytes: opts.maxBytes }
2650
+ });
2651
+ }
2652
+ return errors;
2653
+ }
2654
+ function validateDimensions(buffer, opts) {
2655
+ const errors = [];
2656
+ const dims = imageSize.imageSize(buffer);
2657
+ if (!dims || !dims.width || !dims.height) {
2658
+ return errors;
2659
+ }
2660
+ const { width, height } = dims;
2661
+ if (opts.minWidth != null && width < opts.minWidth) {
2662
+ errors.push({
2663
+ rule: "min-width",
2664
+ message: `Image width ${width}px is below minimum ${opts.minWidth}px`,
2665
+ details: { width, minWidth: opts.minWidth }
2666
+ });
2667
+ }
2668
+ if (opts.maxWidth != null && width > opts.maxWidth) {
2669
+ errors.push({
2670
+ rule: "max-width",
2671
+ message: `Image width ${width}px exceeds maximum ${opts.maxWidth}px`,
2672
+ details: { width, maxWidth: opts.maxWidth }
2673
+ });
2674
+ }
2675
+ if (opts.minHeight != null && height < opts.minHeight) {
2676
+ errors.push({
2677
+ rule: "min-height",
2678
+ message: `Image height ${height}px is below minimum ${opts.minHeight}px`,
2679
+ details: { height, minHeight: opts.minHeight }
2680
+ });
2681
+ }
2682
+ if (opts.maxHeight != null && height > opts.maxHeight) {
2683
+ errors.push({
2684
+ rule: "max-height",
2685
+ message: `Image height ${height}px exceeds maximum ${opts.maxHeight}px`,
2686
+ details: { height, maxHeight: opts.maxHeight }
2687
+ });
2688
+ }
2689
+ return errors;
2690
+ }
2691
+ function validateChecksum(buffer, metadata, opts) {
2692
+ const errors = [];
2693
+ const cfg = opts.checksum;
2694
+ if (!cfg) return errors;
2695
+ const algo = cfg.algorithm;
2696
+ const expected = cfg.metadataKey != null ? metadata[cfg.metadataKey] : void 0;
2697
+ if (expected == null || typeof expected !== "string") {
2698
+ errors.push({
2699
+ rule: "checksum",
2700
+ message: `Expected checksum not found in metadata (key: ${cfg.metadataKey ?? "default"})`,
2701
+ details: { metadataKey: cfg.metadataKey }
2702
+ });
2703
+ return errors;
2704
+ }
2705
+ const hash = crypto__default.default.createHash(algo).update(buffer).digest("hex");
2706
+ const expectedNorm = expected.toLowerCase().trim();
2707
+ if (hash.toLowerCase() !== expectedNorm) {
2708
+ errors.push({
2709
+ rule: "checksum",
2710
+ message: `${algo} hash mismatch`,
2711
+ details: { expected: expectedNorm, computed: hash }
2712
+ });
2713
+ }
2714
+ return errors;
2715
+ }
2716
+ async function validateChecksumUniqueness(buffer, fileKey, database, opts) {
2717
+ const errors = [];
2718
+ const cfg = opts.preventDuplicates;
2719
+ if (!cfg) return errors;
2720
+ const algo = "sha256";
2721
+ const hash = crypto__default.default.createHash(algo).update(buffer).digest("hex");
2722
+ const existing = await database.findOne({
2723
+ model: "media",
2724
+ where: [{ field: "checksum", value: hash }]
2725
+ });
2726
+ if (existing && existing.id !== fileKey) {
2727
+ errors.push({
2728
+ rule: "duplicate-content",
2729
+ message: `File with same ${algo} checksum already exists: ${existing.id}`,
2730
+ details: { duplicateId: existing.id, checksum: hash, algorithm: algo }
2731
+ });
2732
+ }
2733
+ return errors;
2734
+ }
2735
+
2736
+ // src/validators/security-scanner.ts
2737
+ var INJECTION_PATTERNS = [
2738
+ /(%27)|(')|(--)|(%23)|(#)/i,
2739
+ // SQL basics
2740
+ /(<script)/i,
2741
+ // XSS basics
2742
+ /(\$where)/i,
2743
+ // NoSQL basics
2744
+ /(%|\\x)[0-9a-f]{2}/i
2745
+ // Malicious encoding
2746
+ ];
2747
+ function runSecurityScan(value, path2 = "root") {
2748
+ const errors = [];
2749
+ if (typeof value === "string") {
2750
+ if (INJECTION_PATTERNS.some((p) => p.test(value))) {
2751
+ errors.push({
2752
+ rule: "security-threat",
2753
+ message: `Suspicious activity detected in ${path2}`,
2754
+ details: { path: path2, value: value.length > 50 ? `${value.substring(0, 50)}...` : value }
2755
+ });
2756
+ }
2757
+ } else if (Array.isArray(value)) {
2758
+ value.forEach((item, index) => {
2759
+ errors.push(...runSecurityScan(item, `${path2}[${index}]`));
2760
+ });
2761
+ } else if (value !== null && typeof value === "object") {
2762
+ Object.entries(value).forEach(([key, val]) => {
2763
+ errors.push(...runSecurityScan(val, `${path2}.${key}`));
2764
+ });
2765
+ }
2766
+ return errors;
2767
+ }
2768
+
2769
+ // src/validators/index.ts
2770
+ async function runValidators(buffer, file, metadata, database, opts) {
2771
+ const allErrors = [];
2772
+ allErrors.push(...runSecurityScan(file.key, "fileKey"));
2773
+ if (file.originalName) {
2774
+ allErrors.push(...runSecurityScan(file.originalName, "filename"));
2775
+ }
2776
+ allErrors.push(...runSecurityScan(metadata, "metadata"));
2777
+ if (opts.allowedExtensions || opts.allowedMimeTypes || opts.useMagicBytes !== false) {
2778
+ allErrors.push(...await validateFileType(buffer, file, metadata, opts));
2779
+ }
2780
+ if (opts.minBytes || opts.maxBytes) {
2781
+ allErrors.push(...validateFileSize(buffer, opts));
2782
+ }
2783
+ if (opts.minWidth || opts.maxWidth || opts.minHeight || opts.maxHeight) {
2784
+ allErrors.push(...await validateDimensions(buffer, opts));
2785
+ }
2786
+ if (opts.checksum) {
2787
+ allErrors.push(...validateChecksum(buffer, metadata, opts));
2788
+ }
2789
+ if (opts.preventDuplicates) {
2790
+ allErrors.push(...await validateChecksumUniqueness(buffer, file.key, database, opts));
2791
+ }
2792
+ if (opts.customValidators) {
2793
+ for (const v of opts.customValidators) {
2794
+ const e = await v(buffer, metadata, file.key);
2795
+ allErrors.push(...e);
2796
+ }
2797
+ }
2798
+ return allErrors;
2799
+ }
2800
+
2801
+ // src/runtime/runner.ts
2802
+ var SecurityLogger = {
2803
+ logSuspiciousActivity: (fileKey, errors) => {
2804
+ const threats = errors.filter((e) => e.rule === "security-threat" || e.rule === "magic-bytes");
2805
+ if (threats.length > 0) {
2806
+ console.warn(`[SECURITY ALERT] Suspicious activity on ${fileKey}:`, threats);
2807
+ }
2808
+ }
2809
+ };
2810
+ async function sleep(ms) {
2811
+ return new Promise((resolve) => setTimeout(resolve, ms));
2812
+ }
2813
+ async function fetchWithRetry(storage, fileKey, opts) {
2814
+ const behavior = opts.fileNotFoundBehavior ?? "fail";
2815
+ const retryOpts = opts.retryOptions ?? { maxAttempts: 3, delayMs: 1e3, backoff: "exponential" };
2816
+ const maxAttempts = retryOpts.maxAttempts ?? 3;
2817
+ const delayMs = retryOpts.delayMs ?? 1e3;
2818
+ const backoff = retryOpts.backoff ?? "exponential";
2819
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2820
+ const buffer = await storage.get(fileKey);
2821
+ if (buffer != null) return buffer;
2822
+ if (behavior === "skip") return null;
2823
+ if (behavior === "fail" || attempt === maxAttempts) return null;
2824
+ const wait = backoff === "exponential" ? delayMs * Math.pow(2, attempt - 1) : delayMs * attempt;
2825
+ await sleep(wait);
2826
+ }
2827
+ return null;
2828
+ }
2829
+ async function readBufferForValidation(context) {
2830
+ const fileContent = context.utilities?.fileContent;
2831
+ if (fileContent?.buffer) return fileContent.buffer;
2832
+ if (fileContent?.tempPath) return fs__default.default.readFile(fileContent.tempPath);
2833
+ return null;
2834
+ }
2835
+ async function recordValidationResult(database, recordId, valid, errors) {
2836
+ const model = "media_validation_results";
2837
+ const pluginId = "better-media-validation";
2838
+ const data = {
2839
+ mediaId: recordId,
2840
+ valid,
2841
+ pluginId,
2842
+ errors,
2843
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2844
+ };
2845
+ const existing = await database.findOne({
2846
+ model,
2847
+ where: [
2848
+ { field: "mediaId", value: recordId },
2849
+ { field: "pluginId", value: pluginId }
2850
+ ]
2851
+ });
2852
+ if (existing) {
2853
+ await database.update({
2854
+ model,
2855
+ where: [{ field: "id", value: existing.id }],
2856
+ update: data
2857
+ });
2858
+ } else {
2859
+ await database.create({
2860
+ model,
2861
+ data: { id: crypto.randomUUID(), ...data }
2862
+ });
2863
+ }
2864
+ }
2865
+ async function runValidation(context, api, opts) {
2866
+ const { file, metadata, storage, database } = context;
2867
+ const fileKey = file.key;
2868
+ let buffer = await readBufferForValidation(context);
2869
+ if (buffer == null) buffer = await fetchWithRetry(storage, fileKey, opts);
2870
+ if (buffer == null) {
2871
+ const notFoundError = {
2872
+ rule: "file-not-found",
2873
+ message: opts.fileNotFoundBehavior === "retry" ? "File not found in storage after retries (e.g. presigned URL upload not complete)" : "File not found in storage",
2874
+ details: { fileKey }
2875
+ };
2876
+ if (opts.fileNotFoundBehavior === "skip") {
2877
+ return;
2878
+ }
2879
+ await recordValidationResult(database, context.recordId, false, [notFoundError]);
2880
+ if (opts.onFailure === "continue" || opts.onFailure === "custom") {
2881
+ if (opts.onFailure === "custom" && opts.onFailureCallback) {
2882
+ const result2 = await opts.onFailureCallback(fileKey, [notFoundError]);
2883
+ if (result2 && result2.valid === false) return result2;
2884
+ }
2885
+ return;
2886
+ }
2887
+ return {
2888
+ valid: false,
2889
+ message: notFoundError.message
2890
+ };
2891
+ }
2892
+ core.markFileContentVerified(context);
2893
+ const patch = await extractMetadataFromBuffer(buffer, context, opts);
2894
+ const hasTrustedPatch = patch.file && Object.keys(patch.file).length > 0 || patch.checksums && Object.keys(patch.checksums).length > 0 || patch.media && Object.keys(patch.media).length > 0;
2895
+ if (hasTrustedPatch) {
2896
+ api.proposeTrusted(patch);
2897
+ }
2898
+ const errors = await runValidators(buffer, context.file, metadata, database, opts);
2899
+ if (errors.length === 0) {
2900
+ await recordValidationResult(database, context.recordId, true, []);
2901
+ return;
2902
+ }
2903
+ SecurityLogger.logSuspiciousActivity(fileKey, errors);
2904
+ await recordValidationResult(database, context.recordId, false, errors);
2905
+ const message = errors.map((e) => e.message).join("; ");
2906
+ const result = { valid: false, message };
2907
+ switch (opts.onFailure ?? "abort") {
2908
+ case "continue":
2909
+ return;
2910
+ case "custom":
2911
+ if (opts.onFailureCallback) {
2912
+ const customResult = await opts.onFailureCallback(fileKey, errors);
2913
+ if (customResult && customResult.valid === false) return customResult;
2914
+ }
2915
+ return result;
2916
+ case "abort":
2917
+ default:
2918
+ return result;
2919
+ }
2920
+ }
2921
+
2922
+ // src/index.ts
2923
+ function validationPlugin(opts = {}) {
2924
+ const executionMode = opts.executionMode ?? "background";
2925
+ const isBackground = executionMode === "background";
2926
+ return {
2927
+ name: "validation",
2928
+ runtimeManifest: {
2929
+ id: "better-media-validation",
2930
+ version: "1.0.0",
2931
+ trustLevel: "trusted",
2932
+ // Authorized for core metadata (size, mime, checksums)
2933
+ capabilities: ["file.read", "metadata.write.own", "processing.write.own", "trusted.propose"],
2934
+ namespace: "validation"
2935
+ },
2936
+ executionMode,
2937
+ intensive: isBackground,
2938
+ apply(runtime) {
2939
+ runtime.hooks["validation:run"].tap(
2940
+ "validation",
2941
+ async (context, api) => {
2942
+ return runValidation(context, api, opts);
2943
+ },
2944
+ { mode: executionMode }
2945
+ );
2946
+ }
2947
+ };
2948
+ }
2949
+ /*! Bundled license information:
2950
+
2951
+ ieee754/index.js:
2952
+ (*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> *)
2953
+ */
2954
+
2955
+ exports.validationPlugin = validationPlugin;
2956
+ //# sourceMappingURL=index.js.map
2957
+ //# sourceMappingURL=index.js.map