@juspay/neurolink 12.9.6 → 12.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 (39) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/artifacts/artifactBanking.d.ts +6 -2
  3. package/dist/artifacts/artifactBanking.js +9 -14
  4. package/dist/artifacts/artifactReader.d.ts +69 -0
  5. package/dist/artifacts/artifactReader.js +157 -0
  6. package/dist/artifacts/artifactStore.d.ts +7 -3
  7. package/dist/artifacts/artifactStore.js +10 -21
  8. package/dist/artifacts/artifactStoreFactory.d.ts +39 -0
  9. package/dist/artifacts/artifactStoreFactory.js +101 -0
  10. package/dist/artifacts/redisArtifactStore.d.ts +84 -0
  11. package/dist/artifacts/redisArtifactStore.js +270 -0
  12. package/dist/browser/neurolink.min.js +381 -383
  13. package/dist/constants/enums.d.ts +77 -0
  14. package/dist/constants/enums.js +82 -0
  15. package/dist/core/modules/GenerationHandler.d.ts +15 -3
  16. package/dist/core/modules/GenerationHandler.js +172 -14
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.js +7 -0
  19. package/dist/memory/memoryRetrievalTools.js +104 -35
  20. package/dist/neurolink.d.ts +45 -1
  21. package/dist/neurolink.js +128 -18
  22. package/dist/providers/catalog/baseten.json +263 -0
  23. package/dist/providers/catalog/gmicloud.json +64 -0
  24. package/dist/providers/catalog/inception-labs.json +75 -0
  25. package/dist/providers/catalog/index.generated.d.ts +1 -1
  26. package/dist/providers/catalog/index.generated.js +15 -0
  27. package/dist/providers/catalog/io-intelligence.json +463 -0
  28. package/dist/providers/catalog/schema.d.ts +1 -1
  29. package/dist/providers/catalog/upstage.json +165 -0
  30. package/dist/providers/openaiChatCompletionsClient.js +18 -1
  31. package/dist/types/artifact.d.ts +124 -3
  32. package/dist/types/config.d.ts +7 -0
  33. package/dist/types/generate.d.ts +17 -0
  34. package/dist/types/openaiCompatible.d.ts +5 -1
  35. package/dist/types/providerCatalog.generated.d.ts +2 -2
  36. package/dist/types/providers.d.ts +20 -0
  37. package/dist/utils/redis.d.ts +15 -0
  38. package/dist/utils/redis.js +64 -6
  39. package/package.json +10 -6
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * @module types/artifactTypes
9
9
  */
10
+ import type { RedisStorageConfig } from "./conversation.js";
10
11
  /** Metadata recorded alongside a stored artifact. */
11
12
  export type ArtifactMeta = {
12
13
  /** Tool name that produced the output. */
@@ -83,10 +84,63 @@ export type ArtifactPageRequest = {
83
84
  limit?: number;
84
85
  };
85
86
  /**
86
- * Pluggable storage contract for externalized MCP tool outputs.
87
+ * One window of an artifact, as returned by `ArtifactStore.retrieveRange` or
88
+ * by the shared reader when the store only supports whole-payload reads.
87
89
  *
88
- * Default backend: LocalTempArtifactStore (filesystem, single-process).
89
- * Future backends can implement this interface for S3, Redis blobs, etc.
90
+ * Offsets and lengths are CHARACTERS (UTF-16 code units, the unit
91
+ * `String.prototype.slice` and `retrieve_context`'s `offset` / `limit` use),
92
+ * never bytes — so a model advancing `offset` by the characters it received
93
+ * lands exactly where the previous window ended.
94
+ */
95
+ export type ArtifactWindow = {
96
+ /** The characters in `[offset, offset + content.length)`. */
97
+ content: string;
98
+ /** Character offset this window starts at. */
99
+ offset: number;
100
+ /** Total character length of the whole payload. */
101
+ totalLength: number;
102
+ };
103
+ /** One hit from a literal search over an artifact. */
104
+ export type ArtifactSearchMatch = {
105
+ /** Character offset of the match — pass it back as `offset` to read there. */
106
+ offset: number;
107
+ /** 1-based line number the match sits on. */
108
+ line: number;
109
+ /** Character offset the snippet starts at (≤ `offset`). */
110
+ snippetOffset: number;
111
+ /**
112
+ * Bounded context around the match. Bounded on purpose: an MCP artifact is
113
+ * usually one compact JSON line, so "the matching line" would be the whole
114
+ * payload.
115
+ */
116
+ snippet: string;
117
+ };
118
+ /** Result of a literal search over an artifact. */
119
+ export type ArtifactSearchResult = {
120
+ /** Matches returned, in payload order. */
121
+ matches: ArtifactSearchMatch[];
122
+ /** `matches.length`. */
123
+ matchCount: number;
124
+ /** Every match in the payload, including the ones not returned. */
125
+ totalMatches: number;
126
+ /** True when `totalMatches > matchCount`. */
127
+ truncated: boolean;
128
+ /** Character offset to pass as `offset` to search for the next matches. */
129
+ nextSearchOffset?: number;
130
+ };
131
+ /**
132
+ * Pluggable storage contract for externalized MCP tool outputs and banked
133
+ * payloads.
134
+ *
135
+ * Shipped backends: `LocalTempArtifactStore` (filesystem, per-process index
136
+ * with a cross-process sidecar) and `RedisArtifactStore` (TTL-expired, shared
137
+ * across replicas, range reads). Pick one with `artifacts.storage` or the
138
+ * `STORAGE_TYPE` environment variable, or inject any implementation via
139
+ * `artifacts.store` / `setArtifactStore()`.
140
+ *
141
+ * Only `store`, `retrieve`, `delete`, `cleanup` and `generatePreview` are
142
+ * required. `retrieveRange` and `close` are optional capabilities: NeuroLink
143
+ * uses them when present and falls back cleanly when absent.
90
144
  */
91
145
  export type ArtifactStore = {
92
146
  /**
@@ -100,6 +154,22 @@ export type ArtifactStore = {
100
154
  * Returns `null` if the artifact is not found or has been cleaned up.
101
155
  */
102
156
  retrieve(id: string): Promise<string | null>;
157
+ /**
158
+ * Retrieve one character window without materialising the whole payload.
159
+ *
160
+ * Optional. When present, `retrieve_context` and `readArtifact` call it for
161
+ * every paged read instead of `retrieve()` + slice, so a backend with native
162
+ * range reads (Redis `GETRANGE`, S3 `Range`) moves only the window. The
163
+ * result carries `totalLength` so `hasMore` never needs the payload.
164
+ *
165
+ * `offset` and `limit` are characters. A backend that can only address
166
+ * bytes must either know the payload is single-byte (ASCII) or fall back to
167
+ * a full read and slice — it must never return a window that starts at the
168
+ * wrong character. `limit` omitted means "to the end".
169
+ *
170
+ * Returns `null` if the artifact is not found or has expired.
171
+ */
172
+ retrieveRange?(id: string, range: ArtifactPageRequest): Promise<ArtifactWindow | null>;
103
173
  /** Delete a single artifact. No-op if the ID does not exist. */
104
174
  delete(id: string): Promise<void>;
105
175
  /**
@@ -109,6 +179,13 @@ export type ArtifactStore = {
109
179
  cleanup(olderThanMs: number): Promise<number>;
110
180
  /** Generate a short preview string from a serialized payload. */
111
181
  generatePreview(payload: string): string;
182
+ /**
183
+ * Release whatever the store holds open (a pooled connection, a file
184
+ * handle). Optional. NeuroLink calls it — from `shutdown()`, and when
185
+ * `setArtifactStore()` replaces the store — only for stores it built
186
+ * itself; a store you inject is yours to close.
187
+ */
188
+ close?(): Promise<void>;
112
189
  };
113
190
  /**
114
191
  * In-memory index row tracked by LocalTempArtifactStore.
@@ -122,3 +199,47 @@ export type IndexEntry = ArtifactMeta & {
122
199
  */
123
200
  rehydrated?: boolean;
124
201
  };
202
+ /**
203
+ * Where artifacts live. Mirrors conversation memory's `STORAGE_TYPE`:
204
+ * - "local" OS temp directory, per-process index with a cross-process
205
+ * sidecar. Fine for one machine; artifacts do not survive a pod.
206
+ * - "redis" Shared across replicas, expired by TTL, range reads via
207
+ * `GETRANGE`. Uses the same connection pool as Redis conversation
208
+ * memory.
209
+ */
210
+ export type ArtifactStorageType = "local" | "redis";
211
+ /**
212
+ * Artifact storage configuration (`new NeuroLink({ artifacts })`).
213
+ *
214
+ * Resolution order for the backend: `store` → `storage` → `STORAGE_TYPE`
215
+ * environment variable → `"local"`. Resolution order for the Redis connection:
216
+ * `redisConfig` → `conversationMemory.redisConfig` → `REDIS_URL` / `REDIS_HOST`
217
+ * environment variables — so a deployment already running conversation memory
218
+ * on Redis keeps its artifacts on the same Redis without new settings.
219
+ */
220
+ export type ArtifactStorageConfig = {
221
+ /** Backend to use. Default: `STORAGE_TYPE` env var, else `"local"`. */
222
+ storage?: ArtifactStorageType;
223
+ /**
224
+ * Redis connection for `storage: "redis"`. `keyPrefix` defaults to
225
+ * `neurolink:artifact:` (NOT the conversation prefix). `ttl` is seconds,
226
+ * must be positive, and defaults to 86400; zero or negative is replaced by
227
+ * the default with a warning — artifacts in Redis always expire.
228
+ * `userSessionsKeyPrefix` is ignored.
229
+ */
230
+ redisConfig?: RedisStorageConfig;
231
+ /**
232
+ * A ready-made backend. Wins over `storage`. Use this for S3, a database,
233
+ * or a wrapped store; `setArtifactStore()` does the same after construction.
234
+ */
235
+ store?: ArtifactStore;
236
+ };
237
+ /**
238
+ * What `RedisArtifactStore` keeps beside each payload. `charLength` is what
239
+ * makes range reads honest: when it equals `sizeBytes` the payload is pure
240
+ * ASCII and a byte range IS a character range.
241
+ */
242
+ export type RedisArtifactRecord = ArtifactMeta & {
243
+ /** `payload.length` at store time — characters, not bytes. */
244
+ charLength: number;
245
+ };
@@ -6,6 +6,7 @@ import type { MCPToolRegistry } from "../mcp/toolRegistry.js";
6
6
  import type { TaskManagerConfig } from "./task.js";
7
7
  import type { HITLConfig } from "../types/hitl.js";
8
8
  import type { ConversationMemoryConfig } from "./conversation.js";
9
+ import type { ArtifactStorageConfig } from "./artifact.js";
9
10
  import type { ObservabilityConfig } from "./observability.js";
10
11
  import type { AuthProvider, AuthProviderType, AuthProviderConfig, Auth0Config, ClerkConfig, FirebaseConfig, SupabaseConfig, WorkOSConfig, BetterAuthConfig, JWTConfig, OAuth2Config, CognitoConfig, KeycloakConfig, AuthenticatedContext } from "./auth.js";
11
12
  import type { NeurolinkCredentials } from "./providers.js";
