callman-core 1.2.0 → 1.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/dist/scenario-runner/adapters/connection-config.d.ts +34 -0
- package/dist/scenario-runner/adapters/connection-config.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/connection-config.js +6 -0
- package/dist/scenario-runner/adapters/connection-config.js.map +1 -0
- package/dist/scenario-runner/adapters/db-executor.d.ts +4 -0
- package/dist/scenario-runner/adapters/db-executor.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/db-executor.js +42 -0
- package/dist/scenario-runner/adapters/db-executor.js.map +1 -0
- package/dist/scenario-runner/adapters/helpers/abort.d.ts +4 -0
- package/dist/scenario-runner/adapters/helpers/abort.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/helpers/abort.js +47 -0
- package/dist/scenario-runner/adapters/helpers/abort.js.map +1 -0
- package/dist/scenario-runner/adapters/helpers/failure-messages.d.ts +29 -0
- package/dist/scenario-runner/adapters/helpers/failure-messages.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/helpers/failure-messages.js +64 -0
- package/dist/scenario-runner/adapters/helpers/failure-messages.js.map +1 -0
- package/dist/scenario-runner/adapters/helpers/mutations.d.ts +7 -0
- package/dist/scenario-runner/adapters/helpers/mutations.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/helpers/mutations.js +28 -0
- package/dist/scenario-runner/adapters/helpers/mutations.js.map +1 -0
- package/dist/scenario-runner/adapters/helpers/request-helpers.d.ts +31 -0
- package/dist/scenario-runner/adapters/helpers/request-helpers.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/helpers/request-helpers.js +247 -0
- package/dist/scenario-runner/adapters/helpers/request-helpers.js.map +1 -0
- package/dist/scenario-runner/adapters/helpers/response-shapes.d.ts +3 -0
- package/dist/scenario-runner/adapters/helpers/response-shapes.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/helpers/response-shapes.js +32 -0
- package/dist/scenario-runner/adapters/helpers/response-shapes.js.map +1 -0
- package/dist/scenario-runner/adapters/helpers/templates.d.ts +2 -0
- package/dist/scenario-runner/adapters/helpers/templates.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/helpers/templates.js +30 -0
- package/dist/scenario-runner/adapters/helpers/templates.js.map +1 -0
- package/dist/scenario-runner/adapters/index.d.ts +12 -0
- package/dist/scenario-runner/adapters/index.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/index.js +30 -0
- package/dist/scenario-runner/adapters/index.js.map +1 -0
- package/dist/scenario-runner/adapters/kafka-executor.d.ts +4 -0
- package/dist/scenario-runner/adapters/kafka-executor.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/kafka-executor.js +35 -0
- package/dist/scenario-runner/adapters/kafka-executor.js.map +1 -0
- package/dist/scenario-runner/adapters/notification-executor.d.ts +4 -0
- package/dist/scenario-runner/adapters/notification-executor.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/notification-executor.js +67 -0
- package/dist/scenario-runner/adapters/notification-executor.js.map +1 -0
- package/dist/scenario-runner/adapters/redis-executor.d.ts +4 -0
- package/dist/scenario-runner/adapters/redis-executor.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/redis-executor.js +78 -0
- package/dist/scenario-runner/adapters/redis-executor.js.map +1 -0
- package/dist/scenario-runner/adapters/request-executor.d.ts +4 -0
- package/dist/scenario-runner/adapters/request-executor.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/request-executor.js +262 -0
- package/dist/scenario-runner/adapters/request-executor.js.map +1 -0
- package/dist/scenario-runner/adapters/script-executor.d.ts +4 -0
- package/dist/scenario-runner/adapters/script-executor.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/script-executor.js +113 -0
- package/dist/scenario-runner/adapters/script-executor.js.map +1 -0
- package/dist/scenario-runner/adapters/types.d.ts +82 -0
- package/dist/scenario-runner/adapters/types.d.ts.map +1 -0
- package/dist/scenario-runner/adapters/types.js +9 -0
- package/dist/scenario-runner/adapters/types.js.map +1 -0
- package/dist/scenario-runner/condition.d.ts.map +1 -1
- package/dist/scenario-runner/condition.js +87 -78
- package/dist/scenario-runner/condition.js.map +1 -1
- package/dist/scenario-runner/index.d.ts +2 -1
- package/dist/scenario-runner/index.d.ts.map +1 -1
- package/dist/scenario-runner/index.js +7 -1
- package/dist/scenario-runner/index.js.map +1 -1
- package/dist-cjs/scenario-runner/adapters/connection-config.js +7 -0
- package/dist-cjs/scenario-runner/adapters/connection-config.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/db-executor.js +46 -0
- package/dist-cjs/scenario-runner/adapters/db-executor.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/helpers/abort.js +52 -0
- package/dist-cjs/scenario-runner/adapters/helpers/abort.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/helpers/failure-messages.js +69 -0
- package/dist-cjs/scenario-runner/adapters/helpers/failure-messages.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/helpers/mutations.js +32 -0
- package/dist-cjs/scenario-runner/adapters/helpers/mutations.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/helpers/request-helpers.js +263 -0
- package/dist-cjs/scenario-runner/adapters/helpers/request-helpers.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/helpers/response-shapes.js +36 -0
- package/dist-cjs/scenario-runner/adapters/helpers/response-shapes.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/helpers/templates.js +34 -0
- package/dist-cjs/scenario-runner/adapters/helpers/templates.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/index.js +40 -0
- package/dist-cjs/scenario-runner/adapters/index.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/kafka-executor.js +39 -0
- package/dist-cjs/scenario-runner/adapters/kafka-executor.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/notification-executor.js +71 -0
- package/dist-cjs/scenario-runner/adapters/notification-executor.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/redis-executor.js +82 -0
- package/dist-cjs/scenario-runner/adapters/redis-executor.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/request-executor.js +266 -0
- package/dist-cjs/scenario-runner/adapters/request-executor.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/script-executor.js +117 -0
- package/dist-cjs/scenario-runner/adapters/script-executor.js.map +1 -0
- package/dist-cjs/scenario-runner/adapters/types.js +10 -0
- package/dist-cjs/scenario-runner/adapters/types.js.map +1 -0
- package/dist-cjs/scenario-runner/condition.js +87 -78
- package/dist-cjs/scenario-runner/condition.js.map +1 -1
- package/dist-cjs/scenario-runner/index.js +8 -5
- package/dist-cjs/scenario-runner/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Build the response template root object used by downstream nodes
|
|
2
|
+
// (template substitution, condition evaluation). Lifted from
|
|
3
|
+
// p_man_backend's `buildResponseRoot` (line 167). The shape preserves
|
|
4
|
+
// what the existing scenario template engine expects:
|
|
5
|
+
// - top-level fields from JSON body (spread)
|
|
6
|
+
// - `data` resolved from body.data when present, else parsed body
|
|
7
|
+
// - `body`, `rawBody`, `text` for body access variants
|
|
8
|
+
// - `status`, `statusText`, `headers` (record form), `cookies`,
|
|
9
|
+
// `durationMs`
|
|
10
|
+
//
|
|
11
|
+
// Pure transformation — used by both the request executor and any node
|
|
12
|
+
// that needs to expose a request response to subsequent nodes.
|
|
13
|
+
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
export const buildResponseRoot = (response) => {
|
|
15
|
+
const responsePayload = response.bodyJson ?? response.bodyText;
|
|
16
|
+
const responseData = isPlainObject(response.bodyJson) && "data" in response.bodyJson
|
|
17
|
+
? response.bodyJson.data
|
|
18
|
+
: responsePayload;
|
|
19
|
+
return {
|
|
20
|
+
...(isPlainObject(response.bodyJson) ? response.bodyJson : {}),
|
|
21
|
+
data: responseData,
|
|
22
|
+
body: responsePayload,
|
|
23
|
+
rawBody: response.bodyText,
|
|
24
|
+
text: response.bodyText,
|
|
25
|
+
status: response.status,
|
|
26
|
+
statusText: response.statusText,
|
|
27
|
+
headers: Object.fromEntries(response.headers.map((entry) => [entry.key, entry.value])),
|
|
28
|
+
cookies: Object.fromEntries(response.cookies.map((value) => ["set-cookie", value])),
|
|
29
|
+
durationMs: response.durationMs,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=response-shapes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-shapes.js","sourceRoot":"","sources":["../../../../src/scenario-runner/adapters/helpers/response-shapes.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,6DAA6D;AAC7D,sEAAsE;AACtE,sDAAsD;AACtD,+CAA+C;AAC/C,oEAAoE;AACpE,yDAAyD;AACzD,kEAAkE;AAClE,mBAAmB;AACnB,EAAE;AACF,uEAAuE;AACvE,+DAA+D;AAI/D,MAAM,aAAa,GAAG,CAAC,KAAc,EAAoC,EAAE,CACzE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAiC,EAA2B,EAAE;IAC9F,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC;IAC/D,MAAM,YAAY,GAChB,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,IAAI,QAAQ,CAAC,QAAQ;QAC7D,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI;QACxB,CAAC,CAAC,eAAe,CAAC;IAEtB,OAAO;QACL,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,QAAQ,CAAC,QAAQ;QAC1B,IAAI,EAAE,QAAQ,CAAC,QAAQ;QACvB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACtF,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;QACnF,UAAU,EAAE,QAAQ,CAAC,UAAU;KAChC,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../../src/scenario-runner/adapters/helpers/templates.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,kCAAkC,GAC7C,OAAO,OAAO,EACd,gBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,EACvE,OAAM,OAAO,CAAC,MAAM,CAAyB,KAC5C,OAsBF,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Deep template resolver for scenario script return values, mutation
|
|
2
|
+
// values, and any nested unknown payload that may carry `{{token}}`
|
|
3
|
+
// strings. Lifted from backend's `resolveScenarioScriptTemplatesDeep`
|
|
4
|
+
// (runtime-adapters.ts:1154).
|
|
5
|
+
//
|
|
6
|
+
// Pure recursion with cycle detection via WeakSet so caller-supplied
|
|
7
|
+
// objects cannot trigger infinite loops. The caller passes the actual
|
|
8
|
+
// resolver (typically `resolveScenarioTemplateString` from
|
|
9
|
+
// `templateResolver.ts`) so this helper stays runtime-agnostic.
|
|
10
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
export const resolveScenarioScriptTemplatesDeep = (value, templateValues, resolveValue, seen = new WeakSet()) => {
|
|
12
|
+
if (typeof value === "string") {
|
|
13
|
+
return resolveValue(value, templateValues);
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map((entry) => resolveScenarioScriptTemplatesDeep(entry, templateValues, resolveValue, seen));
|
|
17
|
+
}
|
|
18
|
+
if (!isObject(value)) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
if (seen.has(value)) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
seen.add(value);
|
|
25
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [
|
|
26
|
+
key,
|
|
27
|
+
resolveScenarioScriptTemplatesDeep(entryValue, templateValues, resolveValue, seen),
|
|
28
|
+
]));
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../../src/scenario-runner/adapters/helpers/templates.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,oEAAoE;AACpE,sEAAsE;AACtE,8BAA8B;AAC9B,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,2DAA2D;AAC3D,gEAAgE;AAEhE,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAChD,KAAc,EACd,cAAsC,EACtC,YAAuE,EACvE,OAAwB,IAAI,OAAO,EAAU,EACpC,EAAE;IACX,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACzB,kCAAkC,CAAC,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,IAAI,CAAC,CAC9E,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChB,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;QAC/C,GAAG;QACH,kCAAkC,CAAC,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,IAAI,CAAC;KACnF,CAAC,CACH,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ScenarioRuntimeAdapters } from "../types.js";
|
|
2
|
+
import type { ScenarioRuntimeAdapterFactoryOptions } from "./types.js";
|
|
3
|
+
export declare const buildScenarioRuntimeAdapters: (options: ScenarioRuntimeAdapterFactoryOptions) => ScenarioRuntimeAdapters;
|
|
4
|
+
export { buildDbExecutor } from "./db-executor.js";
|
|
5
|
+
export { buildKafkaExecutor } from "./kafka-executor.js";
|
|
6
|
+
export { buildNotificationExecutor } from "./notification-executor.js";
|
|
7
|
+
export { buildRedisExecutor } from "./redis-executor.js";
|
|
8
|
+
export { buildRequestExecutor } from "./request-executor.js";
|
|
9
|
+
export { buildScriptExecutor } from "./script-executor.js";
|
|
10
|
+
export type { DatabaseExecuteInput, DatabaseExecuteResult, HttpExecuteInput, HttpExecuteResult, KafkaCaptureInput, KafkaCaptureOutcome, RedisCommandOutcome, RedisExecuteInput, RedisExecuteResult, ScenarioConnectionProvider, ScenarioConnectionRuntime, ScenarioConnectionType, ScenarioRuntimeAdapterDrivers, ScenarioRuntimeAdapterFactoryOptions, } from "./types.js";
|
|
11
|
+
export type { ConnectionRuntimeConfig, DatabaseConnectionConfig, DatabaseConnectionMode, KafkaConnectionConfig, NotificationConnectionConfig, RedisConnectionConfig, } from "./connection-config.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAS3D,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAC;AAEvE,eAAO,MAAM,4BAA4B,GACvC,SAAS,oCAAoC,KAC5C,uBAQD,CAAC;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,yBAAyB,EACzB,sBAAsB,EACtB,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Public surface for scenario runtime adapter orchestration.
|
|
2
|
+
//
|
|
3
|
+
// `buildScenarioRuntimeAdapters(options)` returns a fully wired
|
|
4
|
+
// `ScenarioRuntimeAdapters` instance suitable for passing to
|
|
5
|
+
// `runScenario(...)`. Hosts (backend / electron / future CLI) supply
|
|
6
|
+
// driver callbacks via `options.drivers` for the I/O-heavy executors
|
|
7
|
+
// (HTTP, DB, Redis, Kafka). Script + notification executors default to
|
|
8
|
+
// canonical core implementations but can be overridden.
|
|
9
|
+
import { buildDbExecutor } from "./db-executor.js";
|
|
10
|
+
import { buildKafkaExecutor } from "./kafka-executor.js";
|
|
11
|
+
import { buildNotificationExecutor } from "./notification-executor.js";
|
|
12
|
+
import { buildRedisExecutor } from "./redis-executor.js";
|
|
13
|
+
import { buildRequestExecutor } from "./request-executor.js";
|
|
14
|
+
import { buildScriptExecutor } from "./script-executor.js";
|
|
15
|
+
export const buildScenarioRuntimeAdapters = (options) => ({
|
|
16
|
+
requestExecutor: buildRequestExecutor(options),
|
|
17
|
+
dbExecutor: buildDbExecutor(options),
|
|
18
|
+
redisExecutor: buildRedisExecutor(options),
|
|
19
|
+
kafkaExecutor: buildKafkaExecutor(options),
|
|
20
|
+
scriptExecutor: options.scriptExecutor ?? buildScriptExecutor(options),
|
|
21
|
+
notificationExecutor: options.notificationExecutor ?? buildNotificationExecutor(options),
|
|
22
|
+
});
|
|
23
|
+
// Re-exports so callers don't have to import from sibling files.
|
|
24
|
+
export { buildDbExecutor } from "./db-executor.js";
|
|
25
|
+
export { buildKafkaExecutor } from "./kafka-executor.js";
|
|
26
|
+
export { buildNotificationExecutor } from "./notification-executor.js";
|
|
27
|
+
export { buildRedisExecutor } from "./redis-executor.js";
|
|
28
|
+
export { buildRequestExecutor } from "./request-executor.js";
|
|
29
|
+
export { buildScriptExecutor } from "./script-executor.js";
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,gEAAgE;AAChE,6DAA6D;AAC7D,qEAAqE;AACrE,qEAAqE;AACrE,uEAAuE;AACvE,wDAAwD;AAIxD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAI3D,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,OAA6C,EACpB,EAAE,CAAC,CAAC;IAC7B,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC;IAC9C,UAAU,EAAE,eAAe,CAAC,OAAO,CAAC;IACpC,aAAa,EAAE,kBAAkB,CAAC,OAAO,CAAC;IAC1C,aAAa,EAAE,kBAAkB,CAAC,OAAO,CAAC;IAC1C,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,mBAAmB,CAAC,OAAO,CAAC;IACtE,oBAAoB,EAClB,OAAO,CAAC,oBAAoB,IAAI,yBAAyB,CAAC,OAAO,CAAC;CACrE,CAAC,CAAC;AAEH,iEAAiE;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScenarioKafkaExecutionInput, ScenarioKafkaExecutionResult } from "../types.js";
|
|
2
|
+
import type { ScenarioRuntimeAdapterFactoryOptions } from "./types.js";
|
|
3
|
+
export declare const buildKafkaExecutor: (options: ScenarioRuntimeAdapterFactoryOptions) => (input: ScenarioKafkaExecutionInput) => Promise<ScenarioKafkaExecutionResult>;
|
|
4
|
+
//# sourceMappingURL=kafka-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kafka-executor.d.ts","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/kafka-executor.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAEV,2BAA2B,EAC3B,4BAA4B,EAC7B,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAC;AAOvE,eAAO,MAAM,kBAAkB,GAC7B,SAAS,oCAAoC,MAEtC,OAAO,2BAA2B,KAAG,OAAO,CAAC,4BAA4B,CA2B/E,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Canonical Kafka executor — orchestrates a standalone Kafka scenario node.
|
|
2
|
+
// (Request-prefetch Kafka capture is handled inside request-executor.ts
|
|
3
|
+
// where it is composed with the HTTP call.)
|
|
4
|
+
//
|
|
5
|
+
// The actual Kafka consumer + matcher logic lives in the host-supplied
|
|
6
|
+
// `kafkaExecutor` driver (kafkajs or equivalent). We resolve the
|
|
7
|
+
// connection, validate the topic, and delegate.
|
|
8
|
+
const KAFKA_DEFAULT_CAPTURE_TIMEOUT_MS = 5000;
|
|
9
|
+
const buildKafkaCaptureTimeoutMs = (nodeTimeoutMs) => Math.min(nodeTimeoutMs, KAFKA_DEFAULT_CAPTURE_TIMEOUT_MS);
|
|
10
|
+
export const buildKafkaExecutor = (options) => async (input) => {
|
|
11
|
+
const topic = input.resolvedTopic.trim();
|
|
12
|
+
const timeoutMs = buildKafkaCaptureTimeoutMs(options.nodeTimeoutMs);
|
|
13
|
+
if (!topic) {
|
|
14
|
+
const captureResult = {
|
|
15
|
+
topic: "",
|
|
16
|
+
durationMs: 0,
|
|
17
|
+
timeoutMs,
|
|
18
|
+
messages: [],
|
|
19
|
+
timeline: [],
|
|
20
|
+
isListening: false,
|
|
21
|
+
timedOut: false,
|
|
22
|
+
errorMessage: "Kafka topic is required",
|
|
23
|
+
};
|
|
24
|
+
return { captureResult };
|
|
25
|
+
}
|
|
26
|
+
const connection = await options.resolveConnection(input.node.data.config.connectionId);
|
|
27
|
+
const outcome = await options.drivers.kafkaExecutor({
|
|
28
|
+
connection,
|
|
29
|
+
topic,
|
|
30
|
+
matcher: input.matcher,
|
|
31
|
+
timeoutMs,
|
|
32
|
+
});
|
|
33
|
+
return { captureResult: outcome.capture };
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=kafka-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kafka-executor.js","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/kafka-executor.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,wEAAwE;AACxE,4CAA4C;AAC5C,EAAE;AACF,uEAAuE;AACvE,iEAAiE;AACjE,gDAAgD;AAUhD,MAAM,gCAAgC,GAAG,IAAI,CAAC;AAE9C,MAAM,0BAA0B,GAAG,CAAC,aAAqB,EAAU,EAAE,CACnE,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,gCAAgC,CAAC,CAAC;AAE5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,OAA6C,EAC7C,EAAE,CACF,KAAK,EAAE,KAAkC,EAAyC,EAAE;IAClF,MAAM,KAAK,GAAG,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,0BAA0B,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAEpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,aAAa,GAA+B;YAChD,KAAK,EAAE,EAAE;YACT,UAAU,EAAE,CAAC;YACb,SAAS;YACT,QAAQ,EAAE,EAAE;YACZ,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,KAAK;YAClB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,yBAAyB;SACxC,CAAC;QACF,OAAO,EAAE,aAAa,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACxF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAClD,UAAU;QACV,KAAK;QACL,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS;KACV,CAAC,CAAC;IAEH,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;AAC5C,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScenarioNotificationExecutionInput, ScenarioNotificationExecutionResult } from "../types.js";
|
|
2
|
+
import type { ScenarioRuntimeAdapterFactoryOptions } from "./types.js";
|
|
3
|
+
export declare const buildNotificationExecutor: (options: ScenarioRuntimeAdapterFactoryOptions) => (input: ScenarioNotificationExecutionInput) => Promise<ScenarioNotificationExecutionResult>;
|
|
4
|
+
//# sourceMappingURL=notification-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-executor.d.ts","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/notification-executor.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,kCAAkC,EAClC,mCAAmC,EACpC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAC;AA6DvE,eAAO,MAAM,yBAAyB,GACpC,SAAS,oCAAoC,MAG3C,OAAO,kCAAkC,KACxC,OAAO,CAAC,mCAAmC,CA6B7C,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Canonical notification executor — orchestrates a single Notification
|
|
2
|
+
// scenario node. Resolves the connection, posts the rendered delivery
|
|
3
|
+
// request to the Slack webhook URL via native `fetch`, wraps the result.
|
|
4
|
+
//
|
|
5
|
+
// Slack is the only provider supported today. Adding more providers
|
|
6
|
+
// (email, Discord, etc.) goes in this file as additional dispatchers
|
|
7
|
+
// keyed off `connection.provider`.
|
|
8
|
+
import { withAbortAndTimeout } from "./helpers/abort.js";
|
|
9
|
+
const NOTIFICATION_HARD_TIMEOUT_MS = 10_000;
|
|
10
|
+
const isNotificationConfig = (config) => typeof config === "object" &&
|
|
11
|
+
config !== null &&
|
|
12
|
+
"webhookUrl" in config &&
|
|
13
|
+
typeof config.webhookUrl === "string";
|
|
14
|
+
const sendSlackWebhook = async (config, request, timeoutMs) => {
|
|
15
|
+
const response = await new Promise((resolve, reject) => {
|
|
16
|
+
const timer = setTimeout(() => {
|
|
17
|
+
reject(new Error(`Notification request timed out after ${timeoutMs}ms`));
|
|
18
|
+
}, timeoutMs);
|
|
19
|
+
fetch(config.webhookUrl, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: request.headers,
|
|
22
|
+
body: JSON.stringify(request.body),
|
|
23
|
+
}).then((value) => {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
resolve(value);
|
|
26
|
+
}, (error) => {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
reject(error);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
const bodyText = await response.text();
|
|
32
|
+
return {
|
|
33
|
+
delivered: response.ok,
|
|
34
|
+
responseMeta: {
|
|
35
|
+
status: response.status,
|
|
36
|
+
statusText: response.statusText || null,
|
|
37
|
+
bodyText: bodyText || null,
|
|
38
|
+
},
|
|
39
|
+
failureMessage: response.ok
|
|
40
|
+
? null
|
|
41
|
+
: bodyText || `Slack webhook rejected the message with status ${response.status}`,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export const buildNotificationExecutor = (options) => async (input) => {
|
|
45
|
+
const connection = await options.resolveConnection(input.node.data.config.connectionId);
|
|
46
|
+
if (!isNotificationConfig(connection.config)) {
|
|
47
|
+
throw new Error("Notification connection is missing webhookUrl");
|
|
48
|
+
}
|
|
49
|
+
const config = connection.config;
|
|
50
|
+
const timeoutMs = Math.min(options.nodeTimeoutMs, NOTIFICATION_HARD_TIMEOUT_MS);
|
|
51
|
+
const deliveryResult = await withAbortAndTimeout(sendSlackWebhook(config, input.deliveryRequest, timeoutMs), timeoutMs, `Notification node ${input.stepId}`, input.signal);
|
|
52
|
+
const outputData = {
|
|
53
|
+
delivered: deliveryResult.delivered,
|
|
54
|
+
provider: input.deliveryRequest.provider,
|
|
55
|
+
severity: input.deliveryRequest.severity,
|
|
56
|
+
channel: input.deliveryRequest.channel ?? config.defaultChannel ?? null,
|
|
57
|
+
messagePreview: input.deliveryRequest.messagePreview,
|
|
58
|
+
responseMeta: deliveryResult.responseMeta,
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
...outputData,
|
|
62
|
+
responseMeta: deliveryResult.responseMeta,
|
|
63
|
+
outputData,
|
|
64
|
+
failureMessage: deliveryResult.failureMessage,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=notification-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-executor.js","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/notification-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScenarioRedisExecutionInput, ScenarioRedisExecutionResult } from "../types.js";
|
|
2
|
+
import type { ScenarioRuntimeAdapterFactoryOptions } from "./types.js";
|
|
3
|
+
export declare const buildRedisExecutor: (options: ScenarioRuntimeAdapterFactoryOptions) => (input: ScenarioRedisExecutionInput) => Promise<ScenarioRedisExecutionResult>;
|
|
4
|
+
//# sourceMappingURL=redis-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-executor.d.ts","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/redis-executor.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAEV,2BAA2B,EAC3B,4BAA4B,EAC7B,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAC;AAmBvE,eAAO,MAAM,kBAAkB,GAC7B,SAAS,oCAAoC,MAEtC,OAAO,2BAA2B,KAAG,OAAO,CAAC,4BAA4B,CA0D/E,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Canonical Redis executor — orchestrates a single Redis scenario node.
|
|
2
|
+
// Loops the resolved command list through the host-supplied
|
|
3
|
+
// `redisExecutor` driver, aggregates per-command outcomes, and unwraps
|
|
4
|
+
// to a single scalar result when the node has only one command.
|
|
5
|
+
//
|
|
6
|
+
// Host driver responsibilities:
|
|
7
|
+
// - establish the Redis client (host owns ioredis or equivalent)
|
|
8
|
+
// - run each command, capture timing + raw result
|
|
9
|
+
// - propagate connection-level errors as throws
|
|
10
|
+
// - close the client when done
|
|
11
|
+
import { withAbortAndTimeout } from "./helpers/abort.js";
|
|
12
|
+
const stringifyRedisValue = (value) => {
|
|
13
|
+
if (value === null || value === undefined) {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
20
|
+
return String(value);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.stringify(value);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export const buildRedisExecutor = (options) => async (input) => {
|
|
30
|
+
if (!input.resolvedCommands.length) {
|
|
31
|
+
return {
|
|
32
|
+
commandResults: [],
|
|
33
|
+
redisResult: null,
|
|
34
|
+
outputData: null,
|
|
35
|
+
failureMessage: "Redis node has no executable commands.",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const connection = await options.resolveConnection(input.node.data.config.connectionId);
|
|
39
|
+
const commandResults = [];
|
|
40
|
+
try {
|
|
41
|
+
const driverResults = await withAbortAndTimeout(options.drivers.redisExecutor({
|
|
42
|
+
connection,
|
|
43
|
+
resolvedCommands: input.resolvedCommands,
|
|
44
|
+
}), options.nodeTimeoutMs, `Redis node ${input.stepId}`, input.signal);
|
|
45
|
+
for (const outcome of driverResults) {
|
|
46
|
+
commandResults.push({
|
|
47
|
+
success: true,
|
|
48
|
+
result: outcome.result,
|
|
49
|
+
resultText: stringifyRedisValue(outcome.result),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const redisResult = commandResults.length === 1
|
|
53
|
+
? commandResults[0]?.result ?? null
|
|
54
|
+
: commandResults.map((entry) => entry.result);
|
|
55
|
+
return {
|
|
56
|
+
commandResults,
|
|
57
|
+
redisResult,
|
|
58
|
+
outputData: redisResult,
|
|
59
|
+
failureMessage: null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
64
|
+
commandResults.push({
|
|
65
|
+
success: false,
|
|
66
|
+
result: null,
|
|
67
|
+
resultText: "",
|
|
68
|
+
error: message,
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
commandResults,
|
|
72
|
+
redisResult: null,
|
|
73
|
+
outputData: { commands: commandResults },
|
|
74
|
+
failureMessage: message || "Redis command failed.",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=redis-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-executor.js","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/redis-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScenarioRequestExecutionInput, ScenarioRequestExecutionResult } from "../types.js";
|
|
2
|
+
import type { ScenarioRuntimeAdapterFactoryOptions } from "./types.js";
|
|
3
|
+
export declare const buildRequestExecutor: (options: ScenarioRuntimeAdapterFactoryOptions) => (input: ScenarioRequestExecutionInput) => Promise<ScenarioRequestExecutionResult>;
|
|
4
|
+
//# sourceMappingURL=request-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-executor.d.ts","sourceRoot":"","sources":["../../../src/scenario-runner/adapters/request-executor.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAE/B,MAAM,aAAa,CAAC;AAuBrB,OAAO,KAAK,EAEV,oCAAoC,EACrC,MAAM,YAAY,CAAC;AAiBpB,eAAO,MAAM,oBAAoB,GAC/B,SAAS,oCAAoC,MAEtC,OAAO,6BAA6B,KAAG,OAAO,CAAC,8BAA8B,CA8OnF,CAAC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// Canonical request executor — orchestrates a single Request scenario node:
|
|
2
|
+
// 1. (optional) pre-request script → mutate environment/globals/request
|
|
3
|
+
// 2. resolve auth + templates + body → executable shape
|
|
4
|
+
// 3. (optional) Kafka prefetch — start consumer, run HTTP, capture matched message
|
|
5
|
+
// 4. fall through to plain HTTP when no prefetch is configured
|
|
6
|
+
// 5. (optional) post-response script → mutate env/globals
|
|
7
|
+
// 6. (optional) tests script → collect test results, mutate env/globals
|
|
8
|
+
// 7. validate contract assertions
|
|
9
|
+
// 8. assemble failure message + return ScenarioRequestExecutionResult
|
|
10
|
+
//
|
|
11
|
+
// Driver injection point: `httpExecutor` and `kafkaExecutor`. Everything
|
|
12
|
+
// else (script execution, contract validation, template resolution) uses
|
|
13
|
+
// canonical core utilities.
|
|
14
|
+
import { buildScenarioTemplateValues, resolveScenarioJsonTemplateString, resolveScenarioTemplateString, replaceScenarioTemplateTokensForJsonValidation, } from "../templateResolver.js";
|
|
15
|
+
import { validateContracts } from "../../contract/contractValidator.js";
|
|
16
|
+
import { withAbortAndTimeout } from "./helpers/abort.js";
|
|
17
|
+
import { buildRequestFailureMessage, summarizeContractResults, } from "./helpers/failure-messages.js";
|
|
18
|
+
import { applyVariableMutations } from "./helpers/mutations.js";
|
|
19
|
+
import { buildResponseRoot } from "./helpers/response-shapes.js";
|
|
20
|
+
import { createRequestStateFromNode, normalizeExecuteRequestFailure, normalizeRequestForExecution, toRequestResponse, toRequestScriptResponse, } from "./helpers/request-helpers.js";
|
|
21
|
+
const buildBodyResolverHelpers = () => ({
|
|
22
|
+
replaceScenarioTemplateTokensForJsonValidation,
|
|
23
|
+
resolveScenarioJsonTemplateString,
|
|
24
|
+
resolveScenarioTemplateString,
|
|
25
|
+
});
|
|
26
|
+
const httpResultToExecuteRequestResult = (http) => ({
|
|
27
|
+
status: http.status,
|
|
28
|
+
data: http.data,
|
|
29
|
+
headers: http.headers,
|
|
30
|
+
duration: http.duration,
|
|
31
|
+
});
|
|
32
|
+
export const buildRequestExecutor = (options) => async (input) => {
|
|
33
|
+
// We can't directly call the canonical scriptExecutor from here —
|
|
34
|
+
// it lives on the same factory result and may be the host's override.
|
|
35
|
+
// Instead, we receive it via closure when the factory wires up.
|
|
36
|
+
// For now, we depend on the host's `scriptExecutor` being supplied
|
|
37
|
+
// via options so request-stage scripts run through whatever sandbox
|
|
38
|
+
// the host configured.
|
|
39
|
+
//
|
|
40
|
+
// Note: the script executor in this orchestration path runs *request
|
|
41
|
+
// stage* scripts (pre-request / post-response / tests). The default
|
|
42
|
+
// implementation in script-executor.ts knows how to dispatch to the
|
|
43
|
+
// canonical sandbox; hosts that want to customize override it.
|
|
44
|
+
const requestState = createRequestStateFromNode(input.node);
|
|
45
|
+
const scopes = {
|
|
46
|
+
environment: { ...input.context.environment },
|
|
47
|
+
globals: { ...input.context.globals },
|
|
48
|
+
};
|
|
49
|
+
let currentRequestState = requestState;
|
|
50
|
+
const runRequestStageScript = async (stage, script, response) => {
|
|
51
|
+
// The host's scriptExecutor adapter handles request-stage scripts
|
|
52
|
+
// when it receives a payload with `stage !== "script"`. We delegate
|
|
53
|
+
// through the same adapter shape that runScenario uses so behavior
|
|
54
|
+
// matches the script-only flow exactly.
|
|
55
|
+
const scriptResult = await invokeRequestStageScript(options, {
|
|
56
|
+
stage,
|
|
57
|
+
script,
|
|
58
|
+
timeoutMs: options.scriptTimeoutMs,
|
|
59
|
+
request: currentRequestState,
|
|
60
|
+
response: response
|
|
61
|
+
? toRequestScriptResponse(response)
|
|
62
|
+
: null,
|
|
63
|
+
environment: scopes.environment,
|
|
64
|
+
globals: scopes.globals,
|
|
65
|
+
workflowContext: input.context.workflowContext,
|
|
66
|
+
nodeMetaByLabel: input.context.nodeMetaByLabel,
|
|
67
|
+
});
|
|
68
|
+
if (scriptResult.request) {
|
|
69
|
+
currentRequestState = scriptResult.request;
|
|
70
|
+
}
|
|
71
|
+
applyVariableMutations(scopes, scriptResult.variableMutations);
|
|
72
|
+
return scriptResult;
|
|
73
|
+
};
|
|
74
|
+
// 1. Pre-request script ------------------------------------------------
|
|
75
|
+
if (input.node.data.config.preRequestScript?.trim()) {
|
|
76
|
+
const preResult = await runRequestStageScript("pre-request", input.node.data.config.preRequestScript, null);
|
|
77
|
+
if (preResult.error) {
|
|
78
|
+
const failureResponse = toRequestResponse(normalizeExecuteRequestFailure(preResult.error.message));
|
|
79
|
+
return {
|
|
80
|
+
cancelled: false,
|
|
81
|
+
response: failureResponse,
|
|
82
|
+
responseRoot: null,
|
|
83
|
+
environment: scopes.environment,
|
|
84
|
+
globals: scopes.globals,
|
|
85
|
+
outputData: null,
|
|
86
|
+
testResults: preResult.testResults,
|
|
87
|
+
contractResults: [],
|
|
88
|
+
contractSummary: null,
|
|
89
|
+
contractValidationError: null,
|
|
90
|
+
dbAssertionResults: [],
|
|
91
|
+
redisAssertionResults: [],
|
|
92
|
+
prefetchedKafkaResults: [],
|
|
93
|
+
failureMessage: `Pre-request script failed: ${preResult.error.message}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// 2. Resolve templates + auth + body ----------------------------------
|
|
98
|
+
const templateValues = buildScenarioTemplateValues({
|
|
99
|
+
environment: scopes.environment,
|
|
100
|
+
globals: scopes.globals,
|
|
101
|
+
workflowContext: input.context.workflowContext,
|
|
102
|
+
nodeMetaByLabel: input.context.nodeMetaByLabel,
|
|
103
|
+
responseRoot: input.context.responseRoot,
|
|
104
|
+
dbResult: input.context.dbResult,
|
|
105
|
+
kafkaEvent: input.context.kafkaEvent,
|
|
106
|
+
redisResult: input.context.redisResult,
|
|
107
|
+
});
|
|
108
|
+
const runtimeRequest = normalizeRequestForExecution(currentRequestState, input.node.data.config.auth, buildBodyResolverHelpers(), templateValues);
|
|
109
|
+
// 3+4. Execute the HTTP call (with optional Kafka prefetch) -----------
|
|
110
|
+
const requestTimeoutMs = options.requestTimeoutMs;
|
|
111
|
+
const runHttp = async () => {
|
|
112
|
+
try {
|
|
113
|
+
const httpResult = await withAbortAndTimeout(options.drivers.httpExecutor({
|
|
114
|
+
method: runtimeRequest.method,
|
|
115
|
+
url: runtimeRequest.url,
|
|
116
|
+
headers: runtimeRequest.headers,
|
|
117
|
+
query: runtimeRequest.query,
|
|
118
|
+
body: runtimeRequest.body,
|
|
119
|
+
timeoutMs: requestTimeoutMs,
|
|
120
|
+
}), requestTimeoutMs, `Request node ${input.stepId}`, input.signal);
|
|
121
|
+
return httpResultToExecuteRequestResult(httpResult);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return normalizeExecuteRequestFailure(error);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
let executionResult;
|
|
128
|
+
let prefetchedKafkaResults = [];
|
|
129
|
+
const transientCapture = input.kafkaCapturePlans.length === 1 ? input.kafkaCapturePlans[0] ?? null : null;
|
|
130
|
+
if (transientCapture?.connectionId && transientCapture.topic.trim()) {
|
|
131
|
+
try {
|
|
132
|
+
const kafkaConnection = await options.resolveConnection(transientCapture.connectionId);
|
|
133
|
+
const captureResult = await options.drivers.kafkaExecutor({
|
|
134
|
+
connection: kafkaConnection,
|
|
135
|
+
topic: transientCapture.topic.trim(),
|
|
136
|
+
matcher: null,
|
|
137
|
+
timeoutMs: options.nodeTimeoutMs,
|
|
138
|
+
operation: async () => {
|
|
139
|
+
const er = await runHttp();
|
|
140
|
+
return {
|
|
141
|
+
status: er.status,
|
|
142
|
+
headers: er.headers,
|
|
143
|
+
data: er.data,
|
|
144
|
+
duration: er.duration,
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
executionResult = captureResult.operationResult
|
|
149
|
+
? httpResultToExecuteRequestResult(captureResult.operationResult)
|
|
150
|
+
: await runHttp();
|
|
151
|
+
prefetchedKafkaResults = input.kafkaCapturePlans.map((plan) => ({
|
|
152
|
+
stepId: plan.stepId,
|
|
153
|
+
result: captureResult.capture,
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Kafka prefetch setup failed — fall through to plain HTTP.
|
|
158
|
+
// Backend's existing behavior is to silently degrade and rely
|
|
159
|
+
// on a separate kafka node (if any) to report the issue.
|
|
160
|
+
executionResult = await runHttp();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
executionResult = await runHttp();
|
|
165
|
+
}
|
|
166
|
+
const response = toRequestResponse(executionResult);
|
|
167
|
+
const combinedTestResults = [];
|
|
168
|
+
// 5. Post-response script ---------------------------------------------
|
|
169
|
+
let postResponseError = null;
|
|
170
|
+
if (input.node.data.config.postResponseScript?.trim()) {
|
|
171
|
+
const postResult = await runRequestStageScript("post-response", input.node.data.config.postResponseScript, response);
|
|
172
|
+
combinedTestResults.push(...postResult.testResults);
|
|
173
|
+
if (postResult.error) {
|
|
174
|
+
postResponseError = { message: postResult.error.message };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// 6. Tests script -----------------------------------------------------
|
|
178
|
+
let testsError = null;
|
|
179
|
+
if (input.node.data.config.testsScript?.trim()) {
|
|
180
|
+
const testsResult = await runRequestStageScript("tests", input.node.data.config.testsScript, response);
|
|
181
|
+
combinedTestResults.push(...testsResult.testResults);
|
|
182
|
+
if (testsResult.error) {
|
|
183
|
+
testsError = { message: testsResult.error.message };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// 7. Contract validation ----------------------------------------------
|
|
187
|
+
const contractResults = validateContracts(input.node.data.config.contractAssertions ?? [], {
|
|
188
|
+
status: executionResult.status,
|
|
189
|
+
data: executionResult.data,
|
|
190
|
+
headers: executionResult.headers,
|
|
191
|
+
duration: executionResult.duration,
|
|
192
|
+
});
|
|
193
|
+
const contractCounts = summarizeContractResults(contractResults);
|
|
194
|
+
const contractSummary = {
|
|
195
|
+
total: contractCounts.total,
|
|
196
|
+
passed: contractCounts.passed,
|
|
197
|
+
failed: contractCounts.failed,
|
|
198
|
+
};
|
|
199
|
+
// 8. Failure message + return -----------------------------------------
|
|
200
|
+
const failureMessage = buildRequestFailureMessage({
|
|
201
|
+
response,
|
|
202
|
+
postResponseError,
|
|
203
|
+
testsError,
|
|
204
|
+
testResults: combinedTestResults,
|
|
205
|
+
contractResults,
|
|
206
|
+
});
|
|
207
|
+
const responseRoot = buildResponseRoot(response);
|
|
208
|
+
return {
|
|
209
|
+
cancelled: Boolean(input.signal?.aborted),
|
|
210
|
+
response,
|
|
211
|
+
responseRoot,
|
|
212
|
+
environment: scopes.environment,
|
|
213
|
+
globals: scopes.globals,
|
|
214
|
+
outputData: responseRoot,
|
|
215
|
+
testResults: combinedTestResults,
|
|
216
|
+
contractResults,
|
|
217
|
+
contractSummary,
|
|
218
|
+
contractValidationError: null,
|
|
219
|
+
dbAssertionResults: [],
|
|
220
|
+
redisAssertionResults: [],
|
|
221
|
+
prefetchedKafkaResults,
|
|
222
|
+
failureMessage,
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
// ---------------------------------------------------------------------
|
|
226
|
+
// Request-stage script invocation. The host supplies a script executor
|
|
227
|
+
// either as `options.scriptExecutor` (which handles the standalone
|
|
228
|
+
// "script" stage from runScenario) or via the dedicated request-stage
|
|
229
|
+
// hook below. Default routes to the canonical executeScript with the
|
|
230
|
+
// "stage" capability set so pre/post/test scripts get the request +
|
|
231
|
+
// response surface they expect.
|
|
232
|
+
// ---------------------------------------------------------------------
|
|
233
|
+
import { executeScript } from "../../script/executor.js";
|
|
234
|
+
const invokeRequestStageScript = async (options, input) => {
|
|
235
|
+
// Hosts can swap the request-stage script behavior by overriding
|
|
236
|
+
// `scriptExecutor` and inspecting the input to dispatch to their own
|
|
237
|
+
// pre/post/tests handler. In this default path we go directly to the
|
|
238
|
+
// canonical sandbox.
|
|
239
|
+
const result = await executeScript({
|
|
240
|
+
script: input.script,
|
|
241
|
+
timeoutMs: input.timeoutMs,
|
|
242
|
+
filename: "callman-request.script.js",
|
|
243
|
+
stage: input.stage,
|
|
244
|
+
capabilities: {
|
|
245
|
+
environment: { values: input.environment, emitMutations: true },
|
|
246
|
+
globals: { values: input.globals, emitMutations: true },
|
|
247
|
+
request: input.request,
|
|
248
|
+
response: input.response ?? undefined,
|
|
249
|
+
workflowContext: input.workflowContext,
|
|
250
|
+
nodeMetaByLabel: input.nodeMetaByLabel,
|
|
251
|
+
exposeLabelsAsGlobals: true,
|
|
252
|
+
extensions: { log: true, node: true },
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
return {
|
|
256
|
+
request: result.request,
|
|
257
|
+
variableMutations: result.variableMutations,
|
|
258
|
+
testResults: result.testResults,
|
|
259
|
+
error: result.error,
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
//# sourceMappingURL=request-executor.js.map
|