callman-core 1.9.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/ai-packs/feature-policy.md +26 -0
  2. package/ai-packs/pm-api.md +213 -0
  3. package/ai-packs/sandbox-globals.md +20 -0
  4. package/ai-packs/templating.md +104 -0
  5. package/dist/host/connections.d.ts +67 -0
  6. package/dist/host/connections.d.ts.map +1 -0
  7. package/dist/host/connections.js +156 -0
  8. package/dist/host/connections.js.map +1 -0
  9. package/dist/host/db/mongoAdapter.d.ts +7 -0
  10. package/dist/host/db/mongoAdapter.d.ts.map +1 -0
  11. package/dist/host/db/mongoAdapter.js +426 -0
  12. package/dist/host/db/mongoAdapter.js.map +1 -0
  13. package/dist/host/db/mysqlAdapter.d.ts +7 -0
  14. package/dist/host/db/mysqlAdapter.d.ts.map +1 -0
  15. package/dist/host/db/mysqlAdapter.js +68 -0
  16. package/dist/host/db/mysqlAdapter.js.map +1 -0
  17. package/dist/host/db/oracleAdapter.d.ts +7 -0
  18. package/dist/host/db/oracleAdapter.d.ts.map +1 -0
  19. package/dist/host/db/oracleAdapter.js +142 -0
  20. package/dist/host/db/oracleAdapter.js.map +1 -0
  21. package/dist/host/db/oracleConnectString.d.ts +10 -0
  22. package/dist/host/db/oracleConnectString.d.ts.map +1 -0
  23. package/dist/host/db/oracleConnectString.js +58 -0
  24. package/dist/host/db/oracleConnectString.js.map +1 -0
  25. package/dist/host/db/postgresAdapter.d.ts +7 -0
  26. package/dist/host/db/postgresAdapter.d.ts.map +1 -0
  27. package/dist/host/db/postgresAdapter.js +67 -0
  28. package/dist/host/db/postgresAdapter.js.map +1 -0
  29. package/dist/host/db/queryExecutor.d.ts +3 -0
  30. package/dist/host/db/queryExecutor.d.ts.map +1 -0
  31. package/dist/host/db/queryExecutor.js +97 -0
  32. package/dist/host/db/queryExecutor.js.map +1 -0
  33. package/dist/host/db/sqlNormalize.d.ts +3 -0
  34. package/dist/host/db/sqlNormalize.d.ts.map +1 -0
  35. package/dist/host/db/sqlNormalize.js +29 -0
  36. package/dist/host/db/sqlNormalize.js.map +1 -0
  37. package/dist/host/db/templateResolver.d.ts +3 -0
  38. package/dist/host/db/templateResolver.d.ts.map +1 -0
  39. package/dist/host/db/templateResolver.js +77 -0
  40. package/dist/host/db/templateResolver.js.map +1 -0
  41. package/dist/host/db/types.d.ts +59 -0
  42. package/dist/host/db/types.d.ts.map +1 -0
  43. package/dist/host/db/types.js +6 -0
  44. package/dist/host/db/types.js.map +1 -0
  45. package/dist/host/index.d.ts +6 -0
  46. package/dist/host/index.d.ts.map +1 -0
  47. package/dist/host/index.js +7 -0
  48. package/dist/host/index.js.map +1 -0
  49. package/dist/host/lazy.d.ts +3 -0
  50. package/dist/host/lazy.d.ts.map +1 -0
  51. package/dist/host/lazy.js +31 -0
  52. package/dist/host/lazy.js.map +1 -0
  53. package/dist/host/redis/redisService.d.ts +9 -0
  54. package/dist/host/redis/redisService.d.ts.map +1 -0
  55. package/dist/host/redis/redisService.js +264 -0
  56. package/dist/host/redis/redisService.js.map +1 -0
  57. package/dist/host/redis/types.d.ts +44 -0
  58. package/dist/host/redis/types.d.ts.map +1 -0
  59. package/dist/host/redis/types.js +4 -0
  60. package/dist/host/redis/types.js.map +1 -0
  61. package/dist/index.d.ts +5 -0
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +4 -0
  64. package/dist/index.js.map +1 -1
  65. package/dist/runner/requestRunner.d.ts.map +1 -1
  66. package/dist/runner/requestRunner.js +120 -0
  67. package/dist/runner/requestRunner.js.map +1 -1
  68. package/dist/runner/runCollection.d.ts +1 -1
  69. package/dist/runner/runCollection.d.ts.map +1 -1
  70. package/dist/runner/runCollection.js +3 -1
  71. package/dist/runner/runCollection.js.map +1 -1
  72. package/dist/scenario-runner/adapters/script-executor.js +3 -3
  73. package/dist/scenario-runner/adapters/script-executor.js.map +1 -1
  74. package/dist/script/features/log.d.ts.map +1 -1
  75. package/dist/script/features/log.js +23 -4
  76. package/dist/script/features/log.js.map +1 -1
  77. package/dist/script/isolated-runner.d.ts +11 -0
  78. package/dist/script/isolated-runner.d.ts.map +1 -0
  79. package/dist/script/isolated-runner.js +172 -0
  80. package/dist/script/isolated-runner.js.map +1 -0
  81. package/dist/script/legacy.d.ts.map +1 -1
  82. package/dist/script/legacy.js +8 -0
  83. package/dist/script/legacy.js.map +1 -1
  84. package/dist/script/pm-api-metadata.d.ts +19 -0
  85. package/dist/script/pm-api-metadata.d.ts.map +1 -0
  86. package/dist/script/pm-api-metadata.js +264 -0
  87. package/dist/script/pm-api-metadata.js.map +1 -0
  88. package/dist/script/scope.d.ts +2 -0
  89. package/dist/script/scope.d.ts.map +1 -1
  90. package/dist/script/scope.js +4 -0
  91. package/dist/script/scope.js.map +1 -1
  92. package/dist/script/worker-entry.d.ts +2 -0
  93. package/dist/script/worker-entry.d.ts.map +1 -0
  94. package/dist/script/worker-entry.js +79 -0
  95. package/dist/script/worker-entry.js.map +1 -0
  96. package/dist/types/index.d.ts +11 -0
  97. package/dist/types/index.d.ts.map +1 -1
  98. package/dist-cjs/host/connections.js +164 -0
  99. package/dist-cjs/host/connections.js.map +1 -0
  100. package/dist-cjs/host/db/mongoAdapter.js +431 -0
  101. package/dist-cjs/host/db/mongoAdapter.js.map +1 -0
  102. package/dist-cjs/host/db/mysqlAdapter.js +73 -0
  103. package/dist-cjs/host/db/mysqlAdapter.js.map +1 -0
  104. package/dist-cjs/host/db/oracleAdapter.js +147 -0
  105. package/dist-cjs/host/db/oracleAdapter.js.map +1 -0
  106. package/dist-cjs/host/db/oracleConnectString.js +63 -0
  107. package/dist-cjs/host/db/oracleConnectString.js.map +1 -0
  108. package/dist-cjs/host/db/postgresAdapter.js +72 -0
  109. package/dist-cjs/host/db/postgresAdapter.js.map +1 -0
  110. package/dist-cjs/host/db/queryExecutor.js +101 -0
  111. package/dist-cjs/host/db/queryExecutor.js.map +1 -0
  112. package/dist-cjs/host/db/sqlNormalize.js +33 -0
  113. package/dist-cjs/host/db/sqlNormalize.js.map +1 -0
  114. package/dist-cjs/host/db/templateResolver.js +81 -0
  115. package/dist-cjs/host/db/templateResolver.js.map +1 -0
  116. package/dist-cjs/host/db/types.js +7 -0
  117. package/dist-cjs/host/db/types.js.map +1 -0
  118. package/dist-cjs/host/index.js +17 -0
  119. package/dist-cjs/host/index.js.map +1 -0
  120. package/dist-cjs/host/lazy.js +69 -0
  121. package/dist-cjs/host/lazy.js.map +1 -0
  122. package/dist-cjs/host/redis/redisService.js +268 -0
  123. package/dist-cjs/host/redis/redisService.js.map +1 -0
  124. package/dist-cjs/host/redis/types.js +5 -0
  125. package/dist-cjs/host/redis/types.js.map +1 -0
  126. package/dist-cjs/index.js +10 -1
  127. package/dist-cjs/index.js.map +1 -1
  128. package/dist-cjs/runner/requestRunner.js +120 -0
  129. package/dist-cjs/runner/requestRunner.js.map +1 -1
  130. package/dist-cjs/runner/runCollection.js +3 -1
  131. package/dist-cjs/runner/runCollection.js.map +1 -1
  132. package/dist-cjs/scenario-runner/adapters/script-executor.js +3 -3
  133. package/dist-cjs/scenario-runner/adapters/script-executor.js.map +1 -1
  134. package/dist-cjs/script/features/log.js +23 -4
  135. package/dist-cjs/script/features/log.js.map +1 -1
  136. package/dist-cjs/script/isolated-runner.js +209 -0
  137. package/dist-cjs/script/isolated-runner.js.map +1 -0
  138. package/dist-cjs/script/legacy.js +8 -0
  139. package/dist-cjs/script/legacy.js.map +1 -1
  140. package/dist-cjs/script/pm-api-metadata.js +267 -0
  141. package/dist-cjs/script/pm-api-metadata.js.map +1 -0
  142. package/dist-cjs/script/scope.js +5 -1
  143. package/dist-cjs/script/scope.js.map +1 -1
  144. package/dist-cjs/script/worker-entry.js +81 -0
  145. package/dist-cjs/script/worker-entry.js.map +1 -0
  146. package/package.json +15 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/host/db/types.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,6EAA6E;AAC7E,8EAA8E;AAC9E,oBAAoB"}
