@juspay/neurolink 10.8.14 → 10.8.16

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 (34) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/browser/neurolink.min.js +484 -664
  3. package/dist/cli/factories/sagemakerCommandFactory.js +2 -11
  4. package/dist/core/baseProvider.js +2 -2
  5. package/dist/core/modules/GenerationHandler.d.ts +34 -2
  6. package/dist/core/modules/GenerationHandler.js +20 -5
  7. package/dist/features/ppt/slideGenerator.js +10 -18
  8. package/dist/lib/core/baseProvider.js +2 -2
  9. package/dist/lib/core/modules/GenerationHandler.d.ts +34 -2
  10. package/dist/lib/core/modules/GenerationHandler.js +20 -5
  11. package/dist/lib/features/ppt/slideGenerator.js +10 -18
  12. package/dist/lib/processors/document/ExcelProcessor.js +11 -19
  13. package/dist/lib/processors/document/WordProcessor.js +3 -11
  14. package/dist/lib/processors/media/AudioProcessor.js +3 -12
  15. package/dist/lib/processors/media/VideoProcessor.d.ts +11 -0
  16. package/dist/lib/processors/media/VideoProcessor.js +33 -24
  17. package/dist/lib/providers/sagemaker/client.js +2 -11
  18. package/dist/lib/server/voice/voiceWebSocketHandler.js +3 -12
  19. package/dist/lib/services/server/ai/observability/instrumentation.js +2 -11
  20. package/dist/lib/tasks/backends/bullmqBackend.js +2 -10
  21. package/dist/lib/utils/tryImport.d.ts +35 -0
  22. package/dist/lib/utils/tryImport.js +95 -0
  23. package/dist/processors/document/ExcelProcessor.js +11 -19
  24. package/dist/processors/document/WordProcessor.js +3 -11
  25. package/dist/processors/media/AudioProcessor.js +3 -12
  26. package/dist/processors/media/VideoProcessor.d.ts +11 -0
  27. package/dist/processors/media/VideoProcessor.js +33 -24
  28. package/dist/providers/sagemaker/client.js +2 -11
  29. package/dist/server/voice/voiceWebSocketHandler.js +3 -12
  30. package/dist/services/server/ai/observability/instrumentation.js +2 -11
  31. package/dist/tasks/backends/bullmqBackend.js +2 -10
  32. package/dist/utils/tryImport.d.ts +35 -0
  33. package/dist/utils/tryImport.js +94 -0
  34. package/package.json +9 -13
@@ -2,19 +2,10 @@ import chalk from "chalk";
2
2
  import ora from "ora";
3
3
  import inquirer from "inquirer";
4
4
  async function loadSageMakerControl() {
5
- try {
6
- return await import(/* @vite-ignore */ "@aws-sdk/client-sagemaker");
7
- }
8
- catch (err) {
9
- const e = err instanceof Error ? err : null;
10
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
11
- e.message.includes("client-sagemaker")) {
12
- throw new Error('SageMaker setup requires "@aws-sdk/client-sagemaker". Install it with:\n pnpm add @aws-sdk/client-sagemaker', { cause: err });
13
- }
14
- throw err;
15
- }
5
+ return tryImport("@aws-sdk/client-sagemaker", "SageMaker setup");
16
6
  }
17
7
  import { logger } from "../../lib/utils/logger.js";
8
+ import { tryImport } from "../../lib/utils/tryImport.js";
18
9
  import { checkSageMakerConfiguration, getSageMakerConfig, getConfigurationSummary, clearConfigurationCache, } from "../../lib/providers/sagemaker/config.js";
19
10
  import { AmazonSageMakerProvider } from "../../lib/providers/sagemaker/index.js";
20
11
  import { runQuickDiagnostics, formatDiagnosticReport, } from "../../lib/providers/sagemaker/diagnostics.js";
@@ -90,7 +90,7 @@ export class BaseProvider {
90
90
  this.messageBuilder = new MessageBuilder(this.providerName, this.modelName);
91
91
  this.streamHandler = new StreamHandler(this.providerName, this.modelName);
92
92
  this.telemetryHandler = new TelemetryHandler(this.providerName, this.modelName, this.neurolink);
93
- this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), () => this.neurolink?.getEventEmitter());
93
+ this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), { getEmitterFn: () => this.neurolink?.getEventEmitter() });
94
94
  this.utilities = new Utilities(this.providerName, this.modelName, this.defaultTimeout, this.middlewareOptions);
