@juspay/neurolink 10.8.13 → 10.8.15
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.
- package/CHANGELOG.md +4 -0
- package/dist/browser/neurolink.min.js +484 -664
- package/dist/cli/factories/sagemakerCommandFactory.js +2 -11
- package/dist/features/ppt/slideGenerator.js +10 -18
- package/dist/lib/features/ppt/slideGenerator.js +10 -18
- package/dist/lib/processors/document/ExcelProcessor.js +11 -19
- package/dist/lib/processors/document/WordProcessor.js +3 -11
- package/dist/lib/processors/media/AudioProcessor.js +3 -12
- package/dist/lib/processors/media/VideoProcessor.d.ts +11 -0
- package/dist/lib/processors/media/VideoProcessor.js +33 -24
- package/dist/lib/providers/sagemaker/client.js +2 -11
- package/dist/lib/server/voice/voiceWebSocketHandler.js +3 -12
- package/dist/lib/services/server/ai/observability/instrumentation.js +2 -11
- package/dist/lib/tasks/backends/bullmqBackend.js +2 -10
- package/dist/lib/utils/tryImport.d.ts +35 -0
- package/dist/lib/utils/tryImport.js +95 -0
- package/dist/processors/document/ExcelProcessor.js +11 -19
- package/dist/processors/document/WordProcessor.js +3 -11
- package/dist/processors/media/AudioProcessor.js +3 -12
- package/dist/processors/media/VideoProcessor.d.ts +11 -0
- package/dist/processors/media/VideoProcessor.js +33 -24
- package/dist/providers/sagemaker/client.js +2 -11
- package/dist/server/voice/voiceWebSocketHandler.js +3 -12
- package/dist/services/server/ai/observability/instrumentation.js +2 -11
- package/dist/tasks/backends/bullmqBackend.js +2 -10
- package/dist/utils/tryImport.d.ts +35 -0
- package/dist/utils/tryImport.js +94 -0
- package/package.json +9 -5
|
@@ -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
|
-
|
|
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";
|
|
@@ -21,26 +21,18 @@ export async function loadPptxGenJS() {
|
|
|
21
21
|
if (_pptxGenJS) {
|
|
22
22
|
return _pptxGenJS;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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";
|
|
@@ -21,26 +21,18 @@ export async function loadPptxGenJS() {
|
|
|
21
21
|
if (_pptxGenJS) {
|
|
22
22
|
return _pptxGenJS;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamically import an optional dependency, converting a missing package into
|
|
3
|
+
* an actionable error.
|
|
4
|
+
*
|
|
5
|
+
* NeuroLink declares 42 `optionalDependencies`, and each loader had grown its
|
|
6
|
+
* own copy of the same try/catch: check `err.code`, substring-match the package
|
|
7
|
+
* name, rethrow a friendlier Error. The copies had drifted — some said
|
|
8
|
+
* `pnpm add`, one said `npm install`, and the phrasing varied per call site.
|
|
9
|
+
*
|
|
10
|
+
* Only missing-module failures are intercepted. A package that is installed but
|
|
11
|
+
* throws while evaluating (syntax error, ESM/CJS interop, a broken transitive
|
|
12
|
+
* dep) rethrows unchanged — turning those into "please install X" would send
|
|
13
|
+
* the caller after a package they already have. The same applies to specifiers
|
|
14
|
+
* that are not installable packages at all (relative paths, file/data URLs):
|
|
15
|
+
* they rethrow untouched rather than producing an unrunnable install command.
|
|
16
|
+
*
|
|
17
|
+
* @param pkg - Module specifier. The install hint is only produced for bare
|
|
18
|
+
* package names; anything else rethrows the loader's own error.
|
|
19
|
+
* @param feature - Human-readable capability name, used to open the message.
|
|
20
|
+
* @returns The imported module namespace, typed as `T` (`unknown` if the
|
|
21
|
+
* caller does not supply a type argument, since the shape cannot be known
|
|
22
|
+
* for a dynamic specifier).
|
|
23
|
+
* @throws The loader's original error, or an `Error` naming the package and
|
|
24
|
+
* the `pnpm add` command when a bare package is genuinely absent. The
|
|
25
|
+
* original failure is always preserved on `cause`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const ExcelJS = await tryImport<typeof import("exceljs")>(
|
|
30
|
+
* "exceljs",
|
|
31
|
+
* "Excel file processing",
|
|
32
|
+
* );
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function tryImport<T = unknown>(pkg: string, feature: string): Promise<T>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamically import an optional dependency, converting a missing package into
|
|
3
|
+
* an actionable error.
|
|
4
|
+
*
|
|
5
|
+
* NeuroLink declares 42 `optionalDependencies`, and each loader had grown its
|
|
6
|
+
* own copy of the same try/catch: check `err.code`, substring-match the package
|
|
7
|
+
* name, rethrow a friendlier Error. The copies had drifted — some said
|
|
8
|
+
* `pnpm add`, one said `npm install`, and the phrasing varied per call site.
|
|
9
|
+
*
|
|
10
|
+
* Only missing-module failures are intercepted. A package that is installed but
|
|
11
|
+
* throws while evaluating (syntax error, ESM/CJS interop, a broken transitive
|
|
12
|
+
* dep) rethrows unchanged — turning those into "please install X" would send
|
|
13
|
+
* the caller after a package they already have. The same applies to specifiers
|
|
14
|
+
* that are not installable packages at all (relative paths, file/data URLs):
|
|
15
|
+
* they rethrow untouched rather than producing an unrunnable install command.
|
|
16
|
+
*
|
|
17
|
+
* @param pkg - Module specifier. The install hint is only produced for bare
|
|
18
|
+
* package names; anything else rethrows the loader's own error.
|
|
19
|
+
* @param feature - Human-readable capability name, used to open the message.
|
|
20
|
+
* @returns The imported module namespace, typed as `T` (`unknown` if the
|
|
21
|
+
* caller does not supply a type argument, since the shape cannot be known
|
|
22
|
+
* for a dynamic specifier).
|
|
23
|
+
* @throws The loader's original error, or an `Error` naming the package and
|
|
24
|
+
* the `pnpm add` command when a bare package is genuinely absent. The
|
|
25
|
+
* original failure is always preserved on `cause`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const ExcelJS = await tryImport<typeof import("exceljs")>(
|
|
30
|
+
* "exceljs",
|
|
31
|
+
* "Excel file processing",
|
|
32
|
+
* );
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export async function tryImport(pkg, feature) {
|
|
36
|
+
try {
|
|
37
|
+
return (await import(/* @vite-ignore */ pkg));
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (isMissingModule(err, pkg)) {
|
|
41
|
+
throw new Error(`${feature} requires the "${pkg}" package. Install it with:\n pnpm add ${pkg}`, { cause: err });
|
|
42
|
+
}
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* True only when `err` is the module loader failing to resolve `pkg` itself.
|
|
48
|
+
*
|
|
49
|
+
* The package name is matched inside quotes because that is how both loaders
|
|
50
|
+
* format it — ESM `Cannot find package 'x' imported from …`, CJS
|
|
51
|
+
* `Cannot find module 'x'`. Matching the bare substring instead (as the
|
|
52
|
+
* hand-rolled copies did) also matches a *transitive* dependency whose path
|
|
53
|
+
* happens to contain the name, which would blame the wrong package.
|
|
54
|
+
*
|
|
55
|
+
* The specifier must also be a bare package name. Without that check a missing
|
|
56
|
+
* relative path or file URL is rewritten into `pnpm add ./fixtures/thing.mjs`,
|
|
57
|
+
* which is not a command anyone can run — and this helper is already called
|
|
58
|
+
* with `file:` and `data:` URLs, so that path is reachable rather than
|
|
59
|
+
* theoretical.
|
|
60
|
+
*/
|
|
61
|
+
function isMissingModule(err, pkg) {
|
|
62
|
+
if (!(err instanceof Error)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (!isBarePackageSpecifier(pkg)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const code = err.code;
|
|
69
|
+
if (code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return err.message.includes(`'${pkg}'`) || err.message.includes(`"${pkg}"`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* True for specifiers that name an installable package — `exceljs`,
|
|
76
|
+
* `@scope/pkg`, `pkg/sub/path`.
|
|
77
|
+
*
|
|
78
|
+
* False for the two things an install hint cannot help with: filesystem paths
|
|
79
|
+
* (`./x`, `../x`, `/x`, and Windows `C:\x`, which the scheme test below also
|
|
80
|
+
* rejects) and any URL (`file:`, `data:`, `http:`), including the `node:`
|
|
81
|
+
* builtins, which are never installed.
|
|
82
|
+
*/
|
|
83
|
+
function isBarePackageSpecifier(specifier) {
|
|
84
|
+
if (specifier.length === 0) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (specifier.startsWith("/") ||
|
|
88
|
+
specifier.startsWith("./") ||
|
|
89
|
+
specifier.startsWith("../")) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
// RFC 3986 scheme: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ":"
|
|
93
|
+
return !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(specifier);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=tryImport.js.map
|
|
@@ -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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
*
|