@@ -0,0 +1,6 @@
1
+ export { buildDbExecuteCapability, runConfiguredDbQuery, runRedisCommandsForPhase, buildRedisValueCache, buildRedisCapability, type ConnectionRuntime, type ConnectionRuntimeMap, type DbConnectionRuntime, type RedisConnectionRuntime, type DbNodeConfig, type RequestDatabaseRuntimeConfig, type RequestRedisRuntimeConfig, type ScriptTemplateContext, } from "./connections.js";
2
+ export { executeQuery } from "./db/queryExecutor.js";
3
+ export { type DbConnection, type DbConnectionInput, type DbConnectionType, type DbConnectionMode, type DbQueryExecutionResult, type DbQueryPhase, } from "./db/types.js";
4
+ export { executeRedisCommands } from "./redis/redisService.js";
5
+ export { type RedisCommandExecutionResult, type RedisCommandPhase, type RequestRedisResult, } from "./redis/types.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,EACjB,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ // Host execution layer — local DB / Redis drivers + the glue that turns
2
+ // resolved connection runtimes into script capabilities. Lets the collection
3
+ // runner execute `pm.db.*` and `pm.redis` without an external host process.
4
+ export { buildDbExecuteCapability, runConfiguredDbQuery, runRedisCommandsForPhase, buildRedisValueCache, buildRedisCapability, } from "./connections.js";
5
+ export { executeQuery } from "./db/queryExecutor.js";
6
+ export { executeRedisCommands } from "./redis/redisService.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,6EAA6E;AAC7E,4EAA4E;AAE5E,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,GASrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAUrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const loadOptionalModule: (specifier: string) => Promise<any>;
2
+ export declare const loadOptionalModuleOrNull: (specifier: string) => Promise<any | null>;
3
+ //# sourceMappingURL=lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../src/host/lazy.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,kBAAkB,GAAI,WAAW,MAAM,KAAG,OAAO,CAAC,GAAG,CAQjE,CAAC;AAIF,eAAO,MAAM,wBAAwB,GACnC,WAAW,MAAM,KAChB,OAAO,CAAC,GAAG,GAAG,IAAI,CAMpB,CAAC"}
@@ -0,0 +1,31 @@
1
+ // Lazy loader for OPTIONAL native driver packages (mongodb, pg, mysql2,
2
+ // oracledb, ioredis, bson). callman-core stays runtime-agnostic: these are
3
+ // declared as `optionalDependencies` and only loaded the first time a query
4
+ // that needs them runs. Hosts that never touch a given database never pay the
5
+ // native-install cost.
6
+ //
7
+ // The specifier is passed as a *variable* on purpose — that stops the
8
+ // TypeScript compiler from statically resolving (and erroring on) packages
9
+ // that may not be installed, while Node's runtime resolution still finds the
10
+ // real module by walking up `node_modules`.
11
+ const moduleCache = new Map();
12
+ export const loadOptionalModule = (specifier) => {
13
+ const cached = moduleCache.get(specifier);
14
+ if (cached) {
15
+ return cached;
16
+ }
17
+ const loading = import(specifier);
18
+ moduleCache.set(specifier, loading);
19
+ return loading;
20
+ };
21
+ // Like `loadOptionalModule` but resolves to `null` instead of throwing when the
22
+ // module is absent. Used for soft dependencies (e.g. `bson` for Decimal128).
23
+ export const loadOptionalModuleOrNull = async (specifier) => {
24
+ try {
25
+ return await loadOptionalModule(specifier);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ };
31
+ //# sourceMappingURL=lazy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../src/host/lazy.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,2EAA2E;AAC3E,4EAA4E;AAC5E,8EAA8E;AAC9E,uBAAuB;AACvB,EAAE;AACF,sEAAsE;AACtE,2EAA2E;AAC3E,6EAA6E;AAC7E,4CAA4C;AAE5C,MAAM,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAC;AAExD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAgB,EAAE;IACpE,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAsB,CAAC;IAChC,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,OAAuB,CAAC;AACjC,CAAC,CAAC;AAEF,gFAAgF;AAChF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,EAC3C,SAAiB,EACI,EAAE;IACvB,IAAI,CAAC;QACH,OAAO,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { RedisCommandExecutionResult, RedisCommandPhase, RedisConnectionInput, RedisTemplateContext } from "./types.js";
2
+ export declare const executeRedisCommands: ({ connectionId, connection: connectionInput, commands, phase, context, }: {
3
+ connectionId: string;
4
+ connection?: RedisConnectionInput;
5
+ commands: string[];
6
+ phase: RedisCommandPhase;
7
+ context?: RedisTemplateContext;
8
+ }) => Promise<RedisCommandExecutionResult[]>;
9
+ //# sourceMappingURL=redisService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisService.d.ts","sourceRoot":"","sources":["../../../src/host/redis/redisService.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,2BAA2B,EAC3B,iBAAiB,EAEjB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAgUpB,eAAO,MAAM,oBAAoB,GAAU,0EAMxC;IACD,YAAY,EAAE,MAAM,CAAC;IAGrB,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,iBAAiB,CAAC;IACzB,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,KAAG,OAAO,CAAC,2BAA2B,EAAE,CA2CxC,CAAC"}
@@ -0,0 +1,264 @@
1
+ // Host-side Redis command executor. Ported from
2
+ // `pmanfrontend/electron/redis/redisService.ts`. The desktop original resolves
3
+ // connections from a local store (`redisManager`); callman-core has none, so
4
+ // the caller MUST provide the resolved `connection`. `ioredis` is loaded lazily
5
+ // via `loadOptionalModule`.
6
+ import { performance } from "node:perf_hooks";
7
+ import { loadOptionalModule } from "../lazy.js";
8
+ import { resolveTemplate } from "../db/templateResolver.js";
9
+ const unwrap = (mod) => (mod && mod.default ? mod.default : mod);
10
+ const loadRedisDriver = async () => {
11
+ try {
12
+ const mod = await loadOptionalModule("ioredis");
13
+ // ioredis exports both `default` and a named `Redis`. Prefer the named
14
+ // class; fall back to default.
15
+ return mod.Redis ?? unwrap(mod);
16
+ }
17
+ catch {
18
+ throw new Error("Redis driver (`ioredis`) is not installed. Install `ioredis` to use Redis connections.");
19
+ }
20
+ };
21
+ const REDIS_TOKEN_PATTERN = /"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'|(\S+)/g;
22
+ const stripEscapes = (value) => value.replace(/\\(["'\\])/g, "$1");
23
+ const tokenizeRedisCommand = (input) => {
24
+ const tokens = [];
25
+ for (const match of input.matchAll(REDIS_TOKEN_PATTERN)) {
26
+ const [, doubleQuoted, singleQuoted, bare] = match;
27
+ const token = doubleQuoted ?? singleQuoted ?? bare ?? "";
28
+ if (token.length === 0) {
29
+ continue;
30
+ }
31
+ tokens.push(stripEscapes(token));
32
+ }
33
+ return tokens;
34
+ };
35
+ const parseRedisCommand = (command) => {
36
+ const tokens = tokenizeRedisCommand(command.trim());
37
+ if (tokens.length === 0) {
38
+ throw new Error("Redis command cannot be empty");
39
+ }
40
+ const commandName = tokens[0]?.toUpperCase();
41
+ if (commandName !== "GET" &&
42
+ commandName !== "SET" &&
43
+ commandName !== "DEL" &&
44
+ commandName !== "EXISTS" &&
45
+ commandName !== "TTL") {
46
+ throw new Error("Supported Redis commands are GET, SET, DEL, EXISTS, and TTL");
47
+ }
48
+ const key = tokens[1]?.trim() ?? "";
49
+ if (!key) {
50
+ throw new Error(`${commandName} requires a key`);
51
+ }
52
+ if (commandName === "SET") {
53
+ if (tokens.length < 3) {
54
+ throw new Error("SET requires a key and a value");
55
+ }
56
+ return {
57
+ commandName,
58
+ key,
59
+ args: [tokens.slice(2).join(" ")],
60
+ };
61
+ }
62
+ if (tokens.length > 2) {
63
+ throw new Error(`${commandName} accepts only a single key`);
64
+ }
65
+ return {
66
+ commandName,
67
+ key,
68
+ args: [],
69
+ };
70
+ };
71
+ const createRedisClient = async (connection) => {
72
+ const IORedis = await loadRedisDriver();
73
+ return new IORedis({
74
+ host: connection.host,
75
+ port: connection.port,
76
+ password: connection.password,
77
+ db: connection.dbIndex,
78
+ lazyConnect: true,
79
+ enableOfflineQueue: false,
80
+ maxRetriesPerRequest: 1,
81
+ });
82
+ };
83
+ const stringifyRedisResult = (value) => {
84
+ if (value === null || value === undefined) {
85
+ return "null";
86
+ }
87
+ if (typeof value === "string") {
88
+ return value;
89
+ }
90
+ if (typeof value === "number" || typeof value === "boolean") {
91
+ return String(value);
92
+ }
93
+ return JSON.stringify(value);
94
+ };
95
+ const normalizeRedisResultValue = (value) => {
96
+ if (value === null || value === undefined) {
97
+ return null;
98
+ }
99
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
100
+ return value;
101
+ }
102
+ if (Buffer.isBuffer(value)) {
103
+ return value.toString("utf-8");
104
+ }
105
+ if (Array.isArray(value)) {
106
+ return value.map((entry) => normalizeRedisResultValue(entry));
107
+ }
108
+ if (typeof value === "object") {
109
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
110
+ key,
111
+ normalizeRedisResultValue(entry),
112
+ ]));
113
+ }
114
+ return String(value);
115
+ };
116
+ const executeParsedCommand = async (client, parsedCommand) => {
117
+ switch (parsedCommand.commandName) {
118
+ case "GET":
119
+ return await client.get(parsedCommand.key);
120
+ case "SET":
121
+ await client.set(parsedCommand.key, parsedCommand.args[0] ?? "");
122
+ return parsedCommand.args[0] ?? "";
123
+ case "DEL":
124
+ return await client.del(parsedCommand.key);
125
+ case "EXISTS":
126
+ return (await client.exists(parsedCommand.key)) > 0;
127
+ case "TTL":
128
+ return await client.ttl(parsedCommand.key);
129
+ }
130
+ };
131
+ const buildFailureResult = ({ connectionId, connectionName, command, resolvedCommand, phase, error, startedAt, commandName = "", key = null, }) => ({
132
+ success: false,
133
+ phase,
134
+ connectionId,
135
+ connectionName,
136
+ command,
137
+ resolvedCommand,
138
+ commandName,
139
+ key,
140
+ result: null,
141
+ resultText: "",
142
+ durationMs: Math.round(performance.now() - startedAt),
143
+ executedAt: new Date().toISOString(),
144
+ error: error instanceof Error ? error.message : String(error),
145
+ });
146
+ const executeResolvedCommand = async ({ client, connection, command, resolvedCommand, phase, }) => {
147
+ const startedAt = performance.now();
148
+ try {
149
+ const parsedCommand = parseRedisCommand(resolvedCommand);
150
+ const rawResult = await executeParsedCommand(client, parsedCommand);
151
+ const normalizedResult = normalizeRedisResultValue(rawResult);
152
+ return {
153
+ success: true,
154
+ phase,
155
+ connectionId: connection.id,
156
+ connectionName: connection.name,
157
+ command,
158
+ resolvedCommand,
159
+ commandName: parsedCommand.commandName,
160
+ key: parsedCommand.key,
161
+ result: normalizedResult,
162
+ resultText: stringifyRedisResult(normalizedResult),
163
+ durationMs: Math.round(performance.now() - startedAt),
164
+ executedAt: new Date().toISOString(),
165
+ };
166
+ }
167
+ catch (error) {
168
+ let commandName = "";
169
+ let key = null;
170
+ try {
171
+ const parsedCommand = parseRedisCommand(resolvedCommand);
172
+ commandName = parsedCommand.commandName;
173
+ key = parsedCommand.key;
174
+ }
175
+ catch {
176
+ // ignore parse failure metadata
177
+ }
178
+ return buildFailureResult({
179
+ connectionId: connection.id,
180
+ connectionName: connection.name,
181
+ command,
182
+ resolvedCommand,
183
+ phase,
184
+ error,
185
+ startedAt,
186
+ commandName,
187
+ key,
188
+ });
189
+ }
190
+ };
191
+ const executeWithConnection = async ({ connection, commands, phase, context, }) => {
192
+ if (commands.length === 0) {
193
+ return [];
194
+ }
195
+ const client = await createRedisClient(connection);
196
+ try {
197
+ await client.connect();
198
+ const results = [];
199
+ for (const command of commands) {
200
+ const resolvedCommand = resolveTemplate(command, context ?? {});
201
+ const result = await executeResolvedCommand({
202
+ client,
203
+ connection,
204
+ command,
205
+ resolvedCommand,
206
+ phase,
207
+ });
208
+ results.push(result);
209
+ }
210
+ return results;
211
+ }
212
+ finally {
213
+ client.disconnect();
214
+ }
215
+ };
216
+ const normalizeConnectionInput = (input) => ({
217
+ id: input.id?.trim() || "runtime-connection",
218
+ name: input.name.trim(),
219
+ type: "redis",
220
+ host: input.host.trim(),
221
+ port: Number(input.port),
222
+ password: input.password?.trim() ? input.password : undefined,
223
+ dbIndex: typeof input.dbIndex === "number" && Number.isFinite(input.dbIndex) && input.dbIndex >= 0
224
+ ? Math.trunc(input.dbIndex)
225
+ : undefined,
226
+ });
227
+ export const executeRedisCommands = async ({ connectionId, connection: connectionInput, commands, phase, context, }) => {
228
+ const normalizedCommands = commands.map((command) => command.trim()).filter(Boolean);
229
+ if (normalizedCommands.length === 0) {
230
+ return [];
231
+ }
232
+ const connection = connectionInput ? normalizeConnectionInput(connectionInput) : null;
233
+ if (!connection) {
234
+ return normalizedCommands.map((command) => buildFailureResult({
235
+ connectionId,
236
+ connectionName: "",
237
+ command,
238
+ resolvedCommand: command,
239
+ phase,
240
+ error: "Redis connection not found",
241
+ startedAt: performance.now(),
242
+ }));
243
+ }
244
+ try {
245
+ return await executeWithConnection({
246
+ connection,
247
+ commands: normalizedCommands,
248
+ phase,
249
+ context,
250
+ });
251
+ }
252
+ catch (error) {
253
+ return normalizedCommands.map((command) => buildFailureResult({
254
+ connectionId: connection.id,
255
+ connectionName: connection.name,
256
+ command,
257
+ resolvedCommand: resolveTemplate(command, context ?? {}),
258
+ phase,
259
+ error,
260
+ startedAt: performance.now(),
261
+ }));
262
+ }
263
+ };
264
+ //# sourceMappingURL=redisService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisService.js","sourceRoot":"","sources":["../../../src/host/redis/redisService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export type RedisCommandPhase = "pre" | "post" | "manual";
2
+ export interface RedisConnection {
3
+ id: string;
4
+ name: string;
5
+ type: "redis";
6
+ host: string;
7
+ port: number;
8
+ password?: string;
9
+ dbIndex?: number;
10
+ }
11
+ export interface RedisConnectionInput {
12
+ id?: string;
13
+ name: string;
14
+ type: "redis";
15
+ host: string;
16
+ port: number;
17
+ password?: string;
18
+ dbIndex?: number;
19
+ }
20
+ export interface RedisTemplateContext {
21
+ response?: unknown | null;
22
+ env?: Record<string, string>;
23
+ global?: Record<string, string>;
24
+ }
25
+ export interface RedisCommandExecutionResult {
26
+ success: boolean;
27
+ phase: RedisCommandPhase;
28
+ connectionId: string;
29
+ connectionName: string;
30
+ command: string;
31
+ resolvedCommand: string;
32
+ commandName: string;
33
+ key: string | null;
34
+ result: unknown;
35
+ resultText: string;
36
+ durationMs: number;
37
+ executedAt: string;
38
+ error?: string;
39
+ }
40
+ export interface RequestRedisResult {
41
+ pre: RedisCommandExecutionResult[];
42
+ post: RedisCommandExecutionResult[];
43
+ }
44
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/host/redis/types.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,iBAAiB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,2BAA2B,EAAE,CAAC;IACnC,IAAI,EAAE,2BAA2B,EAAE,CAAC;CACrC"}
@@ -0,0 +1,4 @@
1
+ // Host-side Redis execution types. Ported from
2
+ // `pmanfrontend/electron/redis/types.ts`.
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/host/redis/types.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,0CAA0C"}
package/dist/index.d.ts CHANGED
@@ -7,7 +7,11 @@ export { EnvStore } from "./variables/envStore.js";
7
7
  export { runScript } from "./script/legacy.js";
8
8
  export * from "./templating/index.js";
9
9
  export { executeScript, validateScript } from "./script/executor.js";
10
+ export { runScriptIsolated } from "./script/isolated-runner.js";
11
+ export type { RunScriptIsolatedOptions } from "./script/isolated-runner.js";
10
12
  export type { CounterSnapshotEntry, ExecuteScriptInput, ExecuteScriptResult, ScriptCapabilities, ScriptCounterCapability, ScriptDbCapability, ScriptDbExecutionInput, ScriptDbExecutionResult, ScriptError, ScriptFeaturePolicy, ScriptKafkaCapability, ScriptKeyValuePair, ScriptLogEntry, ScriptRedisCapability, ScriptRequestBodyState, ScriptRequestState, ScriptResponseState, ScriptStage, ScriptTestResult, ScriptVariableMutation, SendRequestExecutor, } from "./script/types.js";
13
+ export { PM_API_METADATA, type PmApiMember, type PmApiNamespace, type PmApiStage, } from "./script/pm-api-metadata.js";
14
+ export { ALLOWED_GLOBAL_NAMES, BLOCKED_GLOBAL_NAMES, RESERVED_LABEL_NAMES, } from "./script/scope.js";
11
15
  export { PmAssertion, createExpectApi } from "./script/features/assertions.js";