95
95
  this.toolsManager = new ToolsManager(this.providerName, this.directTools, this.neurolink, {
96
96
  isZodSchema: (schema) => this.isZodSchema(schema),
@@ -115,7 +115,7 @@ export class BaseProvider {
115
115
  this.messageBuilder = new MessageBuilder(this.providerName, this.modelName);
116
116
  this.streamHandler = new StreamHandler(this.providerName, this.modelName);
117
117
  this.telemetryHandler = new TelemetryHandler(this.providerName, this.modelName, this.neurolink);
118
- this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), () => this.neurolink?.getEventEmitter());
118
+ this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), { getEmitterFn: () => this.neurolink?.getEventEmitter() });
119
119
  this.utilities = new Utilities(this.providerName, this.modelName, this.defaultTimeout, this.middlewareOptions);
120
120
  }
121
121
  /**
@@ -48,12 +48,44 @@ export declare class GenerationHandler {
48
48
  private readonly supportsToolsFn;
49
49
  private readonly getTelemetryConfigFn;
50
50
  private readonly handleToolStorageFn;
51
- private readonly getEmitterFn?;
51
+ /**
52
+ * The remaining, optional dependencies.
53
+ *
54
+ * Grouped rather than added as further positional parameters: the
55
+ * constructor is already at the six-parameter cap, and both of these are
56
+ * optional injection seams rather than required collaborators.
57
+ *
58
+ * `generateTextFn` exists because every other dependency of this class
59
+ * arrives through the constructor while `generateText` was reached by
60
+ * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
61
+ * ESM re-export cannot be substituted from a test, so asserting on the
62
+ * arguments this class builds — the entire contract of the system-message
63
+ * hoisting below — had no seam to work through. Production passes neither.
64
+ */
65
+ private readonly deps;
52
66
  constructor(providerName: AIProviderName, modelName: string, supportsToolsFn: () => boolean, getTelemetryConfigFn: (options: TextGenerationOptions, type: string) => {
53
67
  isEnabled: boolean;
54
68
  functionId?: string;
55
69
  metadata?: Record<string, string | number | boolean>;
56
- } | undefined, handleToolStorageFn: (toolCalls: unknown[], toolResults: unknown[], options: TextGenerationOptions, timestamp: Date) => Promise<void>, getEmitterFn?: (() => TypedEventEmitter<NeuroLinkEvents> | undefined) | undefined);
70
+ } | undefined, handleToolStorageFn: (toolCalls: unknown[], toolResults: unknown[], options: TextGenerationOptions, timestamp: Date) => Promise<void>,
71
+ /**
72
+ * The remaining, optional dependencies.
73
+ *
74
+ * Grouped rather than added as further positional parameters: the
75
+ * constructor is already at the six-parameter cap, and both of these are
76
+ * optional injection seams rather than required collaborators.
77
+ *
78
+ * `generateTextFn` exists because every other dependency of this class
79
+ * arrives through the constructor while `generateText` was reached by
80
+ * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
81
+ * ESM re-export cannot be substituted from a test, so asserting on the
82
+ * arguments this class builds — the entire contract of the system-message
83
+ * hoisting below — had no seam to work through. Production passes neither.
84
+ */
85
+ deps?: {
86
+ getEmitterFn?: () => TypedEventEmitter<NeuroLinkEvents> | undefined;
87
+ generateTextFn?: typeof generateText;
88
+ });
57
89
  /**
58
90
  * Helper method to call generateText with optional structured output
59
91
  * @private
@@ -159,14 +159,29 @@ export class GenerationHandler {
159
159
  supportsToolsFn;
160
160
  getTelemetryConfigFn;
161
161
  handleToolStorageFn;
162
- getEmitterFn;
163
- constructor(providerName, modelName, supportsToolsFn, getTelemetryConfigFn, handleToolStorageFn, getEmitterFn) {
162
+ deps;
163
+ constructor(providerName, modelName, supportsToolsFn, getTelemetryConfigFn, handleToolStorageFn,
164
+ /**
165
+ * The remaining, optional dependencies.
166
+ *
167
+ * Grouped rather than added as further positional parameters: the
168
+ * constructor is already at the six-parameter cap, and both of these are
169
+ * optional injection seams rather than required collaborators.
170
+ *
171
+ * `generateTextFn` exists because every other dependency of this class
172
+ * arrives through the constructor while `generateText` was reached by
173
+ * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
174
+ * ESM re-export cannot be substituted from a test, so asserting on the
175
+ * arguments this class builds — the entire contract of the system-message
176
+ * hoisting below — had no seam to work through. Production passes neither.
177
+ */
178
+ deps = {}) {
164
179
  this.providerName = providerName;
165
180
  this.modelName = modelName;
166
181
  this.supportsToolsFn = supportsToolsFn;
167
182
  this.getTelemetryConfigFn = getTelemetryConfigFn;
168
183
  this.handleToolStorageFn = handleToolStorageFn;
169
- this.getEmitterFn = getEmitterFn;
184
+ this.deps = deps;
170
185
  }
