@n8n/expression-runtime 0.31.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/ARCHITECTURE.md +16 -42
  2. package/README.md +6 -3
  3. package/dist/bundle/runtime.esm.js +17 -17
  4. package/dist/bundle/runtime.esm.js.map +3 -3
  5. package/dist/bundle/runtime.iife.js +17 -17
  6. package/dist/bundle/runtime.iife.js.map +3 -3
  7. package/dist/cjs/bridge/quickjs-bridge.d.ts.map +1 -1
  8. package/dist/cjs/bridge/quickjs-bridge.js +13 -2
  9. package/dist/cjs/bridge/quickjs-bridge.js.map +1 -1
  10. package/dist/cjs/build.tsbuildinfo +1 -1
  11. package/dist/cjs/extensions/array-extensions.d.ts.map +1 -1
  12. package/dist/cjs/extensions/array-extensions.js +22 -22
  13. package/dist/cjs/extensions/array-extensions.js.map +1 -1
  14. package/dist/cjs/extensions/boolean-extensions.js +1 -1
  15. package/dist/cjs/extensions/boolean-extensions.js.map +1 -1
  16. package/dist/cjs/extensions/date-extensions.d.ts.map +1 -1
  17. package/dist/cjs/extensions/date-extensions.js +15 -15
  18. package/dist/cjs/extensions/date-extensions.js.map +1 -1
  19. package/dist/cjs/extensions/function-extensions.d.ts.map +1 -1
  20. package/dist/cjs/extensions/function-extensions.js +1 -1
  21. package/dist/cjs/extensions/function-extensions.js.map +1 -1
  22. package/dist/cjs/extensions/number-extensions.d.ts.map +1 -1
  23. package/dist/cjs/extensions/number-extensions.js +10 -10
  24. package/dist/cjs/extensions/number-extensions.js.map +1 -1
  25. package/dist/cjs/extensions/object-extensions.d.ts.map +1 -1
  26. package/dist/cjs/extensions/object-extensions.js +11 -11
  27. package/dist/cjs/extensions/object-extensions.js.map +1 -1
  28. package/dist/cjs/extensions/string-extensions.d.ts.map +1 -1
  29. package/dist/cjs/extensions/string-extensions.js +31 -31
  30. package/dist/cjs/extensions/string-extensions.js.map +1 -1
  31. package/dist/cjs/pool/idle-scaling-pool.d.ts.map +1 -1
  32. package/dist/cjs/pool/idle-scaling-pool.js +3 -1
  33. package/dist/cjs/pool/idle-scaling-pool.js.map +1 -1
  34. package/dist/cjs/pool/isolate-pool.d.ts.map +1 -1
  35. package/dist/cjs/pool/isolate-pool.js +4 -1
  36. package/dist/cjs/pool/isolate-pool.js.map +1 -1
  37. package/dist/cjs/types/bridge.d.ts +6 -0
  38. package/dist/cjs/types/bridge.d.ts.map +1 -1
  39. package/dist/cjs/types/bridge.js +1 -0
  40. package/dist/cjs/types/bridge.js.map +1 -1
  41. package/dist/esm/bridge/quickjs-bridge.d.ts.map +1 -1
  42. package/dist/esm/bridge/quickjs-bridge.js +13 -2
  43. package/dist/esm/bridge/quickjs-bridge.js.map +1 -1
  44. package/dist/esm/build.tsbuildinfo +1 -1
  45. package/dist/esm/extensions/array-extensions.d.ts.map +1 -1
  46. package/dist/esm/extensions/array-extensions.js +22 -22
  47. package/dist/esm/extensions/array-extensions.js.map +1 -1
  48. package/dist/esm/extensions/boolean-extensions.js +1 -1
  49. package/dist/esm/extensions/boolean-extensions.js.map +1 -1
  50. package/dist/esm/extensions/date-extensions.d.ts.map +1 -1
  51. package/dist/esm/extensions/date-extensions.js +15 -15
  52. package/dist/esm/extensions/date-extensions.js.map +1 -1
  53. package/dist/esm/extensions/function-extensions.d.ts.map +1 -1
  54. package/dist/esm/extensions/function-extensions.js +1 -1
  55. package/dist/esm/extensions/function-extensions.js.map +1 -1
  56. package/dist/esm/extensions/number-extensions.d.ts.map +1 -1
  57. package/dist/esm/extensions/number-extensions.js +10 -10
  58. package/dist/esm/extensions/number-extensions.js.map +1 -1
  59. package/dist/esm/extensions/object-extensions.d.ts.map +1 -1
  60. package/dist/esm/extensions/object-extensions.js +11 -11
  61. package/dist/esm/extensions/object-extensions.js.map +1 -1
  62. package/dist/esm/extensions/string-extensions.d.ts.map +1 -1
  63. package/dist/esm/extensions/string-extensions.js +31 -31
  64. package/dist/esm/extensions/string-extensions.js.map +1 -1
  65. package/dist/esm/pool/idle-scaling-pool.d.ts.map +1 -1
  66. package/dist/esm/pool/idle-scaling-pool.js +3 -1
  67. package/dist/esm/pool/idle-scaling-pool.js.map +1 -1
  68. package/dist/esm/pool/isolate-pool.d.ts.map +1 -1
  69. package/dist/esm/pool/isolate-pool.js +4 -1
  70. package/dist/esm/pool/isolate-pool.js.map +1 -1
  71. package/dist/esm/types/bridge.d.ts +6 -0
  72. package/dist/esm/types/bridge.d.ts.map +1 -1
  73. package/dist/esm/types/bridge.js +1 -0
  74. package/dist/esm/types/bridge.js.map +1 -1
  75. package/package.json +5 -4
