@juspay/neurolink 10.10.12 → 10.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/adapters/imageFormatSupport.d.ts +75 -0
  3. package/dist/adapters/imageFormatSupport.js +283 -0
  4. package/dist/adapters/video/ffmpegAdapter.d.ts +6 -0
  5. package/dist/adapters/video/ffmpegAdapter.js +1 -1
  6. package/dist/browser/neurolink.min.js +399 -398
  7. package/dist/lib/adapters/imageFormatSupport.d.ts +75 -0
  8. package/dist/lib/adapters/imageFormatSupport.js +284 -0
  9. package/dist/lib/adapters/video/ffmpegAdapter.d.ts +6 -0
  10. package/dist/lib/adapters/video/ffmpegAdapter.js +1 -1
  11. package/dist/lib/processors/config/fileExtensions.d.ts +32 -15
  12. package/dist/lib/processors/config/fileExtensions.js +27 -66
  13. package/dist/lib/processors/config/fileTypeRegistry.d.ts +106 -0
  14. package/dist/lib/processors/config/fileTypeRegistry.js +702 -0
  15. package/dist/lib/processors/config/index.d.ts +2 -1
  16. package/dist/lib/processors/config/index.js +5 -1
  17. package/dist/lib/processors/config/mimeConstants.d.ts +22 -7
  18. package/dist/lib/processors/config/mimeConstants.js +45 -66
  19. package/dist/lib/processors/media/AudioProcessor.js +16 -38
  20. package/dist/lib/processors/media/VideoProcessor.js +11 -32
  21. package/dist/lib/providers/googleAiStudio/client.js +12 -1
  22. package/dist/lib/providers/googleVertex/client.js +123 -66
  23. package/dist/lib/types/file.d.ts +41 -0
  24. package/dist/lib/utils/fileDetector.js +367 -251
  25. package/dist/lib/utils/imageProcessor.js +14 -17
  26. package/dist/lib/utils/markupSniff.d.ts +37 -0
  27. package/dist/lib/utils/markupSniff.js +125 -0
  28. package/dist/lib/utils/messageBuilder.d.ts +14 -0
  29. package/dist/lib/utils/messageBuilder.js +172 -92
  30. package/dist/processors/config/fileExtensions.d.ts +32 -15
  31. package/dist/processors/config/fileExtensions.js +27 -66
  32. package/dist/processors/config/fileTypeRegistry.d.ts +106 -0
  33. package/dist/processors/config/fileTypeRegistry.js +701 -0
  34. package/dist/processors/config/index.d.ts +2 -1
  35. package/dist/processors/config/index.js +5 -1
  36. package/dist/processors/config/mimeConstants.d.ts +22 -7
  37. package/dist/processors/config/mimeConstants.js +45 -66
  38. package/dist/processors/media/AudioProcessor.js +16 -38
  39. package/dist/processors/media/VideoProcessor.js +11 -32
  40. package/dist/providers/googleAiStudio/client.js +12 -1
  41. package/dist/providers/googleVertex/client.js +123 -66
  42. package/dist/types/file.d.ts +41 -0
  43. package/dist/utils/fileDetector.js +367 -251
  44. package/dist/utils/imageProcessor.js +14 -17
  45. package/dist/utils/markupSniff.d.ts +37 -0
  46. package/dist/utils/markupSniff.js +124 -0
  47. package/dist/utils/messageBuilder.d.ts +14 -0
  48. package/dist/utils/messageBuilder.js +172 -92
  49. package/package.json +3 -2
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Vision-provider image format compatibility.
3
+ *
4
+ * NeuroLink identifies far more image formats than any vision API accepts. BMP,
5
+ * TIFF, AVIF, ICO and JPEG 2000 are accepted by none of them; HEIC and HEIF are
6
+ * accepted only by Google. Until this module existed those formats were
7
+ * detected correctly, labelled correctly, and then forwarded verbatim — the
8
+ * request reached the provider and came back as an opaque HTTP 400, which is
9
+ * the least useful outcome available: the file was clearly an image, NeuroLink
10
+ * knew exactly which kind, and still nothing worked.
11
+ *
12
+ * A phone photo is the common case. iOS writes HEIC by default, so "attach a
13
+ * photo and ask what is in it" failed for every provider except Google.
14
+ *
15
+ * Anything outside the universal set is transcoded to PNG. PNG rather than JPEG
16
+ * because the sources are frequently lossless (TIFF, BMP, ICO) or already
17
+ * carry alpha, and a lossy re-encode of an image the model is about to read
18
+ * closely is the wrong default.
19
+ *
20
+ * @module adapters/imageFormatSupport
21
+ */
22
+ import type { ImageWithAltText, VisionImageConversion } from "../types/index.js";
23
+ /**
24
+ * MIME types every vision-capable provider accepts as-is.
25
+ *
26
+ * This is the intersection across OpenAI, Anthropic, Google (AI Studio and
27
+ * Vertex), Bedrock, Azure and Mistral — deliberately the intersection and not
28
+ * a per-provider matrix. Google additionally accepts HEIC/HEIF natively, but
29
+ * converting those for Google as well costs one transcode and removes an
30
+ * entire axis of provider-specific branching from the dispatch path.
31
+ */
32
+ export declare const UNIVERSAL_VISION_IMAGE_MIME_TYPES: ReadonlySet<string>;
33
+ /**
34
+ * Every image MIME type NeuroLink accepts as *input*.
35
+ *
36
+ * The union of what providers take as-is and what this module can convert for
37
+ * them. Intake validation must use this rather than the universal set alone:
38
+ * a format we can transcode is a format we accept, and gating intake on the
39
+ * provider-acceptable list rejects the file before conversion ever runs.
40
+ */
41
+ export declare const SUPPORTED_INPUT_IMAGE_MIME_TYPES: ReadonlySet<string>;
42
+ /**
43
+ * True when a MIME type needs transcoding before it can be sent to a vision
44
+ * provider. Cheap enough to call on every image; callers use it to avoid
45
+ * reading a file off disk that would not have been converted anyway.
46
+ */
47
+ export declare function needsVisionTranscode(mimeType: string): boolean;
48
+ /**
49
+ * Return image bytes every vision provider can read, transcoding to PNG when
50
+ * the source format is one no provider accepts.
51
+ *
52
+ * Never throws for image reasons. When neither backend can decode the input,
53
+ * the original bytes are returned with a warning naming the format — the
54
+ * request then fails at the provider exactly as it did before, rather than this
55
+ * compatibility step becoming a new way for a previously working request to
56
+ * break.
57
+ *
58
+ * @param buffer - Raw image bytes.
59
+ * @param mimeType - Detected MIME type of `buffer`.
60
+ */
61
+ export declare function toVisionCompatibleImage(buffer: Buffer, mimeType: string): Promise<VisionImageConversion>;
62
+ /**
63
+ * Unwrap an `input.images` entry to its payload.
64
+ *
65
+ * `ImageWithAltText` (`{ data, altText }`) is a documented public input shape,
66
+ * but the provider image loops typed the array as `Buffer | string` and so
67
+ * treated a wrapper as raw bytes — `toString("base64")` on the object yields
68
+ * the literal "[object Object]", which is valid base64 that decodes to seven
69
+ * bytes of garbage. The request therefore reached the API and failed as
70
+ * "invalid image data", with nothing pointing at the stringification.
71
+ *
72
+ * Alt text has no representation in Vertex's `inlineData` part, so it is
73
+ * dropped here deliberately rather than corrupting the payload to carry it.
74
+ */
75
+ export declare function unwrapImagePayload(entry: Buffer | string | ImageWithAltText): Buffer | string;
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Vision-provider image format compatibility.
3
+ *
4
+ * NeuroLink identifies far more image formats than any vision API accepts. BMP,
5
+ * TIFF, AVIF, ICO and JPEG 2000 are accepted by none of them; HEIC and HEIF are
6
+ * accepted only by Google. Until this module existed those formats were
7
+ * detected correctly, labelled correctly, and then forwarded verbatim — the
8
+ * request reached the provider and came back as an opaque HTTP 400, which is
9
+ * the least useful outcome available: the file was clearly an image, NeuroLink
10
+ * knew exactly which kind, and still nothing worked.
11
+ *
12
+ * A phone photo is the common case. iOS writes HEIC by default, so "attach a
13
+ * photo and ask what is in it" failed for every provider except Google.
14
+ *
15
+ * Anything outside the universal set is transcoded to PNG. PNG rather than JPEG
16
+ * because the sources are frequently lossless (TIFF, BMP, ICO) or already
17
+ * carry alpha, and a lossy re-encode of an image the model is about to read
18
+ * closely is the wrong default.
19
+ *
20
+ * @module adapters/imageFormatSupport
21
+ */
22
+ import { extensionForMimeType } from "../processors/config/fileTypeRegistry.js";
23
+ import { withTimeout } from "../utils/errorHandling.js";
24
+ import { logger } from "../utils/logger.js";
25
+ import { tryImport } from "../utils/tryImport.js";
26
+ import { getFfmpegPath, runFfmpeg } from "./video/ffmpegAdapter.js";
27
+ /**
28
+ * Per-backend ceiling for one image conversion.
29
+ *
30
+ * Generous enough for a large TIFF or a HEIC burst frame on a loaded machine,
31
+ * short enough that a hung decoder cannot hold a generation request open.
32
+ */
33
+ const IMAGE_TRANSCODE_TIMEOUT_MS = 30_000;
34
+ /**
35
+ * MIME types every vision-capable provider accepts as-is.
36
+ *
37
+ * This is the intersection across OpenAI, Anthropic, Google (AI Studio and
38
+ * Vertex), Bedrock, Azure and Mistral — deliberately the intersection and not
39
+ * a per-provider matrix. Google additionally accepts HEIC/HEIF natively, but
40
+ * converting those for Google as well costs one transcode and removes an
41
+ * entire axis of provider-specific branching from the dispatch path.
42
+ */
43
+ export const UNIVERSAL_VISION_IMAGE_MIME_TYPES = new Set([
44
+ "image/png",
45
+ "image/jpeg",
46
+ "image/gif",
47
+ "image/webp",
48
+ ]);
49
+ /**
50
+ * Formats that are transcoded to PNG before dispatch.
51
+ *
52
+ * An explicit allowlist rather than "anything not universal": a MIME type this
53
+ * module does not recognise is more likely a mislabelled file than a format
54
+ * sharp can decode, and passing it through unchanged preserves the provider's
55
+ * own error message instead of replacing it with a decode failure here.
56
+ */
57
+ const TRANSCODABLE_IMAGE_MIME_TYPES = new Set([
58
+ "image/bmp",
59
+ "image/x-ms-bmp",
60
+ "image/tiff",
61
+ "image/x-tiff",
62
+ "image/avif",
63
+ "image/heic",
64
+ "image/heic-sequence",
65
+ "image/heif",
66
+ "image/heif-sequence",
67
+ "image/x-icon",
68
+ "image/vnd.microsoft.icon",
69
+ "image/jp2",
70
+ "image/jpx",
71
+ "image/apng",
72
+ // SVG reaches this module only when it arrives as raw bytes in `input.images`
73
+ // rather than through detection (which routes .svg to the sanitizer). No
74
+ // vision provider accepts image/svg+xml, and sharp rasterises SVG natively,
75
+ // so converting is strictly better than shipping markup labelled as an image.
76
+ "image/svg+xml",
77
+ ]);
78
+ /**
79
+ * Every image MIME type NeuroLink accepts as *input*.
80
+ *
81
+ * The union of what providers take as-is and what this module can convert for
82
+ * them. Intake validation must use this rather than the universal set alone:
83
+ * a format we can transcode is a format we accept, and gating intake on the
84
+ * provider-acceptable list rejects the file before conversion ever runs.
85
+ */
86
+ export const SUPPORTED_INPUT_IMAGE_MIME_TYPES = new Set([
87
+ ...UNIVERSAL_VISION_IMAGE_MIME_TYPES,
88
+ ...TRANSCODABLE_IMAGE_MIME_TYPES,
89
+ ]);
90
+ /**
91
+ * True when a MIME type needs transcoding before it can be sent to a vision
92
+ * provider. Cheap enough to call on every image; callers use it to avoid
93
+ * reading a file off disk that would not have been converted anyway.
94
+ */
95
+ export function needsVisionTranscode(mimeType) {
96
+ const normalized = mimeType.split(";")[0].trim().toLowerCase();
97
+ return (!UNIVERSAL_VISION_IMAGE_MIME_TYPES.has(normalized) &&
98
+ TRANSCODABLE_IMAGE_MIME_TYPES.has(normalized));
99
+ }
100
+ /**
101
+ * Decode with sharp. Covers TIFF, AVIF, GIF and SVG in-process with no temp
102
+ * files, which is the fast path.
103
+ *
104
+ * Deliberately does NOT cover every transcodable format: sharp's prebuilt
105
+ * binaries report `heif` as an input format but that is AV1-in-HEIF (AVIF)
106
+ * only — actual HEVC-coded HEIC fails inside libheif, and BMP, ICO and
107
+ * JPEG 2000 are not compiled in at all. Those fall through to ffmpeg below.
108
+ */
109
+ async function transcodeWithSharp(buffer) {
110
+ const sharpModule = await tryImport("sharp", "Image format conversion for vision providers");
111
+ // A shape guard, not a crash guard: an absent or malformed sharp already
112
+ // fails safely, because `tryImport` throws a named install error and any
113
+ // TypeError from calling a non-function is caught by the backend loop, which
114
+ // then tries ffmpeg. What this adds is a legible reason in that loop's
115
+ // failure list instead of "sharpModule.default is not a function".
116
+ //
117
+ // It deliberately throws rather than returning `buffer`: returning the input
118
+ // would report a successful conversion and relabel the original bytes as PNG,
119
+ // and would also skip the ffmpeg backend — the one that actually handles
120
+ // HEIC, BMP, ICO and JPEG 2000.
121
+ if (typeof sharpModule?.default !== "function") {
122
+ throw new Error("the installed sharp package does not expose a callable default export");
123
+ }
124
+ // `pages: 1` keeps a multi-frame source (animated AVIF, a .heics sequence,
125
+ // a multi-page TIFF) from being flattened into one tall strip — the first
126
+ // frame is what a vision model should receive.
127
+ const pipeline = sharpModule.default(buffer, { pages: 1 });
128
+ // The instance shape is checked as well as the factory: a build that exports
129
+ // a callable but returns something without `.png()` would otherwise fail as
130
+ // an opaque TypeError inside the backend loop.
131
+ if (typeof pipeline?.png !== "function") {
132
+ throw new Error("the installed sharp package returned a pipeline without a png() encoder");
133
+ }
134
+ return pipeline.png().toBuffer();
135
+ }
136
+ /**
137
+ * Decode with ffmpeg, which handles what sharp cannot — most importantly HEIC,
138
+ * the format iPhones write by default and therefore the single most common
139
+ * "why can't the model see my photo" case.
140
+ *
141
+ * ffmpeg is already a soft dependency for video keyframe extraction and is
142
+ * resolved through the same `FFMPEG_PATH` → `ffmpeg-static` → system-PATH
143
+ * chain, so this adds a code path rather than a new requirement.
144
+ *
145
+ * Requires temp files: ffmpeg's image demuxers seek, so piping through stdin is
146
+ * not reliable for these formats. The temp directory is removed in `finally`
147
+ * whether or not the conversion succeeded.
148
+ *
149
+ * The Node builtins are imported dynamically rather than at module scope
150
+ * because the browser bundle stubs `node:fs/promises` and its stub has no
151
+ * `mkdtemp`. This whole path is server-only — nothing in a browser is going to
152
+ * spawn ffmpeg — so the import belongs where it is used.
153
+ */
154
+ async function transcodeWithFfmpeg(buffer, extension, binaryPath) {
155
+ const [{ randomUUID }, { mkdtemp, readFile, rm, writeFile }, { tmpdir }, { join },] = await Promise.all([
156
+ import("node:crypto"),
157
+ import("node:fs/promises"),
158
+ import("node:os"),
159
+ import("node:path"),
160
+ ]);
161
+ const workDir = await mkdtemp(join(tmpdir(), "neurolink-img-"));
162
+ const inputPath = join(workDir, `${randomUUID()}${extension}`);
163
+ const outputPath = join(workDir, `${randomUUID()}.png`);
164
+ try {
165
+ await writeFile(inputPath, buffer);
166
+ await runFfmpeg([
167
+ "-y",
168
+ "-v",
169
+ "error",
170
+ "-i",
171
+ inputPath,
172
+ // Take a single frame so a multi-image container yields one PNG rather
173
+ // than ffmpeg erroring on a missing output-sequence pattern.
174
+ "-frames:v",
175
+ "1",
176
+ "-f",
177
+ "image2",
178
+ "-c:v",
179
+ "png",
180
+ outputPath,
181
+ ], binaryPath ? { binaryPath } : {});
182
+ return await readFile(outputPath);
183
+ }
184
+ finally {
185
+ await rm(workDir, { recursive: true, force: true }).catch(() => undefined);
186
+ }
187
+ }
188
+ /**
189
+ * The decode backends to try, in order, cheapest first.
190
+ *
191
+ * The two ffmpeg entries are not redundant. `getFfmpegPath()` prefers the
192
+ * `ffmpeg-static` package, whose LGPL build omits HEVC and therefore cannot
193
+ * read HEIC — the format iPhones write by default. A system ffmpeg usually can,
194
+ * so when the resolved binary is not already the system one it is retried
195
+ * explicitly rather than reporting a photo as unsupported.
196
+ */
197
+ async function* transcodeBackends(buffer, extension) {
198
+ yield { name: "sharp", run: () => transcodeWithSharp(buffer) };
199
+ const resolved = await getFfmpegPath().catch(() => "ffmpeg");
200
+ yield {
201
+ name: "ffmpeg",
202
+ run: () => transcodeWithFfmpeg(buffer, extension),
203
+ };
204
+ if (resolved !== "ffmpeg") {
205
+ yield {
206
+ name: "system ffmpeg",
207
+ run: () => transcodeWithFfmpeg(buffer, extension, "ffmpeg"),
208
+ };
209
+ }
210
+ }
211
+ /**
212
+ * Return image bytes every vision provider can read, transcoding to PNG when
213
+ * the source format is one no provider accepts.
214
+ *
215
+ * Never throws for image reasons. When neither backend can decode the input,
216
+ * the original bytes are returned with a warning naming the format — the
217
+ * request then fails at the provider exactly as it did before, rather than this
218
+ * compatibility step becoming a new way for a previously working request to
219
+ * break.
220
+ *
221
+ * @param buffer - Raw image bytes.
222
+ * @param mimeType - Detected MIME type of `buffer`.
223
+ */
224
+ export async function toVisionCompatibleImage(buffer, mimeType) {
225
+ if (!needsVisionTranscode(mimeType)) {
226
+ return { buffer, mimeType, converted: false };
227
+ }
228
+ const normalized = mimeType.split(";")[0].trim().toLowerCase();
229
+ const extension = extensionForMimeType(normalized) ?? ".bin";
230
+ const failures = [];
231
+ for await (const backend of transcodeBackends(buffer, extension)) {
232
+ try {
233
+ // Both backends can stall — sharp on a malformed stream, ffmpeg on a
234
+ // container it half-understands — and this runs inline on the request
235
+ // path. A bounded failure falls through to the next backend and finally
236
+ // to pass-through, which is the same degradation as a decode error.
237
+ const converted = await withTimeout(backend.run(), IMAGE_TRANSCODE_TIMEOUT_MS, new Error(`${backend.name} image transcode exceeded ${IMAGE_TRANSCODE_TIMEOUT_MS}ms`));
238
+ if (converted.length === 0) {
239
+ throw new Error("produced an empty image");
240
+ }
241
+ logger.debug(`[imageFormatSupport] Transcoded ${normalized} → image/png via ${backend.name} ` +
242
+ `(${buffer.length} → ${converted.length} bytes) for vision compatibility`);
243
+ return { buffer: converted, mimeType: "image/png", converted: true };
244
+ }
245
+ catch (error) {
246
+ failures.push(`${backend.name}: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`);
247
+ }
248
+ }
249
+ logger.warn(`[imageFormatSupport] Could not transcode ${normalized} to PNG — sending the ` +
250
+ `original bytes, which most vision providers will reject. Install a full ` +
251
+ `ffmpeg build (or set FFMPEG_PATH to one) to enable this format. ` +
252
+ `Tried ${failures.join("; ")}`);
253
+ return { buffer, mimeType, converted: false };
254
+ }
255
+ /**
256
+ * Whether an `input.images` entry is the `{ data, altText }` wrapper.
257
+ *
258
+ * Narrowing on the `data` property rather than on `typeof entry === "object"`:
259
+ * a Buffer is also an object, so the looser test leaves `ImageWithAltText` in
260
+ * the union on the false branch and only compiles behind a cast.
261
+ */
262
+ function isImageWithAltTextEntry(entry) {
263
+ return (typeof entry === "object" &&
264
+ entry !== null &&
265
+ !Buffer.isBuffer(entry) &&
266
+ "data" in entry);
267
+ }
268
+ /**
269
+ * Unwrap an `input.images` entry to its payload.
270
+ *
271
+ * `ImageWithAltText` (`{ data, altText }`) is a documented public input shape,
272
+ * but the provider image loops typed the array as `Buffer | string` and so
273
+ * treated a wrapper as raw bytes — `toString("base64")` on the object yields
274
+ * the literal "[object Object]", which is valid base64 that decodes to seven
275
+ * bytes of garbage. The request therefore reached the API and failed as
276
+ * "invalid image data", with nothing pointing at the stringification.
277
+ *
278
+ * Alt text has no representation in Vertex's `inlineData` part, so it is
279
+ * dropped here deliberately rather than corrupting the payload to carry it.
280
+ */
281
+ export function unwrapImagePayload(entry) {
282
+ return isImageWithAltTextEntry(entry) ? entry.data : entry;
283
+ }
284
+ //# sourceMappingURL=imageFormatSupport.js.map
@@ -62,6 +62,12 @@ export declare function getFfmpegPath(): Promise<string>;
62
62
  export declare function runFfmpeg(args: string[], options?: {
63
63
  timeoutMs?: number;
64
64
  maxBuffer?: number;
65
+ /**
66
+ * Override the resolved binary. Used to retry against the system `ffmpeg`
67
+ * when the `ffmpeg-static` build lacks a codec — its LGPL build ships
68
+ * without HEVC, so it cannot decode HEIC even though a system build can.
69
+ */
70
+ binaryPath?: string;
65
71
  }): Promise<{
66
72
  stdout: string;
67
73
  stderr: string;
@@ -167,7 +167,7 @@ export async function getFfmpegPath() {
167
167
  */
168
168
  export async function runFfmpeg(args, options = {}) {
169
169
  const { execFile } = await import("node:child_process");
170
- const ffmpegPath = await getFfmpegPath();
170
+ const ffmpegPath = options.binaryPath ?? (await getFfmpegPath());
171
171
  const timeoutMs = options.timeoutMs ?? FFMPEG_FRAME_TIMEOUT_MS;
172
172
  const maxBuffer = options.maxBuffer ?? FFMPEG_FRAME_MAX_BUFFER;
173
173
  return new Promise((resolve, reject) => {
@@ -2,20 +2,34 @@
2
2
  * File Extension Constants
3
3
  * Centralized file extension definitions organized by category for file processing
4
4
  *
5
+ * The media/document/data/archive categories are derived from
6
+ * {@link FILE_TYPE_REGISTRY} rather than typed out here — they used to be a
7
+ * second hand-maintained list that drifted from both the registry-equivalent
8
+ * MIME map and the detector, so formats appeared "supported" in one place and
9
+ * were invisible in another. Source-code and text categories, which carry no
10
+ * modality, are still enumerated below.
11
+ *
5
12
  * @module processors/config/fileTypes
6
13
  */
7
14
  /**
8
- * Image file extensions supported by the platform
15
+ * Image file extensions supported by the platform.
16
+ * Includes `.svg`, which is processed as sanitized markup rather than raster.
9
17
  */
10
- export declare const IMAGE_EXTENSIONS: readonly [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp", ".tiff", ".tif", ".avif", ".heic", ".heif", ".ico"];
18
+ export declare const IMAGE_EXTENSIONS: readonly string[];
11
19
  /**
12
- * AI vision-supported image extensions (subset that works with AI models)
20
+ * Image extensions every vision-capable provider accepts as-is.
21
+ *
22
+ * Deliberately NOT derived from the registry: this is the intersection of what
23
+ * OpenAI, Anthropic, Google and Bedrock all accept, not the set NeuroLink can
24
+ * identify. Anything else the registry knows about (BMP, TIFF, AVIF, ICO,
25
+ * HEIC/HEIF) is transcoded to PNG before dispatch — see
26
+ * `adapters/imageFormatSupport.ts`.
13
27
  */
14
28
  export declare const AI_VISION_EXTENSIONS: readonly [".jpg", ".jpeg", ".png", ".gif", ".webp"];
15
29
  /**
16
30
  * Document file extensions for office documents and PDFs
17
31
  */
18
- export declare const DOCUMENT_EXTENSIONS: readonly [".pdf", ".docx", ".doc", ".xlsx", ".xls", ".pptx", ".ppt", ".odt", ".ods", ".odp", ".rtf"];
32
+ export declare const DOCUMENT_EXTENSIONS: readonly string[];
19
33
  /**
20
34
  * PDF document extensions
21
35
  */
@@ -57,9 +71,12 @@ export declare const CSV_EXTENSIONS: readonly [".csv"];
57
71
  */
58
72
  export declare const YAML_EXTENSIONS: readonly [".yaml", ".yml"];
59
73
  /**
60
- * All data format extensions combined
74
+ * All data format extensions combined.
75
+ *
76
+ * The tabular formats (.csv/.tsv) come from the registry; .json/.xml/.yaml are
77
+ * text formats with no modality of their own and stay listed here.
61
78
  */
62
- export declare const DATA_EXTENSIONS: readonly [".json", ".xml", ".csv", ".yaml", ".yml"];
79
+ export declare const DATA_EXTENSIONS: readonly string[];
63
80
  /**
64
81
  * Plain text file extensions
65
82
  */
@@ -173,15 +190,15 @@ export declare const CONFIG_EXTENSIONS: readonly [".env", ".ini", ".toml", ".cfg
173
190
  /**
174
191
  * Archive and compressed file extensions
175
192
  */
176
- export declare const ARCHIVE_EXTENSIONS: readonly [".zip", ".tar", ".gz", ".tgz", ".tar.gz", ".tar.bz2", ".bz2", ".rar", ".7z", ".xz"];
193
+ export declare const ARCHIVE_EXTENSIONS: readonly string[];
177
194
  /**
178
195
  * Video file extensions
179
196
  */
180
- export declare const VIDEO_EXTENSIONS: readonly [".mp4", ".avi", ".mov", ".mkv", ".webm", ".flv", ".wmv", ".m4v", ".mpg", ".mpeg"];
197
+ export declare const VIDEO_EXTENSIONS: readonly string[];
181
198
  /**
182
199
  * Audio file extensions
183
200
  */
184
- export declare const AUDIO_EXTENSIONS: readonly [".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a", ".wma", ".opus"];
201
+ export declare const AUDIO_EXTENSIONS: readonly string[];
185
202
  /**
186
203
  * Design and graphics file extensions
187
204
  */
@@ -198,16 +215,16 @@ export declare const EXECUTABLE_EXTENSIONS: readonly [".exe", ".dll", ".so", ".d
198
215
  * File extensions grouped by category for easy access
199
216
  */
200
217
  export declare const FILE_EXTENSIONS: {
201
- readonly IMAGES: readonly [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp", ".tiff", ".tif", ".avif", ".heic", ".heif", ".ico"];
218
+ readonly IMAGES: readonly string[];
202
219
  readonly AI_VISION: readonly [".jpg", ".jpeg", ".png", ".gif", ".webp"];
203
- readonly DOCUMENTS: readonly [".pdf", ".docx", ".doc", ".xlsx", ".xls", ".pptx", ".ppt", ".odt", ".ods", ".odp", ".rtf"];
220
+ readonly DOCUMENTS: readonly string[];
204
221
  readonly PDF: readonly [".pdf"];
205
222
  readonly WORD: readonly [".docx", ".doc"];
206
223
  readonly EXCEL: readonly [".xlsx", ".xls"];
207
224
  readonly POWERPOINT: readonly [".pptx", ".ppt"];
208
225
  readonly OPENDOCUMENT: readonly [".odt", ".ods", ".odp"];
209
226
  readonly RTF: readonly [".rtf"];
210
- readonly DATA: readonly [".json", ".xml", ".csv", ".yaml", ".yml"];
227
+ readonly DATA: readonly string[];
211
228
  readonly JSON: readonly [".json"];
212
229
  readonly XML: readonly [".xml"];
213
230
  readonly CSV: readonly [".csv"];
@@ -254,9 +271,9 @@ export declare const FILE_EXTENSIONS: {
254
271
  };
255
272
  readonly ALL_CODE: readonly [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".pyw", ".pyi", ".java", ".kt", ".kts", ".go", ".rs", ".c", ".h", ".cpp", ".hpp", ".cc", ".cxx", ".hxx", ".cs", ".rb", ".rake", ".php", ".phtml", ".php3", ".php4", ".php5", ".phps", ".sh", ".bash", ".zsh", ".fish", ".ksh", ".pl", ".pm", ".pod", ".t", ".lua", ".sql", ".swift", ".dart", ".m", ".mm", ".scala", ".sc", ".hs", ".lhs", ".ex", ".exs", ".erl", ".hrl", ".clj", ".cljs", ".cljc", ".edn", ".fs", ".fsx", ".fsi", ".ml", ".mli", ".lisp", ".lsp", ".cl", ".scm", ".ss", ".groovy", ".gvy", ".gy", ".gsh", ".ps1", ".psm1", ".psd1", ".r", ".rmd", ".jl", ".nim", ".nims", ".zig", ".v", ".cr", ".d", ".asm", ".s", ".f", ".f90", ".f95", ".f03", ".for", ".cob", ".cbl", ".cobol", ".pas", ".pp", ".p", ".ada", ".adb", ".ads", ".vue", ".svelte", ".hbs", ".handlebars", ".ejs", ".pug", ".jade", ".css", ".scss", ".sass", ".less", ".styl", ".stylus", ".dockerfile", ".mk"];
256
273
  readonly CONFIG: readonly [".env", ".ini", ".toml", ".cfg", ".conf", ".config", ".properties", ".editorconfig", ".gitignore", ".gitattributes", ".npmrc", ".yarnrc", ".prettierrc", ".eslintrc", ".babelrc"];
257
- readonly ARCHIVES: readonly [".zip", ".tar", ".gz", ".tgz", ".tar.gz", ".tar.bz2", ".bz2", ".rar", ".7z", ".xz"];
258
- readonly VIDEO: readonly [".mp4", ".avi", ".mov", ".mkv", ".webm", ".flv", ".wmv", ".m4v", ".mpg", ".mpeg"];
259
- readonly AUDIO: readonly [".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a", ".wma", ".opus"];
274
+ readonly ARCHIVES: readonly string[];
275
+ readonly VIDEO: readonly string[];
276
+ readonly AUDIO: readonly string[];
260
277
  readonly DESIGN: readonly [".psd", ".psb", ".ai", ".sketch", ".fig", ".xd"];
261
278
  readonly DATABASE: readonly [".db", ".sqlite", ".sqlite3", ".mdb", ".accdb"];
262
279
  readonly EXECUTABLE: readonly [".exe", ".dll", ".so", ".dylib", ".app", ".bat", ".cmd", ".vbs", ".ps1", ".msi", ".dmg", ".bin"];
@@ -2,31 +2,32 @@
2
2
  * File Extension Constants
3
3
  * Centralized file extension definitions organized by category for file processing
4
4
  *
5
+ * The media/document/data/archive categories are derived from
6
+ * {@link FILE_TYPE_REGISTRY} rather than typed out here — they used to be a
7
+ * second hand-maintained list that drifted from both the registry-equivalent
8
+ * MIME map and the detector, so formats appeared "supported" in one place and
9
+ * were invisible in another. Source-code and text categories, which carry no
10
+ * modality, are still enumerated below.
11
+ *
5
12
  * @module processors/config/fileTypes
6
13
  */
14
+ import { extensionsForModality } from "./fileTypeRegistry.js";
7
15
  // =============================================================================
8
16
  // IMAGE FILE EXTENSIONS
9
17
  // =============================================================================
10
18
  /**
11
- * Image file extensions supported by the platform
19
+ * Image file extensions supported by the platform.
20
+ * Includes `.svg`, which is processed as sanitized markup rather than raster.
12
21
  */
13
- export const IMAGE_EXTENSIONS = [
14
- ".jpg",
15
- ".jpeg",
16
- ".png",
17
- ".gif",
18
- ".webp",
19
- ".svg",
20
- ".bmp",
21
- ".tiff",
22
- ".tif",
23
- ".avif",
24
- ".heic",
25
- ".heif",
26
- ".ico",
27
- ];
22
+ export const IMAGE_EXTENSIONS = extensionsForModality("image");
28
23
  /**
29
- * AI vision-supported image extensions (subset that works with AI models)
24
+ * Image extensions every vision-capable provider accepts as-is.
25
+ *
26
+ * Deliberately NOT derived from the registry: this is the intersection of what
27
+ * OpenAI, Anthropic, Google and Bedrock all accept, not the set NeuroLink can
28
+ * identify. Anything else the registry knows about (BMP, TIFF, AVIF, ICO,
29
+ * HEIC/HEIF) is transcoded to PNG before dispatch — see
30
+ * `adapters/imageFormatSupport.ts`.
30
31
  */
31
32
  export const AI_VISION_EXTENSIONS = [
32
33
  ".jpg",
@@ -41,19 +42,7 @@ export const AI_VISION_EXTENSIONS = [
41
42
  /**
42
43
  * Document file extensions for office documents and PDFs
43
44
  */
44
- export const DOCUMENT_EXTENSIONS = [
45
- ".pdf",
46
- ".docx",
47
- ".doc",
48
- ".xlsx",
49
- ".xls",
50
- ".pptx",
51
- ".ppt",
52
- ".odt",
53
- ".ods",
54
- ".odp",
55
- ".rtf",
56
- ];
45
+ export const DOCUMENT_EXTENSIONS = extensionsForModality("document");
57
46
  /**
58
47
  * PDF document extensions
59
48
  */
@@ -98,14 +87,17 @@ export const CSV_EXTENSIONS = [".csv"];
98
87
  */
99
88
  export const YAML_EXTENSIONS = [".yaml", ".yml"];
100
89
  /**
101
- * All data format extensions combined
90
+ * All data format extensions combined.
91
+ *
92
+ * The tabular formats (.csv/.tsv) come from the registry; .json/.xml/.yaml are
93
+ * text formats with no modality of their own and stay listed here.
102
94
  */
103
95
  export const DATA_EXTENSIONS = [
104
96
  ".json",
105
97
  ".xml",
106
- ".csv",
107
98
  ".yaml",
108
99
  ".yml",
100
+ ...extensionsForModality("data"),
109
101
  ];
110
102
  // =============================================================================
111
103
  // TEXT FILE EXTENSIONS
@@ -346,49 +338,18 @@ export const CONFIG_EXTENSIONS = [
346
338
  /**
347
339
  * Archive and compressed file extensions
348
340
  */
349
- export const ARCHIVE_EXTENSIONS = [
350
- ".zip",
351
- ".tar",
352
- ".gz",
353
- ".tgz",
354
- ".tar.gz",
355
- ".tar.bz2",
356
- ".bz2",
357
- ".rar",
358
- ".7z",
359
- ".xz",
360
- ];
341
+ export const ARCHIVE_EXTENSIONS = extensionsForModality("archive");
361
342
  // =============================================================================
362
343
  // MULTIMEDIA FILE EXTENSIONS
363
344
  // =============================================================================
364
345
  /**
365
346
  * Video file extensions
366
347
  */
367
- export const VIDEO_EXTENSIONS = [
368
- ".mp4",
369
- ".avi",
370
- ".mov",
371
- ".mkv",
372
- ".webm",
373
- ".flv",
374
- ".wmv",
375
- ".m4v",
376
- ".mpg",
377
- ".mpeg",
378
- ];
348
+ export const VIDEO_EXTENSIONS = extensionsForModality("video");
379
349
  /**
380
350
  * Audio file extensions
381
351
  */
382
- export const AUDIO_EXTENSIONS = [
383
- ".mp3",
384
- ".wav",
385
- ".aac",
386
- ".flac",
387
- ".ogg",
388
- ".m4a",
389
- ".wma",
390
- ".opus",
391
- ];
352
+ export const AUDIO_EXTENSIONS = extensionsForModality("audio");
392
353
  // =============================================================================
393
354
  // DESIGN FILE EXTENSIONS
394
355
  // =============================================================================