171
186
  /**
172
187
  * Helper method to call generateText with optional structured output
@@ -265,7 +280,7 @@ export class GenerationHandler {
265
280
  const samplingParams = resolveSamplingParams(this.providerName, getModelId(model, this.modelName || ""), options.temperature !== undefined
266
281
  ? { temperature: options.temperature }
267
282
  : {}, "aiSdk.generateText");
268
- const result = await generateText({
283
+ const result = await (this.deps.generateTextFn ?? generateText)({
269
284
  model,
270
285
  ...(system && { system }),
271
286
  messages: nonSystemMessages,
@@ -364,7 +379,7 @@ export class GenerationHandler {
364
379
  // This surfaces AI-SDK-driven tool completions as telemetry events
365
380
  // so that tool spans are created even when the SDK runs tools
366
381
  // internally (gaps G5 / S2).
367
- emitToolEndFromStepFinish(this.getEmitterFn?.(), toolResults);
382
+ emitToolEndFromStepFinish(this.deps.getEmitterFn?.(), toolResults);
368
383
  // Handle tool execution storage
369
384
  this.handleToolStorageFn(toolCalls, toolResults, options, new Date()).catch((error) => {
370
385
  logger.warn("[GenerationHandler] Failed to store tool executions", {
@@ -21,26 +21,18 @@ export async function loadPptxGenJS() {
21
21
  if (_pptxGenJS) {
22
22
  return _pptxGenJS;
23
23
  }
24
- try {
25
- const mod = await import(/* @vite-ignore */ "pptxgenjs");
26
- // ESM/CJS interop: pptxgenjs v4 may double-wrap the default export.
27
- // The runtime shape is genuinely dynamic, so probe it as `unknown`.
28
- const rawDefault = mod.default;
29
- const Ctor = typeof rawDefault === "function"
30
- ? rawDefault
31
- : rawDefault.default;
32
- _pptxGenJS = Ctor;
33
- return _pptxGenJS;
34
- }
35
- catch (err) {
36
- const e = err instanceof Error ? err : null;
37
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("pptxgenjs")) {
38
- throw new Error('PPT generation requires the "pptxgenjs" package. Install it with:\n pnpm add pptxgenjs', { cause: err });
39
- }
40
- throw err;
41
- }
24
+ const mod = await tryImport("pptxgenjs", "PPT generation");
25
+ // ESM/CJS interop: pptxgenjs v4 may double-wrap the default export.
26
+ // The runtime shape is genuinely dynamic, so probe it as `unknown`.
27
+ const rawDefault = mod.default;
28
+ const Ctor = typeof rawDefault === "function"
29
+ ? rawDefault
30
+ : rawDefault.default;
31
+ _pptxGenJS = Ctor;
32
+ return _pptxGenJS;
42
33
  }
43
34
  import { logger } from "../../utils/logger.js";
35
+ import { tryImport } from "../../utils/tryImport.js";
44
36
  import { withTimeout, ErrorFactory, NeuroLinkError, } from "../../utils/errorHandling.js";
45
37
  import { SpanSerializer, SpanType, SpanStatus, getMetricsAggregator, } from "../../observability/index.js";
46
38
  import { LAYOUT_POSITIONS, renderTitleSlide, renderSectionHeaderSlide, renderThankYouSlide, renderContentSlide, renderImageSlide, renderTwoColumnSlide, renderThreeColumnSlide, renderQuoteSlide, renderStatisticsSlide, renderChartSlide, renderTableSlide, renderTimelineSlide, renderProcessFlowSlide, renderComparisonSlide, renderFeaturesSlide, renderTeamSlide, renderConclusionSlide, renderDashboardSlide, renderMixedContentSlide, renderStatsGridSlide, renderIconGridSlide, } from "./slideRenderers.js";
@@ -90,7 +90,7 @@ export class BaseProvider {
90
90
  this.messageBuilder = new MessageBuilder(this.providerName, this.modelName);
91
91
  this.streamHandler = new StreamHandler(this.providerName, this.modelName);
92
92
  this.telemetryHandler = new TelemetryHandler(this.providerName, this.modelName, this.neurolink);
93
- this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), () => this.neurolink?.getEventEmitter());
93
+ this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), { getEmitterFn: () => this.neurolink?.getEventEmitter() });
94
94
  this.utilities = new Utilities(this.providerName, this.modelName, this.defaultTimeout, this.middlewareOptions);