package/ARCHITECTURE.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Expression Runtime Architecture
2
2
 
3
- This package provides a secure, isolated expression evaluation runtime that works across multiple execution environments (isolated-vm, Web Workers, and task runners).
3
+ This package provides a secure, isolated expression evaluation runtime that works across multiple execution environments (isolated-vm, QuickJS in WASM, and task runners).
4
4
 
5
5
  ## Design Goals
6
6
 
7
- 1. **Environment Agnostic**: Single codebase that works in Node.js (isolated-vm), browsers (Web Workers), and task runner processes
7
+ 1. **Environment Agnostic**: Single codebase that works in Node.js (isolated-vm), browsers (QuickJS in WASM), and task runner processes
8
8
  2. **Security**: Expressions run in isolated contexts with memory limits and timeouts
9
9
  3. **Performance**: Lazy data loading, code caching, and efficient data transfer
10
10
  4. **Observability**: Built-in metrics, traces, and logs
@@ -29,7 +29,7 @@ The architecture is split into three distinct layers:
29
29
  │ ┌────────────────▼───────────────────────────────┐ │
30
30
  │ │ Bridge (Layer 2) │ │
31
31
  │ │ - IsolatedVmBridge (Phase 1.1) │ │
32
- │ │ - WebWorkerBridge (Phase 2+) │ │
32
+ │ │ - QuickJsBridge (WASM) │ │
33
33
  │ │ - Task Runner Integration (TBD) │ │
34
34
  │ └────────────────┬───────────────────────────────┘ │
35
35
  │ │ IPC/Message Passing │
@@ -62,7 +62,7 @@ The architecture is split into three distinct layers:
62
62
  - **Libraries**: lodash, Luxon (bundled)
63
63
  - **No Node.js APIs**: Pure JavaScript only
64
64
 
65
- **Bundle**: IIFE format for isolated-vm, ESM for Web Workers
65
+ **Bundle**: IIFE format for both isolated-vm and QuickJS
66
66
 
67
67
  ### Layer 2: Bridge (Host Process)
68
68
 
@@ -73,7 +73,7 @@ The architecture is split into three distinct layers:
73
73
  **Key Components**:
