n8n-nodes-mautic-advanced 1.3.9 → 1.3.10
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.
|
@@ -4,213 +4,181 @@ exports.runtimeZod = exports.RuntimeDynamicStructuredTool = void 0;
|
|
|
4
4
|
// nodes/MauticAdvanced/ai-tools/runtime.ts
|
|
5
5
|
const module_1 = require("module");
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* createRequire() anchor to resolve @langchain/core/tools and zod from n8n's
|
|
9
|
-
* own module tree (not this package's bundled devDependency copies).
|
|
7
|
+
* Runtime resolution of `zod` and `@langchain/core`'s DynamicStructuredTool.
|
|
10
8
|
*
|
|
11
9
|
* IMPORTANT: resolution must NEVER run at module-import time. n8n's
|
|
12
10
|
* node-directory-loader requires every node file in this package (including
|
|
13
11
|
* this one) purely to register node metadata, long before any workflow
|
|
14
12
|
* executes — and it aborts loading the ENTIRE package if any one file throws
|
|
15
|
-
* while being required.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* subtree, which no filesystem-based require.resolve() from here can walk
|
|
21
|
-
* into. The resulting throw took down the plain, non-AI `mauticAdvanced`
|
|
22
|
-
* node and its trigger too, not just this AI-tools node.
|
|
13
|
+
* while being required. Doing filesystem resolution here at import time is
|
|
14
|
+
* exactly what took down the plain, non-AI `mauticAdvanced` node and its
|
|
15
|
+
* trigger under pnpm-strict-isolated installs (issue #2). Resolution is
|
|
16
|
+
* therefore deferred to first actual use inside the Proxy traps below, which
|
|
17
|
+
* only fire when a connected AI tool is invoked at workflow-execution time.
|
|
23
18
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
19
|
+
* By execution time n8n's own Agent/MCP Trigger machinery has already loaded
|
|
20
|
+
* `@n8n/n8n-nodes-langchain` (and therefore zod and @langchain/core) into the
|
|
21
|
+
* process-global require.cache. We resolve the SAME module identity n8n uses —
|
|
22
|
+
* the copy its `normalizeToolSchema` runs `instanceof ZodType` / `instanceof
|
|
23
|
+
* ZodType`-style checks against — by two strategies, in order:
|
|
24
|
+
*
|
|
25
|
+
* 1. `require.main` — n8n's own entry point. For a correctly-installed
|
|
26
|
+
* (non-isolated) host this lands on n8n's top-level copies directly.
|
|
27
|
+
* Guarded for `require.main` being undefined (ESM launch, worker_threads /
|
|
28
|
+
* queue-mode workers). We do NOT fall back to `__filename`: anchoring at
|
|
29
|
+
* this file would resolve THIS package's own bundled copy, which is the
|
|
30
|
+
* wrong module identity and fails n8n's `instanceof` checks silently.
|
|
31
|
+
*
|
|
32
|
+
* 2. An n8n-owned-tree anchor — a module already resident in require.cache
|
|
33
|
+
* whose path belongs to a package only n8n owns and community nodes never
|
|
34
|
+
* bundle. We `createRequire()` the dependency FROM that module's path, so
|
|
35
|
+
* the resolved copy is tied to n8n's tree by identity — independent of
|
|
36
|
+
* cache iteration order and of pnpm virtual-store path naming.
|
|
37
|
+
*
|
|
38
|
+
* If neither strategy resolves, we FAIL CLEAN (throw a diagnostic). We do NOT
|
|
39
|
+
* scan require.cache for a bare `zod` / `@langchain/core` and guess which copy
|
|
40
|
+
* is n8n's: under pnpm strict-isolated installs the cache key is the flat
|
|
41
|
+
* virtual-store realpath (`.../.pnpm/zod@3.x.x/node_modules/zod/...`) which does
|
|
42
|
+
* not encode the dependent package, so another installed community node's
|
|
43
|
+
* bundled copy can win the scan and yield a wrong-instance `ZodType` that
|
|
44
|
+
* n8n's `instanceof` check drops silently (issue #4). Returning nothing and
|
|
45
|
+
* throwing is safer than returning a possibly-wrong copy.
|
|
31
46
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'langchain/
|
|
47
|
+
// n8n-owned anchor packages, in priority order. `@n8n/n8n-nodes-langchain` is
|
|
48
|
+
// always loaded at execution time and is the code that runs the instanceof
|
|
49
|
+
// checks, so resolving from it guarantees identical module identity.
|
|
50
|
+
const N8N_ANCHOR_PACKAGES = [
|
|
51
|
+
{ name: '@n8n/n8n-nodes-langchain', pattern: /[\\/]@n8n[\\/]n8n-nodes-langchain[\\/]/ },
|
|
52
|
+
{ name: '@langchain/classic', pattern: /[\\/]@langchain[\\/]classic[\\/]/ },
|
|
53
|
+
{ name: 'n8n-workflow', pattern: /[\\/]n8n-workflow[\\/]/ },
|
|
54
|
+
{ name: 'n8n-core', pattern: /[\\/]n8n-core[\\/]/ },
|
|
38
55
|
];
|
|
39
|
-
// Memoized ONLY on success — a failed attempt must not permanently disable
|
|
40
|
-
// retries (e.g. the require.cache scan fallback below may only succeed on a
|
|
41
|
-
// later call, once n8n has finished loading its own langchain-dependent
|
|
42
|
-
// nodes).
|
|
43
|
-
let _runtimeReq;
|
|
44
56
|
let _anchorDiagnostic = null;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Yields a createRequire() for EACH n8n-owned anchor package currently resident
|
|
59
|
+
* in Node's process-global module cache, in the priority order of
|
|
60
|
+
* N8N_ANCHOR_PACKAGES. Must be called lazily (not at module load) — n8n requires
|
|
61
|
+
* node files for registration before any workflow runs, i.e. before the anchor
|
|
62
|
+
* packages are necessarily resident in cache.
|
|
63
|
+
*
|
|
64
|
+
* IMPORTANT: this yields ALL available anchors rather than memoizing the first
|
|
65
|
+
* one found. Different anchors resolve different dependencies: `n8n-workflow` /
|
|
66
|
+
* `n8n-core` are imported by this package at registration time, so they are
|
|
67
|
+
* resident FIRST — but they can resolve `zod` and NOT `@langchain/core/tools`.
|
|
68
|
+
* Memoizing `n8n-workflow` as THE anchor would permanently starve
|
|
69
|
+
* DynamicStructuredTool resolution even after `@langchain/*` later loads. So the
|
|
70
|
+
* caller tries each anchor against the actual dependency and memoizes only at
|
|
71
|
+
* the dependency level (on success). Re-scanning the cache each unresolved call
|
|
72
|
+
* is cheap and only happens until the dependency resolves.
|
|
73
|
+
*/
|
|
74
|
+
function* iterN8nAnchorRequires() {
|
|
75
|
+
let cache;
|
|
76
|
+
try {
|
|
77
|
+
// require.cache is the documented CommonJS alias for Module._cache, available
|
|
78
|
+
// directly in CJS module scope (this file compiles to CJS via tsc).
|
|
79
|
+
cache = require.cache;
|
|
80
|
+
}
|
|
81
|
+
catch (_e) {
|
|
82
|
+
// best-effort — require.cache introspection is not guaranteed across Node versions
|
|
83
|
+
cache = undefined;
|
|
84
|
+
}
|
|
85
|
+
if (!cache) {
|
|
86
|
+
_anchorDiagnostic = 'require.cache unavailable';
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const keys = Object.keys(cache);
|
|
90
|
+
const available = [];
|
|
91
|
+
for (const anchor of N8N_ANCHOR_PACKAGES) {
|
|
92
|
+
const key = keys.find((k) => anchor.pattern.test(k));
|
|
93
|
+
if (!key)
|
|
94
|
+
continue;
|
|
50
95
|
try {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return _runtimeReq;
|
|
96
|
+
const req = (0, module_1.createRequire)(key);
|
|
97
|
+
available.push(anchor.name);
|
|
98
|
+
yield { req, source: `anchor ${anchor.name}` };
|
|
55
99
|
}
|
|
56
|
-
catch (
|
|
57
|
-
|
|
100
|
+
catch (_e) {
|
|
101
|
+
// createRequire failed for this anchor — try the next
|
|
58
102
|
}
|
|
59
103
|
}
|
|
60
|
-
_anchorDiagnostic =
|
|
61
|
-
|
|
104
|
+
_anchorDiagnostic = available.length
|
|
105
|
+
? `anchors available: ${available.join(', ')}`
|
|
106
|
+
: `no n8n-owned anchor resident in require.cache (tried ${N8N_ANCHOR_PACKAGES.map((a) => a.name).join(', ')})`;
|
|
62
107
|
}
|
|
63
108
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* workflow runs, i.e. before langchain/zod are necessarily loaded into cache.
|
|
109
|
+
* Yields candidate requires in resolution priority order: require.main first
|
|
110
|
+
* (guarded for undefined), then EACH n8n-owned-tree anchor. Symmetric for every
|
|
111
|
+
* dependency — no bare-cache scan, no self-exclusion. The caller must try the
|
|
112
|
+
* requested dependency against each yielded require and stop at the first that
|
|
113
|
+
* loads it.
|
|
70
114
|
*/
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return undefined;
|
|
79
|
-
for (const key of Object.keys(cache)) {
|
|
80
|
-
if (!pathPattern.test(key))
|
|
81
|
-
continue;
|
|
82
|
-
if (excludeKey && excludeKey(key))
|
|
83
|
-
continue;
|
|
84
|
-
const entry = cache[key];
|
|
85
|
-
if (!entry)
|
|
86
|
-
continue;
|
|
87
|
-
const result = validate(entry.exports);
|
|
88
|
-
if (result !== undefined)
|
|
89
|
-
return result;
|
|
115
|
+
function* candidateRequires() {
|
|
116
|
+
if (require.main?.filename) {
|
|
117
|
+
try {
|
|
118
|
+
yield { req: (0, module_1.createRequire)(require.main.filename), source: 'require.main' };
|
|
119
|
+
}
|
|
120
|
+
catch (_e) {
|
|
121
|
+
// require.main resolution unavailable — fall through to anchors
|
|
90
122
|
}
|
|
91
123
|
}
|
|
92
|
-
|
|
93
|
-
// best-effort — require.cache introspection is not guaranteed across Node versions
|
|
94
|
-
}
|
|
95
|
-
return undefined;
|
|
124
|
+
yield* iterN8nAnchorRequires();
|
|
96
125
|
}
|
|
97
126
|
let _RuntimeDynamicStructuredTool;
|
|
98
127
|
let _runtimeZod;
|
|
99
128
|
let _langchainLoadError = null;
|
|
129
|
+
let _langchainDiagnostic = null;
|
|
100
130
|
let _zodLoadError = null;
|
|
101
131
|
let _zodDiagnostic = null;
|
|
102
132
|
function resolveDynamicStructuredTool() {
|
|
103
133
|
if (_RuntimeDynamicStructuredTool)
|
|
104
134
|
return _RuntimeDynamicStructuredTool;
|
|
105
|
-
const
|
|
106
|
-
|
|
135
|
+
const attempts = [];
|
|
136
|
+
for (const { req, source } of candidateRequires()) {
|
|
107
137
|
try {
|
|
108
|
-
const coreTools =
|
|
138
|
+
const coreTools = req('@langchain/core/tools');
|
|
109
139
|
if (typeof coreTools?.['DynamicStructuredTool'] === 'function') {
|
|
110
140
|
_RuntimeDynamicStructuredTool = coreTools['DynamicStructuredTool'];
|
|
141
|
+
_langchainDiagnostic = `resolved DynamicStructuredTool via ${source}`;
|
|
142
|
+
_langchainLoadError = null;
|
|
111
143
|
return _RuntimeDynamicStructuredTool;
|
|
112
144
|
}
|
|
145
|
+
attempts.push(`${source}: @langchain/core/tools lacked DynamicStructuredTool`);
|
|
113
146
|
}
|
|
114
147
|
catch (e) {
|
|
115
|
-
|
|
148
|
+
attempts.push(`${source}: ${e.message}`);
|
|
116
149
|
}
|
|
117
150
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const cached = findCachedExports(/[\\/]@langchain[\\/]core[\\/]/, (exports) => typeof exports['DynamicStructuredTool'] === 'function'
|
|
123
|
-
? exports['DynamicStructuredTool']
|
|
124
|
-
: undefined);
|
|
125
|
-
if (cached) {
|
|
126
|
-
_RuntimeDynamicStructuredTool = cached;
|
|
127
|
-
_langchainLoadError = null;
|
|
128
|
-
}
|
|
129
|
-
return _RuntimeDynamicStructuredTool;
|
|
151
|
+
_langchainLoadError = attempts.length
|
|
152
|
+
? attempts.join('; ')
|
|
153
|
+
: 'no n8n-owned resolution source available (require.main undefined, no anchor in cache)';
|
|
154
|
+
return undefined;
|
|
130
155
|
}
|
|
131
156
|
function resolveZod() {
|
|
132
157
|
if (_runtimeZod)
|
|
133
158
|
return _runtimeZod;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// Primary path: resolve zod starting from n8n's own main entry point's module tree
|
|
137
|
-
// (require.main). For a correctly-installed (non-isolated) setup this lands on n8n's
|
|
138
|
-
// top-level zod — the exact copy n8n's own normalizeToolSchema does `instanceof ZodType`
|
|
139
|
-
// against. Even under pnpm-strict isolation this is a DIFFERENT resolution path than this
|
|
140
|
-
// package's own local require('zod'), so it is far more likely to reach n8n's real copy
|
|
141
|
-
// (or fail cleanly) than to silently return this package's OWN bundled zod. Resolved
|
|
142
|
-
// lazily here (NOT at module top level) so node registration never triggers it — that
|
|
143
|
-
// laziness is the entire point of this fix (#2).
|
|
144
|
-
//
|
|
145
|
-
// Only anchor at require.main when it is actually defined. When it is undefined (ESM-
|
|
146
|
-
// launched n8n, worker_threads / queue-mode workers — exactly the deployment mode #2 was
|
|
147
|
-
// filed against), there is NO useful main to anchor at: falling back to `__filename` would
|
|
148
|
-
// anchor at THIS file (mautic's own runtime.js), whose `require.resolve('zod')` lands on
|
|
149
|
-
// this package's OWN bundled zod — the copy that fails n8n's `instanceof ZodType` check.
|
|
150
|
-
// So resolve-then-check the path and skip it if it points into this package's own tree,
|
|
151
|
-
// reusing the same exclusion substring as the cache-scan path below.
|
|
152
|
-
if (require.main?.filename) {
|
|
159
|
+
const attempts = [];
|
|
160
|
+
for (const { req, source } of candidateRequires()) {
|
|
153
161
|
try {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
162
|
+
const z = req('zod');
|
|
163
|
+
// Validate the exports look like zod via `ZodType` (the class n8n's
|
|
164
|
+
// normalizeToolSchema does `instanceof` against — the meaningful correctness
|
|
165
|
+
// signal) plus the `object` factory our schema-generator calls.
|
|
166
|
+
if (typeof z?.['ZodType'] === 'function' && typeof z?.['object'] === 'function') {
|
|
167
|
+
_runtimeZod = z;
|
|
168
|
+
_zodDiagnostic = `resolved zod via ${source}`;
|
|
169
|
+
_zodLoadError = null;
|
|
170
|
+
return _runtimeZod;
|
|
163
171
|
}
|
|
172
|
+
attempts.push(`${source}: module did not look like zod`);
|
|
164
173
|
}
|
|
165
174
|
catch (e) {
|
|
166
|
-
|
|
175
|
+
attempts.push(`${source}: ${e.message}`);
|
|
167
176
|
}
|
|
168
177
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (runtimeReq) {
|
|
174
|
-
try {
|
|
175
|
-
const resolvedPath = runtimeReq.resolve('zod');
|
|
176
|
-
if (!resolvedPath.includes('n8n-nodes-mautic-advanced')) {
|
|
177
|
-
_runtimeZod = runtimeReq('zod');
|
|
178
|
-
if (_runtimeZod) {
|
|
179
|
-
_zodDiagnostic = 'resolved zod via anchor';
|
|
180
|
-
_zodLoadError = null;
|
|
181
|
-
return _runtimeZod;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch (e) {
|
|
186
|
-
_zodLoadError = e.message;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// Tertiary path: scan require.cache for an already-resident zod once neither
|
|
190
|
-
// filesystem resolution reaches it (pnpm-strict-isolated installs). The path regex is
|
|
191
|
-
// narrowed to zod's own entry-point directories (lib/dist/index/v3/v4) so it never
|
|
192
|
-
// matches zod-adjacent package names (e.g. zod-to-json-schema). Validate the exports
|
|
193
|
-
// look like zod via `ZodType` (the class n8n's normalizeToolSchema does `instanceof`
|
|
194
|
-
// against — the meaningful correctness signal) plus the `object` factory our
|
|
195
|
-
// schema-generator calls.
|
|
196
|
-
//
|
|
197
|
-
// Exclusion guard: this package declares zod as a REAL dependency and imports it at
|
|
198
|
-
// node-registration time (schema-generator.ts), so this package's OWN bundled zod is
|
|
199
|
-
// ALWAYS resident in require.cache — potentially earlier in iteration order than n8n's.
|
|
200
|
-
// We specifically want a DIFFERENT copy (n8n's), because a self-bundled copy would fail
|
|
201
|
-
// n8n's `instanceof ZodType` class-identity check. So skip any cache key belonging to
|
|
202
|
-
// this package's own bundled zod. (The pathPattern has already matched the zod segment,
|
|
203
|
-
// so a package-name match on the key is sufficient and robust to both nested
|
|
204
|
-
// `.../n8n-nodes-mautic-advanced/node_modules/zod/...` and flatter install layouts.)
|
|
205
|
-
const cached = findCachedExports(/[\\/]zod[\\/](lib|dist|index|v3|v4)/, (exports) => typeof exports['ZodType'] === 'function' && typeof exports['object'] === 'function'
|
|
206
|
-
? exports
|
|
207
|
-
: undefined, (key) => key.includes('n8n-nodes-mautic-advanced'));
|
|
208
|
-
if (cached) {
|
|
209
|
-
_runtimeZod = cached;
|
|
210
|
-
_zodLoadError = null;
|
|
211
|
-
_zodDiagnostic = 'resolved zod via require.cache scan (pnpm-isolated install)';
|
|
212
|
-
}
|
|
213
|
-
return _runtimeZod;
|
|
178
|
+
_zodLoadError = attempts.length
|
|
179
|
+
? attempts.join('; ')
|
|
180
|
+
: 'no n8n-owned resolution source available (require.main undefined, no anchor in cache)';
|
|
181
|
+
return undefined;
|
|
214
182
|
}
|
|
215
183
|
// IMPORTANT: Proxy target MUST be `function () {}`, not `{}`.
|
|
216
184
|
// ECMAScript spec §10.5.13: a Proxy only has [[Construct]] if its target does.
|
|
@@ -220,9 +188,11 @@ exports.RuntimeDynamicStructuredTool = new Proxy(function () { }, {
|
|
|
220
188
|
construct(_target, args) {
|
|
221
189
|
const ctor = resolveDynamicStructuredTool();
|
|
222
190
|
if (!ctor) {
|
|
223
|
-
throw new Error(`[MauticAdvancedAiTools] Could not resolve LangChain's DynamicStructuredTool
|
|
224
|
-
`
|
|
225
|
-
|
|
191
|
+
throw new Error(`[MauticAdvancedAiTools] Could not resolve LangChain's DynamicStructuredTool ` +
|
|
192
|
+
`via require.main or an n8n-owned require.cache anchor. ` +
|
|
193
|
+
`Ensure @n8n/n8n-nodes-langchain is installed in n8n's node_modules.` +
|
|
194
|
+
(_anchorDiagnostic ? ` Anchor: ${_anchorDiagnostic}.` : '') +
|
|
195
|
+
(_langchainDiagnostic ? ` ${_langchainDiagnostic}.` : '') +
|
|
226
196
|
(_langchainLoadError ? ` Load error: ${_langchainLoadError}` : ''));
|
|
227
197
|
}
|
|
228
198
|
return new ctor(...args);
|
|
@@ -245,13 +215,10 @@ exports.runtimeZod = new Proxy({}, {
|
|
|
245
215
|
const z = resolveZod();
|
|
246
216
|
if (!z) {
|
|
247
217
|
throw new Error(`[MauticAdvancedAiTools] Could not resolve zod (accessing .${String(prop)}) ` +
|
|
248
|
-
`via require.main
|
|
249
|
-
`Ensure @n8n/nodes-langchain is installed in n8n's node_modules.` +
|
|
250
|
-
(
|
|
251
|
-
|
|
252
|
-
: _anchorDiagnostic
|
|
253
|
-
? ` Diagnostic: ${_anchorDiagnostic}`
|
|
254
|
-
: '') +
|
|
218
|
+
`via require.main or an n8n-owned require.cache anchor. ` +
|
|
219
|
+
`Ensure @n8n/n8n-nodes-langchain is installed in n8n's node_modules.` +
|
|
220
|
+
(_anchorDiagnostic ? ` Anchor: ${_anchorDiagnostic}.` : '') +
|
|
221
|
+
(_zodDiagnostic ? ` ${_zodDiagnostic}.` : '') +
|
|
255
222
|
(_zodLoadError ? ` Load error: ${_zodLoadError}` : ''));
|
|
256
223
|
}
|
|
257
224
|
return z[prop];
|
package/package.json
CHANGED