95
95
  this.toolsManager = new ToolsManager(this.providerName, this.directTools, this.neurolink, {
96
96
  isZodSchema: (schema) => this.isZodSchema(schema),
@@ -115,7 +115,7 @@ export class BaseProvider {
115
115
  this.messageBuilder = new MessageBuilder(this.providerName, this.modelName);
116
116
  this.streamHandler = new StreamHandler(this.providerName, this.modelName);
117
117
  this.telemetryHandler = new TelemetryHandler(this.providerName, this.modelName, this.neurolink);
118
- this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), () => this.neurolink?.getEventEmitter());
118
+ this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), { getEmitterFn: () => this.neurolink?.getEventEmitter() });
119
119
  this.utilities = new Utilities(this.providerName, this.modelName, this.defaultTimeout, this.middlewareOptions);
120
120
  }
121
121
  /**
@@ -48,12 +48,44 @@ export declare class GenerationHandler {
48
48
  private readonly supportsToolsFn;
49
49
  private readonly getTelemetryConfigFn;
50
50
  private readonly handleToolStorageFn;
51
- private readonly getEmitterFn?;
51
+ /**
52
+ * The remaining, optional dependencies.
53
+ *
54
+ * Grouped rather than added as further positional parameters: the
55
+ * constructor is already at the six-parameter cap, and both of these are
56
+ * optional injection seams rather than required collaborators.
57
+ *
58
+ * `generateTextFn` exists because every other dependency of this class
59
+ * arrives through the constructor while `generateText` was reached by
60
+ * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
61
+ * ESM re-export cannot be substituted from a test, so asserting on the
62
+ * arguments this class builds — the entire contract of the system-message
63
+ * hoisting below — had no seam to work through. Production passes neither.
64
+ */
65
+ private readonly deps;
52
66
  constructor(providerName: AIProviderName, modelName: string, supportsToolsFn: () => boolean, getTelemetryConfigFn: (options: TextGenerationOptions, type: string) => {
53
67
  isEnabled: boolean;
54
68
  functionId?: string;
55
69
  metadata?: Record<string, string | number | boolean>;
56
- } | undefined, handleToolStorageFn: (toolCalls: unknown[], toolResults: unknown[], options: TextGenerationOptions, timestamp: Date) => Promise<void>, getEmitterFn?: (() => TypedEventEmitter<NeuroLinkEvents> | undefined) | undefined);
70
+ } | undefined, handleToolStorageFn: (toolCalls: unknown[], toolResults: unknown[], options: TextGenerationOptions, timestamp: Date) => Promise<void>,
71
+ /**
72
+ * The remaining, optional dependencies.
73
+ *
74
+ * Grouped rather than added as further positional parameters: the
75
+ * constructor is already at the six-parameter cap, and both of these are
76
+ * optional injection seams rather than required collaborators.
77
+ *
78
+ * `generateTextFn` exists because every other dependency of this class
79
+ * arrives through the constructor while `generateText` was reached by
80
+ * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
81
+ * ESM re-export cannot be substituted from a test, so asserting on the
82
+ * arguments this class builds — the entire contract of the system-message
83
+ * hoisting below — had no seam to work through. Production passes neither.
84
+ */
85
+ deps?: {
86
+ getEmitterFn?: () => TypedEventEmitter<NeuroLinkEvents> | undefined;
87
+ generateTextFn?: typeof generateText;
88
+ });
57
89
  /**
58
90
  * Helper method to call generateText with optional structured output
59
91
  * @private
@@ -159,14 +159,29 @@ export class GenerationHandler {
159
159
  supportsToolsFn;
160
160
  getTelemetryConfigFn;
161
161
  handleToolStorageFn;
162
- getEmitterFn;
163
- constructor(providerName, modelName, supportsToolsFn, getTelemetryConfigFn, handleToolStorageFn, getEmitterFn) {
162
+ deps;
163
+ constructor(providerName, modelName, supportsToolsFn, getTelemetryConfigFn, handleToolStorageFn,
164
+ /**
165
+ * The remaining, optional dependencies.
166
+ *
167
+ * Grouped rather than added as further positional parameters: the
168
+ * constructor is already at the six-parameter cap, and both of these are
169
+ * optional injection seams rather than required collaborators.
170
+ *
171
+ * `generateTextFn` exists because every other dependency of this class
172
+ * arrives through the constructor while `generateText` was reached by
173
+ * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
174
+ * ESM re-export cannot be substituted from a test, so asserting on the
175
+ * arguments this class builds — the entire contract of the system-message
176
+ * hoisting below — had no seam to work through. Production passes neither.
177
+ */
178
+ deps = {}) {
164
179
  this.providerName = providerName;
165
180
  this.modelName = modelName;
166
181
  this.supportsToolsFn = supportsToolsFn;
167
182
  this.getTelemetryConfigFn = getTelemetryConfigFn;
168
183
  this.handleToolStorageFn = handleToolStorageFn;
169
- this.getEmitterFn = getEmitterFn;
184
+ this.deps = deps;
170
185
  }
