@bedolla/enrivision 0.1.4 → 0.1.6

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.
Files changed (64) hide show
  1. package/README.md +31 -6
  2. package/dist/client/EnriProxyClient.d.ts +289 -244
  3. package/dist/client/EnriProxyClient.d.ts.map +1 -1
  4. package/dist/client/EnriProxyClient.js +841 -115
  5. package/dist/client/EnriProxyClient.js.map +1 -1
  6. package/dist/client/EnriProxyClientContract.d.ts +425 -0
  7. package/dist/client/EnriProxyClientContract.d.ts.map +1 -0
  8. package/dist/client/EnriProxyClientContract.js +87 -0
  9. package/dist/client/EnriProxyClientContract.js.map +1 -0
  10. package/dist/index.js +23 -12
  11. package/dist/index.js.map +1 -1
  12. package/dist/package-info.d.ts +28 -0
  13. package/dist/package-info.d.ts.map +1 -1
  14. package/dist/package-info.js +28 -0
  15. package/dist/package-info.js.map +1 -1
  16. package/dist/server/EnriVisionServer.d.ts +186 -0
  17. package/dist/server/EnriVisionServer.d.ts.map +1 -1
  18. package/dist/server/EnriVisionServer.js +780 -93
  19. package/dist/server/EnriVisionServer.js.map +1 -1
  20. package/dist/shared/codepointTruncation.d.ts +61 -0
  21. package/dist/shared/codepointTruncation.d.ts.map +1 -0
  22. package/dist/shared/codepointTruncation.js +73 -0
  23. package/dist/shared/codepointTruncation.js.map +1 -0
  24. package/dist/shared/mediaUrlFetcher.d.ts +247 -9
  25. package/dist/shared/mediaUrlFetcher.d.ts.map +1 -1
  26. package/dist/shared/mediaUrlFetcher.js +712 -53
  27. package/dist/shared/mediaUrlFetcher.js.map +1 -1
  28. package/dist/shared/tar.d.ts +82 -2
  29. package/dist/shared/tar.d.ts.map +1 -1
  30. package/dist/shared/tar.js +106 -43
  31. package/dist/shared/tar.js.map +1 -1
  32. package/dist/shared/validation.d.ts +96 -2
  33. package/dist/shared/validation.d.ts.map +1 -1
  34. package/dist/shared/validation.js +169 -10
  35. package/dist/shared/validation.js.map +1 -1
  36. package/dist/tools/AnalyzeMediaContract.d.ts +457 -0
  37. package/dist/tools/AnalyzeMediaContract.d.ts.map +1 -0
  38. package/dist/tools/AnalyzeMediaContract.js +161 -0
  39. package/dist/tools/AnalyzeMediaContract.js.map +1 -0
  40. package/dist/tools/AnalyzeMediaExtractionSanitizer.d.ts +35 -0
  41. package/dist/tools/AnalyzeMediaExtractionSanitizer.d.ts.map +1 -0
  42. package/dist/tools/AnalyzeMediaExtractionSanitizer.js +214 -0
  43. package/dist/tools/AnalyzeMediaExtractionSanitizer.js.map +1 -0
  44. package/dist/tools/AnalyzeMediaInputResolver.d.ts +250 -0
  45. package/dist/tools/AnalyzeMediaInputResolver.d.ts.map +1 -0
  46. package/dist/tools/AnalyzeMediaInputResolver.js +430 -0
  47. package/dist/tools/AnalyzeMediaInputResolver.js.map +1 -0
  48. package/dist/tools/AnalyzeMediaParamParser.d.ts +299 -0
  49. package/dist/tools/AnalyzeMediaParamParser.d.ts.map +1 -0
  50. package/dist/tools/AnalyzeMediaParamParser.js +824 -0
  51. package/dist/tools/AnalyzeMediaParamParser.js.map +1 -0
  52. package/dist/tools/AnalyzeMediaResumableUploader.d.ts +244 -0
  53. package/dist/tools/AnalyzeMediaResumableUploader.d.ts.map +1 -0
  54. package/dist/tools/AnalyzeMediaResumableUploader.js +549 -0
  55. package/dist/tools/AnalyzeMediaResumableUploader.js.map +1 -0
  56. package/dist/tools/AnalyzeMediaTarPackager.d.ts +42 -0
  57. package/dist/tools/AnalyzeMediaTarPackager.d.ts.map +1 -0
  58. package/dist/tools/AnalyzeMediaTarPackager.js +245 -0
  59. package/dist/tools/AnalyzeMediaTarPackager.js.map +1 -0
  60. package/dist/tools/AnalyzeMediaTool.d.ts +155 -294
  61. package/dist/tools/AnalyzeMediaTool.d.ts.map +1 -1
  62. package/dist/tools/AnalyzeMediaTool.js +600 -457
  63. package/dist/tools/AnalyzeMediaTool.js.map +1 -1
  64. package/package.json +2 -1
