@blokjs/shared 2.0.0 → 2.1.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/BlokError.d.ts +32 -1
- package/dist/BlokError.js +34 -1
- package/dist/GlobalError.js +5 -1
- package/dist/index.d.ts +15 -15
- package/dist/index.js +7 -3
- package/dist/utils/lowerRefs.d.ts +25 -1
- package/dist/utils/lowerRefs.js +8 -3
- package/package.json +1 -1
package/dist/BlokError.d.ts
CHANGED
|
@@ -204,7 +204,9 @@ export default class BlokError extends GlobalError {
|
|
|
204
204
|
export declare const WORKFLOW_INPUT_VALIDATION = "WORKFLOW_INPUT_VALIDATION";
|
|
205
205
|
/**
|
|
206
206
|
* True when `err` is the ADR-0015 **input-validation gate's** deterministic
|
|
207
|
-
* failure — a `GlobalError` carrying the {@link WORKFLOW_INPUT_VALIDATION} tag
|
|
207
|
+
* failure — a `GlobalError` carrying the {@link WORKFLOW_INPUT_VALIDATION} tag
|
|
208
|
+
* (in practice a {@link WorkflowInputValidationError}; the tag is what's matched
|
|
209
|
+
* so an error that crossed a serialization boundary still classifies).
|
|
208
210
|
*
|
|
209
211
|
* Triggers consult this to route the error to DLQ / drop / a 4xx response
|
|
210
212
|
* instead of a poison-message loop (worker burning its retry budget, pub/sub
|
|
@@ -217,3 +219,32 @@ export declare const WORKFLOW_INPUT_VALIDATION = "WORKFLOW_INPUT_VALIDATION";
|
|
|
217
219
|
* never surfaced by the webhook 4xx path.
|
|
218
220
|
*/
|
|
219
221
|
export declare function isNonRetryableValidationError(err: unknown): boolean;
|
|
222
|
+
/** One Zod issue, flattened to plain data for the wire. */
|
|
223
|
+
export interface WorkflowInputValidationIssue {
|
|
224
|
+
path: (string | number)[];
|
|
225
|
+
message: string;
|
|
226
|
+
code: string;
|
|
227
|
+
}
|
|
228
|
+
/** What the ADR-0015 gate knows about the rejection. */
|
|
229
|
+
export interface WorkflowInputValidationInfo {
|
|
230
|
+
/** The workflow whose declared `input` schema rejected the payload. */
|
|
231
|
+
workflowName: string;
|
|
232
|
+
/** Every Zod issue, in schema order. */
|
|
233
|
+
issues: WorkflowInputValidationIssue[];
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* ADR 0015 — thrown by the input gate in `TriggerBase.run()` when the invoking
|
|
237
|
+
* trigger's payload fails the workflow's declared `input` Zod.
|
|
238
|
+
*
|
|
239
|
+
* Named + exported so callers can `instanceof` it, matching the vocabulary of
|
|
240
|
+
* the other gate errors (`ConcurrencyLimitError`, `QueueExpiredError`,
|
|
241
|
+
* `MapperResolutionError`). It extends `GlobalError` — carrying code 400, the
|
|
242
|
+
* {@link WORKFLOW_INPUT_VALIDATION} tag on `context.name`, and the structured
|
|
243
|
+
* `validation_errors` json — so every existing transport translation
|
|
244
|
+
* (HTTP 400, MCP `isError`, gRPC error status, worker/pubsub/webhook DLQ
|
|
245
|
+
* routing via {@link isNonRetryableValidationError}) keeps working untouched.
|
|
246
|
+
*/
|
|
247
|
+
export declare class WorkflowInputValidationError extends GlobalError {
|
|
248
|
+
readonly info: WorkflowInputValidationInfo;
|
|
249
|
+
constructor(info: WorkflowInputValidationInfo);
|
|
250
|
+
}
|
package/dist/BlokError.js
CHANGED
|
@@ -335,7 +335,9 @@ export default class BlokError extends GlobalError {
|
|
|
335
335
|
export const WORKFLOW_INPUT_VALIDATION = "WORKFLOW_INPUT_VALIDATION";
|
|
336
336
|
/**
|
|
337
337
|
* True when `err` is the ADR-0015 **input-validation gate's** deterministic
|
|
338
|
-
* failure — a `GlobalError` carrying the {@link WORKFLOW_INPUT_VALIDATION} tag
|
|
338
|
+
* failure — a `GlobalError` carrying the {@link WORKFLOW_INPUT_VALIDATION} tag
|
|
339
|
+
* (in practice a {@link WorkflowInputValidationError}; the tag is what's matched
|
|
340
|
+
* so an error that crossed a serialization boundary still classifies).
|
|
339
341
|
*
|
|
340
342
|
* Triggers consult this to route the error to DLQ / drop / a 4xx response
|
|
341
343
|
* instead of a poison-message loop (worker burning its retry budget, pub/sub
|
|
@@ -350,3 +352,34 @@ export const WORKFLOW_INPUT_VALIDATION = "WORKFLOW_INPUT_VALIDATION";
|
|
|
350
352
|
export function isNonRetryableValidationError(err) {
|
|
351
353
|
return err instanceof GlobalError && err.context.name === WORKFLOW_INPUT_VALIDATION;
|
|
352
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* ADR 0015 — thrown by the input gate in `TriggerBase.run()` when the invoking
|
|
357
|
+
* trigger's payload fails the workflow's declared `input` Zod.
|
|
358
|
+
*
|
|
359
|
+
* Named + exported so callers can `instanceof` it, matching the vocabulary of
|
|
360
|
+
* the other gate errors (`ConcurrencyLimitError`, `QueueExpiredError`,
|
|
361
|
+
* `MapperResolutionError`). It extends `GlobalError` — carrying code 400, the
|
|
362
|
+
* {@link WORKFLOW_INPUT_VALIDATION} tag on `context.name`, and the structured
|
|
363
|
+
* `validation_errors` json — so every existing transport translation
|
|
364
|
+
* (HTTP 400, MCP `isError`, gRPC error status, worker/pubsub/webhook DLQ
|
|
365
|
+
* routing via {@link isNonRetryableValidationError}) keeps working untouched.
|
|
366
|
+
*/
|
|
367
|
+
export class WorkflowInputValidationError extends GlobalError {
|
|
368
|
+
info;
|
|
369
|
+
constructor(info) {
|
|
370
|
+
const summary = info.issues.map((i) => `${i.path.join(".") || "(root)"} (${i.message})`).join(", ");
|
|
371
|
+
super(`Input validation failed for workflow '${info.workflowName}': ${summary}`);
|
|
372
|
+
// `GlobalError`'s own constructor pins the prototype to GlobalError.prototype,
|
|
373
|
+
// so a subclass MUST re-pin or `instanceof WorkflowInputValidationError` is false.
|
|
374
|
+
Object.setPrototypeOf(this, WorkflowInputValidationError.prototype);
|
|
375
|
+
this.name = "WorkflowInputValidationError";
|
|
376
|
+
this.info = info;
|
|
377
|
+
this.setCode(400);
|
|
378
|
+
this.setName(WORKFLOW_INPUT_VALIDATION);
|
|
379
|
+
this.setJson({
|
|
380
|
+
error: "Input validation failed",
|
|
381
|
+
workflowName: info.workflowName,
|
|
382
|
+
validation_errors: info.issues,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
package/dist/GlobalError.js
CHANGED
|
@@ -2,7 +2,11 @@ export default class GlobalError extends Error {
|
|
|
2
2
|
context = { message: "" };
|
|
3
3
|
constructor(msg) {
|
|
4
4
|
super(msg);
|
|
5
|
-
|
|
5
|
+
// Standard Error-subclass pattern: pin to new.target's prototype (the
|
|
6
|
+
// class actually being constructed), not GlobalError's own — otherwise
|
|
7
|
+
// this clobbers a subclass's correct prototype and `instanceof Subclass`
|
|
8
|
+
// silently fails for any subclass that doesn't re-pin itself (#736).
|
|
9
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
6
10
|
this.context.message = msg;
|
|
7
11
|
}
|
|
8
12
|
setCode(code) {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,30 +6,30 @@
|
|
|
6
6
|
* published as a back-compat alias — existing imports keep working unchanged —
|
|
7
7
|
* but new code should import from `@blokjs/core`.
|
|
8
8
|
*/
|
|
9
|
-
import BlokError, { type BlokErrorOpts, DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, ErrorCategory, ErrorSeverity, isNonRetryableValidationError, type NodeErrorPayload, WORKFLOW_INPUT_VALIDATION } from "./BlokError.js";
|
|
9
|
+
import BlokError, { type BlokErrorOpts, DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, ErrorCategory, ErrorSeverity, isNonRetryableValidationError, type NodeErrorPayload, WORKFLOW_INPUT_VALIDATION, WorkflowInputValidationError, type WorkflowInputValidationInfo, type WorkflowInputValidationIssue } from "./BlokError.js";
|
|
10
10
|
import GlobalError from "./GlobalError.js";
|
|
11
11
|
import GlobalLogger from "./GlobalLogger.js";
|
|
12
12
|
import { Metrics, type MetricsType } from "./Metrics.js";
|
|
13
13
|
import NodeBase from "./NodeBase.js";
|
|
14
14
|
import Trigger from "./Trigger.js";
|
|
15
|
-
import ConfigContext from "./types/ConfigContext.js";
|
|
15
|
+
import type ConfigContext from "./types/ConfigContext.js";
|
|
16
16
|
import type ConnectionContext from "./types/ConnectionContext.js";
|
|
17
|
-
import Context from "./types/Context.js";
|
|
18
|
-
import EnvContext from "./types/EnvContext.js";
|
|
19
|
-
import ErrorContext from "./types/ErrorContext.js";
|
|
20
|
-
import FunctionContext from "./types/FunctionContext.js";
|
|
21
|
-
import LoggerContext from "./types/LoggerContext.js";
|
|
22
|
-
import NodeConfigContext from "./types/NodeConfigContext.js";
|
|
23
|
-
import RequestContext from "./types/RequestContext.js";
|
|
17
|
+
import type Context from "./types/Context.js";
|
|
18
|
+
import type EnvContext from "./types/EnvContext.js";
|
|
19
|
+
import type ErrorContext from "./types/ErrorContext.js";
|
|
20
|
+
import type FunctionContext from "./types/FunctionContext.js";
|
|
21
|
+
import type LoggerContext from "./types/LoggerContext.js";
|
|
22
|
+
import type NodeConfigContext from "./types/NodeConfigContext.js";
|
|
23
|
+
import type RequestContext from "./types/RequestContext.js";
|
|
24
24
|
import { RESPOND_BRAND, type RespondEnvelope, isRespondEnvelope } from "./types/RespondEnvelope.js";
|
|
25
|
-
import ResponseContext from "./types/ResponseContext.js";
|
|
26
|
-
import StateContext from "./types/StateContext.js";
|
|
27
|
-
import Step from "./types/Step.js";
|
|
25
|
+
import type ResponseContext from "./types/ResponseContext.js";
|
|
26
|
+
import type StateContext from "./types/StateContext.js";
|
|
27
|
+
import type Step from "./types/Step.js";
|
|
28
28
|
import type StreamContext from "./types/StreamContext.js";
|
|
29
|
-
import VarsContext from "./types/VarsContext.js";
|
|
29
|
+
import type VarsContext from "./types/VarsContext.js";
|
|
30
30
|
import mapper from "./utils/Mapper.js";
|
|
31
31
|
import { MapperResolutionError } from "./utils/MapperResolutionError.js";
|
|
32
32
|
import MemoryUsage from "./utils/MemoryUsage.js";
|
|
33
33
|
import { NamedMissingStateError } from "./utils/NamedMissingStateError.js";
|
|
34
|
-
import { type StructuralRef, lowerRefs } from "./utils/lowerRefs.js";
|
|
35
|
-
export { NodeBase, Context, RequestContext, ResponseContext, RESPOND_BRAND, type RespondEnvelope, isRespondEnvelope, EnvContext, ErrorContext, LoggerContext, ConfigContext, type ConnectionContext, type StreamContext, Trigger, NodeConfigContext, FunctionContext, StateContext, VarsContext, Step, GlobalLogger, GlobalError, BlokError, type BlokErrorOpts, type NodeErrorPayload, ErrorCategory, ErrorSeverity, DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, isNonRetryableValidationError, WORKFLOW_INPUT_VALIDATION, Metrics, MemoryUsage, type MetricsType, mapper, MapperResolutionError, NamedMissingStateError, lowerRefs, type StructuralRef, };
|
|
34
|
+
import { type StructuralRef, type StructuralTpl, isStructuralRef, isStructuralTpl, lowerRefs } from "./utils/lowerRefs.js";
|
|
35
|
+
export { NodeBase, type Context, type RequestContext, type ResponseContext, RESPOND_BRAND, type RespondEnvelope, isRespondEnvelope, type EnvContext, type ErrorContext, type LoggerContext, type ConfigContext, type ConnectionContext, type StreamContext, Trigger, type NodeConfigContext, type FunctionContext, type StateContext, type VarsContext, type Step, GlobalLogger, GlobalError, BlokError, type BlokErrorOpts, type NodeErrorPayload, ErrorCategory, ErrorSeverity, DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, isNonRetryableValidationError, WORKFLOW_INPUT_VALIDATION, WorkflowInputValidationError, type WorkflowInputValidationInfo, type WorkflowInputValidationIssue, Metrics, MemoryUsage, type MetricsType, mapper, MapperResolutionError, NamedMissingStateError, lowerRefs, isStructuralRef, isStructuralTpl, type StructuralRef, type StructuralTpl, };
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* published as a back-compat alias — existing imports keep working unchanged —
|
|
7
7
|
* but new code should import from `@blokjs/core`.
|
|
8
8
|
*/
|
|
9
|
-
import BlokError, { DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, ErrorCategory, ErrorSeverity, isNonRetryableValidationError, WORKFLOW_INPUT_VALIDATION, } from "./BlokError.js";
|
|
9
|
+
import BlokError, { DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, ErrorCategory, ErrorSeverity, isNonRetryableValidationError, WORKFLOW_INPUT_VALIDATION, WorkflowInputValidationError, } from "./BlokError.js";
|
|
10
10
|
import GlobalError from "./GlobalError.js";
|
|
11
11
|
import GlobalLogger from "./GlobalLogger.js";
|
|
12
12
|
import { Metrics } from "./Metrics.js";
|
|
@@ -17,5 +17,9 @@ import mapper from "./utils/Mapper.js";
|
|
|
17
17
|
import { MapperResolutionError } from "./utils/MapperResolutionError.js";
|
|
18
18
|
import MemoryUsage from "./utils/MemoryUsage.js";
|
|
19
19
|
import { NamedMissingStateError } from "./utils/NamedMissingStateError.js";
|
|
20
|
-
import { lowerRefs } from "./utils/lowerRefs.js";
|
|
21
|
-
export { NodeBase, RESPOND_BRAND, isRespondEnvelope, Trigger, GlobalLogger, GlobalError, BlokError, ErrorCategory, ErrorSeverity, DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, isNonRetryableValidationError, WORKFLOW_INPUT_VALIDATION, Metrics, MemoryUsage, mapper, MapperResolutionError, NamedMissingStateError, lowerRefs,
|
|
20
|
+
import { isStructuralRef, isStructuralTpl, lowerRefs } from "./utils/lowerRefs.js";
|
|
21
|
+
export { NodeBase, RESPOND_BRAND, isRespondEnvelope, Trigger, GlobalLogger, GlobalError, BlokError, ErrorCategory, ErrorSeverity, DEFAULT_HTTP_STATUS, DEFAULT_RETRYABLE, isNonRetryableValidationError, WORKFLOW_INPUT_VALIDATION, WorkflowInputValidationError, Metrics, MemoryUsage, mapper, MapperResolutionError, NamedMissingStateError, lowerRefs,
|
|
22
|
+
// The lowering pass's OWN predicates — exported so the normalizer's
|
|
23
|
+
// post-lowering total invariant (#707) tests the identical rule instead of
|
|
24
|
+
// a second hand-written copy that could drift from what `lowerRefs` lowers.
|
|
25
|
+
isStructuralRef, isStructuralTpl, };
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
* BEFORE the Mapper, and compiles every structural `{$ref}` into EXACTLY the
|
|
15
15
|
* wire string today's engine already resolves. The Mapper stays untouched.
|
|
16
16
|
*
|
|
17
|
-
* Scope: the STEP-INPUTS, TRIGGER-ROOT, and TPL positions
|
|
17
|
+
* Scope: the STEP-INPUTS, TRIGGER-ROOT, and TPL positions — plus, since #707,
|
|
18
|
+
* the three RESOLVED-KEY positions (step `idempotencyKey`, trigger
|
|
19
|
+
* `concurrencyKey`, trigger `debounce.key`), which the normalizer feeds through
|
|
20
|
+
* this same encoder. The caller picks the positions; this pass is unaware of
|
|
21
|
+
* them, which is why control positions (`branch.when`, `forEach.in`,
|
|
22
|
+
* `switch.on`) are simply never handed to it.
|
|
18
23
|
* - step input value: `"js/ctx.state.<root>" + path`
|
|
19
24
|
* - `path` mapping: string segment → `.seg`, numeric → `[n]`,
|
|
20
25
|
* empty `path: []` (whole-output ref) → `"js/ctx.state.<root>"`.
|
|
@@ -36,6 +41,25 @@ export interface StructuralRef {
|
|
|
36
41
|
path?: (string | number)[];
|
|
37
42
|
};
|
|
38
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Is `value` the reserved `{$ref}` sentinel — a single-key object whose only
|
|
46
|
+
* key is `$ref` and whose `$ref.step` is a string?
|
|
47
|
+
*
|
|
48
|
+
* The single-key + string-step guard is what makes `$ref` safe to reserve:
|
|
49
|
+
* a step-inputs object that legitimately carries unrelated data (even data
|
|
50
|
+
* with the literal keys `step`/`path`) is NOT a ref and passes through
|
|
51
|
+
* untouched. ADR 0001 confirmed no current workflow uses `$ref` as user data.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isStructuralRef(value: object): value is StructuralRef;
|
|
54
|
+
/**
|
|
55
|
+
* The structural template sentinel (#425) — a single-key `{$tpl: [...]}` whose
|
|
56
|
+
* segments alternate raw strings and `{$ref}` nodes (plus the occasional literal
|
|
57
|
+
* non-string interpolation). Mirrors `tpl\`...\`` in `core/runner/src/stepBuilder.ts`.
|
|
58
|
+
*/
|
|
59
|
+
export interface StructuralTpl {
|
|
60
|
+
$tpl: unknown[];
|
|
61
|
+
}
|
|
62
|
+
export declare function isStructuralTpl(value: object): value is StructuralTpl;
|
|
39
63
|
/**
|
|
40
64
|
* Recursively lower every structural `{$ref}` inside `value` to its wire
|
|
41
65
|
* string. Pure — returns a NEW value, never mutates the input. Plain
|
package/dist/utils/lowerRefs.js
CHANGED
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
* BEFORE the Mapper, and compiles every structural `{$ref}` into EXACTLY the
|
|
15
15
|
* wire string today's engine already resolves. The Mapper stays untouched.
|
|
16
16
|
*
|
|
17
|
-
* Scope: the STEP-INPUTS, TRIGGER-ROOT, and TPL positions
|
|
17
|
+
* Scope: the STEP-INPUTS, TRIGGER-ROOT, and TPL positions — plus, since #707,
|
|
18
|
+
* the three RESOLVED-KEY positions (step `idempotencyKey`, trigger
|
|
19
|
+
* `concurrencyKey`, trigger `debounce.key`), which the normalizer feeds through
|
|
20
|
+
* this same encoder. The caller picks the positions; this pass is unaware of
|
|
21
|
+
* them, which is why control positions (`branch.when`, `forEach.in`,
|
|
22
|
+
* `switch.on`) are simply never handed to it.
|
|
18
23
|
* - step input value: `"js/ctx.state.<root>" + path`
|
|
19
24
|
* - `path` mapping: string segment → `.seg`, numeric → `[n]`,
|
|
20
25
|
* empty `path: []` (whole-output ref) → `"js/ctx.state.<root>"`.
|
|
@@ -38,7 +43,7 @@
|
|
|
38
43
|
* with the literal keys `step`/`path`) is NOT a ref and passes through
|
|
39
44
|
* untouched. ADR 0001 confirmed no current workflow uses `$ref` as user data.
|
|
40
45
|
*/
|
|
41
|
-
function isStructuralRef(value) {
|
|
46
|
+
export function isStructuralRef(value) {
|
|
42
47
|
const keys = Object.keys(value);
|
|
43
48
|
if (keys.length !== 1 || keys[0] !== "$ref")
|
|
44
49
|
return false;
|
|
@@ -126,7 +131,7 @@ function refExpr(ref) {
|
|
|
126
131
|
function lowerRef(ref) {
|
|
127
132
|
return `js/${refExpr(ref)}`;
|
|
128
133
|
}
|
|
129
|
-
function isStructuralTpl(value) {
|
|
134
|
+
export function isStructuralTpl(value) {
|
|
130
135
|
const keys = Object.keys(value);
|
|
131
136
|
return keys.length === 1 && keys[0] === "$tpl" && Array.isArray(value.$tpl);
|
|
132
137
|
}
|