171
186
  /**
172
187
  * Helper method to call generateText with optional structured output
@@ -265,7 +280,7 @@ export class GenerationHandler {
265
280
  const samplingParams = resolveSamplingParams(this.providerName, getModelId(model, this.modelName || ""), options.temperature !== undefined
266
281
  ? { temperature: options.temperature }
267
282
  : {}, "aiSdk.generateText");
268
- const result = await generateText({
283
+ const result = await (this.deps.generateTextFn ?? generateText)({
269
284
  model,
270
285
  ...(system && { system }),
271
286
  messages: nonSystemMessages,
@@ -364,7 +379,7 @@ export class GenerationHandler {
364
379
  // This surfaces AI-SDK-driven tool completions as telemetry events
365
380
  // so that tool spans are created even when the SDK runs tools
366
381
  // internally (gaps G5 / S2).
367
- emitToolEndFromStepFinish(this.getEmitterFn?.(), toolResults);
382
+ emitToolEndFromStepFinish(this.deps.getEmitterFn?.(), toolResults);
368
383
  // Handle tool execution storage
369
384
  this.handleToolStorageFn(toolCalls, toolResults, options, new Date()).catch((error) => {
370
385
  logger.warn("[GenerationHandler] Failed to store tool executions", {
@@ -21,26 +21,18 @@ export async function loadPptxGenJS() {
21
21
  if (_pptxGenJS) {
22
22
  return _pptxGenJS;
23
23
  }
24
- try {
25
- const mod = await import(/* @vite-ignore */ "pptxgenjs");
26
- // ESM/CJS interop: pptxgenjs v4 may double-wrap the default export.
27
- // The runtime shape is genuinely dynamic, so probe it as `unknown`.
28
- const rawDefault = mod.default;
29
- const Ctor = typeof rawDefault === "function"
30
- ? rawDefault
31
- : rawDefault.default;
32
- _pptxGenJS = Ctor;
33
- return _pptxGenJS;
34
- }
35
- catch (err) {
36
- const e = err instanceof Error ? err : null;
37
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("pptxgenjs")) {
38
- throw new Error('PPT generation requires the "pptxgenjs" package. Install it with:\n pnpm add pptxgenjs', { cause: err });
39
- }
40
- throw err;
41
- }
24
+ const mod = await tryImport("pptxgenjs", "PPT generation");
25
+ // ESM/CJS interop: pptxgenjs v4 may double-wrap the default export.
26
+ // The runtime shape is genuinely dynamic, so probe it as `unknown`.
27
+ const rawDefault = mod.default;
28
+ const Ctor = typeof rawDefault === "function"
29
+ ? rawDefault
30
+ : rawDefault.default;
31
+ _pptxGenJS = Ctor;
32
+ return _pptxGenJS;
42
33
  }
43
34
  import { logger } from "../../utils/logger.js";
35
+ import { tryImport } from "../../utils/tryImport.js";
44
36
  import { withTimeout, ErrorFactory, NeuroLinkError, } from "../../utils/errorHandling.js";
45
37
  import { SpanSerializer, SpanType, SpanStatus, getMetricsAggregator, } from "../../observability/index.js";
46
38
  import { NeuroLink } from "../../neurolink.js";
@@ -38,30 +38,22 @@
38
38
  import { BaseFileProcessor } from "../base/BaseFileProcessor.js";
39
39
  import { SIZE_LIMITS } from "../config/index.js";
40
40
  import { FileErrorCode } from "../errors/index.js";
41
+ import { tryImport } from "../../utils/tryImport.js";
41
42
  let _exceljs = null;
