@juspay/neurolink 11.18.2 → 11.18.3

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.
@@ -5,7 +5,8 @@
5
5
  */
6
6
  import { open, readFile, realpath } from "fs/promises";
7
7
  import { basename, isAbsolute as isAbsolutePath, relative as relativePath, resolve as resolvePath, sep, } from "path";
8
- import { getGlobalDispatcher, interceptors, request } from "undici";
8
+ import { request } from "undici";
9
+ import { redirectFollowingDispatcher } from "./redirectDispatcher.js";
9
10
  // Lazy-loaded processor singletons — avoids loading heavy media deps
10
11
  // (mediabunny, fluent-ffmpeg, music-metadata, adm-zip) on every generate() call.
11
12
  async function getVideoProcessor() {
@@ -1519,7 +1520,7 @@ export class FileDetector {
1519
1520
  if (getCachedUrlContentType(url, Date.now()) === undefined) {
1520
1521
  try {
1521
1522
  const head = await request(url, {
1522
- dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
1523
+ dispatcher: redirectFollowingDispatcher(5),
1523
1524
  method: "HEAD",
1524
1525
  headersTimeout: FileDetector.DEFAULT_HEAD_TIMEOUT,
1525
1526
  bodyTimeout: FileDetector.DEFAULT_HEAD_TIMEOUT,
@@ -1550,7 +1551,7 @@ export class FileDetector {
1550
1551
  return withRetry(async () => {
1551
1552
  try {
1552
1553
  const response = await request(url, {
1553
- dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
1554
+ dispatcher: redirectFollowingDispatcher(5),
1554
1555
  method: "GET",
1555
1556
  headersTimeout: timeout,
1556
1557
  bodyTimeout: timeout,
@@ -2283,7 +2284,7 @@ class MimeTypeStrategy {
2283
2284
  // dump() can't hang detection, per the project's async-timeout guideline.
2284
2285
  contentType = await withTimeout((async () => {
2285
2286
  const response = await request(input, {
2286
- dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
2287
+ dispatcher: redirectFollowingDispatcher(5),
2287
2288
  method: "HEAD",
2288
2289
  headersTimeout: FileDetector.DEFAULT_HEAD_TIMEOUT,
2289
2290
  bodyTimeout: FileDetector.DEFAULT_HEAD_TIMEOUT,
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, statSync } from "fs";
2
2
  import { readFile as readFileAsync, stat as statAsync } from "fs/promises";
3
- import { getGlobalDispatcher, interceptors, request } from "undici";
3
+ import { request } from "undici";
4
+ import { redirectFollowingDispatcher } from "./redirectDispatcher.js";
4
5
  import { MultimodalLogger, ProviderImageAdapter, } from "../adapters/providerImageAdapter.js";
5
6
  import { CONVERSATION_INSTRUCTIONS, STRUCTURED_OUTPUT_INSTRUCTIONS, } from "../config/conversationMemory.js";
6
7
  import { getAvailableInputTokens } from "../constants/contextWindows.js";
@@ -1660,7 +1661,7 @@ async function downloadImageFromUrl(url) {
1660
1661
  await urlDownloadRateLimiter.acquire();
1661
1662
  try {
1662
1663
  const response = await request(url, {
1663
- dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
1664
+ dispatcher: redirectFollowingDispatcher(5),
1664
1665
  method: "GET",
1665
1666
  headersTimeout: 10000, // 10 second timeout for headers
1666
1667
  bodyTimeout: 30000, // 30 second timeout for body,
@@ -0,0 +1,28 @@
1
+ import type { Dispatcher } from "undici";
2
+ /**
3
+ * A dispatcher that follows redirects, when composing one is safe here.
4
+ *
5
+ * `getGlobalDispatcher()` returns Node's **built-in** undici dispatcher, whose
6
+ * major tracks the runtime rather than this package's dependency. The composed
7
+ * result is then passed to the **npm** undici's `request()`, and the two majors
8
+ * do not share a handler contract:
9
+ *
10
+ * node 24 built-in 7.24.4 + npm 7.28.0 request() succeeds
11
+ * node 22 built-in 6.28.0 + npm 7.28.0 throws "invalid onError method"
12
+ *
13
+ * Node 22 is this package's declared minimum, so the broken combination is not
14
+ * exotic — it is the floor. The throw happens at request time rather than at
15
+ * compose(), which is why it surfaces as an opaque runtime error instead of
16
+ * something recognisably about versions.
17
+ *
18
+ * When the majors disagree, return the global dispatcher uncomposed. That drops
19
+ * redirect-following from the pre-flight HEAD only. Callers already treat any
20
+ * non-2xx HEAD — a redirect included — as untrustworthy and fall through to the
21
+ * streaming size guard on the GET, so the size protection is unchanged and the
22
+ * cost is one extra round trip.
23
+ *
24
+ * Composing onto the global dispatcher rather than a fresh `Agent` is
25
+ * deliberate: it preserves whatever the host application configured globally,
26
+ * such as a corporate ProxyAgent.
27
+ */
28
+ export declare function redirectFollowingDispatcher(maxRedirections: number): Dispatcher;
@@ -0,0 +1,43 @@
1
+ import { getGlobalDispatcher, interceptors } from "undici";
2
+ /**
3
+ * The major of the `undici` this package depends on.
4
+ *
5
+ * `dependencies.undici` is `>=7.24.0 <8.0.0`, and `pnpm.overrides` maps
6
+ * `undici@>=8.0.0` back into that same range, so major 7 is a declared
7
+ * invariant rather than an observation. Update both together if it ever moves.
8
+ */
9
+ const NPM_UNDICI_MAJOR = 7;
10
+ /**
11
+ * A dispatcher that follows redirects, when composing one is safe here.
12
+ *
13
+ * `getGlobalDispatcher()` returns Node's **built-in** undici dispatcher, whose
14
+ * major tracks the runtime rather than this package's dependency. The composed
15
+ * result is then passed to the **npm** undici's `request()`, and the two majors
16
+ * do not share a handler contract:
17
+ *
18
+ * node 24 built-in 7.24.4 + npm 7.28.0 request() succeeds
19
+ * node 22 built-in 6.28.0 + npm 7.28.0 throws "invalid onError method"
20
+ *
21
+ * Node 22 is this package's declared minimum, so the broken combination is not
22
+ * exotic — it is the floor. The throw happens at request time rather than at
23
+ * compose(), which is why it surfaces as an opaque runtime error instead of
24
+ * something recognisably about versions.
25
+ *
26
+ * When the majors disagree, return the global dispatcher uncomposed. That drops
27
+ * redirect-following from the pre-flight HEAD only. Callers already treat any
28
+ * non-2xx HEAD — a redirect included — as untrustworthy and fall through to the
29
+ * streaming size guard on the GET, so the size protection is unchanged and the
30
+ * cost is one extra round trip.
31
+ *
32
+ * Composing onto the global dispatcher rather than a fresh `Agent` is
33
+ * deliberate: it preserves whatever the host application configured globally,
34
+ * such as a corporate ProxyAgent.
35
+ */
36
+ export function redirectFollowingDispatcher(maxRedirections) {
37
+ const globalDispatcher = getGlobalDispatcher();
38
+ const builtinMajor = Number.parseInt(process.versions.undici?.split(".")[0] ?? "", 10);
39
+ if (!Number.isFinite(builtinMajor) || builtinMajor !== NPM_UNDICI_MAJOR) {
40
+ return globalDispatcher;
41
+ }
42
+ return globalDispatcher.compose(interceptors.redirect({ maxRedirections }));
43
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "11.18.2",
3
+ "version": "11.18.3",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {