@n8n/expression-runtime 0.21.0 → 0.23.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.
- package/dist/cjs/bridge/bridge-messages.d.ts +8 -8
- package/dist/cjs/bridge/bridge-messages.js +234 -244
- package/dist/cjs/bridge/bridge-messages.js.map +1 -1
- package/dist/cjs/bridge/isolated-vm-bridge.d.ts.map +1 -1
- package/dist/cjs/bridge/isolated-vm-bridge.js +633 -643
- package/dist/cjs/bridge/isolated-vm-bridge.js.map +1 -1
- package/dist/cjs/build.tsbuildinfo +1 -1
- package/dist/cjs/evaluator/error-classification.js +15 -25
- package/dist/cjs/evaluator/error-classification.js.map +1 -1
- package/dist/cjs/evaluator/expression-evaluator.d.ts +7 -1
- package/dist/cjs/evaluator/expression-evaluator.d.ts.map +1 -1
- package/dist/cjs/evaluator/expression-evaluator.js +152 -155
- package/dist/cjs/evaluator/expression-evaluator.js.map +1 -1
- package/dist/cjs/evaluator/lru-cache.d.ts.map +1 -1
- package/dist/cjs/evaluator/lru-cache.js +35 -45
- package/dist/cjs/evaluator/lru-cache.js.map +1 -1
- package/dist/cjs/extensions/array-extensions.js +589 -599
- package/dist/cjs/extensions/array-extensions.js.map +1 -1
- package/dist/cjs/extensions/boolean-extensions.js +38 -48
- package/dist/cjs/extensions/boolean-extensions.js.map +1 -1
- package/dist/cjs/extensions/date-extensions.js +500 -510
- package/dist/cjs/extensions/date-extensions.js.map +1 -1
- package/dist/cjs/extensions/expression-extension-error.d.ts.map +1 -1
- package/dist/cjs/extensions/expression-extension-error.js +12 -22
- package/dist/cjs/extensions/expression-extension-error.js.map +1 -1
- package/dist/cjs/extensions/extend.js +162 -172
- package/dist/cjs/extensions/extend.js.map +1 -1
- package/dist/cjs/extensions/extensions.js +2 -12
- package/dist/cjs/extensions/function-extensions.d.ts +10 -18
- package/dist/cjs/extensions/function-extensions.d.ts.map +1 -1
- package/dist/cjs/extensions/function-extensions.js +78 -88
- package/dist/cjs/extensions/function-extensions.js.map +1 -1
- package/dist/cjs/extensions/number-extensions.js +225 -235
- package/dist/cjs/extensions/number-extensions.js.map +1 -1
- package/dist/cjs/extensions/object-extensions.js +286 -296
- package/dist/cjs/extensions/object-extensions.js.map +1 -1
- package/dist/cjs/extensions/string-extensions.d.ts +1 -1
- package/dist/cjs/extensions/string-extensions.d.ts.map +1 -1
- package/dist/cjs/extensions/string-extensions.js +758 -768
- package/dist/cjs/extensions/string-extensions.js.map +1 -1
- package/dist/cjs/extensions/utils.d.ts.map +1 -1
- package/dist/cjs/extensions/utils.js +28 -38
- package/dist/cjs/extensions/utils.js.map +1 -1
- package/dist/cjs/index.js +32 -42
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/observability/metrics.d.ts +27 -27
- package/dist/cjs/observability/metrics.d.ts.map +1 -1
- package/dist/cjs/observability/metrics.js +59 -69
- package/dist/cjs/observability/metrics.js.map +1 -1
- package/dist/cjs/observability/noop-provider.js +28 -38
- package/dist/cjs/observability/noop-provider.js.map +1 -1
- package/dist/cjs/pool/idle-scaling-pool.d.ts.map +1 -1
- package/dist/cjs/pool/idle-scaling-pool.js +134 -144
- package/dist/cjs/pool/idle-scaling-pool.js.map +1 -1
- package/dist/cjs/pool/isolate-pool.d.ts.map +1 -1
- package/dist/cjs/pool/isolate-pool.js +101 -111
- package/dist/cjs/pool/isolate-pool.js.map +1 -1
- package/dist/cjs/runtime/context.js +418 -428
- package/dist/cjs/runtime/context.js.map +1 -1
- package/dist/cjs/runtime/index.js +26 -36
- package/dist/cjs/runtime/index.js.map +1 -1
- package/dist/cjs/runtime/jmespath.js +72 -82
- package/dist/cjs/runtime/jmespath.js.map +1 -1
- package/dist/cjs/runtime/lazy-proxy.js +238 -248
- package/dist/cjs/runtime/lazy-proxy.js.map +1 -1
- package/dist/cjs/runtime/safe-globals.d.ts.map +1 -1
- package/dist/cjs/runtime/safe-globals.js +134 -144
- package/dist/cjs/runtime/safe-globals.js.map +1 -1
- package/dist/cjs/runtime/serialize.js +54 -64
- package/dist/cjs/runtime/serialize.js.map +1 -1
- package/dist/cjs/runtime/utils.js +20 -30
- package/dist/cjs/runtime/utils.js.map +1 -1
- package/dist/cjs/types/bridge.js +15 -25
- package/dist/cjs/types/bridge.js.map +1 -1
- package/dist/cjs/types/evaluator.d.ts +5 -1
- package/dist/cjs/types/evaluator.d.ts.map +1 -1
- package/dist/cjs/types/evaluator.js +35 -45
- package/dist/cjs/types/evaluator.js.map +1 -1
- package/dist/cjs/types/index.js +14 -24
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/runtime.d.ts.map +1 -1
- package/dist/cjs/types/runtime.js +27 -37
- package/dist/cjs/types/runtime.js.map +1 -1
- package/dist/esm/bridge/bridge-messages.d.ts +8 -8
- package/dist/esm/bridge/isolated-vm-bridge.d.ts.map +1 -1
- package/dist/esm/bridge/isolated-vm-bridge.js.map +1 -1
- package/dist/esm/build.tsbuildinfo +1 -1
- package/dist/esm/evaluator/expression-evaluator.d.ts +7 -1
- package/dist/esm/evaluator/expression-evaluator.d.ts.map +1 -1
- package/dist/esm/evaluator/expression-evaluator.js +8 -1
- package/dist/esm/evaluator/expression-evaluator.js.map +1 -1
- package/dist/esm/evaluator/lru-cache.d.ts.map +1 -1
- package/dist/esm/evaluator/lru-cache.js.map +1 -1
- package/dist/esm/extensions/expression-extension-error.d.ts.map +1 -1
- package/dist/esm/extensions/function-extensions.d.ts +10 -18
- package/dist/esm/extensions/function-extensions.d.ts.map +1 -1
- package/dist/esm/extensions/number-extensions.js.map +1 -1
- package/dist/esm/extensions/string-extensions.d.ts +1 -1
- package/dist/esm/extensions/string-extensions.d.ts.map +1 -1
- package/dist/esm/extensions/string-extensions.js.map +1 -1
- package/dist/esm/extensions/utils.d.ts.map +1 -1
- package/dist/esm/observability/metrics.d.ts +27 -27
- package/dist/esm/observability/metrics.d.ts.map +1 -1
- package/dist/esm/pool/idle-scaling-pool.d.ts.map +1 -1
- package/dist/esm/pool/idle-scaling-pool.js.map +1 -1
- package/dist/esm/pool/isolate-pool.d.ts.map +1 -1
- package/dist/esm/pool/isolate-pool.js.map +1 -1
- package/dist/esm/runtime/lazy-proxy.js.map +1 -1
- package/dist/esm/runtime/safe-globals.d.ts.map +1 -1
- package/dist/esm/types/evaluator.d.ts +5 -1
- package/dist/esm/types/evaluator.d.ts.map +1 -1
- package/dist/esm/types/evaluator.js.map +1 -1
- package/dist/esm/types/runtime.d.ts.map +1 -1
- package/dist/esm/types/runtime.js.map +1 -1
- package/package.json +7 -6
|
@@ -1,449 +1,439 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildContext = buildContext;
|
|
4
|
+
const luxon_1 = require("luxon");
|
|
5
|
+
const extend_1 = require("../extensions/extend");
|
|
6
|
+
const function_extensions_1 = require("../extensions/function-extensions");
|
|
7
|
+
const safe_globals_1 = require("./safe-globals");
|
|
8
|
+
const lazy_proxy_1 = require("./lazy-proxy");
|
|
9
|
+
const jmespath_1 = require("./jmespath");
|
|
10
|
+
const utils_1 = require("./utils");
|
|
11
|
+
// Pre-create safe error subclass wrappers (reused across evaluations)
|
|
12
|
+
const SafeTypeError = (0, safe_globals_1.createSafeErrorSubclass)(TypeError);
|
|
13
|
+
const SafeSyntaxError = (0, safe_globals_1.createSafeErrorSubclass)(SyntaxError);
|
|
14
|
+
const SafeEvalError = (0, safe_globals_1.createSafeErrorSubclass)(EvalError);
|
|
15
|
+
const SafeRangeError = (0, safe_globals_1.createSafeErrorSubclass)(RangeError);
|
|
16
|
+
const SafeReferenceError = (0, safe_globals_1.createSafeErrorSubclass)(ReferenceError);
|
|
17
|
+
const SafeURIError = (0, safe_globals_1.createSafeErrorSubclass)(URIError);
|
|
18
|
+
/**
|
|
19
|
+
* Maps proxy property names on `$('NodeName')` to the typed-RPC discriminator
|
|
20
|
+
* the bridge dispatches on. Single source of truth for the synthetic proxy's
|
|
21
|
+
* `get`/`has` traps and the `sendNodeMethod` envelope builder. Adding a new
|
|
22
|
+
* `getNode*` schema to `BridgeMessage` lets you wire a new method here in
|
|
23
|
+
* one place; `satisfies` catches typos in the discriminator string.
|
|
24
|
+
*/
|
|
25
|
+
const NODE_RPC_TYPES = {
|
|
26
|
+
first: 'getNodeFirst',
|
|
27
|
+
last: 'getNodeLast',
|
|
28
|
+
all: 'getNodeAll',
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Same shape as `NODE_RPC_TYPES`, for the current node's `$input` proxy.
|
|
32
|
+
* Discriminators are `getInput*` and the host enforces zero-arg invocation.
|
|
33
|
+
*/
|
|
34
|
+
const INPUT_RPC_TYPES = {
|
|
35
|
+
first: 'getInputFirst',
|
|
36
|
+
last: 'getInputLast',
|
|
37
|
+
all: 'getInputAll',
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Build a fresh, closure-scoped evaluation context.
|
|
41
|
+
*
|
|
42
|
+
* This function creates a context object that contains everything needed to
|
|
43
|
+
* evaluate an expression, without touching any global mutable state
|
|
44
|
+
* (except luxon's Settings.defaultZone which is process-wide).
|
|
45
|
+
*
|
|
46
|
+
* The returned object is used as tournament's `this` context in the
|
|
47
|
+
* evalClosureSync wrapper.
|
|
48
|
+
*
|
|
49
|
+
* @param callbacks - Bridge callbacks (data-access primitives + typed RPCs)
|
|
50
|
+
* @param timezone - Optional IANA timezone string
|
|
51
|
+
* @returns A context object with all workflow data, proxies, and builtins
|
|
52
|
+
*/
|
|
53
|
+
function buildContext(callbacks, timezone) {
|
|
54
|
+
if (timezone && !luxon_1.IANAZone.isValidZone(timezone)) {
|
|
55
|
+
throw new Error(`Invalid timezone: "${timezone}"`);
|
|
5
56
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Object.defineProperty(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
luxon_1.Settings.defaultZone = timezone ?? 'system';
|
|
58
|
+
const target = {};
|
|
59
|
+
// __sanitize must be on the context because PrototypeSanitizer generates:
|
|
60
|
+
// obj[this.__sanitize(expr)] where 'this' is the context (via .call(ctx) wrapping)
|
|
61
|
+
// Use a non-writable property descriptor so override attempts throw instead of silently succeeding.
|
|
62
|
+
Object.defineProperty(target, '__sanitize', {
|
|
63
|
+
get: () => safe_globals_1.__sanitize,
|
|
64
|
+
set: () => {
|
|
65
|
+
throw new safe_globals_1.ExpressionError('Cannot override "__sanitize" due to security concerns');
|
|
66
|
+
},
|
|
67
|
+
enumerable: false,
|
|
68
|
+
configurable: false,
|
|
69
|
+
});
|
|
70
|
+
// -------------------------------------------------------------------------
|
|
71
|
+
// Create DateTime values inside the isolate (not lazy-loaded from host,
|
|
72
|
+
// because host-side DateTime objects lose their prototype crossing the
|
|
73
|
+
// boundary). The isolate has its own luxon with the correct timezone
|
|
74
|
+
// already set via Settings.defaultZone above.
|
|
75
|
+
// -------------------------------------------------------------------------
|
|
76
|
+
target.$now = luxon_1.DateTime.now();
|
|
77
|
+
target.$today = luxon_1.DateTime.now().set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
|
78
|
+
// -------------------------------------------------------------------------
|
|
79
|
+
// Expose standalone functions (min, max, average, numberList, zip, $ifEmpty, etc.)
|
|
80
|
+
// -------------------------------------------------------------------------
|
|
81
|
+
Object.assign(target, function_extensions_1.extendedFunctions);
|
|
82
|
+
// -------------------------------------------------------------------------
|
|
83
|
+
// Expose globals on target so tournament's "x in this ? this.x : global.x"
|
|
84
|
+
// pattern resolves them correctly (tournament checks ctx before global)
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
86
|
+
target.DateTime = globalThis.DateTime;
|
|
87
|
+
target.Duration = globalThis.Duration;
|
|
88
|
+
target.Interval = globalThis.Interval;
|
|
89
|
+
// Expose extend/extendOptional on target so tournament's "x in this ? this.x : global.x"
|
|
90
|
+
// pattern resolves them correctly when the VM checks ctx first
|
|
91
|
+
target.extend = extend_1.extend;
|
|
92
|
+
target.extendOptional = extend_1.extendOptional;
|
|
93
|
+
// Expose jmespath helpers in-isolate so they shadow the host-side
|
|
94
|
+
// `data.$jmesPath` / `data.$jmespath` from WorkflowDataProxy. Same rationale
|
|
95
|
+
// as extend / extendOptional above: keeping these in-isolate removes them
|
|
96
|
+
// from the bridge's reachable host-callable surface.
|
|
97
|
+
target.$jmesPath = jmespath_1.jmesPath;
|
|
98
|
+
target.$jmespath = jmespath_1.jmesPath;
|
|
99
|
+
// Wire builtins so tournament's VariablePolyfill resolves them from ctx
|
|
100
|
+
initializeBuiltins(target);
|
|
101
|
+
// $item(i) → single lazy proxy at ['$item', i]; the host bridge navigates
|
|
102
|
+
// data.$item(i).<rest> safely (see isolated-vm-bridge getValueAtPath),
|
|
103
|
+
// exposing the full accessor surface ($json, $binary, $node, $nodeId, ...).
|
|
104
|
+
target.$item = function (itemIndex) {
|
|
105
|
+
return (0, lazy_proxy_1.createDeepLazyProxy)(['$item', String(itemIndex)], undefined, callbacks);
|
|
47
106
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
set: () => {
|
|
74
|
-
throw new safe_globals_1.ExpressionError('Cannot override "__sanitize" due to security concerns');
|
|
75
|
-
},
|
|
76
|
-
enumerable: false,
|
|
77
|
-
configurable: false,
|
|
78
|
-
});
|
|
79
|
-
// -------------------------------------------------------------------------
|
|
80
|
-
// Create DateTime values inside the isolate (not lazy-loaded from host,
|
|
81
|
-
// because host-side DateTime objects lose their prototype crossing the
|
|
82
|
-
// boundary). The isolate has its own luxon with the correct timezone
|
|
83
|
-
// already set via Settings.defaultZone above.
|
|
84
|
-
// -------------------------------------------------------------------------
|
|
85
|
-
target.$now = luxon_1.DateTime.now();
|
|
86
|
-
target.$today = luxon_1.DateTime.now().set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
|
87
|
-
// -------------------------------------------------------------------------
|
|
88
|
-
// Expose standalone functions (min, max, average, numberList, zip, $ifEmpty, etc.)
|
|
89
|
-
// -------------------------------------------------------------------------
|
|
90
|
-
Object.assign(target, function_extensions_1.extendedFunctions);
|
|
91
|
-
// -------------------------------------------------------------------------
|
|
92
|
-
// Expose globals on target so tournament's "x in this ? this.x : global.x"
|
|
93
|
-
// pattern resolves them correctly (tournament checks ctx before global)
|
|
94
|
-
// -------------------------------------------------------------------------
|
|
95
|
-
target.DateTime = globalThis.DateTime;
|
|
96
|
-
target.Duration = globalThis.Duration;
|
|
97
|
-
target.Interval = globalThis.Interval;
|
|
98
|
-
// Expose extend/extendOptional on target so tournament's "x in this ? this.x : global.x"
|
|
99
|
-
// pattern resolves them correctly when the VM checks ctx first
|
|
100
|
-
target.extend = extend_1.extend;
|
|
101
|
-
target.extendOptional = extend_1.extendOptional;
|
|
102
|
-
// Expose jmespath helpers in-isolate so they shadow the host-side
|
|
103
|
-
// `data.$jmesPath` / `data.$jmespath` from WorkflowDataProxy. Same rationale
|
|
104
|
-
// as extend / extendOptional above: keeping these in-isolate removes them
|
|
105
|
-
// from the bridge's reachable host-callable surface.
|
|
106
|
-
target.$jmesPath = jmespath_1.jmesPath;
|
|
107
|
-
target.$jmespath = jmespath_1.jmesPath;
|
|
108
|
-
// Wire builtins so tournament's VariablePolyfill resolves them from ctx
|
|
109
|
-
initializeBuiltins(target);
|
|
110
|
-
// $item(i) → single lazy proxy at ['$item', i]; the host bridge navigates
|
|
111
|
-
// data.$item(i).<rest> safely (see isolated-vm-bridge getValueAtPath),
|
|
112
|
-
// exposing the full accessor surface ($json, $binary, $node, $nodeId, ...).
|
|
113
|
-
target.$item = function (itemIndex) {
|
|
114
|
-
return (0, lazy_proxy_1.createDeepLazyProxy)(['$item', String(itemIndex)], undefined, callbacks);
|
|
115
|
-
};
|
|
116
|
-
// $() function for accessing other nodes.
|
|
117
|
-
//
|
|
118
|
-
// The returned object is a Proxy whose `get` trap intercepts properties
|
|
119
|
-
// that have a typed RPC (e.g. `.first` → `getNodeFirst`) and routes them
|
|
120
|
-
// through the `callHost` envelope. Everything else (properties like
|
|
121
|
-
// `.params`, `.json`, and methods that don't yet have a typed RPC) is
|
|
122
|
-
// read from an underlying lazy proxy via explicit delegation.
|
|
123
|
-
//
|
|
124
|
-
// Important: the synthetic Proxy's *target* is a plain `{}` rather than
|
|
125
|
-
// the lazy proxy itself. Nesting one Proxy inside another causes V8 to
|
|
126
|
-
// run invariant checks (`[[OwnPropertyKeys]]`, descriptor consistency)
|
|
127
|
-
// against the inner target, which would trigger the lazy proxy's
|
|
128
|
-
// `ownKeys` trap and an unnecessary `getValueAtPath` round-trip for the
|
|
129
|
-
// whole node's keys. Using `{}` as the target keeps those checks cheap;
|
|
130
|
-
// the lazy proxy lives in closure and is only consulted on demand.
|
|
131
|
-
//
|
|
132
|
-
// As more typed RPCs are added, more cases land in this trap.
|
|
133
|
-
// The `has` trap mirrors the `get` trap for typed-RPC names so that
|
|
134
|
-
// tournament's `"first" in this.$('Foo')` check resolves true even though
|
|
135
|
-
// the inner target is empty.
|
|
136
|
-
target.$ = function (nodeName) {
|
|
137
|
-
const lazyProxy = (0, lazy_proxy_1.createDeepLazyProxy)(['$', nodeName], undefined, callbacks);
|
|
138
|
-
const sendNodeMethod = (type) => {
|
|
139
|
-
return (branchIndex, runIndex) => {
|
|
140
|
-
const result = callbacks.callHost.applySync(null, [{ type, nodeName, branchIndex, runIndex }], { arguments: { copy: true }, result: { copy: true } });
|
|
141
|
-
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
142
|
-
return result;
|
|
143
|
-
};
|
|
144
|
-
};
|
|
145
|
-
// Paired-item cluster: `.pairedItem(idx?)`, `.itemMatching(idx)`,
|
|
146
|
-
// `.item`. Each surface form has its own typed-RPC discriminator
|
|
147
|
-
// (`getNodePairedItem` / `getNodeItemMatching` / `getNodeItem`)
|
|
148
|
-
// because the host's resolver closes over the literal property
|
|
149
|
-
// name to pick error messages and getter-vs-method semantics.
|
|
150
|
-
// The bridge handler for each reads the matching property name.
|
|
151
|
-
const sendPairedRpc = (type, itemIndex) => {
|
|
152
|
-
const result = callbacks.callHost.applySync(null, [{ type, nodeName, itemIndex }], {
|
|
153
|
-
arguments: { copy: true },
|
|
154
|
-
result: { copy: true },
|
|
155
|
-
});
|
|
107
|
+
// $() function for accessing other nodes.
|
|
108
|
+
//
|
|
109
|
+
// The returned object is a Proxy whose `get` trap intercepts properties
|
|
110
|
+
// that have a typed RPC (e.g. `.first` → `getNodeFirst`) and routes them
|
|
111
|
+
// through the `callHost` envelope. Everything else (properties like
|
|
112
|
+
// `.params`, `.json`, and methods that don't yet have a typed RPC) is
|
|
113
|
+
// read from an underlying lazy proxy via explicit delegation.
|
|
114
|
+
//
|
|
115
|
+
// Important: the synthetic Proxy's *target* is a plain `{}` rather than
|
|
116
|
+
// the lazy proxy itself. Nesting one Proxy inside another causes V8 to
|
|
117
|
+
// run invariant checks (`[[OwnPropertyKeys]]`, descriptor consistency)
|
|
118
|
+
// against the inner target, which would trigger the lazy proxy's
|
|
119
|
+
// `ownKeys` trap and an unnecessary `getValueAtPath` round-trip for the
|
|
120
|
+
// whole node's keys. Using `{}` as the target keeps those checks cheap;
|
|
121
|
+
// the lazy proxy lives in closure and is only consulted on demand.
|
|
122
|
+
//
|
|
123
|
+
// As more typed RPCs are added, more cases land in this trap.
|
|
124
|
+
// The `has` trap mirrors the `get` trap for typed-RPC names so that
|
|
125
|
+
// tournament's `"first" in this.$('Foo')` check resolves true even though
|
|
126
|
+
// the inner target is empty.
|
|
127
|
+
target.$ = function (nodeName) {
|
|
128
|
+
const lazyProxy = (0, lazy_proxy_1.createDeepLazyProxy)(['$', nodeName], undefined, callbacks);
|
|
129
|
+
const sendNodeMethod = (type) => {
|
|
130
|
+
return (branchIndex, runIndex) => {
|
|
131
|
+
const result = callbacks.callHost.applySync(null, [{ type, nodeName, branchIndex, runIndex }], { arguments: { copy: true }, result: { copy: true } });
|
|
156
132
|
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
157
133
|
return result;
|
|
158
134
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return sendNodeMethod(NODE_RPC_TYPES[prop]);
|
|
171
|
-
}
|
|
172
|
-
if (prop === 'pairedItem') {
|
|
173
|
-
return (itemIndex) => sendPairedRpc('getNodePairedItem', itemIndex);
|
|
174
|
-
}
|
|
175
|
-
if (prop === 'itemMatching') {
|
|
176
|
-
return (itemIndex) => sendPairedRpc('getNodeItemMatching', itemIndex);
|
|
177
|
-
}
|
|
178
|
-
if (prop === 'item') {
|
|
179
|
-
// Getter form: invoke immediately, return the value.
|
|
180
|
-
return sendGetNodeItem();
|
|
181
|
-
}
|
|
182
|
-
// Everything else: delegate to the lazy proxy. The lazy proxy's
|
|
183
|
-
// own `get` trap handles caching, host fetching, and metadata.
|
|
184
|
-
return lazyProxy[prop];
|
|
185
|
-
},
|
|
186
|
-
has(_emptyTarget, prop) {
|
|
187
|
-
return ((0, utils_1.isKeyOf)(NODE_RPC_TYPES, prop) ||
|
|
188
|
-
prop === 'pairedItem' ||
|
|
189
|
-
prop === 'itemMatching' ||
|
|
190
|
-
prop === 'item' ||
|
|
191
|
-
prop in lazyProxy);
|
|
192
|
-
},
|
|
135
|
+
};
|
|
136
|
+
// Paired-item cluster: `.pairedItem(idx?)`, `.itemMatching(idx)`,
|
|
137
|
+
// `.item`. Each surface form has its own typed-RPC discriminator
|
|
138
|
+
// (`getNodePairedItem` / `getNodeItemMatching` / `getNodeItem`)
|
|
139
|
+
// because the host's resolver closes over the literal property
|
|
140
|
+
// name to pick error messages and getter-vs-method semantics.
|
|
141
|
+
// The bridge handler for each reads the matching property name.
|
|
142
|
+
const sendPairedRpc = (type, itemIndex) => {
|
|
143
|
+
const result = callbacks.callHost.applySync(null, [{ type, nodeName, itemIndex }], {
|
|
144
|
+
arguments: { copy: true },
|
|
145
|
+
result: { copy: true },
|
|
193
146
|
});
|
|
147
|
+
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
148
|
+
return result;
|
|
194
149
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return () => {
|
|
203
|
-
const result = callbacks.callHost.applySync(null, [{ type }], {
|
|
204
|
-
arguments: { copy: true },
|
|
205
|
-
result: { copy: true },
|
|
206
|
-
});
|
|
207
|
-
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
208
|
-
return result;
|
|
209
|
-
};
|
|
150
|
+
const sendGetNodeItem = () => {
|
|
151
|
+
const result = callbacks.callHost.applySync(null, [{ type: 'getNodeItem', nodeName }], {
|
|
152
|
+
arguments: { copy: true },
|
|
153
|
+
result: { copy: true },
|
|
154
|
+
});
|
|
155
|
+
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
156
|
+
return result;
|
|
210
157
|
};
|
|
211
|
-
|
|
158
|
+
return new Proxy({}, {
|
|
212
159
|
get(_emptyTarget, prop) {
|
|
213
|
-
if ((0, utils_1.isKeyOf)(
|
|
214
|
-
return
|
|
215
|
-
|
|
160
|
+
if ((0, utils_1.isKeyOf)(NODE_RPC_TYPES, prop)) {
|
|
161
|
+
return sendNodeMethod(NODE_RPC_TYPES[prop]);
|
|
162
|
+
}
|
|
163
|
+
if (prop === 'pairedItem') {
|
|
164
|
+
return (itemIndex) => sendPairedRpc('getNodePairedItem', itemIndex);
|
|
165
|
+
}
|
|
166
|
+
if (prop === 'itemMatching') {
|
|
167
|
+
return (itemIndex) => sendPairedRpc('getNodeItemMatching', itemIndex);
|
|
168
|
+
}
|
|
169
|
+
if (prop === 'item') {
|
|
170
|
+
// Getter form: invoke immediately, return the value.
|
|
171
|
+
return sendGetNodeItem();
|
|
172
|
+
}
|
|
173
|
+
// Everything else: delegate to the lazy proxy. The lazy proxy's
|
|
174
|
+
// own `get` trap handles caching, host fetching, and metadata.
|
|
175
|
+
return lazyProxy[prop];
|
|
216
176
|
},
|
|
217
177
|
has(_emptyTarget, prop) {
|
|
218
|
-
return (0, utils_1.isKeyOf)(
|
|
178
|
+
return ((0, utils_1.isKeyOf)(NODE_RPC_TYPES, prop) ||
|
|
179
|
+
prop === 'pairedItem' ||
|
|
180
|
+
prop === 'itemMatching' ||
|
|
181
|
+
prop === 'item' ||
|
|
182
|
+
prop in lazyProxy);
|
|
219
183
|
},
|
|
220
184
|
});
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// expression used. `name` is forwarded as-is — host validates it
|
|
235
|
-
// (required, regex-restricted) and emits a structured `ExpressionError`
|
|
236
|
-
// on bad input.
|
|
237
|
-
const sendFromAi = (name, description, valueType, defaultValue) => {
|
|
238
|
-
const result = callbacks.callHost.applySync(null, [{ type: 'fromAi', name, description, valueType, defaultValue }], { arguments: { copy: true }, result: { copy: true } });
|
|
239
|
-
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
240
|
-
return result;
|
|
241
|
-
};
|
|
242
|
-
target.$fromAI = sendFromAi;
|
|
243
|
-
target.$fromAi = sendFromAi;
|
|
244
|
-
target.$fromai = sendFromAi;
|
|
245
|
-
// $evaluateExpression — recursive expression evaluator. Forwards the
|
|
246
|
-
// inner expression string to the host, which re-invokes the engine.
|
|
247
|
-
// Under the VM engine this re-enters the bridge on a fresh evaluation;
|
|
248
|
-
// the legacy engine handles it inline.
|
|
249
|
-
target.$evaluateExpression = (expression, itemIndex) => {
|
|
250
|
-
const result = callbacks.callHost.applySync(null, [{ type: 'evaluateExpression', expression, itemIndex }], { arguments: { copy: true }, result: { copy: true } });
|
|
251
|
-
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
252
|
-
return result;
|
|
253
|
-
};
|
|
254
|
-
// $getPairedItem — walks the paired-item ancestry chain back to the
|
|
255
|
-
// named upstream node. The host validates the structural shape of
|
|
256
|
-
// `incomingSourceData` and `initialPairedItem` via the typed-RPC
|
|
257
|
-
// schema; bad input surfaces as a schema-parse error sentinel rather
|
|
258
|
-
// than a host throw, keeping the protocol surface tight.
|
|
259
|
-
target.$getPairedItem = (destinationNodeName, incomingSourceData, initialPairedItem) => {
|
|
260
|
-
const result = callbacks.callHost.applySync(null, [
|
|
261
|
-
{
|
|
262
|
-
type: 'getPairedItem',
|
|
263
|
-
destinationNodeName,
|
|
264
|
-
incomingSourceData,
|
|
265
|
-
initialPairedItem,
|
|
266
|
-
},
|
|
267
|
-
], { arguments: { copy: true }, result: { copy: true } });
|
|
185
|
+
};
|
|
186
|
+
// $input — current-node input proxy. Same synthetic-Proxy pattern as
|
|
187
|
+
// `target.$()`: intercept the typed-RPC method names (`first`, `last`,
|
|
188
|
+
// `all`, all zero-arg per the host's `WorkflowDataProxy`), delegate
|
|
189
|
+
// everything else (notably the `.item` getter and `.params` / `.context`
|
|
190
|
+
// properties) to a lazy proxy on `$input`.
|
|
191
|
+
const lazyInputProxy = (0, lazy_proxy_1.createDeepLazyProxy)(['$input'], undefined, callbacks);
|
|
192
|
+
const sendInputMethod = (type) => {
|
|
193
|
+
return () => {
|
|
194
|
+
const result = callbacks.callHost.applySync(null, [{ type }], {
|
|
195
|
+
arguments: { copy: true },
|
|
196
|
+
result: { copy: true },
|
|
197
|
+
});
|
|
268
198
|
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
269
199
|
return result;
|
|
270
200
|
};
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
201
|
+
};
|
|
202
|
+
target.$input = new Proxy({}, {
|
|
203
|
+
get(_emptyTarget, prop) {
|
|
204
|
+
if ((0, utils_1.isKeyOf)(INPUT_RPC_TYPES, prop))
|
|
205
|
+
return sendInputMethod(INPUT_RPC_TYPES[prop]);
|
|
206
|
+
return lazyInputProxy[prop];
|
|
207
|
+
},
|
|
208
|
+
has(_emptyTarget, prop) {
|
|
209
|
+
return (0, utils_1.isKeyOf)(INPUT_RPC_TYPES, prop) || prop in lazyInputProxy;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
// $items — global accessor for a node's execution data. Unlike $() and
|
|
213
|
+
// $input this is a plain typed-RPC function (not a synthetic Proxy):
|
|
214
|
+
// the host enforces nothing structural here, the schema validates the
|
|
215
|
+
// args, and the host's `WorkflowDataProxy.$items` applies its own
|
|
216
|
+
// defaults when fields are undefined.
|
|
217
|
+
target.$items = (nodeName, outputIndex, runIndex) => {
|
|
218
|
+
const result = callbacks.callHost.applySync(null, [{ type: 'getItems', nodeName, outputIndex, runIndex }], { arguments: { copy: true }, result: { copy: true } });
|
|
219
|
+
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
220
|
+
return result;
|
|
221
|
+
};
|
|
222
|
+
// $fromAI / $fromAi / $fromai — AI-builder placeholder accessor.
|
|
223
|
+
// All three host aliases route to the same `handleFromAi` callback;
|
|
224
|
+
// the typed-RPC envelope is identical regardless of which name the
|
|
225
|
+
// expression used. `name` is forwarded as-is — host validates it
|
|
226
|
+
// (required, regex-restricted) and emits a structured `ExpressionError`
|
|
227
|
+
// on bad input.
|
|
228
|
+
const sendFromAi = (name, description, valueType, defaultValue) => {
|
|
229
|
+
const result = callbacks.callHost.applySync(null, [{ type: 'fromAi', name, description, valueType, defaultValue }], { arguments: { copy: true }, result: { copy: true } });
|
|
230
|
+
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
231
|
+
return result;
|
|
232
|
+
};
|
|
233
|
+
target.$fromAI = sendFromAi;
|
|
234
|
+
target.$fromAi = sendFromAi;
|
|
235
|
+
target.$fromai = sendFromAi;
|
|
236
|
+
// $evaluateExpression — recursive expression evaluator. Forwards the
|
|
237
|
+
// inner expression string to the host, which re-invokes the engine.
|
|
238
|
+
// Under the VM engine this re-enters the bridge on a fresh evaluation;
|
|
239
|
+
// the legacy engine handles it inline.
|
|
240
|
+
target.$evaluateExpression = (expression, itemIndex) => {
|
|
241
|
+
const result = callbacks.callHost.applySync(null, [{ type: 'evaluateExpression', expression, itemIndex }], { arguments: { copy: true }, result: { copy: true } });
|
|
242
|
+
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
243
|
+
return result;
|
|
244
|
+
};
|
|
245
|
+
// $getPairedItem — walks the paired-item ancestry chain back to the
|
|
246
|
+
// named upstream node. The host validates the structural shape of
|
|
247
|
+
// `incomingSourceData` and `initialPairedItem` via the typed-RPC
|
|
248
|
+
// schema; bad input surfaces as a schema-parse error sentinel rather
|
|
249
|
+
// than a host throw, keeping the protocol surface tight.
|
|
250
|
+
target.$getPairedItem = (destinationNodeName, incomingSourceData, initialPairedItem) => {
|
|
251
|
+
const result = callbacks.callHost.applySync(null, [
|
|
252
|
+
{
|
|
253
|
+
type: 'getPairedItem',
|
|
254
|
+
destinationNodeName,
|
|
255
|
+
incomingSourceData,
|
|
256
|
+
initialPairedItem,
|
|
257
|
+
},
|
|
258
|
+
], { arguments: { copy: true }, result: { copy: true } });
|
|
259
|
+
(0, lazy_proxy_1.throwIfErrorSentinel)(result);
|
|
260
|
+
return result;
|
|
261
|
+
};
|
|
262
|
+
// -------------------------------------------------------------------------
|
|
263
|
+
// Resolve an unknown key from the host. Called by the proxy's has/get traps
|
|
264
|
+
// for keys not already on the target. The resolved value is cached on target
|
|
265
|
+
// so each key is fetched at most once per evaluation.
|
|
266
|
+
// -------------------------------------------------------------------------
|
|
267
|
+
// Track keys we've already probed so we never call applySync twice
|
|
268
|
+
// for the same key — even if the host returned undefined.
|
|
269
|
+
const probedKeys = new Set();
|
|
270
|
+
function resolveFromHost(key) {
|
|
271
|
+
if (probedKeys.has(key))
|
|
272
|
+
return false;
|
|
273
|
+
let value;
|
|
274
|
+
try {
|
|
275
|
+
value = callbacks.getValueAtPath.applySync(null, [[key]], {
|
|
276
|
+
arguments: { copy: true },
|
|
277
|
+
result: { copy: true },
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Don't mark as probed — the throw may be transient
|
|
282
|
+
// (e.g. host data not yet available) and a retry should be allowed.
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
// Mark as probed only after a definitive answer from the host.
|
|
286
|
+
probedKeys.add(key);
|
|
287
|
+
if (value === undefined)
|
|
288
|
+
return false;
|
|
289
|
+
(0, lazy_proxy_1.throwIfErrorSentinel)(value);
|
|
290
|
+
// Object / array metadata — create a shape-matched lazy proxy for deep access
|
|
291
|
+
if ((0, lazy_proxy_1.isArrayMetadata)(value)) {
|
|
292
|
+
target[key] = (0, lazy_proxy_1.createDeepLazyProxy)([key], { kind: 'array', length: value.__length }, callbacks);
|
|
310
293
|
return true;
|
|
311
294
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
// ("x" in this ? this : global).x
|
|
316
|
-
// The has trap intercepts the "in" check, resolves the key on demand, and
|
|
317
|
-
// caches it on target. The get trap then finds it cached.
|
|
318
|
-
// -------------------------------------------------------------------------
|
|
319
|
-
return new Proxy(target, {
|
|
320
|
-
has(_target, prop) {
|
|
321
|
-
if (typeof prop === 'symbol')
|
|
322
|
-
return prop in target;
|
|
323
|
-
if (prop in target)
|
|
324
|
-
return true;
|
|
325
|
-
return resolveFromHost(prop);
|
|
326
|
-
},
|
|
327
|
-
get(_target, prop) {
|
|
328
|
-
if (typeof prop === 'symbol')
|
|
329
|
-
return undefined;
|
|
330
|
-
if (prop in target)
|
|
331
|
-
return target[prop];
|
|
332
|
-
// has() should have resolved it, but handle direct get() too
|
|
333
|
-
resolveFromHost(prop);
|
|
334
|
-
return target[prop];
|
|
335
|
-
},
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
// Matches initializeGlobalContext() lines 262-318 in packages/workflow/src/expression.ts
|
|
339
|
-
const DENYLISTED_GLOBALS = [
|
|
340
|
-
'document',
|
|
341
|
-
'global',
|
|
342
|
-
'window',
|
|
343
|
-
'Window',
|
|
344
|
-
'globalThis',
|
|
345
|
-
'self',
|
|
346
|
-
'alert',
|
|
347
|
-
'prompt',
|
|
348
|
-
'confirm',
|
|
349
|
-
'eval',
|
|
350
|
-
'uneval',
|
|
351
|
-
'setTimeout',
|
|
352
|
-
'setInterval',
|
|
353
|
-
'setImmediate',
|
|
354
|
-
'clearImmediate',
|
|
355
|
-
'queueMicrotask',
|
|
356
|
-
'Function',
|
|
357
|
-
'require',
|
|
358
|
-
'module',
|
|
359
|
-
'Buffer',
|
|
360
|
-
'__dirname',
|
|
361
|
-
'__filename',
|
|
362
|
-
'fetch',
|
|
363
|
-
'XMLHttpRequest',
|
|
364
|
-
'Promise',
|
|
365
|
-
'Generator',
|
|
366
|
-
'GeneratorFunction',
|
|
367
|
-
'AsyncFunction',
|
|
368
|
-
'AsyncGenerator',
|
|
369
|
-
'AsyncGeneratorFunction',
|
|
370
|
-
'WebAssembly',
|
|
371
|
-
'Reflect',
|
|
372
|
-
'Proxy',
|
|
373
|
-
'escape',
|
|
374
|
-
'unescape',
|
|
375
|
-
];
|
|
376
|
-
/**
|
|
377
|
-
* Wire builtins onto a context object so tournament's VariablePolyfill resolves them.
|
|
378
|
-
*
|
|
379
|
-
* Tournament transforms `Object` → `("Object" in this ? this : window).Object`.
|
|
380
|
-
* `this` = ctx. Without these entries on ctx, builtins fall through to
|
|
381
|
-
* `window` which doesn't exist in the isolate, causing a ReferenceError.
|
|
382
|
-
*
|
|
383
|
-
* Mirrors Expression.initializeGlobalContext() in packages/workflow/src/expression.ts.
|
|
384
|
-
*/
|
|
385
|
-
function initializeBuiltins(data) {
|
|
386
|
-
// ── Denylist: dangerous globals → empty objects ──
|
|
387
|
-
for (const key of DENYLISTED_GLOBALS) {
|
|
388
|
-
data[key] = {};
|
|
295
|
+
if ((0, lazy_proxy_1.isObjectMetadata)(value)) {
|
|
296
|
+
target[key] = (0, lazy_proxy_1.createDeepLazyProxy)([key], { kind: 'object', keys: value.__keys }, callbacks);
|
|
297
|
+
return true;
|
|
389
298
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
299
|
+
// Primitive or null — store directly
|
|
300
|
+
target[key] = value;
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
// -------------------------------------------------------------------------
|
|
304
|
+
// Wrap target in a Proxy so unknown keys are resolved lazily from the host.
|
|
305
|
+
// Tournament's VariablePolyfill transforms identifiers to:
|
|
306
|
+
// ("x" in this ? this : global).x
|
|
307
|
+
// The has trap intercepts the "in" check, resolves the key on demand, and
|
|
308
|
+
// caches it on target. The get trap then finds it cached.
|
|
309
|
+
// -------------------------------------------------------------------------
|
|
310
|
+
return new Proxy(target, {
|
|
311
|
+
has(_target, prop) {
|
|
312
|
+
if (typeof prop === 'symbol')
|
|
313
|
+
return prop in target;
|
|
314
|
+
if (prop in target)
|
|
315
|
+
return true;
|
|
316
|
+
return resolveFromHost(prop);
|
|
317
|
+
},
|
|
318
|
+
get(_target, prop) {
|
|
319
|
+
if (typeof prop === 'symbol')
|
|
320
|
+
return undefined;
|
|
321
|
+
if (prop in target)
|
|
322
|
+
return target[prop];
|
|
323
|
+
// has() should have resolved it, but handle direct get() too
|
|
324
|
+
resolveFromHost(prop);
|
|
325
|
+
return target[prop];
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
// Matches initializeGlobalContext() lines 262-318 in packages/workflow/src/expression.ts
|
|
330
|
+
const DENYLISTED_GLOBALS = [
|
|
331
|
+
'document',
|
|
332
|
+
'global',
|
|
333
|
+
'window',
|
|
334
|
+
'Window',
|
|
335
|
+
'globalThis',
|
|
336
|
+
'self',
|
|
337
|
+
'alert',
|
|
338
|
+
'prompt',
|
|
339
|
+
'confirm',
|
|
340
|
+
'eval',
|
|
341
|
+
'uneval',
|
|
342
|
+
'setTimeout',
|
|
343
|
+
'setInterval',
|
|
344
|
+
'setImmediate',
|
|
345
|
+
'clearImmediate',
|
|
346
|
+
'queueMicrotask',
|
|
347
|
+
'Function',
|
|
348
|
+
'require',
|
|
349
|
+
'module',
|
|
350
|
+
'Buffer',
|
|
351
|
+
'__dirname',
|
|
352
|
+
'__filename',
|
|
353
|
+
'fetch',
|
|
354
|
+
'XMLHttpRequest',
|
|
355
|
+
'Promise',
|
|
356
|
+
'Generator',
|
|
357
|
+
'GeneratorFunction',
|
|
358
|
+
'AsyncFunction',
|
|
359
|
+
'AsyncGenerator',
|
|
360
|
+
'AsyncGeneratorFunction',
|
|
361
|
+
'WebAssembly',
|
|
362
|
+
'Reflect',
|
|
363
|
+
'Proxy',
|
|
364
|
+
'escape',
|
|
365
|
+
'unescape',
|
|
366
|
+
];
|
|
367
|
+
/**
|
|
368
|
+
* Wire builtins onto a context object so tournament's VariablePolyfill resolves them.
|
|
369
|
+
*
|
|
370
|
+
* Tournament transforms `Object` → `("Object" in this ? this : window).Object`.
|
|
371
|
+
* `this` = ctx. Without these entries on ctx, builtins fall through to
|
|
372
|
+
* `window` which doesn't exist in the isolate, causing a ReferenceError.
|
|
373
|
+
*
|
|
374
|
+
* Mirrors Expression.initializeGlobalContext() in packages/workflow/src/expression.ts.
|
|
375
|
+
*/
|
|
376
|
+
function initializeBuiltins(data) {
|
|
377
|
+
// ── Denylist: dangerous globals → empty objects ──
|
|
378
|
+
for (const key of DENYLISTED_GLOBALS) {
|
|
379
|
+
data[key] = {};
|
|
447
380
|
}
|
|
448
|
-
|
|
381
|
+
data.__lookupGetter__ = undefined;
|
|
382
|
+
data.__lookupSetter__ = undefined;
|
|
383
|
+
data.__defineGetter__ = undefined;
|
|
384
|
+
data.__defineSetter__ = undefined;
|
|
385
|
+
// ── Allowlist: safe versions of builtins ──
|
|
386
|
+
// Object — use SafeObject wrapper from the runtime bundle
|
|
387
|
+
data.Object = globalThis.SafeObject;
|
|
388
|
+
// Error types — use SafeError and safe subclass wrappers
|
|
389
|
+
data.Error = globalThis.SafeError;
|
|
390
|
+
data.TypeError = SafeTypeError;
|
|
391
|
+
data.SyntaxError = SafeSyntaxError;
|
|
392
|
+
data.EvalError = SafeEvalError;
|
|
393
|
+
data.RangeError = SafeRangeError;
|
|
394
|
+
data.ReferenceError = SafeReferenceError;
|
|
395
|
+
data.URIError = SafeURIError;
|
|
396
|
+
// Arrays
|
|
397
|
+
data.Array = Array;
|
|
398
|
+
data.Int8Array = Int8Array;
|
|
399
|
+
data.Uint8Array = Uint8Array;
|
|
400
|
+
data.Uint8ClampedArray = Uint8ClampedArray;
|
|
401
|
+
data.Int16Array = Int16Array;
|
|
402
|
+
data.Uint16Array = Uint16Array;
|
|
403
|
+
data.Int32Array = Int32Array;
|
|
404
|
+
data.Uint32Array = Uint32Array;
|
|
405
|
+
data.Float32Array = Float32Array;
|
|
406
|
+
data.Float64Array = Float64Array;
|
|
407
|
+
data.BigInt64Array = typeof BigInt64Array !== 'undefined' ? BigInt64Array : {};
|
|
408
|
+
data.BigUint64Array = typeof BigUint64Array !== 'undefined' ? BigUint64Array : {};
|
|
409
|
+
// Collections
|
|
410
|
+
data.Map = Map;
|
|
411
|
+
data.WeakMap = WeakMap;
|
|
412
|
+
data.Set = Set;
|
|
413
|
+
data.WeakSet = WeakSet;
|
|
414
|
+
// Internationalization
|
|
415
|
+
data.Intl = typeof Intl !== 'undefined' ? Intl : {};
|
|
416
|
+
// Text
|
|
417
|
+
data.String = String;
|
|
418
|
+
// Numbers
|
|
419
|
+
data.Number = Number;
|
|
420
|
+
data.BigInt = typeof BigInt !== 'undefined' ? BigInt : {};
|
|
421
|
+
data.Infinity = Infinity;
|
|
422
|
+
data.parseFloat = parseFloat;
|
|
423
|
+
data.parseInt = parseInt;
|
|
424
|
+
// Structured data
|
|
425
|
+
data.JSON = JSON;
|
|
426
|
+
data.ArrayBuffer = typeof ArrayBuffer !== 'undefined' ? ArrayBuffer : {};
|
|
427
|
+
data.SharedArrayBuffer = typeof SharedArrayBuffer !== 'undefined' ? SharedArrayBuffer : {};
|
|
428
|
+
data.Atomics = typeof Atomics !== 'undefined' ? Atomics : {};
|
|
429
|
+
data.DataView = typeof DataView !== 'undefined' ? DataView : {};
|
|
430
|
+
// Encoding
|
|
431
|
+
data.encodeURI = encodeURI;
|
|
432
|
+
data.encodeURIComponent = encodeURIComponent;
|
|
433
|
+
data.decodeURI = decodeURI;
|
|
434
|
+
data.decodeURIComponent = decodeURIComponent;
|
|
435
|
+
// Other
|
|
436
|
+
data.Boolean = Boolean;
|
|
437
|
+
data.Symbol = Symbol;
|
|
438
|
+
}
|
|
449
439
|
//# sourceMappingURL=context.js.map
|