42
43
  async function loadExcelJS() {
43
44
  if (_exceljs) {
44
45
  return _exceljs;
45
46
  }
46
- try {
47
- const mod = await import(/* @vite-ignore */ "exceljs");
48
- // exceljs is a CommonJS module. Under Node ESM (and some bundlers) the
49
- // `Workbook` constructor is exposed at runtime on the namespace's `default`
50
- // export rather than on the namespace itself — so a bare
51
- // `new ExcelJS.Workbook()` throws "ExcelJS.Workbook is not a constructor"
52
- // (TS still types it as present via esModuleInterop, masking the bug).
53
- // Normalise here so the constructor is reachable regardless of interop style.
54
- const ns = mod;
55
- _exceljs = (ns.Workbook ? ns : (ns.default ?? ns));
56
- return _exceljs;
57
- }
58
- catch (err) {
59
- const e = err instanceof Error ? err : null;
60
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("exceljs")) {
61
- throw new Error('Excel file processing requires the "exceljs" package. Install it with:\n pnpm add exceljs', { cause: err });
62
- }
63
- throw err;
64
- }
47
+ const mod = await tryImport("exceljs", "Excel file processing");
48
+ // exceljs is a CommonJS module. Under Node ESM (and some bundlers) the
49
+ // `Workbook` constructor is exposed at runtime on the namespace's `default`
50
+ // export rather than on the namespace itself — so a bare
51
+ // `new ExcelJS.Workbook()` throws "ExcelJS.Workbook is not a constructor"
52
+ // (TS still types it as present via esModuleInterop, masking the bug).
53
+ // Normalise here so the constructor is reachable regardless of interop style.
54
+ const ns = mod;
55
+ _exceljs = (ns.Workbook ? ns : (ns.default ?? ns));
56
+ return _exceljs;
65
57
  }
66
58
  // Re-export for consumers who import from this module
67
59
  // Import for local use
@@ -34,22 +34,14 @@
34
34
  import { BaseFileProcessor } from "../base/BaseFileProcessor.js";
35
35
  import { SIZE_LIMITS } from "../config/index.js";
36
36
  import { FileErrorCode } from "../errors/index.js";
37
+ import { tryImport } from "../../utils/tryImport.js";
37
38
  let _mammoth = null;
38
39
  async function loadMammoth() {
39
40
  if (_mammoth) {
40
41
  return _mammoth;
41
42
  }
42
- try {
43
- _mammoth = await import(/* @vite-ignore */ "mammoth");
44
- return _mammoth;
45
- }
46
- catch (err) {
47
- const e = err instanceof Error ? err : null;
48
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("mammoth")) {
49
- throw new Error('Word document processing requires the "mammoth" package. Install it with:\n pnpm add mammoth', { cause: err });
50
- }
51
- throw err;
52
- }
43
+ _mammoth = await tryImport("mammoth", "Word document processing");
44
+ return _mammoth;
53
45
  }
54
46
  // Re-export for consumers who import from this module
55
47
  // Import for local use
@@ -41,23 +41,14 @@ import { SIZE_LIMITS_MB } from "../config/index.js";
41
41
  import { FileErrorCode } from "../errors/index.js";
42
42
  import { withTimeout } from "../../utils/timeout.js";
43
43
  import { formatMediaDuration } from "../../utils/mediaDuration.js";
44
+ import { tryImport } from "../../utils/tryImport.js";
44
45
  let _musicMetadata = null;
45
46
  async function loadMusicMetadata() {
46
47
  if (_musicMetadata) {
47
48
  return _musicMetadata;
48
49
  }
49
- try {
50
- _musicMetadata = await import(/* @vite-ignore */ "music-metadata");
51
- return _musicMetadata;
52
- }
53
- catch (err) {
54
- const e = err instanceof Error ? err : null;
55
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
56
- e.message.includes("music-metadata")) {
57
- throw new Error('Audio processing requires the "music-metadata" package. Install it with:\n pnpm add music-metadata', { cause: err });
58
- }
59
- throw err;
60
- }
50
+ _musicMetadata = await tryImport("music-metadata", "Audio processing");
51
+ return _musicMetadata;
61
52
  }
62
53
  // =============================================================================
63
54
  // TYPES
@@ -45,6 +45,17 @@
45
45
  */
46
46
  import { BaseFileProcessor } from "../base/BaseFileProcessor.js";
47
47
  import type { FileInfo, ProcessedVideo, ProcessorFileProcessingResult, ProcessOptions } from "../../types/index.js";