@@ -61,6 +62,12 @@ export type NeurolinkConstructorConfig = {
61
62
  modelAliasConfig?: ModelAliasConfig;
62
63
  /** MCP enhancement modules configuration (cache, router, batcher, annotations, middleware) */
63
64
  mcp?: MCPEnhancementsConfig;
65
+ /**
66
+ * Artifact storage: where externalized MCP tool outputs and banked payloads
67
+ * live. The backend follows `STORAGE_TYPE` exactly like conversation memory
68
+ * unless chosen here. See {@link ArtifactStorageConfig}.
69
+ */
70
+ artifacts?: ArtifactStorageConfig;
64
71
  /** Authentication provider configuration */
65
72
  auth?: NeuroLinkAuthConfig;
66
73
  /** TaskManager configuration (scheduled and self-running tasks) */
@@ -1540,3 +1540,20 @@ export type ModelAliasConfig = {
1540
1540
  export type GenerateOptionsNormalized = GenerateOptions & {
1541
1541
  input: NonNullable<GenerateOptions["input"]>;
1542
1542
  };
1543
+ /**
1544
+ * Per-call configuration for GenerationHandler's AI-SDK loop invocation,
1545
+ * shared by the initial call and every fallback retry so they cannot drift.
1546
+ */
1547
+ export type GenerationCallConfig = {
1548
+ shouldUseTools: boolean;
1549
+ includeStructuredOutput: boolean;
1550
+ /** Anchor for the turn deadline — the ORIGINAL executeGeneration start,
1551
+ * shared across fallback/provider retries so they can't refresh the
1552
+ * wall-clock budget. */
1553
+ turnStartMs: number;
1554
+ /** Structured-output fallback retry: also spell the JSON Schema out in the
1555
+ * system prompt, for vendors that ignore `response_format`. */
1556
+ promptJsonInstruction?: boolean;
1557
+ /** Set on the single toolChoice:"none" re-ask so it can never recurse. */
1558
+ isToolReask?: boolean;
1559
+ };
@@ -150,13 +150,17 @@ export type OpenAICompatChatStreamChunk = {
150
150
  choices: OpenAICompatStreamChunkChoice[];
151
151
  usage?: OpenAICompatUsage;
152
152
  };
153
+ export type OpenAICompatErrorMessage = string | ReadonlyArray<{
154
+ msg?: string;
155
+ }>;
153
156
  export type OpenAICompatErrorBody = {
154
157
  error?: {
155
- message?: string;
158
+ message?: OpenAICompatErrorMessage;
156
159
  type?: string;
157
160
  code?: string | number;
158
161
  param?: string | null;
159
162
  };
163
+ detail?: string;
160
164
  };
161
165
  export type OpenAICompatConfig = {
162
166
  provider: string;
@@ -1,2 +1,2 @@
1
- export type CatalogProviderName = "cerebras" | "cloudflare" | "fireworks" | "groq" | "mistral" | "perplexity" | "sambanova" | "together-ai" | "xai";
2
- export type CatalogCredentialKey = "cerebras" | "cloudflare" | "fireworks" | "groq" | "mistral" | "perplexity" | "sambanova" | "together" | "xai";
1
+ export type CatalogProviderName = "baseten" | "cerebras" | "cloudflare" | "fireworks" | "gmicloud" | "groq" | "inception-labs" | "io-intelligence" | "mistral" | "perplexity" | "sambanova" | "together-ai" | "upstage" | "xai";
2
+ export type CatalogCredentialKey = "baseten" | "cerebras" | "cloudflare" | "fireworks" | "gmicloud" | "groq" | "inceptionLabs" | "ioIntelligence" | "mistral" | "perplexity" | "sambanova" | "together" | "upstage" | "xai";
@@ -166,6 +166,10 @@ export type NeurolinkCredentials = {
166
166
  apiKey?: string;
167
167
  baseURL?: string;
168
168
  };
169
+ baseten?: {
170
+ apiKey?: string;
171
+ baseURL?: string;
172
+ };
169
173
  cerebras?: {
170
174
  apiKey?: string;
171
175
  baseURL?: string;
@@ -179,10 +183,22 @@ export type NeurolinkCredentials = {
179
183
  apiKey?: string;
180
184
  baseURL?: string;
181
185
  };
186
+ gmicloud?: {
187
+ apiKey?: string;
188
+ baseURL?: string;
189
+ };
182
190
  groq?: {
183
191
  apiKey?: string;
184
192
  baseURL?: string;
185
193
  };
194
+ inceptionLabs?: {
195
+ apiKey?: string;
196
+ baseURL?: string;
197
+ };
198
+ ioIntelligence?: {
199
+ apiKey?: string;
200
+ baseURL?: string;
201
+ };
186
202
  mistral?: {
187
203
  apiKey?: string;
188
204
  baseURL?: string;
@@ -199,6 +215,10 @@ export type NeurolinkCredentials = {
199
215
  apiKey?: string;
200
216
  baseURL?: string;
201
217
  };
218
+ upstage?: {
219
+ apiKey?: string;
220
+ baseURL?: string;
221
+ };
202
222
  xai?: {
203
223
  apiKey?: string;
204
224
  baseURL?: string;
@@ -3,6 +3,21 @@
3
3
  * Helper functions for Redis storage operations
4
4
  */
5
5
  import type { ChatMessage, RedisClient, RedisConversationObject, RedisStorageConfig } from "../types/index.js";
6
+ /**
7
+ * Redact the userinfo of every `scheme://user:pass@host` in `text`.
8
+ *
9
+ * Deliberately not a regex. `/:\/\/[^@]+@/` and its relatives backtrack
10
+ * quadratically on input with many `://` and no `@` (CodeQL
11
+ * js/polynomial-redos), and the URL here can come straight from a caller —
12
+ * `RedisStorageConfig.url` reaches this through `RedisArtifactStore` and the
13
+ * conversation memory manager. One forward pass, every character visited a
14
+ * bounded number of times.
15
+ *
16
+ * Redacts everything before the LAST `@` of the authority, so a password
17
+ * containing `@` is still hidden. A URL with only a user name is redacted
18
+ * too; the old regexes let that through.
19
+ */
20
+ export declare function redactUrlCredentials(text: string): string;
6
21
  /**
7
22
  * Get a pooled Redis connection. Multiple callers with the same host:port:db
8
23
  * share a single connection, reducing connection count.
@@ -6,17 +6,75 @@ import { randomUUID } from "crypto";
6
6
  import { createClient } from "redis";
7
7
  import { logger } from "./logger.js";
8
8
  const SESSION_ONLY_PREFIX = "session-only:";
9
+ /** Characters that end the authority (`user:pass@host:port`) of a URL in text. */
10
+ const AUTHORITY_TERMINATORS = new Set(["/", "?", "#", " ", "\t", "\n", "\r"]);
11
+ /**
12
+ * Redact the userinfo of every `scheme://user:pass@host` in `text`.
13
+ *
14
+ * Deliberately not a regex. `/:\/\/[^@]+@/` and its relatives backtrack
15
+ * quadratically on input with many `://` and no `@` (CodeQL
16
+ * js/polynomial-redos), and the URL here can come straight from a caller —
17
+ * `RedisStorageConfig.url` reaches this through `RedisArtifactStore` and the
18
+ * conversation memory manager. One forward pass, every character visited a
19
+ * bounded number of times.
20
+ *
21
+ * Redacts everything before the LAST `@` of the authority, so a password
22
+ * containing `@` is still hidden. A URL with only a user name is redacted
23
+ * too; the old regexes let that through.
24
+ */
25
+ export function redactUrlCredentials(text) {
26
+ let out = "";
27
+ let cursor = 0;
28
+ for (;;) {
29
+ const schemeEnd = text.indexOf("://", cursor);
30
+ if (schemeEnd === -1) {
31
+ return out + text.slice(cursor);
32
+ }
33
+ const authorityStart = schemeEnd + 3;
34
+ let authorityEnd = authorityStart;
35
+ let lastAt = -1;
36
+ while (authorityEnd < text.length) {
37
+ const ch = text[authorityEnd];
38
+ if (AUTHORITY_TERMINATORS.has(ch)) {
39
+ break;
40
+ }
41
+ if (ch === "@") {
42
+ lastAt = authorityEnd;
43
+ }
44
+ authorityEnd += 1;
45
+ }
46
+ if (lastAt === -1) {
47
+ out += text.slice(cursor, authorityEnd);
48
+ cursor = authorityEnd;
49
+ }
50
+ else {
51
+ out += `${text.slice(cursor, authorityStart)}[redacted]@`;
52
+ cursor = lastAt + 1;
53
+ }
54
+ }
55
+ }
9
56
  // Connection pool - keyed by host:port:db
10
57
  const connectionPool = new Map();
11
58
  const pendingConnections = new Map();
59
+ /**
60
+ * One pool key per distinct connection. Shared by acquire AND release: they
61
+ * used to compute it separately, and the release side never knew about the
62
+ * `url:` form, so a URL-configured client was acquired under one key and
63
+ * "released" under another that did not exist — its reference count never
64
+ * dropped and the connection outlived every owner. Credentials never appear
65
+ * in the key; it is logged.
66
+ */
67
+ function poolKeyFor(config) {
68
+ return config.url
69
+ ? `url:${redactUrlCredentials(config.url)}`
70
+ : `${config.host}:${config.port}:${config.db}:${config.password ? "auth" : "noauth"}`;
71
+ }
12
72
  /**
13
73
  * Get a pooled Redis connection. Multiple callers with the same host:port:db
14
74
  * share a single connection, reducing connection count.
15
75
  */
16
76
  export async function getPooledRedisClient(config) {
17
- const key = config.url
18
- ? `url:${config.url.replace(/:\/\/[^:]+:[^@]+@/, "://[redacted]@")}`
19
- : `${config.host}:${config.port}:${config.db}:${config.password ? "auth" : "noauth"}`;
77
+ const key = poolKeyFor(config);
20
78
  const existing = connectionPool.get(key);
21
79
  if (existing && existing.client.isOpen) {
22
80
  existing.refCount++;
@@ -77,7 +135,7 @@ export async function getPooledRedisClient(config) {
77
135
  * Release a pooled Redis connection. Only closes when refCount reaches 0.
78
136
  */
79
137
  export async function releasePooledRedisClient(config) {
80
- const key = `${config.host}:${config.port}:${config.db}:${config.password ? "auth" : "noauth"}`;
138
+ const key = poolKeyFor(config);
81
139
  const entry = connectionPool.get(key);
82
140
  if (!entry) {
83
141
  return;
@@ -140,7 +198,7 @@ export async function createRedisClient(config) {
140
198
  // Create client with secured options
141
199
  const client = createClient(clientOptions);
142
200
  client.on("error", (err) => {
143
- const sanitizedMessage = err.message.replace(/redis:\/\/.*?@/g, "redis://[redacted]@");
201
+ const sanitizedMessage = redactUrlCredentials(err.message);
144
202
  logger.error("Redis client error", { error: sanitizedMessage });
145
203
  });
146
204
  client.on("connect", () => {
@@ -420,7 +478,7 @@ export function getNormalizedConfig(config) {
420
478
  : 0;
421
479
  }
422
480
  catch (e) {
423
- const sanitizedUrl = url.replace(/:\/\/[^@]+@/, "://[redacted]@");
481
+ const sanitizedUrl = redactUrlCredentials(url);
424
482
  logger.warn("[redisUtils] Failed to parse Redis URL, falling back to component-based connection", {
425
483
  url: sanitizedUrl,
426
484
  error: e instanceof Error ? e.message : String(e),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.9.6",
3
+ "version": "12.11.0",
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": {
@@ -456,7 +456,7 @@
456
456
  "exceljs": "^4.4.0",
457
457
  "express": "^5.1.0",
458
458
  "express-rate-limit": "^8.2.1",
459
- "fastify": "^5.8.5",
459
+ "fastify": "^5.12.1",
460
460
  "ffmpeg-static": "^5.3.0",
461
461
  "fluent-ffmpeg": "^2.1.3",
462
462
  "koa": "^3.1.1",
@@ -605,7 +605,7 @@
605
605
  "@opentelemetry/sdk-trace-node": "^2.6.0",
606
606
  "jws@<4.0.1": ">=4.0.1",
607
607
  "tar@<7.5.8": ">=7.5.8",
608
- "qs@<6.15.2": ">=6.15.2",
608
+ "qs@<6.16.0": ">=6.16.0",
609
609
  "minimatch@>=10.0.0 <10.2.3": ">=10.2.3",
610
610
  "minimatch@>=9.0.0 <9.0.7": ">=9.0.7",
611
611
  "typedoc>minimatch": ">=10.2.3",
@@ -622,7 +622,7 @@
622
622
  "ajv@>=8.0.0 <8.18.0": ">=8.18.0",
623
623
  "@grpc/grpc-js@<1.14.4": ">=1.14.4",
624
624
  "@protobufjs/utf8@<1.1.1": ">=1.1.1",
625
- "fast-uri@<3.1.5": ">=3.1.5 <4",
625
+ "fast-uri@<3.1.6": ">=3.1.6 <4",
626
626
  "ip-address@<10.1.1": ">=10.1.1",
627
627
  "basic-ftp@<5.2.2": ">=5.2.2",
628
628
  "fast-xml-builder@<1.1.7": ">=1.1.7",
@@ -632,10 +632,14 @@
632
632
  "undici@>=8.0.0": ">=7.24.0 <8.0.0",
633
633
  "pdfjs-dist": "5.4.624",
634
634
  "markdown-it@<14.2.0": ">=14.2.0",
635
- "@xmldom/xmldom@<0.9.11": ">=0.9.11",
635
+ "@xmldom/xmldom@<0.9.12": ">=0.9.12",
636
636
  "protobufjs@<7.6.5": ">=7.6.5",
637
637
  "brace-expansion@>=5.0.0 <5.0.9": ">=5.0.9",
638
- "@opentelemetry/core@>=2.0.0 <2.8.0": ">=2.8.0"
638
+ "@opentelemetry/core@>=2.0.0 <2.8.0": ">=2.8.0",
639
+ "qs": "6.16.0",
640
+ "fastify": "5.12.1",
641
+ "fast-uri": "3.1.6",
642
+ "@xmldom/xmldom": "0.9.12"
639
643
  },
640
644
  "patchedDependencies": {
641
645
  "mammoth@1.12.0": "patches/mammoth@1.12.0.patch"