callman-core 1.34.0 → 1.36.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 (266) hide show
  1. package/ai-packs/feature-policy.md +1 -6
  2. package/ai-packs/pm-api.md +246 -97
  3. package/ai-packs/sandbox-globals.md +6 -5
  4. package/dist/ai/prompts/fragments.d.ts.map +1 -1
  5. package/dist/ai/prompts/fragments.js +0 -1
  6. package/dist/ai/prompts/fragments.js.map +1 -1
  7. package/dist/ai/prompts/unified.js +1 -1
  8. package/dist/ai/prompts/unified.js.map +1 -1
  9. package/dist/ai/types.d.ts +1 -1
  10. package/dist/ai/types.d.ts.map +1 -1
  11. package/dist/ai/types.js +1 -1
  12. package/dist/ai/types.js.map +1 -1
  13. package/dist/index.d.ts +7 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +5 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/runner/requestRunner.d.ts.map +1 -1
  18. package/dist/runner/requestRunner.js +339 -1
  19. package/dist/runner/requestRunner.js.map +1 -1
  20. package/dist/runner/runCollection.d.ts +1 -1
  21. package/dist/runner/runCollection.d.ts.map +1 -1
  22. package/dist/runner/runCollection.js +60 -7
  23. package/dist/runner/runCollection.js.map +1 -1
  24. package/dist/runner/runRequestWithDataset.d.ts.map +1 -1
  25. package/dist/runner/runRequestWithDataset.js +5 -0
  26. package/dist/runner/runRequestWithDataset.js.map +1 -1
  27. package/dist/scenario-runner/adapters/request-executor.d.ts.map +1 -1
  28. package/dist/scenario-runner/adapters/request-executor.js +7 -0
  29. package/dist/scenario-runner/adapters/request-executor.js.map +1 -1
  30. package/dist/scenario-runner/runScenario.d.ts.map +1 -1
  31. package/dist/scenario-runner/runScenario.js +4 -0
  32. package/dist/scenario-runner/runScenario.js.map +1 -1
  33. package/dist/scenario-runner/types.d.ts +11 -0
  34. package/dist/scenario-runner/types.d.ts.map +1 -1
  35. package/dist/script/chain.d.ts +56 -0
  36. package/dist/script/chain.d.ts.map +1 -0
  37. package/dist/script/chain.js +121 -0
  38. package/dist/script/chain.js.map +1 -0
  39. package/dist/script/errors.d.ts.map +1 -1
  40. package/dist/script/errors.js +6 -4
  41. package/dist/script/errors.js.map +1 -1
  42. package/dist/script/executor.d.ts.map +1 -1
  43. package/dist/script/executor.js +298 -64
  44. package/dist/script/executor.js.map +1 -1
  45. package/dist/script/features/collectionVariables.d.ts +4 -4
  46. package/dist/script/features/collectionVariables.d.ts.map +1 -1
  47. package/dist/script/features/collectionVariables.js +20 -11
  48. package/dist/script/features/collectionVariables.js.map +1 -1
  49. package/dist/script/features/environment.d.ts +4 -4
  50. package/dist/script/features/environment.d.ts.map +1 -1
  51. package/dist/script/features/environment.js +20 -11
  52. package/dist/script/features/environment.js.map +1 -1
  53. package/dist/script/features/feature-policy.d.ts +1 -1
  54. package/dist/script/features/feature-policy.d.ts.map +1 -1
  55. package/dist/script/features/feature-policy.js +2 -2
  56. package/dist/script/features/feature-policy.js.map +1 -1
  57. package/dist/script/features/info.d.ts.map +1 -1
  58. package/dist/script/features/info.js +6 -3
  59. package/dist/script/features/info.js.map +1 -1
  60. package/dist/script/features/iterationData.d.ts +12 -0
  61. package/dist/script/features/iterationData.d.ts.map +1 -0
  62. package/dist/script/features/iterationData.js +28 -0
  63. package/dist/script/features/iterationData.js.map +1 -0
  64. package/dist/script/features/legacyPostman.d.ts +14 -0
  65. package/dist/script/features/legacyPostman.d.ts.map +1 -0
  66. package/dist/script/features/legacyPostman.js +106 -0
  67. package/dist/script/features/legacyPostman.js.map +1 -0
  68. package/dist/script/features/log.d.ts +35 -8
  69. package/dist/script/features/log.d.ts.map +1 -1
  70. package/dist/script/features/log.js +343 -27
  71. package/dist/script/features/log.js.map +1 -1
  72. package/dist/script/features/tests.d.ts +7 -8
  73. package/dist/script/features/tests.d.ts.map +1 -1
  74. package/dist/script/features/tests.js +90 -64
  75. package/dist/script/features/tests.js.map +1 -1
  76. package/dist/script/features/timers.d.ts +7 -1
  77. package/dist/script/features/timers.d.ts.map +1 -1
  78. package/dist/script/features/timers.js +17 -5
  79. package/dist/script/features/timers.js.map +1 -1
  80. package/dist/script/features/variables.d.ts +25 -4
  81. package/dist/script/features/variables.d.ts.map +1 -1
  82. package/dist/script/features/variables.js +26 -9
  83. package/dist/script/features/variables.js.map +1 -1
  84. package/dist/script/features/visualizer.d.ts +15 -0
  85. package/dist/script/features/visualizer.d.ts.map +1 -0
  86. package/dist/script/features/visualizer.js +52 -0
  87. package/dist/script/features/visualizer.js.map +1 -0
  88. package/dist/script/isolated-runner.d.ts +4 -1
  89. package/dist/script/isolated-runner.d.ts.map +1 -1
  90. package/dist/script/isolated-runner.js +26 -2
  91. package/dist/script/isolated-runner.js.map +1 -1
  92. package/dist/script/layers.d.ts +46 -0
  93. package/dist/script/layers.d.ts.map +1 -0
  94. package/dist/script/layers.js +52 -0
  95. package/dist/script/layers.js.map +1 -0
  96. package/dist/script/legacy.d.ts +6 -1
  97. package/dist/script/legacy.d.ts.map +1 -1
  98. package/dist/script/legacy.js +15 -5
  99. package/dist/script/legacy.js.map +1 -1
  100. package/dist/script/pm-api-declarations.d.ts +45 -0
  101. package/dist/script/pm-api-declarations.d.ts.map +1 -0
  102. package/dist/script/pm-api-declarations.js +1963 -0
  103. package/dist/script/pm-api-declarations.js.map +1 -0
  104. package/dist/script/pm-api-metadata.d.ts +3 -1
  105. package/dist/script/pm-api-metadata.d.ts.map +1 -1
  106. package/dist/script/pm-api-metadata.js +336 -106
  107. package/dist/script/pm-api-metadata.js.map +1 -1
  108. package/dist/script/pm-api-reference.d.ts.map +1 -1
  109. package/dist/script/pm-api-reference.js +32 -7
  110. package/dist/script/pm-api-reference.js.map +1 -1
  111. package/dist/script/postman/chaiCompat.d.ts +4 -0
  112. package/dist/script/postman/chaiCompat.d.ts.map +1 -0
  113. package/dist/script/postman/chaiCompat.js +176 -0
  114. package/dist/script/postman/chaiCompat.js.map +1 -0
  115. package/dist/script/postman/cookies.d.ts +36 -0
  116. package/dist/script/postman/cookies.d.ts.map +1 -0
  117. package/dist/script/postman/cookies.js +163 -0
  118. package/dist/script/postman/cookies.js.map +1 -0
  119. package/dist/script/postman/modules.d.ts +16 -0
  120. package/dist/script/postman/modules.d.ts.map +1 -0
  121. package/dist/script/postman/modules.js +114 -0
  122. package/dist/script/postman/modules.js.map +1 -0
  123. package/dist/script/postman/proxies.d.ts +13 -0
  124. package/dist/script/postman/proxies.d.ts.map +1 -0
  125. package/dist/script/postman/proxies.js +88 -0
  126. package/dist/script/postman/proxies.js.map +1 -0
  127. package/dist/script/postman/requestAdapter.d.ts +7 -0
  128. package/dist/script/postman/requestAdapter.d.ts.map +1 -0
  129. package/dist/script/postman/requestAdapter.js +223 -0
  130. package/dist/script/postman/requestAdapter.js.map +1 -0
  131. package/dist/script/postman/responseAdapter.d.ts +5 -0
  132. package/dist/script/postman/responseAdapter.d.ts.map +1 -0
  133. package/dist/script/postman/responseAdapter.js +78 -0
  134. package/dist/script/postman/responseAdapter.js.map +1 -0
  135. package/dist/script/postman/runtime.d.ts +22 -0
  136. package/dist/script/postman/runtime.d.ts.map +1 -0
  137. package/dist/script/postman/runtime.js +122 -0
  138. package/dist/script/postman/runtime.js.map +1 -0
  139. package/dist/script/scope.d.ts +5 -1
  140. package/dist/script/scope.d.ts.map +1 -1
  141. package/dist/script/scope.js +47 -5
  142. package/dist/script/scope.js.map +1 -1
  143. package/dist/script/types.d.ts +42 -14
  144. package/dist/script/types.d.ts.map +1 -1
  145. package/dist/script/worker-entry.js +9 -0
  146. package/dist/script/worker-entry.js.map +1 -1
  147. package/dist/script/worker-pool.d.ts +3 -1
  148. package/dist/script/worker-pool.d.ts.map +1 -1
  149. package/dist/script/worker-pool.js +23 -2
  150. package/dist/script/worker-pool.js.map +1 -1
  151. package/dist/templating/generator-metadata.d.ts +1 -0
  152. package/dist/templating/generator-metadata.d.ts.map +1 -1
  153. package/dist/templating/generator-metadata.js +14 -0
  154. package/dist/templating/generator-metadata.js.map +1 -1
  155. package/dist/templating/postman-dynamic-catalog.d.ts +7 -0
  156. package/dist/templating/postman-dynamic-catalog.d.ts.map +1 -0
  157. package/dist/templating/postman-dynamic-catalog.js +126 -0
  158. package/dist/templating/postman-dynamic-catalog.js.map +1 -0
  159. package/dist/templating/postman-generators.d.ts +9 -0
  160. package/dist/templating/postman-generators.d.ts.map +1 -1
  161. package/dist/templating/postman-generators.js +10 -1
  162. package/dist/templating/postman-generators.js.map +1 -1
  163. package/dist/types/index.d.ts +44 -2
  164. package/dist/types/index.d.ts.map +1 -1
  165. package/dist/variables/envStore.d.ts +3 -0
  166. package/dist/variables/envStore.d.ts.map +1 -1
  167. package/dist/variables/envStore.js +5 -0
  168. package/dist/variables/envStore.js.map +1 -1
  169. package/dist/variables/replaceIn.d.ts +10 -0
  170. package/dist/variables/replaceIn.d.ts.map +1 -0
  171. package/dist/variables/replaceIn.js +106 -0
  172. package/dist/variables/replaceIn.js.map +1 -0
  173. package/dist/variables/resolver.d.ts.map +1 -1
  174. package/dist/variables/resolver.js +16 -2
  175. package/dist/variables/resolver.js.map +1 -1
  176. package/dist-cjs/ai/prompts/fragments.js +0 -1
  177. package/dist-cjs/ai/prompts/fragments.js.map +1 -1
  178. package/dist-cjs/ai/prompts/unified.js +1 -1
  179. package/dist-cjs/ai/prompts/unified.js.map +1 -1
  180. package/dist-cjs/ai/types.js +1 -1
  181. package/dist-cjs/ai/types.js.map +1 -1
  182. package/dist-cjs/index.js +23 -5
  183. package/dist-cjs/index.js.map +1 -1
  184. package/dist-cjs/runner/requestRunner.js +339 -1
  185. package/dist-cjs/runner/requestRunner.js.map +1 -1
  186. package/dist-cjs/runner/runCollection.js +60 -7
  187. package/dist-cjs/runner/runCollection.js.map +1 -1
  188. package/dist-cjs/runner/runRequestWithDataset.js +5 -0
  189. package/dist-cjs/runner/runRequestWithDataset.js.map +1 -1
  190. package/dist-cjs/scenario-runner/adapters/request-executor.js +7 -0
  191. package/dist-cjs/scenario-runner/adapters/request-executor.js.map +1 -1
  192. package/dist-cjs/scenario-runner/runScenario.js +4 -0
  193. package/dist-cjs/scenario-runner/runScenario.js.map +1 -1
  194. package/dist-cjs/script/chain.js +126 -0
  195. package/dist-cjs/script/chain.js.map +1 -0
  196. package/dist-cjs/script/errors.js +6 -4
  197. package/dist-cjs/script/errors.js.map +1 -1
  198. package/dist-cjs/script/executor.js +298 -64
  199. package/dist-cjs/script/executor.js.map +1 -1
  200. package/dist-cjs/script/features/collectionVariables.js +20 -11
  201. package/dist-cjs/script/features/collectionVariables.js.map +1 -1
  202. package/dist-cjs/script/features/environment.js +20 -11
  203. package/dist-cjs/script/features/environment.js.map +1 -1
  204. package/dist-cjs/script/features/feature-policy.js +2 -2
  205. package/dist-cjs/script/features/feature-policy.js.map +1 -1
  206. package/dist-cjs/script/features/info.js +6 -3
  207. package/dist-cjs/script/features/info.js.map +1 -1
  208. package/dist-cjs/script/features/iterationData.js +32 -0
  209. package/dist-cjs/script/features/iterationData.js.map +1 -0
  210. package/dist-cjs/script/features/legacyPostman.js +112 -0
  211. package/dist-cjs/script/features/legacyPostman.js.map +1 -0
  212. package/dist-cjs/script/features/log.js +347 -28
  213. package/dist-cjs/script/features/log.js.map +1 -1
  214. package/dist-cjs/script/features/tests.js +90 -64
  215. package/dist-cjs/script/features/tests.js.map +1 -1
  216. package/dist-cjs/script/features/timers.js +17 -5
  217. package/dist-cjs/script/features/timers.js.map +1 -1
  218. package/dist-cjs/script/features/variables.js +26 -9
  219. package/dist-cjs/script/features/variables.js.map +1 -1
  220. package/dist-cjs/script/features/visualizer.js +56 -0
  221. package/dist-cjs/script/features/visualizer.js.map +1 -0
  222. package/dist-cjs/script/isolated-runner.js +26 -2
  223. package/dist-cjs/script/isolated-runner.js.map +1 -1
  224. package/dist-cjs/script/layers.js +57 -0
  225. package/dist-cjs/script/layers.js.map +1 -0
  226. package/dist-cjs/script/legacy.js +20 -6
  227. package/dist-cjs/script/legacy.js.map +1 -1
  228. package/dist-cjs/script/pm-api-declarations.js +1969 -0
  229. package/dist-cjs/script/pm-api-declarations.js.map +1 -0
  230. package/dist-cjs/script/pm-api-metadata.js +336 -106
  231. package/dist-cjs/script/pm-api-metadata.js.map +1 -1
  232. package/dist-cjs/script/pm-api-reference.js +32 -7
  233. package/dist-cjs/script/pm-api-reference.js.map +1 -1
  234. package/dist-cjs/script/postman/chaiCompat.js +181 -0
  235. package/dist-cjs/script/postman/chaiCompat.js.map +1 -0
  236. package/dist-cjs/script/postman/cookies.js +170 -0
  237. package/dist-cjs/script/postman/cookies.js.map +1 -0
  238. package/dist-cjs/script/postman/modules.js +123 -0
  239. package/dist-cjs/script/postman/modules.js.map +1 -0
  240. package/dist-cjs/script/postman/proxies.js +93 -0
  241. package/dist-cjs/script/postman/proxies.js.map +1 -0
  242. package/dist-cjs/script/postman/requestAdapter.js +227 -0
  243. package/dist-cjs/script/postman/requestAdapter.js.map +1 -0
  244. package/dist-cjs/script/postman/responseAdapter.js +83 -0
  245. package/dist-cjs/script/postman/responseAdapter.js.map +1 -0
  246. package/dist-cjs/script/postman/runtime.js +132 -0
  247. package/dist-cjs/script/postman/runtime.js.map +1 -0
  248. package/dist-cjs/script/scope.js +47 -5
  249. package/dist-cjs/script/scope.js.map +1 -1
  250. package/dist-cjs/script/worker-entry.js +9 -0
  251. package/dist-cjs/script/worker-entry.js.map +1 -1
  252. package/dist-cjs/script/worker-pool.js +23 -2
  253. package/dist-cjs/script/worker-pool.js.map +1 -1
  254. package/dist-cjs/templating/generator-metadata.js +15 -1
  255. package/dist-cjs/templating/generator-metadata.js.map +1 -1
  256. package/dist-cjs/templating/postman-dynamic-catalog.js +129 -0
  257. package/dist-cjs/templating/postman-dynamic-catalog.js.map +1 -0
  258. package/dist-cjs/templating/postman-generators.js +13 -3
  259. package/dist-cjs/templating/postman-generators.js.map +1 -1
  260. package/dist-cjs/variables/envStore.js +5 -0
  261. package/dist-cjs/variables/envStore.js.map +1 -1
  262. package/dist-cjs/variables/replaceIn.js +111 -0
  263. package/dist-cjs/variables/replaceIn.js.map +1 -0
  264. package/dist-cjs/variables/resolver.js +16 -2
  265. package/dist-cjs/variables/resolver.js.map +1 -1
  266. package/package.json +12 -2