48
+ /**
49
+ * Narrow a loaded `fluent-ffmpeg` export to the shape this file actually uses:
50
+ * a callable carrying the `ffprobe` and `setFfmpegPath` statics.
51
+ *
52
+ * `tryImport` proves only that the package RESOLVES. Callers invoke the export
53
+ * and reach straight for its statics, so a package whose shape changed (ESM
54
+ * rewrite, major bump, a shim in node_modules) would otherwise surface as
55
+ * "Cannot read properties of undefined (reading 'ffprobe')" from inside
56
+ * probeVideo — blaming the call site instead of the package that is wrong.
57
+ */
58
+ export declare function assertFluentFfmpegShape(mod: unknown): asserts mod is typeof import("fluent-ffmpeg");
48
59
  /**
49
60
  * Video Processor - extracts metadata, keyframes, and subtitles from video files.
50
61
  *
@@ -55,39 +55,48 @@ import { SIZE_LIMITS_MB } from "../config/index.js";
55
55
  import { FileErrorCode } from "../errors/index.js";
56
56
  import { tracers, ATTR, withSpan } from "../../telemetry/index.js";
57
57
  import { logger } from "../../utils/logger.js";
58
+ import { tryImport } from "../../utils/tryImport.js";
59
+ /**
60
+ * Narrow a loaded `fluent-ffmpeg` export to the shape this file actually uses:
61
+ * a callable carrying the `ffprobe` and `setFfmpegPath` statics.
62
+ *
63
+ * `tryImport` proves only that the package RESOLVES. Callers invoke the export
64
+ * and reach straight for its statics, so a package whose shape changed (ESM
65
+ * rewrite, major bump, a shim in node_modules) would otherwise surface as
66
+ * "Cannot read properties of undefined (reading 'ffprobe')" from inside
67
+ * probeVideo — blaming the call site instead of the package that is wrong.
68
+ */
69
+ export function assertFluentFfmpegShape(mod) {
70
+ // Not `Partial<typeof import("fluent-ffmpeg")>`: Partial maps over properties
71
+ // and drops the call signature, so `typeof x === "function"` would narrow the
72
+ // result to `never`. Probe the statics structurally instead.
73
+ const statics = mod;
74
+ if (typeof mod !== "function" ||
75
+ typeof statics.ffprobe !== "function" ||
76
+ typeof statics.setFfmpegPath !== "function") {
77
+ throw new Error(`The installed "fluent-ffmpeg" package does not export a callable with ` +
78
+ `ffprobe and setFfmpegPath statics (got ${typeof mod}). ` +
79
+ `Reinstall a compatible version:\n pnpm add fluent-ffmpeg`);
80
+ }
81
+ }
58
82
  // fluent-ffmpeg's default export is callable + has static methods — avoid caching
59
83
  // the module type (it confuses TS); Node's module cache handles dedup.
60
84
  async function loadFluentFfmpeg() {
61
- try {
62
- const mod = await import(/* @vite-ignore */ "fluent-ffmpeg");
63
- return mod.default;
64
- }
65
- catch (err) {
66
- const e = err instanceof Error ? err : null;
67
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
68
- e.message.includes("fluent-ffmpeg")) {
69
- throw new Error('Video processing requires the "fluent-ffmpeg" package. Install it with:\n pnpm add fluent-ffmpeg', { cause: err });
70
- }
71
- throw err;
72
- }
85
+ // fluent-ffmpeg is CJS (`export =`), so `typeof import(...)` describes the
86
+ // callable itself and carries no `default`. Under Node ESM the namespace
87
+ // still wraps it, so ask for that shape explicitly.
88
+ const mod = await tryImport("fluent-ffmpeg", "Video processing");
89
+ const ffmpeg = mod.default;
90
+ assertFluentFfmpegShape(ffmpeg);
91
+ return ffmpeg;
73
92
  }
74
93
  let _mediabunny = null;
75
94
  async function loadMediaBunny() {
76
95
  if (_mediabunny) {
77
96
  return _mediabunny;
78
97
  }
79
- try {
80
- _mediabunny = await import(/* @vite-ignore */ "mediabunny");
81
- return _mediabunny;
82
- }
83
- catch (err) {
84
- const e = err instanceof Error ? err : null;
85
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
86
- e.message.includes("mediabunny")) {
87
- throw new Error('Video processing requires the "mediabunny" package. Install it with:\n pnpm add mediabunny', { cause: err });
88
- }
89
- throw err;
90
- }
98
+ _mediabunny = await tryImport("mediabunny", "Video processing");
99
+ return _mediabunny;
91
100
  }
92
101
  // =============================================================================
93
102
  // FFMPEG PATH INITIALIZATION
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { handleSageMakerError, SageMakerError, isRetryableError, getRetryDelay, } from "./errors.js";
8
8
  import { logger } from "../../utils/logger.js";
9
+ import { tryImport } from "../../utils/tryImport.js";
9
10
  /**
10
11
  * Lazily load `@aws-sdk/client-sagemaker-runtime`.
11
12
  *
@@ -16,17 +17,7 @@ import { logger } from "../../utils/logger.js";
16
17
  * error instead of a raw resolution failure.
17
18
  */