12
16
  export { PendingKafkaDependencyError, isPendingKafkaValue, } from "./script/features/kafka.js";
13
17
  export { DisabledFeatureDependencyError, isDisabledFeatureValue, } from "./script/features/feature-policy.js";
@@ -15,5 +19,6 @@ export { validateContracts, evaluateContractCandidate, normalizeContractRule, ty
15
19
  export { resolveContractPath, generalizeContractPath, normalizeContractPathKey, type ResolvedCandidate, } from "./contract/pathResolver.js";
16
20
  export { SCENARIO_CONTRACT_FORMATS, FORMAT_VALIDATORS, isContractFormat, type ContractFormat, } from "./contract/formats.js";
17
21
  export * from "./scenario-runner/index.js";
22
+ export * from "./host/index.js";
18
23
  export * from "./types/index.js";
19
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,qBAAqB,EACrB,KAAK,wBAAwB,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,KAAK,iBAAiB,GACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,qBAAqB,EACrB,KAAK,wBAAwB,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,KAAK,iBAAiB,GACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -7,6 +7,9 @@ export { EnvStore } from "./variables/envStore.js";
7
7
  export { runScript } from "./script/legacy.js";
8
8
  export * from "./templating/index.js";
9
9
  export { executeScript, validateScript } from "./script/executor.js";
10
+ export { runScriptIsolated } from "./script/isolated-runner.js";
11
+ export { PM_API_METADATA, } from "./script/pm-api-metadata.js";
12
+ export { ALLOWED_GLOBAL_NAMES, BLOCKED_GLOBAL_NAMES, RESERVED_LABEL_NAMES, } from "./script/scope.js";
10
13
  export { PmAssertion, createExpectApi } from "./script/features/assertions.js";
11
14
  export { PendingKafkaDependencyError, isPendingKafkaValue, } from "./script/features/kafka.js";
12
15
  export { DisabledFeatureDependencyError, isDisabledFeatureValue, } from "./script/features/feature-policy.js";
@@ -14,5 +17,6 @@ export { validateContracts, evaluateContractCandidate, normalizeContractRule, }
14
17
  export { resolveContractPath, generalizeContractPath, normalizeContractPathKey, } from "./contract/pathResolver.js";
15
18
  export { SCENARIO_CONTRACT_FORMATS, FORMAT_VALIDATORS, isContractFormat, } from "./contract/formats.js";
16
19
  export * from "./scenario-runner/index.js";
20
+ export * from "./host/index.js";
17
21
  export * from "./types/index.js";
18
22
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,kBAAkB,GAEnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAwBrE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,qBAAqB,GAEtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,GAEzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,GAEjB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,kBAAkB,GAEnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAyBhE,OAAO,EACL,eAAe,GAIhB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,qBAAqB,GAEtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,GAEzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,GAEjB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"requestRunner.d.ts","sourceRoot":"","sources":["../../src/runner/requestRunner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,eAAe,EACf,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AAkF3B,eAAO,MAAM,UAAU,GACrB,OAAO,eAAe,KACrB,OAAO,CAAC,gBAAgB,CA0C1B,CAAC"}
1
+ {"version":3,"file":"requestRunner.d.ts","sourceRoot":"","sources":["../../src/runner/requestRunner.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAGV,eAAe,EACf,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AA0K3B,eAAO,MAAM,UAAU,GACrB,OAAO,eAAe,KACrB,OAAO,CAAC,gBAAgB,CAiG1B,CAAC"}
@@ -1,7 +1,47 @@
1
1
  import { validateContracts } from "../contract/contractValidator.js";
2
+ import { buildDbExecuteCapability, buildRedisCapability, runConfiguredDbQuery, runRedisCommandsForPhase, } from "../host/connections.js";
2
3
  import { executeRequest } from "../request/executeRequest.js";
3
4
  import { runScript } from "../script/scriptEngine.js";
4
5
  import { resolveTemplateValue } from "../variables/resolver.js";
6
+ const stringifyEnv = (env) => {
7
+ const out = {};
8
+ for (const [key, value] of Object.entries(env)) {
9
+ out[key] = value === undefined || value === null ? "" : String(value);
10
+ }
11
+ return out;
12
+ };
13
+ // Build a `{{response.*}}` template root from an HTTP result so pre/post DB +
14
+ // Redis nodes can reference the response body. Mirrors the desktop host's
15
+ // `buildResponseTemplateRoot`.
16
+ const buildResponseRoot = (response) => {
17
+ if (!response) {
18
+ return null;
19
+ }
20
+ const data = response.data;
21
+ const isObject = data !== null && typeof data === "object" && !Array.isArray(data);
22
+ const rawText = response.rawText ?? (typeof data === "string" ? data : safeStringify(data));
23
+ return {
24
+ ...(isObject ? data : {}),
25
+ data: isObject && "data" in data
26
+ ? data.data
27
+ : data,
28
+ body: data,
29
+ rawBody: rawText,
30
+ text: rawText,
31
+ status: response.status,
32
+ code: response.status,
33
+ headers: response.headers,
34
+ durationMs: response.duration,
35
+ };
36
+ };
37
+ const safeStringify = (value) => {
38
+ try {
39
+ return JSON.stringify(value);
40
+ }
41
+ catch {
42
+ return String(value);
43
+ }
44
+ };
5
45
  const resolveHeaders = (headers, context) => {
6
46
  if (!headers) {
7
47
  return undefined;
@@ -54,15 +94,95 @@ const normalizeRequestForExecution = ({ request, env, previousResponse, }) => {
54
94
  body: resolveTemplateValue(request.body, resolveContext),
55
95
  };
56
96
  };
97
+ // Wires the resolved connections + request DB/Redis config into the script
98
+ // capabilities. Runs the pre-request DB node + pre Redis commands before this
99
+ // is called, the post ones after the response. Returns the capabilities to
100
+ // hand to `runScript` plus the redis results gathered so far.
101
+ const buildScriptExtensions = (input) => {
102
+ const db = input.databaseEnabled
103
+ ? {
104
+ execute: buildDbExecuteCapability(input.connections),
105
+ preResult: input.preDbResult,
106
+ postResult: input.postDbResult,
107
+ requestConfig: {
108
+ preRequest: {
109
+ connectionId: input.request.database?.preRequest?.connectionId ?? undefined,
110
+ },
111
+ postResponse: {
112
+ connectionId: input.request.database?.postResponse?.connectionId ?? undefined,
113
+ },
114
+ },
115
+ }
116
+ : undefined;
117
+ const hasRedis = Boolean(input.request.redis?.connectionId) &&
118
+ (input.redisPre.length > 0 || input.redisPost.length > 0 ||
119
+ (input.request.redis?.preCommands?.length ?? 0) > 0 ||
120
+ (input.request.redis?.postCommands?.length ?? 0) > 0);
121
+ const redis = hasRedis
122
+ ? buildRedisCapability(input.redisPre, input.redisPost)
123
+ : undefined;
124
+ return { db, redis };
125
+ };
57
126
  export const runRequest = async (input) => {
58
127
  const runtimeRequest = normalizeRequestForExecution(input);
128
+ const connections = input.connections ?? {};
129
+ const featurePolicy = input.featurePolicy;
130
+ const databaseEnabled = featurePolicy ? featurePolicy.database : true;
131
+ const redisConfig = input.request.redis;
59
132
  try {
133
+ // ── Pre-request phase: DB node + Redis commands run before the HTTP call.
134
+ const envBefore = stringifyEnv(input.env.all());
135
+ const preDbResult = databaseEnabled
136
+ ? await runConfiguredDbQuery({
137
+ connections,
138
+ config: input.request.database?.preRequest,
139
+ phase: "pre",
140
+ context: { response: null, env: envBefore, global: {} },
141
+ })
142
+ : null;
143
+ const redisPre = await runRedisCommandsForPhase({
144
+ connections,
145
+ connectionId: redisConfig?.connectionId,
146
+ commands: redisConfig?.preCommands ?? [],
147
+ phase: "pre",
148
+ context: { response: null, env: envBefore, global: {} },
149
+ });
60
150
  const response = await executeRequest(runtimeRequest);
61
151
  const contractResults = validateContracts(input.request.contracts, response);
152
+ const responseRoot = buildResponseRoot(response);
153
+ // ── Post-response phase: DB node + Redis commands run after the response.
154
+ const envAfter = stringifyEnv(input.env.all());
155
+ const postDbResult = databaseEnabled
156
+ ? await runConfiguredDbQuery({
157
+ connections,
158
+ config: input.request.database?.postResponse,
159
+ phase: "post",
160
+ context: { response: responseRoot, env: envAfter, global: {} },
161
+ })
162
+ : null;
163
+ const redisPost = await runRedisCommandsForPhase({
164
+ connections,
165
+ connectionId: redisConfig?.connectionId,
166
+ commands: redisConfig?.postCommands ?? [],
167
+ phase: "post",
168
+ context: { response: responseRoot, env: envAfter, global: {} },
169
+ });
170
+ const { db, redis } = buildScriptExtensions({
171
+ connections,
172
+ databaseEnabled,
173
+ request: input.request,
174
+ preDbResult,
175
+ postDbResult,
176
+ redisPre,
177
+ redisPost,
178
+ });
62
179
  const scriptResults = await runScript(input.request.tests, {
63
180
  env: input.env,
64
181
  response,
65
182
  request: runtimeRequest,
183
+ db,
184
+ redis,
185
+ featurePolicy,
66
186
  });
67
187
  return {
68
188
  response,