@@ -5,7 +5,7 @@
5
5
  // Composes:
6
6
  // - sandbox (vm.createContext + scope Proxy + reserved globals)
7
7
  // - pm API (assertions + tests + env/globals + request/response + node + log)
8
- // - sandbox-D extensions (db, kafka, redis, random, counter, cryptojs, variables)
8
+ // - sandbox-D extensions (db, redis, random, counter, cryptojs, variables)
9
9
  // - timeout (vm-level + outer Promise race)
10
10
  // - error serialization (line/column attribution)
11
11
  //
@@ -19,13 +19,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.validateScript = exports.executeScript = void 0;
20
20
  const executeRequest_js_1 = require("../request/executeRequest.js");
21
21
  const environment_js_1 = require("./features/environment.js");
22
- const assertions_js_1 = require("./features/assertions.js");
22
+ const legacy_js_1 = require("./legacy.js");
23
+ const legacyPostman_js_1 = require("./features/legacyPostman.js");
24
+ const modules_js_1 = require("./postman/modules.js");
25
+ const cookies_js_1 = require("./postman/cookies.js");
26
+ const visualizer_js_1 = require("./features/visualizer.js");
27
+ const requestAdapter_js_1 = require("./postman/requestAdapter.js");
28
+ const responseAdapter_js_1 = require("./postman/responseAdapter.js");
29
+ const runtime_js_1 = require("./postman/runtime.js");
23
30
  const node_js_1 = require("./features/node.js");
