n8n-nodes-mautic-advanced 1.3.7 → 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.
@@ -3,32 +3,224 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runtimeZod = exports.RuntimeDynamicStructuredTool = void 0;
4
4
  // nodes/MauticAdvanced/ai-tools/runtime.ts
5
5
  const module_1 = require("module");
6
- const ANCHOR_CANDIDATES = [
7
- // primary: @langchain/classic is a direct dep of @n8n/nodes-langchain, stable since n8n 2.4.x.
8
- // Its @langchain/core peerDep resolves to n8n's hoisted @langchain/core.
9
- '@langchain/classic/agents',
10
- // secondary: langchain package is in the n8n catalog and also has @langchain/core as peerDep.
11
- 'langchain/agents',
6
+ /**
7
+ * Runtime resolution of `zod` and `@langchain/core`'s DynamicStructuredTool.
8
+ *
9
+ * IMPORTANT: resolution must NEVER run at module-import time. n8n's
10
+ * node-directory-loader requires every node file in this package (including
11
+ * this one) purely to register node metadata, long before any workflow
12
+ * executes — and it aborts loading the ENTIRE package if any one file throws
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.
18
+ *
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.
46
+ */
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[\\/]/ },
12
55
  ];
13
- // require.resolve() works because n8n loads community nodes within its own module resolution
14
- // context. If future n8n isolates community node resolution, use
15
- // require.resolve(candidate, {paths: [n8nPackagePath]}) option.
16
- let runtimeRequire = null;
17
- const errors = [];
18
- for (const candidate of ANCHOR_CANDIDATES) {
56
+ let _anchorDiagnostic = null;
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;
19
76
  try {
20
- const resolved = require.resolve(candidate);
21
- runtimeRequire = (0, module_1.createRequire)(resolved);
22
- break;
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;
23
80
  }
24
- catch (e) {
25
- errors.push(`${candidate}: ${e.message}`);
81
+ catch (_e) {
82
+ // best-effort — require.cache introspection is not guaranteed across Node versions
83
+ cache = undefined;
26
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;
95
+ try {
96
+ const req = (0, module_1.createRequire)(key);
97
+ available.push(anchor.name);
98
+ yield { req, source: `anchor ${anchor.name}` };
99
+ }
100
+ catch (_e) {
101
+ // createRequire failed for this anchor — try the next
102
+ }
103
+ }
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(', ')})`;
107
+ }
108
+ /**
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.
114
+ */
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
122
+ }
123
+ }
124
+ yield* iterN8nAnchorRequires();
27
125
  }
28
- if (!runtimeRequire) {
29
- throw new Error(`[runtime.ts] Could not resolve LangChain anchor. Tried:\n${errors.join('\n')}\n` +
30
- `Ensure @n8n/nodes-langchain is installed in n8n's node_modules.`);
126
+ let _RuntimeDynamicStructuredTool;
127
+ let _runtimeZod;
128
+ let _langchainLoadError = null;
129
+ let _langchainDiagnostic = null;
130
+ let _zodLoadError = null;
131
+ let _zodDiagnostic = null;
132
+ function resolveDynamicStructuredTool() {
133
+ if (_RuntimeDynamicStructuredTool)
134
+ return _RuntimeDynamicStructuredTool;
135
+ const attempts = [];
136
+ for (const { req, source } of candidateRequires()) {
137
+ try {
138
+ const coreTools = req('@langchain/core/tools');
139
+ if (typeof coreTools?.['DynamicStructuredTool'] === 'function') {
140
+ _RuntimeDynamicStructuredTool = coreTools['DynamicStructuredTool'];
141
+ _langchainDiagnostic = `resolved DynamicStructuredTool via ${source}`;
142
+ _langchainLoadError = null;
143
+ return _RuntimeDynamicStructuredTool;
144
+ }
145
+ attempts.push(`${source}: @langchain/core/tools lacked DynamicStructuredTool`);
146
+ }
147
+ catch (e) {
148
+ attempts.push(`${source}: ${e.message}`);
149
+ }
150
+ }
151
+ _langchainLoadError = attempts.length
152
+ ? attempts.join('; ')
153
+ : 'no n8n-owned resolution source available (require.main undefined, no anchor in cache)';
154
+ return undefined;
155
+ }
156
+ function resolveZod() {
157
+ if (_runtimeZod)
158
+ return _runtimeZod;
159
+ const attempts = [];
160
+ for (const { req, source } of candidateRequires()) {
161
+ try {
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;
171
+ }
172
+ attempts.push(`${source}: module did not look like zod`);
173
+ }
174
+ catch (e) {
175
+ attempts.push(`${source}: ${e.message}`);
176
+ }
177
+ }
178
+ _zodLoadError = attempts.length
179
+ ? attempts.join('; ')
180
+ : 'no n8n-owned resolution source available (require.main undefined, no anchor in cache)';
181
+ return undefined;
31
182
  }
32
- const coreTools = runtimeRequire('@langchain/core/tools');
33
- exports.RuntimeDynamicStructuredTool = coreTools['DynamicStructuredTool'];
34
- exports.runtimeZod = runtimeRequire('zod');
183
+ // IMPORTANT: Proxy target MUST be `function () {}`, not `{}`.
184
+ // ECMAScript spec §10.5.13: a Proxy only has [[Construct]] if its target does.
185
+ // Plain objects lack [[Construct]], so `new Proxy({}, ...)` throws
186
+ // "is not a constructor" before the construct trap ever fires.
187
+ exports.RuntimeDynamicStructuredTool = new Proxy(function () { }, {
188
+ construct(_target, args) {
189
+ const ctor = resolveDynamicStructuredTool();
190
+ if (!ctor) {
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}.` : '') +
196
+ (_langchainLoadError ? ` Load error: ${_langchainLoadError}` : ''));
197
+ }
198
+ return new ctor(...args);
199
+ },
200
+ get(_target, prop) {
201
+ const ctor = resolveDynamicStructuredTool();
202
+ if (ctor) {
203
+ return ctor[prop];
204
+ }
205
+ return undefined;
206
+ },
207
+ });
208
+ exports.runtimeZod = new Proxy({}, {
209
+ get(_target, prop) {
210
+ // Guard: frameworks probe Symbol.toPrimitive, Symbol.toStringTag, .then
211
+ // (Promise thenable), and .constructor. Throwing on these causes
212
+ // misleading errors during structural inspection.
213
+ if (typeof prop === 'symbol' || prop === 'then' || prop === 'constructor')
214
+ return undefined;
215
+ const z = resolveZod();
216
+ if (!z) {
217
+ throw new Error(`[MauticAdvancedAiTools] Could not resolve zod (accessing .${String(prop)}) ` +
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}.` : '') +
222
+ (_zodLoadError ? ` Load error: ${_zodLoadError}` : ''));
223
+ }
224
+ return z[prop];
225
+ },
226
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-mautic-advanced",
3
- "version": "1.3.7",
3
+ "version": "1.3.10",
4
4
  "description": "Enhanced n8n node for Mautic with comprehensive API coverage including tags, campaigns, categories, and advanced contact management",
5
5
  "keywords": [
6
6
  "n8n",
@@ -72,5 +72,13 @@
72
72
  "@types/change-case": "^0.0.30",
73
73
  "change-case": "^5.4.4",
74
74
  "zod": "^3.22.0"
75
+ },
76
+ "peerDependencies": {
77
+ "@langchain/core": "*"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "@langchain/core": {
81
+ "optional": true
82
+ }
75
83
  }
76
84
  }