74
74
  - **RuntimeBridge Interface**: Abstract interface for all bridge implementations
75
75
  - **IsolatedVmBridge**: Uses isolated-vm API for Node.js backend (Phase 1.1)
76
- - **WebWorkerBridge**: Uses postMessage API for browser (Phase 2+)
76
+ - **QuickJsBridge**: Uses quickjs-emscripten (WASM) for Node.js and the browser
77
77
  - **Task Runner Integration**: TBD - May use IsolatedVmBridge locally or direct evaluation (Phase 2+)
78
78
 
79
79
  **Responsibilities**:
@@ -175,24 +175,12 @@ class IsolatedVmBridge implements RuntimeBridge {
175
175
  }
176
176
  ```
177
177
 
178
- ### WebWorkerBridge (Browser Frontend)
178
+ ### QuickJsBridge (Browser Frontend)
179
179
 
180
- Uses Web Workers for browser-based isolation:
181
-
182
- ```typescript
183
- class WebWorkerBridge implements RuntimeBridge {
184
- private worker: Worker;
185
-
186
- async initialize(): Promise<void> {
187
- this.worker = new Worker('/runtime.worker.js');
188
- // Setup message handlers
189
- }
190
-
191
- async execute(code: string, dataId: string): Promise<unknown> {
192
- // Implementation...
193
- }
194
- }
195
- ```
180
+ The editor runs the same `QuickJsBridge` as Node.
181
+ The browser has no filesystem, so the host passes the runtime bundle in with the
182
+ `runtimeBundle` option. The vite stub in `packages/frontend/editor-ui/vite/` maps
183
+ `@n8n/expression-runtime` to the real bridge for the browser build.
196
184
 
197
185
  ### Task Runner Integration (TBD - Phase 2+)
198
186
 
@@ -294,7 +282,7 @@ packages/@n8n/expression-runtime/
294
282
  **Limitation**: Lazy loading requires **synchronous** callbacks from runtime to host. This works for:
295
283
  - ✅ **isolated-vm**: Uses `ivm.Reference` for true synchronous callbacks
296
284
  - ✅ **Node.js vm**: Direct synchronous function calls
297
- - ❌ **Web Workers**: postMessage is always async (see Known Limitations below)
285
+ - ✅ **QuickJS**: host functions are plain synchronous calls in the WASM context
298
286
 
299
287
  ### 3. Why Bundle the Runtime?
300
288
 
@@ -304,7 +292,7 @@ packages/@n8n/expression-runtime/
304
292
 
305
293
  ### 4. Why Abstract Bridge?
306
294
 
307
- **Future-Proofing**: Frontend will use Web Workers. Backend uses isolated-vm. Abstract bridge allows adding new environments without changing other layers.
295
+ **Portability**: The backend uses isolated-vm. The browser uses QuickJS in WASM. The abstract bridge allows adding new environments without changing other layers.
308
296
 
309
297
  **Testing**: Integration tests use `IsolatedVmBridge` directly (see `src/__tests__/integration.test.ts`).
310
298
 
@@ -334,23 +322,9 @@ const proxy = new Proxy({}, {
334
322
  - Direct synchronous function calls
335
323
  - Full lazy loading support (used for testing)
336
324
 
337
- 3. **Web Workers** ❌
338
- - `postMessage` is always async
339
- - **Phase 1 Limitation**: No lazy loading, must pre-fetch all data before evaluation
340
- - **Future Enhancement (Phase 2+)**: Explore `SharedArrayBuffer` + `Atomics` for synchronous data access
341
-
342
- ### Web Worker Support Roadmap
343
-
344
- **Phase 1** (Initial implementation):
345
- - WebWorkerBridge will pre-fetch all workflow data
346
- - Transfer complete data object to worker before evaluation
347
- - Works for small/medium datasets (< 50MB)
348
- - No lazy loading benefit
349
-
350
- **Phase 2+** (Future enhancement):
351
- - Investigate `SharedArrayBuffer` + `Atomics` for sync access
352
- - Or accept pre-fetching as the Web Worker approach
353
- - Decision based on real-world usage patterns
325
+ 3. **QuickJS (WASM)** ✅
326
+ - Host functions are plain synchronous calls in the QuickJS context
327
+ - Full lazy loading support, in Node.js and in the browser
354
328
 
355
329
  ### Security Boundaries
356
330
 
@@ -422,5 +396,5 @@ See observability package documentation for details.
422
396
  ## References
423
397
 
424
398
  - [isolated-vm GitHub](https://github.com/laverdet/isolated-vm)
425
- - [Web Workers MDN](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
399
+ - [quickjs-emscripten GitHub](https://github.com/justjake/quickjs-emscripten)
426
400
  - [n8n workflow package](../workflow/)
package/README.md CHANGED
@@ -11,10 +11,10 @@ Secure, isolated expression evaluation runtime for n8n workflows.
11
11
  - ✅ `IsolatedVmBridge`: V8 isolate management via `isolated-vm`
12
12
  - ✅ `ExpressionEvaluator`: tournament integration, expression code caching, isolate pooling
13
13
  - ✅ Workflow integration — default engine; `N8N_EXPRESSION_ENGINE=legacy` opts out
14
+ - ✅ Editor support — the `QuickJsBridge` runs in the browser; `N8N_EXPRESSION_ENGINE_FRONTEND=quickjs` opts in
14
15
  - ✅ Observability (metrics, traces, logs) wired up in `packages/cli`
15
16
 
16
17
  Coming later:
17
- - 🚧 Web Worker support (Phase 2+)
18
18
  - 🚧 Performance optimizations (Phase 3)
19
19
 
20
20
  ## Overview
@@ -23,9 +23,9 @@ This package provides a secure runtime for evaluating expressions in isolated co
23
23
 
24
24
  Currently supports:
25
25
  - **Node.js Backend**: Uses `isolated-vm` for V8 isolate-based isolation with lazy data loading
26
+ - **Browser Frontend**: Uses `QuickJsBridge` (QuickJS compiled to WASM); `N8N_EXPRESSION_ENGINE_FRONTEND=quickjs` opts in
26
27
 
27
28
  Future support (Phase 2+):
28
- - **Browser Frontend**: Will use Web Workers for browser-based isolation
29
29
  - **Task Runners**: Will use IPC for separate process isolation
30
30
 
31
31
  ## Features
@@ -170,7 +170,7 @@ interface RuntimeBridge {
170
170
  - Synchronous callbacks via ivm.Reference
171
171
  - Security wrappers (SafeObject, SafeError)
172
172
  - `E()` error handler for tournament-generated try-catch code
173
- - **WebWorkerBridge**: 🚧 For browser frontend (Web Workers) - Phase 2+
173
+ - **QuickJsBridge**: ✅ QuickJS in WASM, for the Node.js backend and the browser frontend
174
174
  - **Task Runner Integration**: 🚧 TBD - May use IsolatedVmBridge locally or direct evaluation - Phase 2+
175
175
 
176
176
  ## Configuration
@@ -195,6 +195,9 @@ In n8n, the evaluator is configured via `ExpressionEngineConfig` (`@n8n/config`)
195
195
  # Engine selection ('vm' is the default; 'legacy' opts out of isolation)
196
196
  N8N_EXPRESSION_ENGINE=vm
197
197
 
198
+ # Editor engine selection ('legacy' is the default; 'quickjs' runs the WASM engine in the browser)
199
+ N8N_EXPRESSION_ENGINE_FRONTEND=legacy
200
+
198
201
  # Isolate pool and code cache
199
202
  N8N_EXPRESSION_ENGINE_POOL_SIZE=1
200
203
  N8N_EXPRESSION_ENGINE_MAX_CODE_CACHE_SIZE=1024