24
31
  const request_js_1 = require("./features/request.js");
25
32
  const response_js_1 = require("./features/response.js");
26
33
  const tests_js_1 = require("./features/tests.js");
27
34
  const log_js_1 = require("./features/log.js");
28
- const kafka_js_1 = require("./features/kafka.js");
29
35
  const redis_js_1 = require("./features/redis.js");
30
36
  const random_js_1 = require("./features/random.js");
31
37
  const counter_js_1 = require("./features/counter.js");
@@ -34,6 +40,7 @@ const variables_js_1 = require("./features/variables.js");
34
40
  const db_js_1 = require("./features/db.js");
35
41
  const timers_js_1 = require("./features/timers.js");
36
42
  const collectionVariables_js_1 = require("./features/collectionVariables.js");
43
+ const iterationData_js_1 = require("./features/iterationData.js");
37
44
  const info_js_1 = require("./features/info.js");
38
45
  const execution_js_1 = require("./features/execution.js");
39
46
  // Lodash is exposed unconditionally as the `_` global — Postman
@@ -46,26 +53,13 @@ const timeout_js_1 = require("./timeout.js");
46
53
  const DEFAULT_TIMEOUT_MS = 15_000;
47
54
  const DEFAULT_FILENAME = "callman-script.js";
48
55
  const DEFAULT_STAGE = "script";