@@ -0,0 +1,430 @@
1
+ /**
2
+ * ANALYZE MEDIA INPUT RESOLVER
3
+ *
4
+ * Turns validated `path`/`paths` arguments into local files ready for
5
+ * upload: URL inputs are materialized through bounded streaming downloads,
6
+ * every input is readability-checked, and its effective upload content type
7
+ * is resolved.
8
+ *
9
+ * Content-type rule: the server-reported content type is authoritative and
10
+ * always wins over the extension-derived guess, so `image/webp` served from
11
+ * `/a.png` can never degrade to `image/png`; the generic
12
+ * `application/octet-stream` never wins.
13
+ *
14
+ * @module tools/AnalyzeMediaInputResolver
15
+ */
16
+ import { constants } from "node:fs";
17
+ import { lstat, open, stat } from "node:fs/promises";
18
+ import { basename } from "node:path";
19
+ import { lookup as mimeLookup } from "mime-types";
20
+ import { MediaUrlFetcher } from "../shared/mediaUrlFetcher.js";
21
+ import { describeFileIdentity } from "./AnalyzeMediaResumableUploader.js";
22
+ import { ANALYZE_MEDIA_LIMITS } from "./AnalyzeMediaContract.js";
23
+ /**
24
+ * Reports whether one filesystem error carries the given `code`.
25
+ *
26
+ * @param error - Caught error value.
27
+ * @param code - Expected Node.js error code (for example `"ELOOP"`).
28
+ * @returns True when the error carries the code.
29
+ */
30
+ function isErrnoCode(error, code) {
31
+ return error instanceof Error && "code" in error && error.code === code;
32
+ }
33
+ /**
34
+ * Resolves validated media arguments into upload-ready local files.
35
+ */
36
+ export class AnalyzeMediaInputResolver {
37
+ /**
38
+ * Do-not-follow flag for strict-mode opens (`0` where advisory).
39
+ *
40
+ * @remarks
41
+ * `O_NOFOLLOW` is honored on POSIX; on Windows it is advisory at best,
42
+ * so the `dev:ino` handle-identity comparison stays the real gate there.
43
+ */
44
+ static NO_FOLLOW_FLAG = process.platform === "win32" ? 0 : constants.O_NOFOLLOW;
45
+ /**
46
+ * Bounded http(s) media fetcher used for URL inputs.
47
+ */
48
+ urlFetcher;
49
+ /**
50
+ * Creates a new {@link AnalyzeMediaInputResolver}.
51
+ *
52
+ * @param urlFetcher - URL fetcher used to materialize http(s) inputs.
53
+ */
54
+ constructor(urlFetcher) {
55
+ this.urlFetcher = urlFetcher;
56
+ }
57
+ /**
58
+ * Resolves validated params into local upload-ready inputs.
59
+ *
60
+ * @remarks
61
+ * When `paths` carries at least one entry it wins over `path` (documented
62
+ * in the tool schema).
63
+ *
64
+ * @param params - Validated tool parameters.
65
+ * @param signal - Optional cancellation signal forwarded to URL downloads.
66
+ * @returns Resolved inputs plus owned downloads awaiting cleanup.
67
+ * @throws Error with an Spanish-first bilingual message when no input was provided, a download fails, or a file is unreadable.
68
+ */
69
+ async resolve(params, signal) {
70
+ const requestedPaths = Array.isArray(params.paths) && params.paths.length > 0
71
+ ? [...params.paths]
72
+ : typeof params.path === "string" && params.path.trim()
73
+ ? [params.path.trim()]
74
+ : [];
75
+ if (requestedPaths.length === 0) {
76
+ throw new Error("Proporcione 'path' o 'paths'. / Provide 'path' or 'paths'.");
77
+ }
78
+ const materialized = [];
79
+ const inputs = [];
80
+ // Fail fast once the resolved bytes exceed the 4 GiB upload ceiling so
81
+ // a 100-URL set stops materializing instead of downloading ~6 GiB of
82
+ // temp files before the tar packager rejects the set.
83
+ // Multi-entry sets are image-only: each entry is validated right after
84
+ // it materializes (authoritative check on the effective content type),
85
+ // so the first non-image entry fails before the rest is downloaded.
86
+ const isImageSet = requestedPaths.length > 1;
87
+ let resolvedBytes = 0;
88
+ try {
89
+ for (const requested of requestedPaths) {
90
+ if (!MediaUrlFetcher.isHttpUrl(requested)) {
91
+ const local = await this.resolveLocalFile(requested);
92
+ this.throwOnNonImageSetEntry(local.localPath, local.contentType, isImageSet);
93
+ resolvedBytes += local.sizeBytes;
94
+ this.throwOnUploadCeilingExceeded(resolvedBytes, inputs.length + 1, isImageSet);
95
+ inputs.push(local);
96
+ continue;
97
+ }
98
+ // A lone oversized URL escalates to server-side `source_url`
99
+ // ingest instead of failing: the fetcher already passed its SSRF
100
+ // guards before hitting the size cap, and multi-entry sets stay
101
+ // local-only (the server tar flow has no URL-ingest branch).
102
+ if (!isImageSet) {
103
+ try {
104
+ const fetched = await this.urlFetcher.fetch(requested, { signal });
105
+ materialized.push(fetched);
106
+ const downloaded = await this.resolveDownloadedFile(fetched);
107
+ resolvedBytes += downloaded.sizeBytes;
108
+ this.throwOnUploadCeilingExceeded(resolvedBytes, inputs.length + 1, isImageSet);
109
+ inputs.push(downloaded);
110
+ continue;
111
+ }
112
+ catch (error) {
113
+ if (signal?.aborted !== true && MediaUrlFetcher.isSizeCapError(error)) {
114
+ for (const fetched of materialized) {
115
+ await fetched.cleanup();
116
+ }
117
+ return { inputs: [], materialized: [], remoteUrl: requested };
118
+ }
119
+ throw error;
120
+ }
121
+ }
122
+ const fetched = await this.urlFetcher.fetch(requested, { signal });
123
+ materialized.push(fetched);
124
+ const downloaded = await this.resolveDownloadedFile(fetched);
125
+ this.throwOnNonImageSetEntry(downloaded.localPath, downloaded.contentType, isImageSet);
126
+ resolvedBytes += downloaded.sizeBytes;
127
+ this.throwOnUploadCeilingExceeded(resolvedBytes, inputs.length + 1, isImageSet);
128
+ inputs.push(downloaded);
129
+ }
130
+ }
131
+ catch (error) {
132
+ for (const fetched of materialized) {
133
+ await fetched.cleanup();
134
+ }
135
+ throw error;
136
+ }
137
+ return { inputs, materialized };
138
+ }
139
+ /**
140
+ * Fails fast when resolved bytes already exceed the upload ceiling.
141
+ *
142
+ * @remarks
143
+ * Multi-entry sets travel as one tar archive (headers, padding, manifest,
144
+ * end marker), so the fail-fast estimates the tar framing overhead (see
145
+ * {@link estimateMediaSetTarBytes}) instead of the raw byte sum: a set
146
+ * summing to 3.99 GiB raw would otherwise materialize ~100 temp downloads
147
+ * only for the packager to reject it once framing pushes it over 4 GiB.
148
+ * Over-estimating is correct for a fail-fast.
149
+ *
150
+ * @param resolvedBytes - Running total of resolved input bytes.
151
+ * @param entryCount - Resolved entries so far (including the latest).
152
+ * @param isImageSet - Whether the call resolves a multi-entry set.
153
+ * @throws Error with an Spanish-first bilingual message when the set already exceeds 4 GiB.
154
+ */
155
+ throwOnUploadCeilingExceeded(resolvedBytes, entryCount, isImageSet) {
156
+ const effectiveBytes = isImageSet
157
+ ? estimateMediaSetTarBytes(resolvedBytes, entryCount)
158
+ : resolvedBytes;
159
+ if (effectiveBytes > ANALYZE_MEDIA_LIMITS.maxUploadBytes) {
160
+ throw new Error(isImageSet
161
+ ? `El conjunto de archivos excede el límite de subida de 4 GiB (${String(effectiveBytes)} bytes estimados con tar overhead); divida el conjunto en varias llamadas. / File set exceeds the 4 GiB upload limit (${String(effectiveBytes)} bytes estimated with tar overhead); split the set into several calls.`
162
+ : `El archivo excede el límite de subida de 4 GiB (${String(effectiveBytes)} bytes); use un archivo más liviano. / File exceeds the 4 GiB upload limit (${String(effectiveBytes)} bytes); use a lighter file.`);
163
+ }
164
+ }
165
+ /**
166
+ * Resolves one local file into an upload-ready input.
167
+ *
168
+ * @param localPath - Absolute local file path.
169
+ * @returns Upload-ready input.
170
+ * @throws Error with an Spanish-first bilingual message when the file is missing or unreadable.
171
+ */
172
+ async resolveLocalFile(localPath) {
173
+ const staged = await this.assertReadableFile(localPath);
174
+ const sizeBytes = staged.sizeBytes;
175
+ const contentType = this.detectMimeType(localPath);
176
+ // Fail fast before any upload session exists: a 4 GiB non-media file
177
+ // must never be uploaded only for the server to reject it.
178
+ if (!MediaUrlFetcher.isAllowedMediaContentType(contentType)) {
179
+ throw new Error(`El archivo local no es media analizable (${localPath}, content-type: ${contentType}). Solo se aceptan imagen, video, audio, PDF y documentos de Office. / Local file is not analyzable media (${localPath}, content-type: ${contentType}). Only image, video, audio, PDF, and Office documents are accepted.`);
180
+ }
181
+ return {
182
+ localPath,
183
+ filename: basename(localPath),
184
+ sizeBytes,
185
+ contentType,
186
+ extensionSynthesized: false,
187
+ stagedIdentity: staged.stagedIdentity,
188
+ };
189
+ }
190
+ /**
191
+ * Rejects a non-image entry of a multi-entry set right after it materializes.
192
+ *
193
+ * @remarks
194
+ * The tar packager repeats this check authoritatively before packing; the
195
+ * early check here only stops the resolver from downloading the rest of
196
+ * the set after the first failure. Single inputs keep any allowed type.
197
+ *
198
+ * @param localPath - Materialized local path (for error messages).
199
+ * @param contentType - Effective content type of the entry.
200
+ * @param isImageSet - Whether the call resolves a multi-entry set.
201
+ * @throws Error with an Spanish-first bilingual message when a set entry is not an image.
202
+ */
203
+ throwOnNonImageSetEntry(localPath, contentType, isImageSet) {
204
+ if (isImageSet && !contentType.toLowerCase().startsWith("image/")) {
205
+ throw new Error(`paths debe contener sólo archivos de imagen. No es imagen: ${localPath} (${contentType}) / paths must contain only image files. Not an image: ${localPath} (${contentType}).`);
206
+ }
207
+ }
208
+ /**
209
+ * Resolves one downloaded URL file into an upload-ready input.
210
+ *
211
+ * @param fetched - Owned download result.
212
+ * @returns Upload-ready input preferring the server content type when the extension was synthesized.
213
+ * @throws Error with an Spanish-first bilingual message when the downloaded file is unreadable.
214
+ */
215
+ async resolveDownloadedFile(fetched) {
216
+ const staged = await this.assertReadableFile(fetched.localPath);
217
+ const sizeBytes = staged.sizeBytes;
218
+ const urlContentType = fetched.contentType;
219
+ const contentType = this.resolveEffectiveContentType(fetched.localPath, urlContentType);
220
+ // Same allowlist as local files: a download that resolves to
221
+ // non-media (blanked disallowed type plus an uninferred extension)
222
+ // fails before any upload session exists, never mid-upload.
223
+ if (!MediaUrlFetcher.isAllowedMediaContentType(contentType)) {
224
+ throw new Error(`La URL no sirvió un archivo de media válido (${fetched.localPath}, content-type: ${contentType}). Solo se aceptan imagen, video, audio, PDF y documentos de Office. / URL did not serve a valid media file (${fetched.localPath}, content-type: ${contentType}). Only image, video, audio, PDF, and Office documents are accepted.`);
225
+ }
226
+ return {
227
+ localPath: fetched.localPath,
228
+ filename: basename(fetched.localPath),
229
+ sizeBytes,
230
+ contentType,
231
+ urlContentType,
232
+ extensionSynthesized: fetched.extensionSynthesized,
233
+ stagedIdentity: staged.stagedIdentity,
234
+ };
235
+ }
236
+ /**
237
+ * Resolves the upload content type for one media input.
238
+ *
239
+ * @remarks
240
+ * The server-reported content type is authoritative: whenever it differs
241
+ * from the extension-derived guess it wins (a synthesized download
242
+ * extension is only a filesystem hint, and a mismatched authored
243
+ * extension such as `/a.png` serving `image/webp` must never shadow the
244
+ * real type). The generic `application/octet-stream` never wins.
245
+ *
246
+ * @param localPath - Local file path (already materialized for URLs).
247
+ * @param urlContentType - Server-reported content type for downloaded URLs.
248
+ * @returns Effective MIME type.
249
+ */
250
+ resolveEffectiveContentType(localPath, urlContentType) {
251
+ const reported = typeof urlContentType === "string" ? urlContentType.trim().toLowerCase() : "";
252
+ if (reported !== "" && reported !== "application/octet-stream") {
253
+ return reported;
254
+ }
255
+ return this.detectMimeType(localPath);
256
+ }
257
+ /**
258
+ * Detects a MIME type using the file extension.
259
+ *
260
+ * @param filePath - File path.
261
+ * @returns MIME type string.
262
+ */
263
+ detectMimeType(filePath) {
264
+ const detected = mimeLookup(filePath);
265
+ if (typeof detected === "string" && detected.trim()) {
266
+ return detected.trim();
267
+ }
268
+ return "application/octet-stream";
269
+ }
270
+ /**
271
+ * Validates that a path exists and is a readable file.
272
+ *
273
+ * @remarks
274
+ * `path` follows symlinks by design (the MCP host trusts the model with
275
+ * host-file access, like the documented DNS-rebinding residual in
276
+ * `mediaUrlFetcher.ts`). Operators that need strict mode set
277
+ * `ENRIVISION_DENY_SYMLINKS=1` to reject symlinked inputs with a Spanish
278
+ * error instead.
279
+ *
280
+ * @param filePath - Local filesystem path.
281
+ * @returns File size in bytes.
282
+ * @throws Error with an Spanish-first bilingual message when the file is missing, not readable, a rejected symlink, or exceeds 4 GiB.
283
+ */
284
+ async assertReadableFile(filePath) {
285
+ if (process.env["ENRIVISION_DENY_SYMLINKS"] === "1") {
286
+ return this.assertReadableFileWithoutSymlinks(filePath);
287
+ }
288
+ let fileStat;
289
+ try {
290
+ fileStat = await stat(filePath);
291
+ }
292
+ catch (error) {
293
+ this.throwOnMissingFile(filePath, error);
294
+ throw new Error(`Archivo no encontrado: ${filePath} / File not found: ${filePath}.`);
295
+ }
296
+ // Ensure the file is readable.
297
+ const handle = await open(filePath, "r");
298
+ try {
299
+ // Stage the identity from `fstat` on the opened handle (never the
300
+ // path stat): the uploader compares it at open time so a same-size
301
+ // replacement between resolve and upload fails loudly.
302
+ const openedStat = await handle.stat();
303
+ return {
304
+ sizeBytes: this.checkFileStat(filePath, fileStat.size, fileStat.isFile()),
305
+ stagedIdentity: describeFileIdentity(openedStat),
306
+ };
307
+ }
308
+ finally {
309
+ await handle.close();
310
+ }
311
+ }
312
+ /**
313
+ * Validates one file without ever trusting a path re-stat.
314
+ *
315
+ * @remarks
316
+ * `lstat` + `stat` + `open` on the same path races: a symlink swapped in
317
+ * between the check and the open defeats `ENRIVISION_DENY_SYMLINKS`.
318
+ * Opening with `O_NOFOLLOW` (POSIX) makes a swapped-in symlink fail with
319
+ * `ELOOP` instead, and the size/kind verdicts come from `fstat` on the
320
+ * opened handle — never from a second path lookup. The `dev:ino`
321
+ * comparison additionally rejects a real file swapped between `lstat`
322
+ * and `open` on platforms where `O_NOFOLLOW` is advisory.
323
+ *
324
+ * @param filePath - Local filesystem path.
325
+ * @returns File size in bytes plus the staged `fstat` identity.
326
+ * @throws Error with an Spanish-first bilingual message when the file is missing, a symlink, swapped mid-check, or exceeds 4 GiB.
327
+ */
328
+ async assertReadableFileWithoutSymlinks(filePath) {
329
+ let linkStat;
330
+ try {
331
+ linkStat = await lstat(filePath);
332
+ if (linkStat.isSymbolicLink()) {
333
+ throw new Error(`No se permiten enlaces simbólicos: ${filePath} / Symbolic links are not allowed: ${filePath}.`);
334
+ }
335
+ }
336
+ catch (error) {
337
+ this.throwOnMissingFile(filePath, error);
338
+ throw new Error(`Archivo no encontrado: ${filePath} / File not found: ${filePath}.`);
339
+ }
340
+ let handle;
341
+ try {
342
+ handle = await open(filePath, constants.O_RDONLY | AnalyzeMediaInputResolver.NO_FOLLOW_FLAG);
343
+ }
344
+ catch (error) {
345
+ if (isErrnoCode(error, "ELOOP")) {
346
+ throw new Error(`No se permiten enlaces simbólicos: ${filePath} / Symbolic links are not allowed: ${filePath}.`);
347
+ }
348
+ this.throwOnMissingFile(filePath, error);
349
+ throw new Error(`Archivo no encontrado: ${filePath} / File not found: ${filePath}.`);
350
+ }
351
+ try {
352
+ const openedStat = await handle.stat();
353
+ if (openedStat.dev !== linkStat.dev || openedStat.ino !== linkStat.ino) {
354
+ throw new Error(`El archivo cambió durante la validación (${filePath}); reintente la llamada. / File changed during validation (${filePath}); retry the call.`);
355
+ }
356
+ return {
357
+ sizeBytes: this.checkFileStat(filePath, openedStat.size, openedStat.isFile()),
358
+ stagedIdentity: describeFileIdentity(openedStat),
359
+ };
360
+ }
361
+ finally {
362
+ await handle.close();
363
+ }
364
+ }
365
+ /**
366
+ * Applies the shared size/kind verdicts to one stat result.
367
+ *
368
+ * @param filePath - Local filesystem path (for error messages).
369
+ * @param size - File size in bytes.
370
+ * @param isFile - Whether the stat target is a regular file.
371
+ * @returns File size in bytes.
372
+ * @throws Error with an Spanish-first bilingual message when the target is not a file, is empty, or exceeds 4 GiB.
373
+ */
374
+ checkFileStat(filePath, size, isFile) {
375
+ if (!isFile) {
376
+ throw new Error(`No es un archivo: ${filePath} / Not a file: ${filePath}.`);
377
+ }
378
+ if (size === 0) {
379
+ throw new Error(`El archivo está vacío (0 bytes): ${filePath}. No hay nada que analizar. / File is empty (0 bytes): ${filePath}. Nothing to analyze.`);
380
+ }
381
+ if (size > ANALYZE_MEDIA_LIMITS.maxUploadBytes) {
382
+ throw new Error(`El archivo excede el límite de subida de 4 GiB: ${filePath} (${String(size)} bytes). / File exceeds the 4 GiB upload limit: ${filePath} (${String(size)} bytes).`);
383
+ }
384
+ return size;
385
+ }
386
+ /**
387
+ * Maps filesystem lookup failures to the stable not-found error.
388
+ *
389
+ * @param filePath - Local filesystem path (for error messages).
390
+ * @param error - Raw filesystem error.
391
+ * @throws Error with an Spanish-first bilingual message when the file is missing; rethrows unreadable-file errors otherwise.
392
+ */
393
+ throwOnMissingFile(filePath, error) {
394
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
395
+ throw new Error(`Archivo no encontrado: ${filePath} / File not found: ${filePath}.`);
396
+ }
397
+ if (error instanceof Error && /no se permiten enlaces/i.test(error.message)) {
398
+ throw error;
399
+ }
400
+ throw new Error(`No se puede leer el archivo: ${filePath} / Cannot read file: ${filePath}.`);
401
+ }
402
+ }
403
+ /**
404
+ * Estimates the tar archive size for a multi-image set before packing.
405
+ *
406
+ * @remarks
407
+ * Fail-fast estimator (over-estimates on purpose): raw bytes plus one 512 B
408
+ * header per file, the manifest entry (512 B header plus content padded to
409
+ * 512 B), worst-case content padding (511 B per file), and the 1024 B end
410
+ * marker. The manifest term budgets 256 B of envelope plus 512 B per entry
411
+ * (worst case: 255 B basenames plus content type plus JSON overhead), so
412
+ * long filenames never under-estimate. Used by the resolver so a set that
413
+ * only fits raw never pays ~100 temp downloads before the packager rejects
414
+ * it.
415
+ *
416
+ * @param rawBytes - Summed input bytes.
417
+ * @param fileCount - Number of files in the set.
418
+ * @returns Estimated tar size in bytes.
419
+ */
420
+ export function estimateMediaSetTarBytes(rawBytes, fileCount) {
421
+ const manifestContentBytes = 256 + Math.max(0, fileCount) * 512;
422
+ const manifestPaddedBytes = Math.ceil(manifestContentBytes / 512) * 512;
423
+ return (Math.max(0, rawBytes) +
424
+ Math.max(0, fileCount) * 512 +
425
+ 512 +
426
+ manifestPaddedBytes +
427
+ Math.max(0, fileCount) * 511 +
428
+ 1024);
429
+ }
430
+ //# sourceMappingURL=AnalyzeMediaInputResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyzeMediaInputResolver.js","sourceRoot":"","sources":["../../src/tools/AnalyzeMediaInputResolver.ts"],"names":[],"mappings":"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"}