@blokjs/shared 0.2.2 → 0.4.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/__tests__/unit/utils/Mapper.test.ts +299 -31
- package/__tests__/unit/utils/MapperResolutionError.test.ts +64 -0
- package/dist/NodeBase.d.ts +58 -0
- package/dist/NodeBase.js +79 -1
- package/dist/NodeBase.js.map +1 -1
- package/dist/types/Context.d.ts +17 -0
- package/dist/utils/Mapper.d.ts +111 -2
- package/dist/utils/Mapper.js +256 -23
- package/dist/utils/Mapper.js.map +1 -1
- package/dist/utils/MapperResolutionError.d.ts +84 -0
- package/dist/utils/MapperResolutionError.js +61 -0
- package/dist/utils/MapperResolutionError.js.map +1 -0
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/types/Context.d.ts
CHANGED
|
@@ -94,6 +94,23 @@ type Context = {
|
|
|
94
94
|
* publish something other than its return value (most nodes don't).
|
|
95
95
|
*/
|
|
96
96
|
publish?: (name: string, value: unknown) => void;
|
|
97
|
+
/**
|
|
98
|
+
* Tier 2 follow-up · cooperative cancellation. AbortSignal whose
|
|
99
|
+
* `aborted` flips to true when an operator cancels the run via
|
|
100
|
+
* `POST /__blok/runs/:runId/cancel` while it's in `running` status.
|
|
101
|
+
*
|
|
102
|
+
* Nodes that perform long-running work (loops, fetch, db queries)
|
|
103
|
+
* should consult `ctx.signal.aborted` periodically and abort early
|
|
104
|
+
* — or pass the signal to fetch/abort-aware APIs:
|
|
105
|
+
*
|
|
106
|
+
* await fetch(url, { signal: ctx.signal });
|
|
107
|
+
* if (ctx.signal?.aborted) throw new Error("aborted");
|
|
108
|
+
*
|
|
109
|
+
* Always present on contexts created by `TriggerBase.createContext`.
|
|
110
|
+
* Optional on the type for back-compat with hand-built contexts in
|
|
111
|
+
* tests.
|
|
112
|
+
*/
|
|
113
|
+
signal?: AbortSignal;
|
|
97
114
|
_PRIVATE_: unknown;
|
|
98
115
|
};
|
|
99
116
|
export default Context;
|
package/dist/utils/Mapper.d.ts
CHANGED
|
@@ -1,10 +1,119 @@
|
|
|
1
1
|
import type Context from "../types/Context";
|
|
2
2
|
import type ParamsDictionary from "../types/ParamsDictionary";
|
|
3
|
+
/**
|
|
4
|
+
* Mapper — the workflow input resolver.
|
|
5
|
+
*
|
|
6
|
+
* Every step's `inputs` object is walked by `replaceObjectStrings`
|
|
7
|
+
* before the step runs (see `NodeBase.process` → `blueprintMapper`).
|
|
8
|
+
* Two template syntaxes are recognised:
|
|
9
|
+
*
|
|
10
|
+
* 1. **`${path.to.value}`** — interpolated string placeholder.
|
|
11
|
+
* Resolved via lodash `_.get(data, key)` first, with a JS-eval
|
|
12
|
+
* fallback when the key is not in `data`. Multiple placeholders
|
|
13
|
+
* in one string are concatenated.
|
|
14
|
+
*
|
|
15
|
+
* 2. **`"js/..."`** — full-string JS expression. The string is
|
|
16
|
+
* replaced ENTIRELY by the result of evaluating the expression
|
|
17
|
+
* (after stripping the `js/` prefix) against the live `ctx`.
|
|
18
|
+
* Returns whatever value the expression produces (string, number,
|
|
19
|
+
* object, array, etc.) — preserves type fidelity end-to-end.
|
|
20
|
+
*
|
|
21
|
+
* Both syntaxes evaluate inside a sandboxed `Function` with these
|
|
22
|
+
* names in scope: `ctx`, `data`, `func`, `vars`. (Symmetric scope
|
|
23
|
+
* across both syntaxes since v0.3.x — pre-v0.3.x `${...}` evaluator
|
|
24
|
+
* lacked `func`+`vars`, leading to surprising scope mismatches.)
|
|
25
|
+
*
|
|
26
|
+
* ## Failure modes — see {@link MapperResolutionError}
|
|
27
|
+
*
|
|
28
|
+
* Resolution failures (typo, undefined access, syntax error) used to
|
|
29
|
+
* be swallowed silently with a noisy `console.log("Mapper Error N", e)`
|
|
30
|
+
* — and worse, the unresolved expression string passed through to the
|
|
31
|
+
* node, producing silent miscompiles downstream. Since v0.3.x the
|
|
32
|
+
* Mapper packages every failure in a `MapperResolutionError` with full
|
|
33
|
+
* context (workflow, step, expression, underlying cause + heuristic
|
|
34
|
+
* hint) and routes it according to `BLOK_MAPPER_MODE`:
|
|
35
|
+
*
|
|
36
|
+
* - `"warn"` (default) — log via `ctx.logger.logLevel("warn", ...)`,
|
|
37
|
+
* pass through the original string. Backward-compatible diagnostics.
|
|
38
|
+
* - `"strict"` — throw the error, fail the step fast. **Recommended
|
|
39
|
+
* for production.**
|
|
40
|
+
* - `"silent"` — full suppression. Tests / opt-out only.
|
|
41
|
+
*
|
|
42
|
+
* ## Bug fixes shipped alongside the diagnostic upgrade (v0.3.x)
|
|
43
|
+
*
|
|
44
|
+
* - **Falsy values now preserved** — `_.get(data, key) || runJs(...)`
|
|
45
|
+
* used to fall through to `runJs` when the lookup returned `0`,
|
|
46
|
+
* `false`, `null`, or `""` (all valid values incorrectly treated
|
|
47
|
+
* as missing). Now uses an explicit `=== undefined` check.
|
|
48
|
+
* - **Object interpolation now JSON-encodes** — `value as string`
|
|
49
|
+
* used to produce `"[object Object]"` for object values. Now
|
|
50
|
+
* round-trips via `JSON.stringify`.
|
|
51
|
+
* - **`js/` prefix stripping** uses `slice(3)` instead of
|
|
52
|
+
* `replace("js/", "")` (the latter only strips the FIRST
|
|
53
|
+
* occurrence — fragile if the expression itself contained `js/`).
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* How the Mapper reacts to expression resolution failures. Read from
|
|
57
|
+
* the `BLOK_MAPPER_MODE` env var at every call (no caching) so unit
|
|
58
|
+
* tests can flip the mode between cases.
|
|
59
|
+
*/
|
|
60
|
+
export type MapperMode = "warn" | "strict" | "silent";
|
|
3
61
|
declare class Mapper {
|
|
62
|
+
/**
|
|
63
|
+
* Walk an object recursively, resolving every string value via
|
|
64
|
+
* `replaceString`. Mutates in place. Used by `NodeBase.process` to
|
|
65
|
+
* resolve a step's `inputs` against the live `ctx` before the
|
|
66
|
+
* step runs.
|
|
67
|
+
*
|
|
68
|
+
* Object/array values are recursed into; primitive non-string
|
|
69
|
+
* values are left untouched. Null values are NOT recursed (avoids
|
|
70
|
+
* a TypeError on `for (const k in null)`).
|
|
71
|
+
*/
|
|
4
72
|
replaceObjectStrings(obj: ParamsDictionary, ctx: Context, data: ParamsDictionary): void;
|
|
5
|
-
|
|
6
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Resolve a single string. Returns `unknown` because a `js/...`
|
|
75
|
+
* expression may yield any value (number, object, array, etc.) —
|
|
76
|
+
* type fidelity is preserved across the resolver boundary.
|
|
77
|
+
*
|
|
78
|
+
* Pre-v0.3.x return was typed as `string` via `as string` cast
|
|
79
|
+
* which was a type lie; downstream consumers received the actual
|
|
80
|
+
* runtime value but couldn't see it through the type system.
|
|
81
|
+
*/
|
|
82
|
+
replaceString: (strData: string, ctx: Context, data: ParamsDictionary) => unknown;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a `${path}` expression. Lodash lookup first; JS-eval
|
|
85
|
+
* fallback when the path is not in `data`. Returns the resolved
|
|
86
|
+
* value OR the failure sentinel.
|
|
87
|
+
*/
|
|
88
|
+
private resolveTemplateExpression;
|
|
89
|
+
/**
|
|
90
|
+
* Evaluate a `js/...` full-string expression. Returns whatever the
|
|
91
|
+
* expression produces (any type), or — in warn/silent mode on
|
|
92
|
+
* failure — the original literal `js/...` string.
|
|
93
|
+
*
|
|
94
|
+
* Strict-mode failures throw `MapperResolutionError`.
|
|
95
|
+
*/
|
|
7
96
|
private jsMapper;
|
|
97
|
+
/**
|
|
98
|
+
* Apply the configured mode (`BLOK_MAPPER_MODE`) to a resolution
|
|
99
|
+
* failure. In strict mode, the error escapes here and propagates
|
|
100
|
+
* up through `replaceString` → `NodeBase.blueprintMapper` →
|
|
101
|
+
* `NodeBase.process` → step error envelope.
|
|
102
|
+
*/
|
|
103
|
+
private handleResolutionError;
|
|
104
|
+
/**
|
|
105
|
+
* Sandboxed JS evaluation. Compiles the expression as a function
|
|
106
|
+
* body returning the expression value; binds `ctx`, `data`, `func`,
|
|
107
|
+
* `vars` as positional arguments; runs in `"use strict"` mode.
|
|
108
|
+
*
|
|
109
|
+
* Throws on any evaluation error (typo, undefined access, syntax,
|
|
110
|
+
* unknown identifier). Callers wrap in try/catch and translate to
|
|
111
|
+
* `MapperResolutionError`.
|
|
112
|
+
*
|
|
113
|
+
* Public via the prototype but documented as internal — the only
|
|
114
|
+
* supported call sites are inside this class.
|
|
115
|
+
*/
|
|
116
|
+
private runJs;
|
|
8
117
|
}
|
|
9
118
|
declare const _default: Mapper;
|
|
10
119
|
export default _default;
|
package/dist/utils/Mapper.js
CHANGED
|
@@ -1,55 +1,288 @@
|
|
|
1
1
|
import _ from "lodash";
|
|
2
|
+
import { MapperResolutionError } from "./MapperResolutionError";
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Internal sentinels + helpers
|
|
5
|
+
// =============================================================================
|
|
6
|
+
/**
|
|
7
|
+
* Returned by the template-resolution path to signal "this placeholder
|
|
8
|
+
* could not be resolved; leave the literal `${...}` in place". Using a
|
|
9
|
+
* Symbol avoids ambiguity with the legitimate `undefined` value an
|
|
10
|
+
* expression might produce.
|
|
11
|
+
*/
|
|
12
|
+
const TEMPLATE_RESOLUTION_FAILED = Symbol("TEMPLATE_RESOLUTION_FAILED");
|
|
13
|
+
function readMode() {
|
|
14
|
+
const raw = process.env.BLOK_MAPPER_MODE;
|
|
15
|
+
if (raw === "strict")
|
|
16
|
+
return "strict";
|
|
17
|
+
if (raw === "silent")
|
|
18
|
+
return "silent";
|
|
19
|
+
return "warn";
|
|
20
|
+
}
|
|
21
|
+
function readStepContext(ctx) {
|
|
22
|
+
const ctxAny = ctx;
|
|
23
|
+
const stepInfo = ctxAny._stepInfo;
|
|
24
|
+
const stepName = typeof stepInfo?.name === "string" ? stepInfo.name : undefined;
|
|
25
|
+
const workflowName = typeof ctx.workflow_name === "string" ? ctx.workflow_name : undefined;
|
|
26
|
+
return { workflowName, stepName };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build the actionable error message — every line carries information
|
|
30
|
+
* a developer can act on: WHERE it failed, WHAT failed, WHY it likely
|
|
31
|
+
* failed, and HOW to fix it.
|
|
32
|
+
*/
|
|
33
|
+
function buildErrorMessage(opts) {
|
|
34
|
+
const wf = opts.workflowName ?? "<unknown workflow>";
|
|
35
|
+
const step = opts.stepName ?? "<unknown step>";
|
|
36
|
+
const literal = opts.syntax === "js" ? `js/${opts.expression}` : `\${${opts.expression}}`;
|
|
37
|
+
const causeMsg = opts.cause instanceof Error ? opts.cause.message : String(opts.cause);
|
|
38
|
+
const hint = guessHint(opts.expression, causeMsg);
|
|
39
|
+
const lines = [
|
|
40
|
+
`[blok][mapper] Failed to resolve \`${literal}\` in step "${step}" of workflow "${wf}"`,
|
|
41
|
+
` underlying: ${causeMsg}`,
|
|
42
|
+
];
|
|
43
|
+
if (hint)
|
|
44
|
+
lines.push(` hint: ${hint}`);
|
|
45
|
+
lines.push(" fix: verify the referenced ctx path exists at run time. Set BLOK_MAPPER_MODE=strict in production to fail fast on these errors.");
|
|
46
|
+
return lines.join("\n");
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Heuristic — translate common JS evaluation errors into actionable
|
|
50
|
+
* developer hints. Returns `null` when the error doesn't match any
|
|
51
|
+
* known pattern (the underlying message is still surfaced).
|
|
52
|
+
*/
|
|
53
|
+
function guessHint(expression, errorMessage) {
|
|
54
|
+
// Most common case — `Cannot read properties of undefined (reading 'X')`
|
|
55
|
+
// or the older `Cannot read property 'X' of undefined`. Both forms
|
|
56
|
+
// appear depending on Node/Bun version.
|
|
57
|
+
const undefMatch = errorMessage.match(/Cannot read propert(?:y '(\w+)' of undefined|ies of undefined \(reading '(\w+)'\))/);
|
|
58
|
+
if (undefMatch) {
|
|
59
|
+
const prop = undefMatch[1] ?? undefMatch[2];
|
|
60
|
+
const segments = expression.split(".");
|
|
61
|
+
const parent = segments.slice(0, -1).join(".") || expression;
|
|
62
|
+
return `the path \`${parent}\` is undefined or doesn't have a "${prop}" field at run time. Check the trigger payload (ctx.req.body) or the upstream step's output (ctx.state.<id>).`;
|
|
63
|
+
}
|
|
64
|
+
// Identifier not in scope — only `ctx`, `data`, `func`, `vars` are
|
|
65
|
+
// available inside expressions.
|
|
66
|
+
const refMatch = errorMessage.match(/(\w+) is not defined/);
|
|
67
|
+
if (refMatch) {
|
|
68
|
+
return `\`${refMatch[1]}\` is not in scope. Available identifiers inside expressions: ctx, data, func, vars.`;
|
|
69
|
+
}
|
|
70
|
+
// Syntax error.
|
|
71
|
+
if (/SyntaxError/.test(errorMessage) || /Unexpected token/.test(errorMessage)) {
|
|
72
|
+
return "the expression is not valid JavaScript. Check for typos, unmatched parentheses, or stray characters.";
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Route a warn-mode log line to the best available sink. Prefers
|
|
78
|
+
* `ctx.logger.logLevel("warn", ...)` so the warning lands in BOTH the
|
|
79
|
+
* console (via DefaultLogger) AND Studio's log viewer (via
|
|
80
|
+
* TracingLogger.normalizeLevel → addLog). Falls back to console.warn
|
|
81
|
+
* when no logger is attached (early-boot or hand-rolled test ctx).
|
|
82
|
+
*/
|
|
83
|
+
function logViaCtxOrConsole(ctx, message) {
|
|
84
|
+
const logger = ctx.logger;
|
|
85
|
+
if (logger?.logLevel) {
|
|
86
|
+
try {
|
|
87
|
+
logger.logLevel("warn", message);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// fall through to console.warn — never let logging itself crash a step.
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (logger?.log) {
|
|
95
|
+
try {
|
|
96
|
+
logger.log(message);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// fall through
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
console.warn(message);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Coerce a resolved value to its string form for `${...}` interpolation.
|
|
107
|
+
*
|
|
108
|
+
* Pre-v0.3.x used `value as string` which fell back to `String(value)`
|
|
109
|
+
* via implicit coercion — producing `"[object Object]"` for any object/
|
|
110
|
+
* array. Now JSON-encodes complex values so interpolated strings
|
|
111
|
+
* preserve information instead of silently miscompiling.
|
|
112
|
+
*
|
|
113
|
+
* - `null` / `undefined` → empty string (matches user intent for
|
|
114
|
+
* missing optional fields)
|
|
115
|
+
* - `string` → identity
|
|
116
|
+
* - `number` / `boolean` / `bigint` → `String(value)`
|
|
117
|
+
* - everything else → `JSON.stringify(value)` with a defensive fallback
|
|
118
|
+
* to `String(value)` for circular structures.
|
|
119
|
+
*/
|
|
120
|
+
function toInterpolatedString(value) {
|
|
121
|
+
if (value === null || value === undefined)
|
|
122
|
+
return "";
|
|
123
|
+
if (typeof value === "string")
|
|
124
|
+
return value;
|
|
125
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
126
|
+
return String(value);
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
return JSON.stringify(value) ?? String(value);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// JSON.stringify throws on circular references — degrade gracefully.
|
|
133
|
+
return String(value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// Mapper
|
|
138
|
+
// =============================================================================
|
|
2
139
|
class Mapper {
|
|
140
|
+
/**
|
|
141
|
+
* Walk an object recursively, resolving every string value via
|
|
142
|
+
* `replaceString`. Mutates in place. Used by `NodeBase.process` to
|
|
143
|
+
* resolve a step's `inputs` against the live `ctx` before the
|
|
144
|
+
* step runs.
|
|
145
|
+
*
|
|
146
|
+
* Object/array values are recursed into; primitive non-string
|
|
147
|
+
* values are left untouched. Null values are NOT recursed (avoids
|
|
148
|
+
* a TypeError on `for (const k in null)`).
|
|
149
|
+
*/
|
|
3
150
|
replaceObjectStrings(obj, ctx, data) {
|
|
4
151
|
for (const key in obj) {
|
|
5
152
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
6
153
|
const value = obj[key];
|
|
7
154
|
if (typeof value === "string") {
|
|
155
|
+
// `ParamsDictionary[key]` is typed `string`, but the
|
|
156
|
+
// runtime contract has always been "the resolved value
|
|
157
|
+
// keeps its actual type" (so `js/ctx.req.body.count`
|
|
158
|
+
// produces a number, not the string "42"). The
|
|
159
|
+
// `as unknown as string` boundary cast acknowledges
|
|
160
|
+
// that the type system can't express this widening
|
|
161
|
+
// without changing the global ParamsDictionary shape
|
|
162
|
+
// (a much larger refactor).
|
|
8
163
|
obj[key] = this.replaceString(value, ctx, data);
|
|
9
164
|
}
|
|
10
|
-
else if (typeof value === "object") {
|
|
165
|
+
else if (value !== null && typeof value === "object") {
|
|
11
166
|
this.replaceObjectStrings(value, ctx, data);
|
|
12
167
|
}
|
|
13
168
|
}
|
|
14
169
|
}
|
|
15
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Resolve a single string. Returns `unknown` because a `js/...`
|
|
173
|
+
* expression may yield any value (number, object, array, etc.) —
|
|
174
|
+
* type fidelity is preserved across the resolver boundary.
|
|
175
|
+
*
|
|
176
|
+
* Pre-v0.3.x return was typed as `string` via `as string` cast
|
|
177
|
+
* which was a type lie; downstream consumers received the actual
|
|
178
|
+
* runtime value but couldn't see it through the type system.
|
|
179
|
+
*/
|
|
16
180
|
replaceString = (strData, ctx, data) => {
|
|
17
181
|
let str = strData;
|
|
18
|
-
//
|
|
19
|
-
// throw new Error("Input too long");
|
|
20
|
-
// }
|
|
182
|
+
// === 1. `${path}` template interpolation ===
|
|
21
183
|
const regex = /\${(.*?)}/g;
|
|
22
184
|
const matches = str.match(regex);
|
|
23
185
|
if (matches) {
|
|
24
186
|
for (const match of matches) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
str = str.replace(match, value);
|
|
30
|
-
}
|
|
31
|
-
catch (e) {
|
|
32
|
-
console.log("Mapper Error 1", e);
|
|
187
|
+
const key = match.slice(2, -1); // strip `${` and `}`
|
|
188
|
+
const value = this.resolveTemplateExpression(key, ctx, data);
|
|
189
|
+
if (value !== TEMPLATE_RESOLUTION_FAILED) {
|
|
190
|
+
str = str.replace(match, toInterpolatedString(value));
|
|
33
191
|
}
|
|
192
|
+
// On failure: leave the literal `${...}` placeholder in
|
|
193
|
+
// place. The failure was already reported per the active
|
|
194
|
+
// mode (warn/strict/silent).
|
|
34
195
|
}
|
|
35
196
|
}
|
|
36
|
-
|
|
37
|
-
return
|
|
197
|
+
// === 2. `js/...` full-string evaluation ===
|
|
198
|
+
return this.jsMapper(str, ctx, data);
|
|
38
199
|
};
|
|
39
|
-
|
|
40
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Resolve a `${path}` expression. Lodash lookup first; JS-eval
|
|
202
|
+
* fallback when the path is not in `data`. Returns the resolved
|
|
203
|
+
* value OR the failure sentinel.
|
|
204
|
+
*/
|
|
205
|
+
resolveTemplateExpression(key, ctx, data) {
|
|
206
|
+
// Lodash lookup. Use explicit `=== undefined` instead of `||`
|
|
207
|
+
// so falsy-but-valid values (0, false, "", null) are preserved.
|
|
208
|
+
const lookupValue = _.get(data, key);
|
|
209
|
+
if (lookupValue !== undefined)
|
|
210
|
+
return lookupValue;
|
|
211
|
+
// Fallback to JS evaluation against ctx. Symmetric with jsMapper:
|
|
212
|
+
// pass `ctx.func` and `ctx.vars` so `${func.X}` and `${vars.X}`
|
|
213
|
+
// have the same scope as `js/func.X` / `js/vars.X`.
|
|
214
|
+
try {
|
|
215
|
+
return this.runJs(key, ctx, data, (ctx.func ?? {}), (ctx.vars ?? {}));
|
|
216
|
+
}
|
|
217
|
+
catch (cause) {
|
|
218
|
+
const stepCtx = readStepContext(ctx);
|
|
219
|
+
const error = new MapperResolutionError(buildErrorMessage({ expression: key, syntax: "template", ...stepCtx, cause }), { expression: key, syntax: "template", ...stepCtx, cause });
|
|
220
|
+
this.handleResolutionError(ctx, error);
|
|
221
|
+
return TEMPLATE_RESOLUTION_FAILED;
|
|
222
|
+
}
|
|
41
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Evaluate a `js/...` full-string expression. Returns whatever the
|
|
226
|
+
* expression produces (any type), or — in warn/silent mode on
|
|
227
|
+
* failure — the original literal `js/...` string.
|
|
228
|
+
*
|
|
229
|
+
* Strict-mode failures throw `MapperResolutionError`.
|
|
230
|
+
*/
|
|
42
231
|
jsMapper(str, ctx, data) {
|
|
232
|
+
if (typeof str !== "string" || !str.startsWith("js/"))
|
|
233
|
+
return str;
|
|
234
|
+
// `slice(3)` strips exactly the leading `js/` prefix.
|
|
235
|
+
// Pre-v0.3.x used `replace("js/", "")` which only strips the
|
|
236
|
+
// FIRST occurrence — fragile if the expression itself contained
|
|
237
|
+
// the substring `js/` (e.g., a URL like `https://js/foo`).
|
|
238
|
+
const expression = str.slice(3);
|
|
43
239
|
try {
|
|
44
|
-
|
|
45
|
-
const fn = str.replace("js/", "");
|
|
46
|
-
return this.runJs(fn, ctx, data, ctx.func, ctx.vars);
|
|
47
|
-
}
|
|
240
|
+
return this.runJs(expression, ctx, data, (ctx.func ?? {}), (ctx.vars ?? {}));
|
|
48
241
|
}
|
|
49
|
-
catch (
|
|
50
|
-
|
|
242
|
+
catch (cause) {
|
|
243
|
+
const stepCtx = readStepContext(ctx);
|
|
244
|
+
const error = new MapperResolutionError(buildErrorMessage({ expression, syntax: "js", ...stepCtx, cause }), {
|
|
245
|
+
expression,
|
|
246
|
+
syntax: "js",
|
|
247
|
+
...stepCtx,
|
|
248
|
+
cause,
|
|
249
|
+
});
|
|
250
|
+
this.handleResolutionError(ctx, error);
|
|
251
|
+
return str; // pre-v0.3.x behavior — pass through the literal string
|
|
51
252
|
}
|
|
52
|
-
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Apply the configured mode (`BLOK_MAPPER_MODE`) to a resolution
|
|
256
|
+
* failure. In strict mode, the error escapes here and propagates
|
|
257
|
+
* up through `replaceString` → `NodeBase.blueprintMapper` →
|
|
258
|
+
* `NodeBase.process` → step error envelope.
|
|
259
|
+
*/
|
|
260
|
+
handleResolutionError(ctx, error) {
|
|
261
|
+
const mode = readMode();
|
|
262
|
+
if (mode === "strict")
|
|
263
|
+
throw error;
|
|
264
|
+
if (mode === "silent")
|
|
265
|
+
return;
|
|
266
|
+
// mode === "warn"
|
|
267
|
+
logViaCtxOrConsole(ctx, error.message);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Sandboxed JS evaluation. Compiles the expression as a function
|
|
271
|
+
* body returning the expression value; binds `ctx`, `data`, `func`,
|
|
272
|
+
* `vars` as positional arguments; runs in `"use strict"` mode.
|
|
273
|
+
*
|
|
274
|
+
* Throws on any evaluation error (typo, undefined access, syntax,
|
|
275
|
+
* unknown identifier). Callers wrap in try/catch and translate to
|
|
276
|
+
* `MapperResolutionError`.
|
|
277
|
+
*
|
|
278
|
+
* Public via the prototype but documented as internal — the only
|
|
279
|
+
* supported call sites are inside this class.
|
|
280
|
+
*/
|
|
281
|
+
runJs(str, ctx, data = {}, func = {}, vars = {}) {
|
|
282
|
+
// Function constructor (NOT eval) — creates a fresh function
|
|
283
|
+
// scope without lexical access to the surrounding module. Same
|
|
284
|
+
// security profile as the v1 implementation.
|
|
285
|
+
return Function("ctx", "data", "func", "vars", `"use strict";return (${str});`)(ctx, data, func, vars);
|
|
53
286
|
}
|
|
54
287
|
}
|
|
55
288
|
export default new Mapper();
|
package/dist/utils/Mapper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Mapper.js","sourceRoot":"","sources":["../../src/utils/Mapper.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"Mapper.js","sourceRoot":"","sources":["../../src/utils/Mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error thrown by `Mapper` when a workflow input expression
|
|
3
|
+
* cannot be resolved against the live `Context`.
|
|
4
|
+
*
|
|
5
|
+
* The Mapper resolves two template syntaxes inside step `inputs`:
|
|
6
|
+
* - **`${path.to.value}`** — interpolated string (lodash-path lookup
|
|
7
|
+
* with a JS-eval fallback)
|
|
8
|
+
* - **`"js/..."`** — full-string JS evaluation against `ctx`
|
|
9
|
+
*
|
|
10
|
+
* When evaluation throws (typo, undefined access, syntax error,
|
|
11
|
+
* unknown identifier), the Mapper packages the failure in a
|
|
12
|
+
* `MapperResolutionError` carrying full diagnostic context — the
|
|
13
|
+
* literal expression, which syntax it was, the workflow + step name,
|
|
14
|
+
* and the original underlying error as `cause`.
|
|
15
|
+
*
|
|
16
|
+
* **What happens next depends on `BLOK_MAPPER_MODE`** (read from env):
|
|
17
|
+
*
|
|
18
|
+
* - `"warn"` (default) — the error is caught inside the Mapper, logged
|
|
19
|
+
* via `ctx.logger.logLevel("warn", ...)` (which routes to both the
|
|
20
|
+
* console AND Studio's log viewer via `TracingLogger`), and the
|
|
21
|
+
* original expression string passes through to the node. Backward-
|
|
22
|
+
* compatible with v1 behavior + actionable diagnostics.
|
|
23
|
+
*
|
|
24
|
+
* - `"strict"` — the error escapes the Mapper, is re-thrown by
|
|
25
|
+
* `NodeBase.blueprintMapper`, and the step fails fast with a
|
|
26
|
+
* structured error. **Recommended for production deployments** —
|
|
27
|
+
* silent input resolution failures are a source of subtle bugs
|
|
28
|
+
* (the node receives a literal `"js/ctx.bad.path"` string instead
|
|
29
|
+
* of the resolved value, then produces wrong output downstream).
|
|
30
|
+
*
|
|
31
|
+
* - `"silent"` — pre-v0.3.x behavior: completely suppress the error
|
|
32
|
+
* (no log, no throw). Provided as an opt-out for tests / workflows
|
|
33
|
+
* that intentionally use undefined-tolerant resolution for optional
|
|
34
|
+
* fields. Discouraged.
|
|
35
|
+
*
|
|
36
|
+
* This class is a `core/shared` concern (not `core/runner`) because
|
|
37
|
+
* the Mapper itself lives in shared. Consumers in any package may
|
|
38
|
+
* `instanceof` check it to handle resolution failures specifically
|
|
39
|
+
* (e.g., a custom trigger may want to translate it into a 400-class
|
|
40
|
+
* HTTP response).
|
|
41
|
+
*/
|
|
42
|
+
export declare class MapperResolutionError extends Error {
|
|
43
|
+
/** Always the literal string `"MapperResolutionError"`. */
|
|
44
|
+
readonly name = "MapperResolutionError";
|
|
45
|
+
/** Structured diagnostic context attached at construction time. */
|
|
46
|
+
readonly context: {
|
|
47
|
+
/**
|
|
48
|
+
* The literal expression that failed, WITHOUT the surrounding
|
|
49
|
+
* syntax markers. For `js/ctx.bad.path` the value is
|
|
50
|
+
* `"ctx.bad.path"`; for `${ctx.user.name}` it is `"ctx.user.name"`.
|
|
51
|
+
*/
|
|
52
|
+
readonly expression: string;
|
|
53
|
+
/**
|
|
54
|
+
* Which template syntax was being parsed.
|
|
55
|
+
* - `"js"` — full-string `"js/..."` expression
|
|
56
|
+
* - `"template"` — interpolated `${...}` placeholder
|
|
57
|
+
*/
|
|
58
|
+
readonly syntax: "js" | "template";
|
|
59
|
+
/**
|
|
60
|
+
* The workflow's `name:` field, when known. Read from
|
|
61
|
+
* `ctx.workflow_name`. Absent on hand-rolled test contexts.
|
|
62
|
+
*/
|
|
63
|
+
readonly workflowName?: string;
|
|
64
|
+
/**
|
|
65
|
+
* The step's `id` (or v1 `name`), when known. Read from
|
|
66
|
+
* `ctx._stepInfo.name` which is set by `RunnerSteps` before
|
|
67
|
+
* each step runs. Absent during early-boot or test contexts.
|
|
68
|
+
*/
|
|
69
|
+
readonly stepName?: string;
|
|
70
|
+
/**
|
|
71
|
+
* The original error thrown by the JS evaluator (typically a
|
|
72
|
+
* `TypeError` or `ReferenceError`). Preserved for full stack
|
|
73
|
+
* trace + downstream `instanceof` checks.
|
|
74
|
+
*/
|
|
75
|
+
readonly cause?: unknown;
|
|
76
|
+
};
|
|
77
|
+
constructor(message: string, context: {
|
|
78
|
+
readonly expression: string;
|
|
79
|
+
readonly syntax: "js" | "template";
|
|
80
|
+
readonly workflowName?: string;
|
|
81
|
+
readonly stepName?: string;
|
|
82
|
+
readonly cause?: unknown;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error thrown by `Mapper` when a workflow input expression
|
|
3
|
+
* cannot be resolved against the live `Context`.
|
|
4
|
+
*
|
|
5
|
+
* The Mapper resolves two template syntaxes inside step `inputs`:
|
|
6
|
+
* - **`${path.to.value}`** — interpolated string (lodash-path lookup
|
|
7
|
+
* with a JS-eval fallback)
|
|
8
|
+
* - **`"js/..."`** — full-string JS evaluation against `ctx`
|
|
9
|
+
*
|
|
10
|
+
* When evaluation throws (typo, undefined access, syntax error,
|
|
11
|
+
* unknown identifier), the Mapper packages the failure in a
|
|
12
|
+
* `MapperResolutionError` carrying full diagnostic context — the
|
|
13
|
+
* literal expression, which syntax it was, the workflow + step name,
|
|
14
|
+
* and the original underlying error as `cause`.
|
|
15
|
+
*
|
|
16
|
+
* **What happens next depends on `BLOK_MAPPER_MODE`** (read from env):
|
|
17
|
+
*
|
|
18
|
+
* - `"warn"` (default) — the error is caught inside the Mapper, logged
|
|
19
|
+
* via `ctx.logger.logLevel("warn", ...)` (which routes to both the
|
|
20
|
+
* console AND Studio's log viewer via `TracingLogger`), and the
|
|
21
|
+
* original expression string passes through to the node. Backward-
|
|
22
|
+
* compatible with v1 behavior + actionable diagnostics.
|
|
23
|
+
*
|
|
24
|
+
* - `"strict"` — the error escapes the Mapper, is re-thrown by
|
|
25
|
+
* `NodeBase.blueprintMapper`, and the step fails fast with a
|
|
26
|
+
* structured error. **Recommended for production deployments** —
|
|
27
|
+
* silent input resolution failures are a source of subtle bugs
|
|
28
|
+
* (the node receives a literal `"js/ctx.bad.path"` string instead
|
|
29
|
+
* of the resolved value, then produces wrong output downstream).
|
|
30
|
+
*
|
|
31
|
+
* - `"silent"` — pre-v0.3.x behavior: completely suppress the error
|
|
32
|
+
* (no log, no throw). Provided as an opt-out for tests / workflows
|
|
33
|
+
* that intentionally use undefined-tolerant resolution for optional
|
|
34
|
+
* fields. Discouraged.
|
|
35
|
+
*
|
|
36
|
+
* This class is a `core/shared` concern (not `core/runner`) because
|
|
37
|
+
* the Mapper itself lives in shared. Consumers in any package may
|
|
38
|
+
* `instanceof` check it to handle resolution failures specifically
|
|
39
|
+
* (e.g., a custom trigger may want to translate it into a 400-class
|
|
40
|
+
* HTTP response).
|
|
41
|
+
*/
|
|
42
|
+
export class MapperResolutionError extends Error {
|
|
43
|
+
/** Always the literal string `"MapperResolutionError"`. */
|
|
44
|
+
name = "MapperResolutionError";
|
|
45
|
+
/** Structured diagnostic context attached at construction time. */
|
|
46
|
+
context;
|
|
47
|
+
constructor(message, context) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.context = context;
|
|
50
|
+
// Preserve the prototype chain across Babel/TS down-compilation —
|
|
51
|
+
// without this, `instanceof MapperResolutionError` fails on
|
|
52
|
+
// constructors transpiled to ES5 targets.
|
|
53
|
+
Object.setPrototypeOf(this, MapperResolutionError.prototype);
|
|
54
|
+
// Standard `Error.cause` mirror (Node 16.9+, ES2022). Lets
|
|
55
|
+
// `console.error(e)` pretty-print the underlying cause too.
|
|
56
|
+
if (context.cause !== undefined) {
|
|
57
|
+
this.cause = context.cause;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=MapperResolutionError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapperResolutionError.js","sourceRoot":"","sources":["../../src/utils/MapperResolutionError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC/C,2DAA2D;IAClC,IAAI,GAAG,uBAAuB,CAAC;IAExD,mEAAmE;IACnD,OAAO,CA8BrB;IAEF,YACC,OAAe,EACf,OAMC;QAED,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,kEAAkE;QAClE,4DAA4D;QAC5D,0CAA0C;QAC1C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;QAC7D,2DAA2D;QAC3D,4DAA4D;QAC5D,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,IAAoC,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC7D,CAAC;IACF,CAAC;CACD"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as CpuUsage } from "./CpuUsage";
|
|
2
|
-
export { default as Mapper } from "./Mapper";
|
|
2
|
+
export { default as Mapper, type MapperMode } from "./Mapper";
|
|
3
|
+
export { MapperResolutionError } from "./MapperResolutionError";
|
|
3
4
|
export { default as MemoryUsage } from "./MemoryUsage";
|
|
4
5
|
export { default as Metrics } from "./MetricsBase";
|
|
5
6
|
export { default as Time } from "./Time";
|
package/dist/utils/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as CpuUsage } from "./CpuUsage";
|
|
2
2
|
export { default as Mapper } from "./Mapper";
|
|
3
|
+
export { MapperResolutionError } from "./MapperResolutionError";
|
|
3
4
|
export { default as MemoryUsage } from "./MemoryUsage";
|
|
4
5
|
export { default as Metrics } from "./MetricsBase";
|
|
5
6
|
export { default as Time } from "./Time";
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAmB,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC"}
|