49
- const buildSandboxDFeatures = (capabilities, isInsideTestCallback, envValues, globalValues, responseTemplateRoot) => {
56
+ const buildSandboxDFeatures = (capabilities, isInsideTestCallback, envValues, globalValues, responseTemplateRoot, collectionValues, live) => {
50
57
  const extensions = capabilities.extensions ?? {};
51
58
  const featurePolicy = capabilities.featurePolicy;
52
59
  const pm = {};
53
60
  const scopeShortcuts = {};
54
61
  let counterSnapshotFn = null;
55
- if (extensions.kafka) {
56
- const kafkaEnabled = featurePolicy ? featurePolicy.kafka : true;
57
- if (kafkaEnabled) {
58
- const api = (0, kafka_js_1.buildKafkaApi)(extensions.kafka, capabilities.allowDeferredKafkaTests ?? false);
59
- pm.kafka = api;
60
- scopeShortcuts.kafka = api;
61
- }
62
- else {
63
- // Disabled kafka — replace with feature-policy guarded value
64
- // (handled at the test-collector level via DisabledFeatureDependencyError).
65
- // We still expose the binding so user scripts don't crash at scope lookup.
66
- void isInsideTestCallback;
67
- }
68
- }
62
+ let variablesApi = null;
69
63
  if (extensions.redis) {
70
64
  const api = (0, redis_js_1.buildRedisApi)({
71
65
  capability: extensions.redis,
@@ -105,8 +99,16 @@ const buildSandboxDFeatures = (capabilities, isInsideTestCallback, envValues, gl
105
99
  // (top-level `variables` identifier) is only registered when the
106
100
  // extension was explicitly requested, to preserve the prior surface.
107
101
  {
108
- const api = (0, variables_js_1.buildVariablesApi)(envValues, globalValues);
102
+ const api = (0, variables_js_1.buildVariablesApi)(envValues, globalValues, {
103
+ collectionVariables: collectionValues,
104
+ ...(live ? { live } : {}),
105
+ ...(capabilities.iterationData ? { iterationData: capabilities.iterationData.values } : {}),
106
+ ...(capabilities.localVariables ? { local: capabilities.localVariables.values } : {}),
107
+ });
109
108
  pm.variables = api;
109
+ // Postman exposes pm.iterationData in every script (empty outside a data run).
110
+ pm.iterationData = (0, iterationData_js_1.buildIterationDataApi)(capabilities.iterationData?.values);
111
+ variablesApi = api;
110
112
  if (extensions.variables) {
111
113
  scopeShortcuts.variables = api;
112
114
  }
@@ -124,7 +126,7 @@ const buildSandboxDFeatures = (capabilities, isInsideTestCallback, envValues, gl
124
126
  pm.db = api;
125
127
  scopeShortcuts.db = api;
126
128
  }
127
- return { pm, scopeShortcuts, counterSnapshotFn };
129
+ return { pm, scopeShortcuts, counterSnapshotFn, variablesApi: variablesApi };
128
130
  };
129
131
  const buildSendRequestPendingTracker = () => {
130
132
  const pending = [];
@@ -198,7 +200,16 @@ const normalizeSendBody = (body) => {
198
200
  if (isPlainRecord(body)) {
199
201
  const mode = body.mode;
200
202
  if (mode === "raw") {
201
- return { data: body.raw ?? "", impliedContentType: null };
203
+ // Postman picks the Content-Type from the raw language (Text → text/plain);
204
+ // without one axios would label a string body as form-urlencoded.
205
+ const language = String(body.options?.raw?.language ?? "text").toLowerCase();
206
+ const byLanguage = {
207
+ json: "application/json",
208
+ xml: "application/xml",
209
+ html: "text/html",
210
+ javascript: "application/javascript",
211
+ };
212
+ return { data: body.raw ?? "", impliedContentType: byLanguage[language] ?? "text/plain" };
202
213
  }
203
214
  if (mode === "urlencoded" && Array.isArray(body.urlencoded)) {
204
215
  const params = new URLSearchParams();
@@ -255,7 +266,33 @@ const resolveSendTemplates = (value, resolve) => {
255
266
  }
256
267
  return value;
257
268
  };
258
- const createPmSendRequest = (tracker, executor, resolveTemplate) => {
269
+ const MULTIPART_BOUNDARY_PREFIX = "----CallmanFormBoundary";
270
+ // Multipart is built as a STRING body so every host executor (axios, the
271
+ // desktop's engine over worker RPC) sends it the same way. Text parts only —
272
+ // a file part has no bytes to read inside the sandbox.
273
+ const buildMultipartBody = (entries) => {
274
+ const boundary = `${MULTIPART_BOUNDARY_PREFIX}${Math.random().toString(16).slice(2)}${Date.now().toString(16)}`;
275
+ const parts = [];
276
+ for (const entry of entries) {
277
+ if (entry.disabled === true)
278
+ continue;
279
+ const key = String(entry.key ?? "").trim();
280
+ if (!key)
281
+ continue;
282
+ if (entry.type === "file") {
283
+ throw new Error(`pm.sendRequest: file part "${key}" is not supported (the sandbox cannot read files).`);
284
+ }
285
+ parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="${key.replace(/"/g, "%22")}"\r\n\r\n${String(entry.value ?? "")}\r\n`);
286
+ }
287
+ return { data: `${parts.join("")}--${boundary}--\r\n`, contentType: `multipart/form-data; boundary=${boundary}` };
288
+ };
289
+ const setCookieLines = (headers) => {
290
+ const raw = Object.entries(headers).find(([key]) => key.toLowerCase() === "set-cookie")?.[1];
291
+ if (raw === undefined || raw === null)
292
+ return [];
293
+ return Array.isArray(raw) ? raw.map(String) : String(raw).split(/\r?\n/);
294
+ };
295
+ const createPmSendRequest = (tracker, executor, resolveTemplate, context = { sdk: null, chai: null }) => {
259
296
  // Host-injected executor (desktop's real engine) takes precedence; backend
260
297
  // and CLI omit it and fall back to the built-in bare-axios client.
261
298
  const run = executor ?? executeRequest_js_1.executeRequest;
@@ -264,20 +301,60 @@ const createPmSendRequest = (tracker, executor, resolveTemplate) => {
264
301
  // pm.environment.set writes made earlier in this script). No resolver
265
302
  // wired → identity passthrough so behaviour is unchanged.
266
303
  const resolve = resolveTemplate ?? ((template) => template);
304
+ const { sdk, chai, jar } = context;
305
+ // Request objects (sdk.Request) and the Url/HeaderList inside them become the
306
+ // plain definition the rest of this function understands.
307
+ const toDefinition = (options) => {
308
+ if (typeof options === "string")
309
+ return { url: options, method: "GET" };
310
+ if (sdk && options && typeof options === "object" && sdk.Request.isRequest(options)) {
311
+ return options.toJSON();
312
+ }
313
+ return isPlainRecord(options) ? options : {};
314
+ };
315
+ const urlFrom = (input) => {
316
+ if (sdk && input && typeof input === "object" && !Array.isArray(input) && !("raw" in input) && "host" in input) {
317
+ return new sdk.Url(input).toString();
318
+ }
319
+ return normalizeSendUrl(input);
320
+ };
267
321
  return (options, callback) => {
268
322
  const promise = (async () => {
269
- const rawOptions = typeof options === "string"
270
- ? { url: options, method: "GET" }
271
- : isPlainRecord(options)
272
- ? options
273
- : {};
323
+ const rawOptions = toDefinition(options);
274
324
  // Resolve templates up-front against the live scope, then normalize.
275
325
  const normalized = resolveSendTemplates(rawOptions, resolve);
276
- const url = normalizeSendUrl(normalized.url);
326
+ const url = urlFrom(normalized.url);
277
327
  const method = String(normalized.method ?? "GET").toUpperCase();
278
328
  const headerInput = normalized.header ?? normalized.headers;
279
329
  const headers = normalizeSendHeaderInput(headerInput);
280
- const { data, impliedContentType } = normalizeSendBody(normalized.body);
330
+ const body = normalized.body;
331
+ let data;
332
+ let impliedContentType = null;
333
+ if (isPlainRecord(body) && body.mode === "formdata" && Array.isArray(body.formdata)) {
334
+ const multipart = buildMultipartBody(body.formdata);
335
+ data = multipart.data;
336
+ impliedContentType = multipart.contentType;
337
+ }
338
+ else if (isPlainRecord(body) && body.mode === "graphql" && isPlainRecord(body.graphql)) {
339
+ const graphql = body.graphql;
340
+ let variables = graphql.variables;
341
+ if (typeof variables === "string" && variables.trim()) {
342
+ try {
343
+ variables = JSON.parse(variables);
344
+ }
345
+ catch {
346
+ // leave the text as the user wrote it
347
+ }
348
+ }
349
+ data = JSON.stringify({ query: graphql.query, ...(variables ? { variables } : {}) });
350
+ impliedContentType = "application/json";
351
+ }
352
+ else if (isPlainRecord(body) && body.mode === "file") {
353
+ throw new Error("pm.sendRequest: body mode \"file\" is not supported (the sandbox cannot read files).");
354
+ }
355
+ else {
356
+ ({ data, impliedContentType } = normalizeSendBody(body));
357
+ }
281
358
  if (impliedContentType &&
282
359
  !Object.keys(headers).some((k) => k.toLowerCase() === "content-type")) {
283
360
  headers["Content-Type"] = impliedContentType;
@@ -285,6 +362,14 @@ const createPmSendRequest = (tracker, executor, resolveTemplate) => {
285
362
  const timeoutMs = typeof normalized.timeout === "number" && normalized.timeout > 0
286
363
  ? Number(normalized.timeout)
287
364
  : undefined;
365
+ // Hosts that provide a persistent cookie jar get Postman's behaviour:
366
+ // matching cookies are sent, Set-Cookie responses are stored.
367
+ if (jar && !Object.keys(headers).some((key) => key.toLowerCase() === "cookie")) {
368
+ const stored = await jar.getAll(url);
369
+ if (stored.length > 0) {
370
+ headers.Cookie = stored.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
371
+ }
372
+ }
288
373
  const result = await run({
289
374
  method,
290
375
  url,
@@ -293,21 +378,47 @@ const createPmSendRequest = (tracker, executor, resolveTemplate) => {
293
378
  body: data,
294
379
  timeoutMs,
295
380
  });
296
- const bodyText = typeof result.data === "string"
297
- ? result.data
298
- : (() => {
299
- try {
300
- return JSON.stringify(result.data ?? null);
301
- }
302
- catch {
303
- return String(result.data ?? "");
304
- }
305
- })();
381
+ const bodyText = typeof result.rawText === "string"
382
+ ? result.rawText
383
+ : typeof result.data === "string"
384
+ ? result.data
385
+ : (() => {
386
+ try {
387
+ return JSON.stringify(result.data ?? null);
388
+ }
389
+ catch {
390
+ return String(result.data ?? "");
391
+ }
392
+ })();
393
+ const responseHeaders = (result.headers ?? {});
394
+ const cookieLines = setCookieLines(responseHeaders);
395
+ if (jar) {
396
+ for (const line of cookieLines) {
397
+ const cookie = (0, cookies_js_1.parseSetCookieLine)(line);
398
+ if (cookie)
399
+ await jar.set(url, cookie);
400
+ }
401
+ }
402
+ if (sdk && chai) {
403
+ return (0, responseAdapter_js_1.buildSdkResponseApi)(sdk, chai, {
404
+ ok: result.status >= 200 && result.status < 400,
405
+ status: result.status,
406
+ statusText: "",
407
+ headers: responseHeaders,
408
+ body: result.data,
409
+ jsonBody: result.data !== null && typeof result.data === "object" ? result.data : null,
410
+ rawBody: bodyText,
411
+ contentType: Object.entries(responseHeaders).find(([key]) => key.toLowerCase() === "content-type")?.[1] ?? "",
412
+ durationMs: result.duration ?? 0,
413
+ sizeBytes: bodyText.length,
414
+ cookies: cookieLines,
415
+ });
416
+ }
306
417
  return {
307
418
  code: result.status,
308
419
  status: result.status,
309
420
  statusText: "",
310
- headers: result.headers ?? {},
421
+ headers: responseHeaders,
311
422
  body: result.data,
312
423
  responseTime: result.duration,
313
424
  duration: result.duration,
@@ -324,7 +435,7 @@ const createPmSendRequest = (tracker, executor, resolveTemplate) => {
324
435
  if (typeof callback === "function") {
325
436
  promise.then((response) => {
326
437
  try {
327
- callback(null, response);
438
+ callback(null, response, undefined);
328
439
  }
329
440
  catch (callbackError) {
330
441
  // Surface callback throws to the host log; suppressing
@@ -334,7 +445,7 @@ const createPmSendRequest = (tracker, executor, resolveTemplate) => {
334
445
  }
335
446
  }, (error) => {
336
447
  try {
337
- callback(error instanceof Error ? error : new Error(String(error)), null);
448
+ callback(error instanceof Error ? error : new Error(String(error)), null, undefined);
338
449
  }
339
450
  catch (callbackError) {
340
451
  // eslint-disable-next-line no-console
@@ -379,8 +490,12 @@ const buildPm = (capabilities, logs, collectors, sandboxDPm) => {
379
490
  // interpolates {{var}} exactly like pm.variables.replaceIn(...).
380
491
  const variablesApi = sandboxDPm.variables;
381
492
  pm.sendRequest = createPmSendRequest(collectors.sendRequestTracker, capabilities.extensions?.sendRequest?.execute, variablesApi
382
- ? (template) => variablesApi.replaceIn(template)
383
- : undefined);
493
+ ? (template) => String(variablesApi.replaceIn(template))
494
+ : undefined, {
495
+ sdk: collectors.runtime?.sdk ?? null,
496
+ chai: collectors.runtime?.chai ?? null,
497
+ jar: capabilities.cookies?.jar,
498
+ });
384
499
  }
385
500
  const extensions = capabilities.extensions ?? {};
386
501
  if (extensions.log) {
@@ -394,7 +509,7 @@ const buildPm = (capabilities, logs, collectors, sandboxDPm) => {
394
509
  }
395
510
  return pm;
396
511
  };
397
- const buildScopeBindings = ({ pm, capabilities, consoleApi, envValues, environmentApi, request, response, expect, test, sandboxDShortcuts, timers, }) => {
512
+ const buildScopeBindings = ({ pm, capabilities, consoleApi, envValues, environmentApi, request, response, expect, test, sandboxDShortcuts, timers, extraGlobals, }) => {
398
513
  const exposeLabels = capabilities.workflowContext &&
399
514
  capabilities.exposeLabelsAsGlobals !== false;
400
515
  const labelGlobals = exposeLabels
@@ -413,7 +528,6 @@ const buildScopeBindings = ({ pm, capabilities, consoleApi, envValues, environme
413
528
  expect,
414
529
  console: consoleApi,
415
530
  db: sandboxDShortcuts.db,
416
- kafka: sandboxDShortcuts.kafka,
417
531
  redis: sandboxDShortcuts.redis,
418
532
  random: sandboxDShortcuts.random,
419
533
  counter: sandboxDShortcuts.counter,
@@ -421,14 +535,26 @@ const buildScopeBindings = ({ pm, capabilities, consoleApi, envValues, environme
421
535
  CryptoJS: sandboxDShortcuts.CryptoJS,
422
536
  lodash: lodash_1.default,
423
537
  timers,
538
+ extraGlobals,
424
539
  labelGlobals,
425
540
  };
426
541
  };
542
+ const NUMERIC_STATUS_COMPARISON = /(?:\b(?:pm|cm|callman)\.response\.status\s*(?:===?|!==?|<=?|>=?)\s*\d{3}\b)|(?:\b\d{3}\s*(?:===?|!==?|<=?|>=?)\s*(?:pm|cm|callman)\.response\.status\b)|(?:expect\(\s*(?:pm|cm|callman)\.response\.status\s*\)[^;\n]*\b\d{3}\b)/;
427
543
  const executeScript = async (input) => {
428
544
  const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
429
545
  const filename = input.filename ?? DEFAULT_FILENAME;
430
546
  const stage = input.stage ?? DEFAULT_STAGE;
431
- const capabilities = input.capabilities;
547
+ // A host may hand over several Set-Cookie headers joined into one string
548
+ // ("a=1; Path=/, b=2; …"); split them so each cookie is its own entry.
549
+ const capabilities = input.capabilities.response?.cookies?.some((line) => /,\s*[^;,=\s]+=/.test(line))
550
+ ? {
551
+ ...input.capabilities,
552
+ response: {
553
+ ...input.capabilities.response,
554
+ cookies: input.capabilities.response.cookies.flatMap(legacy_js_1.splitSetCookieHeader),
555
+ },
556
+ }
557
+ : input.capabilities;
432
558
  const extensions = capabilities.extensions ?? {};
433
559
  // Empty / whitespace-only scripts short-circuit with an empty success.
434
560
  if (!input.script.trim()) {
@@ -443,8 +569,8 @@ const executeScript = async (input) => {
443
569
  if (extensions.counter) {
444
570
  passthrough.counterSnapshot = extensions.counter.snapshot;
445
571
  }
446
- if (capabilities.allowDeferredKafkaTests) {
447
- passthrough.deferredTestNames = [];
572
+ if (capabilities.localVariables) {
573
+ passthrough.localVariables = { ...capabilities.localVariables.values };
448
574
  }
449
575
  return passthrough;
450
576
  }
@@ -468,23 +594,46 @@ const executeScript = async (input) => {
468
594
  : (0, collectionVariables_js_1.buildCollectionVariablesApi)({}, null);
469
595
  const infoApi = (0, info_js_1.buildInfoApi)(capabilities.info, stage);
470
596
  const executionFeature = (0, execution_js_1.buildExecutionFeature)(capabilities.execution);
597
+ // pm.request / pm.response are real `postman-collection` objects and pm.expect
598
+ // is chai: load them before the (synchronous) sandbox is assembled.
599
+ const runtime = await (0, runtime_js_1.loadPostmanRuntime)({
600
+ sdk: (0, runtime_js_1.scriptNeedsSdk)(input.script, Boolean(capabilities.request || capabilities.response)),
601
+ });
471
602
  const requestBundle = capabilities.request
472
- ? (0, request_js_1.buildRequestApi)(capabilities.request)
603
+ ? runtime.sdk
604
+ ? (0, requestAdapter_js_1.buildSdkRequestApi)(runtime.sdk, capabilities.request)
605
+ : (0, request_js_1.buildRequestApi)(capabilities.request)
473
606
  : undefined;
474
- const responseApi = (0, response_js_1.buildResponseApi)(capabilities.response ?? null) ?? undefined;
475
- const expect = (0, assertions_js_1.createExpectApi)();
476
- const tests = (0, tests_js_1.buildTestCollector)(stage, {
477
- allowDeferredKafkaTests: capabilities.allowDeferredKafkaTests,
478
- selectedTestNames: capabilities.deferredTestNames,
479
- });
607
+ if (requestBundle && runtime.sdk) {
608
+ (0, responseAdapter_js_1.attachRequestAssertions)(runtime.chai, requestBundle.api);
609
+ }
610
+ const responseApi = capabilities.response
611
+ ? runtime.sdk
612
+ ? (0, responseAdapter_js_1.buildSdkResponseApi)(runtime.sdk, runtime.chai, capabilities.response)
613
+ : ((0, response_js_1.buildResponseApi)(capabilities.response) ?? undefined)
614
+ : undefined;
615
+ const expect = runtime.chai.expect;
616
+ const tests = (0, tests_js_1.buildTestCollector)(stage);
480
617
  const logCollector = extensions.log
481
- ? (0, log_js_1.createLogCollector)()
618
+ ? (0, log_js_1.createLogCollector)(typeof extensions.log === "object" ? { onEntry: extensions.log.onEntry } : {})
482
619
  : (0, log_js_1.createSilentLogCollector)();
620
+ // `pm.response.status` is Postman's reason phrase ("OK"); scripts written
621
+ // against Callman's old numeric status silently stop matching. Say so once.
622
+ if (extensions.log && NUMERIC_STATUS_COMPARISON.test(input.script)) {
623
+ logCollector.consoleApi.warn('pm.response.status is the reason phrase (e.g. "OK") like in Postman — compare pm.response.code for the numeric status.');
624
+ }
483
625
  // Sandbox D feature bundle — depends on env/globals being constructed first
484
626
  // (cryptojs/random/counter don't, but variables/db do).
485
- const sandboxD = buildSandboxDFeatures(capabilities, () => tests.isInsideCallback(), environmentApi?.values ?? capabilities.environment?.values ?? {}, globalsApi?.values ?? capabilities.globals?.values ?? {}, capabilities.response ?? null);
627
+ const sandboxD = buildSandboxDFeatures(capabilities, () => tests.isInsideCallback(), environmentApi?.values ?? capabilities.environment?.values ?? {}, globalsApi?.values ?? capabilities.globals?.values ?? {}, capabilities.response ?? null, collectionVariablesApi.values, {
628
+ ...(environmentApi ? { environment: () => environmentApi.toObject() } : {}),
629
+ ...(globalsApi ? { globals: () => globalsApi.toObject() } : {}),
630
+ collection: () => collectionVariablesApi.toObject(),
631
+ });
486
632
  const sendRequestTracker = buildSendRequestPendingTracker();
487
- const timerFeature = (0, timers_js_1.buildTimerFeature)();
633
+ const timerFeature = (0, timers_js_1.buildTimerFeature)((kind, error) => {
634
+ const detail = error instanceof Error ? (error.stack ?? error.message) : String(error);
635
+ logCollector.consoleApi.error(`Uncaught error in ${kind === "timeout" ? "setTimeout" : "setInterval"} callback: ${detail}`);
636
+ });
488
637
  const pm = buildPm(capabilities, { pmLog: logCollector.pmLog }, {
489
638
  test: tests,
490
639
  expect,
@@ -496,9 +645,88 @@ const executeScript = async (input) => {
496
645
  request: requestBundle,
497
646
  response: responseApi ?? undefined,
498
647
  sendRequestTracker,
648
+ runtime,
499
649
  }, sandboxD.pm);
650
+ // Postman APIs Callman does not implement: fail with an explanation instead
651
+ // of "Cannot read properties of undefined".
652
+ const unsupportedApi = (name) => new Proxy(function unsupported() {
653
+ return undefined;
654
+ }, {
655
+ get: (_target, property) => {
656
+ if (property === Symbol.toPrimitive || property === "then" || property === "toJSON")
657
+ return undefined;
658
+ throw new Error(`${name} is not supported in Callman yet.`);
659
+ },
660
+ apply: () => {
661
+ throw new Error(`${name} is not supported in Callman yet.`);
662
+ },
663
+ });
664
+ for (const name of ["vault", "datasets", "mock", "state", "message"]) {
665
+ if (!(name in pm))
666
+ pm[name] = unsupportedApi(`pm.${name}`);
667
+ }
668
+ if (pm.execution && typeof pm.execution === "object" && !("runRequest" in pm.execution)) {
669
+ pm.execution.runRequest = async () => {
670
+ throw new Error("pm.execution.runRequest is not supported in Callman yet.");
671
+ };
672
+ }
673
+ const visualizerFeature = (0, visualizer_js_1.buildVisualizerFeature)();
674
+ pm.visualizer = visualizerFeature.api;
675
+ pm.getData = visualizerFeature.getData;
676
+ // pm.cookies (CookieList of the request's domain) + pm.cookies.jar().
677
+ pm.cookies = (0, cookies_js_1.buildPmCookies)({
678
+ sdk: runtime.sdk,
679
+ lines: capabilities.cookies?.snapshot ?? capabilities.response?.cookies ?? [],
680
+ jar: capabilities.cookies?.jar ?? (0, cookies_js_1.createMemoryCookieJar)(),
681
+ });
682
+ // require() / pm.require() and Postman's legacy globals (postman.*, tests[],
683
+ // responseBody…). Modules are loaded up front: the sandbox itself is sync.
684
+ const optsOutOfLegacy = (0, legacyPostman_js_1.scriptOptsOutOfLegacy)(input.script);
685
+ const legacyEnabled = !optsOutOfLegacy && stage !== "script";
686
+ const legacyLibraries = legacyEnabled
687
+ ? ["tv4", "cheerio"].filter((name) => new RegExp(`\\b${name}\\b`).test(input.script))
688
+ : [];
689
+ const wantsXml2Json = legacyEnabled && /\bxml2Json\b/.test(input.script);
690
+ const loadedModules = await (0, modules_js_1.preloadScriptModules)([
691
+ input.script,
692
+ ...legacyLibraries.map((name) => `require("${name}")`),
693
+ ...(wantsXml2Json ? [`require("xml2js")`] : []),
694
+ ].join("\n"), {
695
+ sdk: runtime.sdk,
696
+ chai: runtime.chai,
697
+ cryptoJs: sandboxD.scopeShortcuts.CryptoJS,
698
+ timers: timerFeature.api,
699
+ });
700
+ pm.require = (0, modules_js_1.createSandboxRequire)(loadedModules, "pm.require");
701
+ const extraGlobals = {
702
+ require: (0, modules_js_1.createSandboxRequire)(loadedModules),
703
+ };
704
+ if (legacyEnabled) {
705
+ Object.assign(extraGlobals, (0, legacyPostman_js_1.buildLegacyGlobals)({
706
+ pm,
707
+ environmentApi,
708
+ globalsApi,
709
+ response: capabilities.response ?? null,
710
+ testFn: tests.test,
711
+ iterationDataValues: capabilities.iterationData?.values ?? {},
712
+ }));
713
+ for (const name of legacyLibraries) {
714
+ extraGlobals[name] = loadedModules.get(name);
715
+ }
716
+ if (wantsXml2Json) {
717
+ const xml2js = loadedModules.get("xml2js");
718
+ extraGlobals.xml2Json = (xml) => {
719
+ let out;
720
+ xml2js?.parseString(String(xml ?? ""), { explicitArray: false, async: false }, (_error, result) => {
721
+ out = result;
722
+ });
723
+ return out;
724
+ };
725
+ }
726
+ }
500
727
  const bindings = buildScopeBindings({
501
728
  pm,
729
+ extraGlobals,
502
730
  capabilities,
503
731
  consoleApi: logCollector.consoleApi,
504
732
  envValues: environmentApi?.values,
@@ -513,6 +741,8 @@ const executeScript = async (input) => {
513
741
  clearTimeout: timerFeature.api.clearTimeout,
514
742
  setInterval: timerFeature.api.setInterval,
515
743
  clearInterval: timerFeature.api.clearInterval,
744
+ setImmediate: timerFeature.api.setImmediate,
745
+ clearImmediate: timerFeature.api.clearImmediate,
516
746
  },
517
747
  });
518
748
  let compiled;
@@ -535,8 +765,8 @@ const executeScript = async (input) => {
535
765
  if (sandboxD.counterSnapshotFn) {
536
766
  result.counterSnapshot = sandboxD.counterSnapshotFn();
537
767
  }
538
- if (capabilities.allowDeferredKafkaTests) {
539
- result.deferredTestNames = [];
768
+ if (capabilities.localVariables) {
769
+ result.localVariables = { ...capabilities.localVariables.values };
540
770
  }
541
771
  return result;
542
772
  }
@@ -580,15 +810,19 @@ const executeScript = async (input) => {
580
810
  if (sandboxD.counterSnapshotFn) {
581
811
  result.counterSnapshot = sandboxD.counterSnapshotFn();
582
812
  }
583
- if (capabilities.allowDeferredKafkaTests) {
584
- result.deferredTestNames = [...tests.deferredTestNames];
585
- }
586
813
  // Only surface executionControl when the host actually wired the
587
814
  // capability — keeps the result shape unchanged for callers that
588
815
  // don't know about pm.execution yet.
589
816
  if (capabilities.execution) {
590
817
  result.executionControl = executionFeature.getControl();
591
818
  }
819
+ if (capabilities.localVariables) {
820
+ result.localVariables = sandboxD.variablesApi.localSnapshot?.() ?? {};
821
+ }
822
+ const visualization = visualizerFeature.payload();
823
+ if (visualization) {
824
+ result.visualizer = visualization;
825
+ }
592
826
  return result;
593
827
  };
594
828
  exports.executeScript = executeScript;