18
19
  async function loadSageMakerRuntime() {
19
- try {
20
- return await import(/* @vite-ignore */ "@aws-sdk/client-sagemaker-runtime");
21
- }
22
- catch (err) {
23
- const e = err instanceof Error ? err : null;
24
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
25
- e.message.includes("client-sagemaker-runtime")) {
26
- throw new Error('SageMaker inference requires "@aws-sdk/client-sagemaker-runtime". Install it with:\n pnpm add @aws-sdk/client-sagemaker-runtime', { cause: err });
27
- }
28
- throw err;
29
- }
20
+ return tryImport("@aws-sdk/client-sagemaker-runtime", "SageMaker inference");
30
21
  }
31
22
  /**
32
23
  * Enhanced SageMaker Runtime client with retry logic and error handling
@@ -5,20 +5,11 @@ import { timingSafeEqualString } from "./tokenCompare.js";
5
5
  import { CartesiaStream } from "../../adapters/tts/cartesiaHandler.js";
6
6
  import { NeuroLink } from "../../neurolink.js";
7
7
  import { logger } from "../../utils/logger.js";
8
+ import { tryImport } from "../../utils/tryImport.js";
8
9
  import { withTimeout } from "../../utils/async/withTimeout.js";
9
10
  async function loadCobra(accessKey) {
10
- try {
11
- const mod = (await import(/* @vite-ignore */ "@picovoice/cobra-node"));
12
- return new mod.Cobra(accessKey);
13
- }
14
- catch (err) {
15
- const e = err instanceof Error ? err : null;
16
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
17
- e.message.includes("cobra-node")) {
18
- throw new Error('Voice activity detection requires "@picovoice/cobra-node". Install it with:\n pnpm add @picovoice/cobra-node', { cause: err });
19
- }
20
- throw err;
21
- }
11
+ const mod = await tryImport("@picovoice/cobra-node", "Voice activity detection");
12
+ return new mod.Cobra(accessKey);
22
13
  }
23
14
  const SONIOX_URL = process.env.SONIOX_WS_URL ?? "wss://stt-rt.soniox.com/transcribe-websocket";
24
15
  function getRequiredEnv(name) {
@@ -20,6 +20,7 @@ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semanti
20
20
  import { AsyncLocalStorage } from "async_hooks";
21
21
  import { extractMcpErrorText } from "../../../../utils/mcpErrorText.js";
22
22
  import { logger } from "../../../../utils/logger.js";
23
+ import { tryImport } from "../../../../utils/tryImport.js";
23
24
  import { LANGFUSE_ATTR } from "../../../../telemetry/attributes.js";
24
25
  const LOG_PREFIX = "[OpenTelemetry]";
25
26
  function createOtelResource(config, serviceName) {
@@ -568,17 +569,7 @@ class ContextEnricher {
568
569
  }
569
570
  }
570
571
  async function createLangfuseProcessor(config) {
571
- let mod;
572
- try {
573
- mod = await import(/* @vite-ignore */ "@langfuse/otel");
574
- }
575
- catch (err) {
576
- const e = err instanceof Error ? err : null;
577
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("langfuse")) {
578
- throw new Error('Langfuse observability requires "@langfuse/otel". Install it with:\n pnpm add @langfuse/otel', { cause: err });
579
- }
580
- throw err;
581
- }
572
+ const mod = await tryImport("@langfuse/otel", "Langfuse observability");
582
573
  return new mod.LangfuseSpanProcessor({
583
574
  publicKey: config.publicKey,
584
575
  secretKey: config.secretKey,
@@ -7,19 +7,11 @@
7
7
  * - Survives process restarts (Redis-persisted)
8
8
  */
9
9
  import { logger } from "../../utils/logger.js";
10
+ import { tryImport } from "../../utils/tryImport.js";
10
11
  import { TaskError } from "../errors.js";
11
12
  import { TASK_DEFAULTS, } from "../../types/index.js";
12
13
  async function loadBullMQ() {
13
- try {
14
- return await import(/* @vite-ignore */ "bullmq");
15
- }
16
- catch (err) {
17
- const e = err instanceof Error ? err : null;
18
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("bullmq")) {
19
- throw new Error('BullMQ task backend requires the "bullmq" package. Install it with:\n pnpm add bullmq', { cause: err });
20
- }
21
- throw err;
22
- }
14
+ return tryImport("bullmq", "BullMQ task backend");
23
15
  }
24
16
  const QUEUE_NAME = "neurolink-tasks";
25
17
  export class BullMQBackend {