@apiquest/fracture 1.0.2 → 1.0.4

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 (168) hide show
  1. package/README.md +119 -0
  2. package/bin/cli.js +2 -2
  3. package/dist/CollectionRunner.js +3 -3
  4. package/dist/ScriptEngine.js +4 -4
  5. package/dist/cli/plugin-commands.d.ts.map +1 -1
  6. package/dist/cli/plugin-commands.js +2 -1
  7. package/dist/cli/plugin-commands.js.map +1 -1
  8. package/package.json +55 -50
  9. package/src/CollectionAnalyzer.ts +102 -102
  10. package/src/CollectionRunner.ts +1423 -1423
  11. package/src/CollectionRunner.types.ts +9 -9
  12. package/src/CollectionValidator.ts +289 -289
  13. package/src/ConsoleReporter.ts +143 -143
  14. package/src/CookieJar.ts +258 -258
  15. package/src/DagScheduler.ts +439 -439
  16. package/src/Logger.ts +85 -85
  17. package/src/PluginLoader.ts +126 -126
  18. package/src/PluginManager.ts +208 -208
  19. package/src/PluginResolver.ts +154 -154
  20. package/src/QuestAPI.ts +764 -764
  21. package/src/QuestAPI.types.ts +33 -33
  22. package/src/QuestTestAPI.ts +164 -164
  23. package/src/RequestFilter.ts +224 -224
  24. package/src/ScriptEngine.ts +219 -219
  25. package/src/ScriptValidator.ts +428 -428
  26. package/src/TaskGraph.ts +598 -598
  27. package/src/TestCounter.ts +109 -109
  28. package/src/VariableResolver.ts +114 -114
  29. package/src/cli/index.ts +480 -480
  30. package/src/cli/plugin-commands.ts +342 -341
  31. package/src/cli/plugin-discovery.ts +44 -44
  32. package/src/index.ts +24 -24
  33. package/src/utils.ts +52 -52
  34. package/tsconfig.json +20 -20
  35. package/tsconfig.test.json +5 -5
  36. package/vitest.config.ts +22 -22
  37. package/dist/ExecutionTree.d.ts +0 -77
  38. package/dist/ExecutionTree.d.ts.map +0 -1
  39. package/dist/ExecutionTree.js +0 -265
  40. package/dist/ExecutionTree.js.map +0 -1
  41. package/dist/fracture/src/CollectionAnalyzer.d.ts +0 -17
  42. package/dist/fracture/src/CollectionAnalyzer.d.ts.map +0 -1
  43. package/dist/fracture/src/CollectionAnalyzer.js +0 -70
  44. package/dist/fracture/src/CollectionAnalyzer.js.map +0 -1
  45. package/dist/fracture/src/CollectionRunner.d.ts +0 -39
  46. package/dist/fracture/src/CollectionRunner.d.ts.map +0 -1
  47. package/dist/fracture/src/CollectionRunner.js +0 -802
  48. package/dist/fracture/src/CollectionRunner.js.map +0 -1
  49. package/dist/fracture/src/CollectionRunner.types.d.ts +0 -8
  50. package/dist/fracture/src/CollectionRunner.types.d.ts.map +0 -1
  51. package/dist/fracture/src/CollectionRunner.types.js +0 -2
  52. package/dist/fracture/src/CollectionRunner.types.js.map +0 -1
  53. package/dist/fracture/src/CollectionValidator.d.ts +0 -14
  54. package/dist/fracture/src/CollectionValidator.d.ts.map +0 -1
  55. package/dist/fracture/src/CollectionValidator.js +0 -145
  56. package/dist/fracture/src/CollectionValidator.js.map +0 -1
  57. package/dist/fracture/src/ConsoleReporter.d.ts +0 -24
  58. package/dist/fracture/src/ConsoleReporter.d.ts.map +0 -1
  59. package/dist/fracture/src/ConsoleReporter.js +0 -123
  60. package/dist/fracture/src/ConsoleReporter.js.map +0 -1
  61. package/dist/fracture/src/CookieJar.d.ts +0 -70
  62. package/dist/fracture/src/CookieJar.d.ts.map +0 -1
  63. package/dist/fracture/src/CookieJar.js +0 -233
  64. package/dist/fracture/src/CookieJar.js.map +0 -1
  65. package/dist/fracture/src/ExecutionTree.d.ts +0 -77
  66. package/dist/fracture/src/ExecutionTree.d.ts.map +0 -1
  67. package/dist/fracture/src/ExecutionTree.js +0 -258
  68. package/dist/fracture/src/ExecutionTree.js.map +0 -1
  69. package/dist/fracture/src/Logger.d.ts +0 -25
  70. package/dist/fracture/src/Logger.d.ts.map +0 -1
  71. package/dist/fracture/src/Logger.js +0 -78
  72. package/dist/fracture/src/Logger.js.map +0 -1
  73. package/dist/fracture/src/PluginLoader.d.ts +0 -23
  74. package/dist/fracture/src/PluginLoader.d.ts.map +0 -1
  75. package/dist/fracture/src/PluginLoader.js +0 -102
  76. package/dist/fracture/src/PluginLoader.js.map +0 -1
  77. package/dist/fracture/src/PluginManager.d.ts +0 -64
  78. package/dist/fracture/src/PluginManager.d.ts.map +0 -1
  79. package/dist/fracture/src/PluginManager.js +0 -162
  80. package/dist/fracture/src/PluginManager.js.map +0 -1
  81. package/dist/fracture/src/PluginResolver.d.ts +0 -35
  82. package/dist/fracture/src/PluginResolver.d.ts.map +0 -1
  83. package/dist/fracture/src/PluginResolver.js +0 -128
  84. package/dist/fracture/src/PluginResolver.js.map +0 -1
  85. package/dist/fracture/src/QuestAPI.d.ts +0 -9
  86. package/dist/fracture/src/QuestAPI.d.ts.map +0 -1
  87. package/dist/fracture/src/QuestAPI.js +0 -679
  88. package/dist/fracture/src/QuestAPI.js.map +0 -1
  89. package/dist/fracture/src/QuestAPI.types.d.ts +0 -35
  90. package/dist/fracture/src/QuestAPI.types.d.ts.map +0 -1
  91. package/dist/fracture/src/QuestAPI.types.js +0 -3
  92. package/dist/fracture/src/QuestAPI.types.js.map +0 -1
  93. package/dist/fracture/src/QuestTestAPI.d.ts +0 -12
  94. package/dist/fracture/src/QuestTestAPI.d.ts.map +0 -1
  95. package/dist/fracture/src/QuestTestAPI.js +0 -133
  96. package/dist/fracture/src/QuestTestAPI.js.map +0 -1
  97. package/dist/fracture/src/ScriptEngine.d.ts +0 -21
  98. package/dist/fracture/src/ScriptEngine.d.ts.map +0 -1
  99. package/dist/fracture/src/ScriptEngine.js +0 -183
  100. package/dist/fracture/src/ScriptEngine.js.map +0 -1
  101. package/dist/fracture/src/ScriptValidator.d.ts +0 -68
  102. package/dist/fracture/src/ScriptValidator.d.ts.map +0 -1
  103. package/dist/fracture/src/ScriptValidator.js +0 -351
  104. package/dist/fracture/src/ScriptValidator.js.map +0 -1
  105. package/dist/fracture/src/TestCounter.d.ts +0 -18
  106. package/dist/fracture/src/TestCounter.d.ts.map +0 -1
  107. package/dist/fracture/src/TestCounter.js +0 -82
  108. package/dist/fracture/src/TestCounter.js.map +0 -1
  109. package/dist/fracture/src/VariableResolver.d.ts +0 -20
  110. package/dist/fracture/src/VariableResolver.d.ts.map +0 -1
  111. package/dist/fracture/src/VariableResolver.js +0 -100
  112. package/dist/fracture/src/VariableResolver.js.map +0 -1
  113. package/dist/fracture/src/cli/index.d.ts +0 -3
  114. package/dist/fracture/src/cli/index.d.ts.map +0 -1
  115. package/dist/fracture/src/cli/index.js +0 -347
  116. package/dist/fracture/src/cli/index.js.map +0 -1
  117. package/dist/fracture/src/cli/plugin-commands.d.ts +0 -6
  118. package/dist/fracture/src/cli/plugin-commands.d.ts.map +0 -1
  119. package/dist/fracture/src/cli/plugin-commands.js +0 -263
  120. package/dist/fracture/src/cli/plugin-commands.js.map +0 -1
  121. package/dist/fracture/src/cli/plugin-discovery.d.ts +0 -11
  122. package/dist/fracture/src/cli/plugin-discovery.d.ts.map +0 -1
  123. package/dist/fracture/src/cli/plugin-discovery.js +0 -64
  124. package/dist/fracture/src/cli/plugin-discovery.js.map +0 -1
  125. package/dist/fracture/src/index.d.ts +0 -13
  126. package/dist/fracture/src/index.d.ts.map +0 -1
  127. package/dist/fracture/src/index.js +0 -17
  128. package/dist/fracture/src/index.js.map +0 -1
  129. package/dist/fracture/src/utils.d.ts +0 -28
  130. package/dist/fracture/src/utils.d.ts.map +0 -1
  131. package/dist/fracture/src/utils.js +0 -48
  132. package/dist/fracture/src/utils.js.map +0 -1
  133. package/dist/plugin-auth/src/apikey-auth.d.ts +0 -3
  134. package/dist/plugin-auth/src/apikey-auth.d.ts.map +0 -1
  135. package/dist/plugin-auth/src/apikey-auth.js +0 -73
  136. package/dist/plugin-auth/src/apikey-auth.js.map +0 -1
  137. package/dist/plugin-auth/src/basic-auth.d.ts +0 -3
  138. package/dist/plugin-auth/src/basic-auth.d.ts.map +0 -1
  139. package/dist/plugin-auth/src/basic-auth.js +0 -61
  140. package/dist/plugin-auth/src/basic-auth.js.map +0 -1
  141. package/dist/plugin-auth/src/bearer-auth.d.ts +0 -3
  142. package/dist/plugin-auth/src/bearer-auth.d.ts.map +0 -1
  143. package/dist/plugin-auth/src/bearer-auth.js +0 -49
  144. package/dist/plugin-auth/src/bearer-auth.js.map +0 -1
  145. package/dist/plugin-auth/src/helpers.d.ts +0 -3
  146. package/dist/plugin-auth/src/helpers.d.ts.map +0 -1
  147. package/dist/plugin-auth/src/helpers.js +0 -8
  148. package/dist/plugin-auth/src/helpers.js.map +0 -1
  149. package/dist/plugin-auth/src/index.d.ts +0 -10
  150. package/dist/plugin-auth/src/index.d.ts.map +0 -1
  151. package/dist/plugin-auth/src/index.js +0 -25
  152. package/dist/plugin-auth/src/index.js.map +0 -1
  153. package/dist/plugin-auth/src/oauth2-auth.d.ts +0 -35
  154. package/dist/plugin-auth/src/oauth2-auth.d.ts.map +0 -1
  155. package/dist/plugin-auth/src/oauth2-auth.js +0 -266
  156. package/dist/plugin-auth/src/oauth2-auth.js.map +0 -1
  157. package/dist/plugin-http/src/index.d.ts +0 -4
  158. package/dist/plugin-http/src/index.d.ts.map +0 -1
  159. package/dist/plugin-http/src/index.js +0 -266
  160. package/dist/plugin-http/src/index.js.map +0 -1
  161. package/dist/plugin-vault-file/src/index.d.ts +0 -67
  162. package/dist/plugin-vault-file/src/index.d.ts.map +0 -1
  163. package/dist/plugin-vault-file/src/index.js +0 -171
  164. package/dist/plugin-vault-file/src/index.js.map +0 -1
  165. package/dist/types.d.ts +0 -374
  166. package/dist/types.d.ts.map +0 -1
  167. package/dist/types.js +0 -13
  168. package/dist/types.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"QuestAPI.d.ts","sourceRoot":"","sources":["../../../src/QuestAPI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAA4B,MAAM,iBAAiB,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA8G7C;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,UAAU,EAAE,EAAE,gCAAgC;AACrD,aAAa,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GACxC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6kBzB"}
@@ -1,679 +0,0 @@
1
- import { ScriptType } from '@apiquest/types';
2
- import { createQuestTestAPI } from './QuestTestAPI.js';
3
- import { isNullOrWhitespace } from './utils.js';
4
- /**
5
- * Helper: Execute HTTP request and return response object
6
- * Used by quest.sendRequest() to make requests from scripts
7
- */
8
- async function executeHttpRequest(config) {
9
- // Use native fetch
10
- const url = config.url;
11
- const method = config.method ?? 'GET';
12
- const headers = config.header ?? config.headers ?? {};
13
- if (isNullOrWhitespace(url)) {
14
- throw new Error('sendRequest requires a "url" property');
15
- }
16
- const fetchOptions = {
17
- method,
18
- headers
19
- };
20
- // Handle body
21
- if (config.body !== null && config.body !== undefined) {
22
- if (typeof config.body === 'object' && 'mode' in config.body && config.body.mode !== null && config.body.mode !== undefined) {
23
- // Handle different body modes
24
- if (config.body.mode === 'raw') {
25
- fetchOptions.body = config.body.raw;
26
- }
27
- else if (config.body.mode === 'urlencoded' && config.body.urlencoded !== null && config.body.urlencoded !== undefined) {
28
- // Convert to URLSearchParams
29
- const params = new URLSearchParams();
30
- for (const item of config.body.urlencoded) {
31
- params.append(item.key, item.value);
32
- }
33
- fetchOptions.body = params.toString();
34
- fetchOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded';
35
- }
36
- else if (config.body.mode === 'formdata' && config.body.formdata !== null && config.body.formdata !== undefined) {
37
- // FormData
38
- const formData = new FormData();
39
- for (const item of config.body.formdata) {
40
- formData.append(item.key, item.value);
41
- }
42
- fetchOptions.body = formData;
43
- }
44
- }
45
- else if (typeof config.body === 'string') {
46
- fetchOptions.body = config.body;
47
- }
48
- else {
49
- // Assume JSON
50
- fetchOptions.body = JSON.stringify(config.body);
51
- const headersRecord = fetchOptions.headers;
52
- headersRecord['Content-Type'] ??= 'application/json';
53
- }
54
- }
55
- try {
56
- const startTime = Date.now();
57
- const response = await fetch(url, fetchOptions);
58
- const duration = Date.now() - startTime;
59
- const body = await response.text();
60
- // Convert headers, preserving multiple values (e.g., set-cookie)
61
- const headers = {};
62
- const headerCounts = {};
63
- response.headers.forEach((value, key) => {
64
- const lowerKey = key.toLowerCase();
65
- if (headerCounts[lowerKey] === 0 || headerCounts[lowerKey] === undefined) {
66
- headerCounts[lowerKey] = 0;
67
- headers[lowerKey] = value;
68
- }
69
- else {
70
- // Multiple values for this header - convert to array
71
- const existing = headers[lowerKey];
72
- if (!Array.isArray(existing)) {
73
- headers[lowerKey] = [existing];
74
- }
75
- headers[lowerKey].push(value);
76
- }
77
- headerCounts[lowerKey]++;
78
- });
79
- // Return response object compatible with quest API
80
- const responseObj = {
81
- status: response.status,
82
- statusText: response.statusText,
83
- body: body,
84
- headers: headers,
85
- time: duration,
86
- // Helper methods
87
- json() {
88
- try {
89
- return JSON.parse(body);
90
- }
91
- catch {
92
- return null;
93
- }
94
- },
95
- text() {
96
- return body;
97
- }
98
- };
99
- return responseObj;
100
- }
101
- catch (error) {
102
- const errorMsg = error.message ?? 'Unknown error';
103
- throw new Error(`Request failed: ${errorMsg}`);
104
- }
105
- }
106
- /**
107
- * Creates the complete quest API object
108
- * Returns all quest.* methods and properties for script execution
109
- */
110
- export function createQuestAPI(context, scriptType, tests, // Array to collect test results
111
- emitAssertion) {
112
- // Create test API (test, skip, fail)
113
- const testAPI = createQuestTestAPI(tests, scriptType, emitAssertion);
114
- return {
115
- // Test API
116
- test: testAPI.test,
117
- skip: testAPI.skip,
118
- fail: testAPI.fail,
119
- // Send HTTP request - supports BOTH async/await and callback patterns
120
- sendRequest(config, callback) {
121
- const requestPromise = executeHttpRequest(config);
122
- // If callback provided, use callback pattern
123
- if (callback !== null && callback !== undefined && typeof callback === 'function') {
124
- requestPromise
125
- .then((res) => {
126
- callback(null, res);
127
- })
128
- .catch((err) => {
129
- callback(err, null);
130
- });
131
- return undefined; // Don't return promise in callback mode
132
- }
133
- else {
134
- // No callback, return Promise for async/await
135
- return requestPromise;
136
- }
137
- },
138
- // Wait/delay execution
139
- wait(ms) {
140
- if (typeof ms !== 'number' || isNaN(ms)) {
141
- throw new Error('quest.wait() requires a valid number of milliseconds');
142
- }
143
- if (ms < 0) {
144
- throw new Error('quest.wait() milliseconds must be non-negative');
145
- }
146
- return new Promise(resolve => setTimeout(resolve, ms));
147
- },
148
- // Variables API
149
- variables: (() => {
150
- const variablesAPI = {
151
- get(key) {
152
- // Priority: iteration > scope stack > collection > env => global
153
- if (context.currentIterationData !== null && context.currentIterationData !== undefined && key in context.currentIterationData) {
154
- return String(context.currentIterationData[key]);
155
- }
156
- // Search scope stack (top to bottom)
157
- for (let i = context.scopeStack.length - 1; i >= 0; i--) {
158
- if (key in context.scopeStack[i].vars) {
159
- return context.scopeStack[i].vars[key];
160
- }
161
- }
162
- if (key in context.collectionVariables) {
163
- return context.collectionVariables[key];
164
- }
165
- if (context.environment !== null && context.environment !== undefined && key in context.environment.variables) {
166
- return context.environment.variables[key];
167
- }
168
- if (key in context.globalVariables) {
169
- return context.globalVariables[key];
170
- }
171
- return null;
172
- },
173
- set(key, value) {
174
- // Search scope stack for existing key, or set in top scope
175
- for (let i = context.scopeStack.length - 1; i >= 0; i--) {
176
- if (key in context.scopeStack[i].vars) {
177
- context.scopeStack[i].vars[key] = value;
178
- return;
179
- }
180
- }
181
- // Not found: set in current (top) scope
182
- if (context.scopeStack.length > 0) {
183
- context.scopeStack[context.scopeStack.length - 1].vars[key] = value;
184
- }
185
- },
186
- replaceIn(template) {
187
- if (isNullOrWhitespace(template))
188
- return template;
189
- // Replace all {{variable}} patterns
190
- return template.replace(/\{\{([^}]+)\}\}/g, (match, varName) => {
191
- const value = variablesAPI.get(varName.trim());
192
- // If variable not found, leave the placeholder
193
- return value !== null ? String(value) : match;
194
- });
195
- },
196
- has(key) {
197
- return variablesAPI.get(key) !== null;
198
- }
199
- };
200
- return variablesAPI;
201
- })(),
202
- // Global variables
203
- global: {
204
- variables: {
205
- get(key) {
206
- return context.globalVariables[key] ?? null;
207
- },
208
- set(key, value) {
209
- context.globalVariables[key] = value;
210
- },
211
- has(key) {
212
- return key in context.globalVariables;
213
- },
214
- remove(key) {
215
- if (key in context.globalVariables) {
216
- delete context.globalVariables[key];
217
- return true;
218
- }
219
- return false;
220
- },
221
- clear() {
222
- context.globalVariables = {};
223
- },
224
- toObject() {
225
- return { ...context.globalVariables };
226
- }
227
- }
228
- },
229
- // Collection API
230
- collection: {
231
- // Collection info
232
- info: {
233
- ...context.collectionInfo,
234
- version: context.collectionInfo.version !== null &&
235
- context.collectionInfo.version !== undefined &&
236
- context.collectionInfo.version !== ''
237
- ? context.collectionInfo.version
238
- : null,
239
- description: context.collectionInfo.description !== null &&
240
- context.collectionInfo.description !== undefined &&
241
- context.collectionInfo.description !== ''
242
- ? context.collectionInfo.description
243
- : null,
244
- },
245
- // Collection variables
246
- variables: {
247
- get(key) {
248
- return context.collectionVariables[key] ?? null;
249
- },
250
- set(key, value) {
251
- context.collectionVariables[key] = value;
252
- },
253
- has(key) {
254
- return key in context.collectionVariables;
255
- },
256
- remove(key) {
257
- if (key in context.collectionVariables) {
258
- delete context.collectionVariables[key];
259
- return true;
260
- }
261
- return false;
262
- },
263
- clear() {
264
- context.collectionVariables = {};
265
- },
266
- toObject() {
267
- return { ...context.collectionVariables };
268
- }
269
- }
270
- },
271
- // Environment variables
272
- environment: {
273
- name: context.environment?.name ?? null,
274
- variables: {
275
- get(key) {
276
- return context.environment?.variables[key] ?? null;
277
- },
278
- set(key, value) {
279
- context.environment ??= { name: 'Runtime Environment', variables: {} };
280
- context.environment.variables[key] = value;
281
- },
282
- has(key) {
283
- return context.environment !== null && context.environment !== undefined ? key in context.environment.variables : false;
284
- },
285
- remove(key) {
286
- if (context.environment !== null && context.environment !== undefined && key in context.environment.variables) {
287
- delete context.environment.variables[key];
288
- return true;
289
- }
290
- return false;
291
- },
292
- clear() {
293
- if (context.environment !== null && context.environment !== undefined) {
294
- context.environment.variables = {};
295
- }
296
- },
297
- toObject() {
298
- return context.environment !== null && context.environment !== undefined ? { ...context.environment.variables } : {};
299
- }
300
- }
301
- },
302
- // Scope variables (hierarchical)
303
- scope: {
304
- variables: {
305
- get(key) {
306
- // Search scope stack top to bottom
307
- for (let i = context.scopeStack.length - 1; i >= 0; i--) {
308
- if (key in context.scopeStack[i].vars) {
309
- return context.scopeStack[i].vars[key];
310
- }
311
- }
312
- return null;
313
- },
314
- set(key, value) {
315
- // Search stack for existing key, or set in top scope
316
- for (let i = context.scopeStack.length - 1; i >= 0; i--) {
317
- if (key in context.scopeStack[i].vars) {
318
- context.scopeStack[i].vars[key] = value;
319
- return;
320
- }
321
- }
322
- // Not found: set in current (top) scope
323
- if (context.scopeStack.length > 0) {
324
- context.scopeStack[context.scopeStack.length - 1].vars[key] = value;
325
- }
326
- },
327
- has(key) {
328
- return this.get(key) !== null;
329
- },
330
- remove(key) {
331
- // Remove from the scope where it exists
332
- for (let i = context.scopeStack.length - 1; i >= 0; i--) {
333
- if (key in context.scopeStack[i].vars) {
334
- delete context.scopeStack[i].vars[key];
335
- return true;
336
- }
337
- }
338
- return false;
339
- },
340
- clear() {
341
- // Clear current (top) scope only
342
- if (context.scopeStack.length > 0) {
343
- context.scopeStack[context.scopeStack.length - 1].vars = {};
344
- }
345
- },
346
- toObject() {
347
- // Merge all scopes (bottom to top, so top overrides)
348
- const result = {};
349
- for (let i = 0; i < context.scopeStack.length; i++) {
350
- Object.assign(result, context.scopeStack[i].vars);
351
- }
352
- return result;
353
- }
354
- }
355
- },
356
- // Response API
357
- response: context.currentResponse !== null && context.currentResponse !== undefined ? {
358
- status: context.currentResponse.status,
359
- statusText: context.currentResponse.statusText,
360
- headers: {
361
- // Method API
362
- get(name) {
363
- if (context.currentResponse?.headers === null || context.currentResponse?.headers === undefined)
364
- return null;
365
- // Case-insensitive lookup
366
- const lowerName = name.toLowerCase();
367
- for (const [key, value] of Object.entries(context.currentResponse.headers)) {
368
- if (key.toLowerCase() === lowerName) {
369
- return value;
370
- }
371
- }
372
- return null;
373
- },
374
- has(name) {
375
- if (context.currentResponse?.headers === null || context.currentResponse?.headers === undefined)
376
- return false;
377
- // Case-insensitive lookup
378
- const lowerName = name.toLowerCase();
379
- for (const key of Object.keys(context.currentResponse.headers)) {
380
- if (key.toLowerCase() === lowerName) {
381
- return true;
382
- }
383
- }
384
- return false;
385
- },
386
- toObject() {
387
- return context.currentResponse?.headers ?? {};
388
- }
389
- },
390
- body: context.currentResponse.body,
391
- text() {
392
- return context.currentResponse?.body ?? '';
393
- },
394
- json() {
395
- try {
396
- return JSON.parse(context.currentResponse?.body ?? '{}');
397
- }
398
- catch {
399
- return {};
400
- }
401
- },
402
- time: context.currentResponse.duration,
403
- size: context.currentResponse.body?.length ?? 0,
404
- // Assertion helpers
405
- to: {
406
- be: {
407
- ok: context.currentResponse.status === 200,
408
- success: context.currentResponse.status >= 200 && context.currentResponse.status < 300,
409
- clientError: context.currentResponse.status >= 400 && context.currentResponse.status < 500,
410
- serverError: context.currentResponse.status >= 500 && context.currentResponse.status < 600
411
- },
412
- have: {
413
- status(code) {
414
- return context.currentResponse?.status === code;
415
- },
416
- header(name) {
417
- if (context.currentResponse?.headers === null || context.currentResponse?.headers === undefined)
418
- return false;
419
- const lowerName = name.toLowerCase();
420
- for (const key of Object.keys(context.currentResponse.headers)) {
421
- if (key.toLowerCase() === lowerName) {
422
- return true;
423
- }
424
- }
425
- return false;
426
- },
427
- jsonBody(field) {
428
- try {
429
- const data = JSON.parse(context.currentResponse?.body ?? '{}');
430
- return field in data;
431
- }
432
- catch {
433
- return false;
434
- }
435
- }
436
- }
437
- }
438
- } : null,
439
- // Request info and modification API
440
- request: {
441
- info: {
442
- name: context.currentRequest?.name ?? '',
443
- id: context.currentRequest?.id ?? '',
444
- protocol: context.protocol,
445
- description: context.currentRequest?.description ?? ''
446
- },
447
- dependsOn: context.currentRequest?.dependsOn ?? null,
448
- condition: context.currentRequest?.condition ?? null,
449
- url: (context.currentRequest?.data.url ?? ''),
450
- method: (context.currentRequest?.data.method ?? ''),
451
- body: {
452
- get() {
453
- if (context.currentRequest?.data.body === null || context.currentRequest?.data.body === undefined)
454
- return null;
455
- const body = context.currentRequest.data.body;
456
- // Handle different body modes
457
- if (typeof body === 'string')
458
- return body;
459
- if (typeof body === 'object' && 'mode' in body && body.mode === 'raw')
460
- return body.raw ?? null;
461
- if (typeof body === 'object' && 'mode' in body && body.mode === 'urlencoded')
462
- return null; // Return null for non-raw modes
463
- if (typeof body === 'object' && 'mode' in body && body.mode === 'formdata')
464
- return null;
465
- return null;
466
- },
467
- set(content) {
468
- if (context.currentRequest === null || context.currentRequest === undefined)
469
- return;
470
- if (context.currentRequest.data.body === null || context.currentRequest.data.body === undefined) {
471
- context.currentRequest.data.body = { mode: 'raw', raw: content };
472
- }
473
- else if (typeof context.currentRequest.data.body === 'string') {
474
- context.currentRequest.data.body = content;
475
- }
476
- else if (typeof context.currentRequest.data.body === 'object') {
477
- context.currentRequest.data.body.raw = content;
478
- }
479
- },
480
- get mode() {
481
- if (context.currentRequest?.data.body === null || context.currentRequest?.data.body === undefined)
482
- return null;
483
- const body = context.currentRequest.data.body;
484
- if (typeof body === 'string')
485
- return 'raw';
486
- return (typeof body === 'object' && 'mode' in body ? body.mode : 'raw');
487
- }
488
- },
489
- headers: {
490
- add(header) {
491
- if (context.currentRequest === null || context.currentRequest === undefined)
492
- return;
493
- const headers = context.currentRequest.data.headers;
494
- if (headers === null || headers === undefined) {
495
- context.currentRequest.data.headers = {};
496
- }
497
- context.currentRequest.data.headers[header.key] = header.value;
498
- },
499
- remove(key) {
500
- if (context.currentRequest?.data.headers === null || context.currentRequest?.data.headers === undefined)
501
- return;
502
- delete context.currentRequest.data.headers[key];
503
- },
504
- get(key) {
505
- if (context.currentRequest?.data.headers === null || context.currentRequest?.data.headers === undefined)
506
- return null;
507
- // Case-insensitive lookup
508
- const lowerKey = key.toLowerCase();
509
- for (const [headerKey, value] of Object.entries(context.currentRequest.data.headers)) {
510
- if (headerKey.toLowerCase() === lowerKey) {
511
- return value;
512
- }
513
- }
514
- return null;
515
- },
516
- upsert(header) {
517
- if (context.currentRequest === null || context.currentRequest === undefined)
518
- return;
519
- const headers = context.currentRequest.data.headers;
520
- if (headers === null || headers === undefined) {
521
- context.currentRequest.data.headers = {};
522
- }
523
- context.currentRequest.data.headers[header.key] = header.value;
524
- },
525
- toObject() {
526
- return (context.currentRequest?.data.headers ?? {});
527
- }
528
- }
529
- },
530
- // Iteration API
531
- iteration: {
532
- current: context.iterationCurrent,
533
- count: context.iterationCount,
534
- data: {
535
- get(key) {
536
- return context.currentIterationData?.[key] ?? null;
537
- },
538
- has(key) {
539
- return context.currentIterationData !== null && context.currentIterationData !== undefined ? key in context.currentIterationData : false;
540
- },
541
- toObject() {
542
- return context.currentIterationData ?? {};
543
- },
544
- keys() {
545
- return context.currentIterationData !== null && context.currentIterationData !== undefined ? Object.keys(context.currentIterationData) : [];
546
- },
547
- all() {
548
- return context.iterationData ?? [];
549
- }
550
- }
551
- },
552
- // Execution history API
553
- history: {
554
- requests: {
555
- count() {
556
- return context.executionHistory.length;
557
- },
558
- get(idOrName) {
559
- return context.executionHistory.find(entry => entry.id === idOrName || entry.name === idOrName) ?? null;
560
- },
561
- all() {
562
- return context.executionHistory;
563
- },
564
- last() {
565
- return context.executionHistory.length > 0
566
- ? context.executionHistory[context.executionHistory.length - 1]
567
- : null;
568
- },
569
- filter(criteria) {
570
- return context.executionHistory.filter(entry => {
571
- // Filter by path (with wildcard support)
572
- if (criteria.path !== null && criteria.path !== undefined) {
573
- const pathPattern = criteria.path.replace(/\*/g, '.*');
574
- const pathRegex = new RegExp(`^${pathPattern}$`);
575
- if (!pathRegex.test(entry.path)) {
576
- return false;
577
- }
578
- }
579
- // Filter by name
580
- if (criteria.name !== null && criteria.name !== undefined && entry.name !== criteria.name) {
581
- return false;
582
- }
583
- // Filter by iteration
584
- if (criteria.iteration !== null && criteria.iteration !== undefined && entry.iteration !== criteria.iteration) {
585
- return false;
586
- }
587
- // Filter by id
588
- if (criteria.id !== null && criteria.id !== undefined && entry.id !== criteria.id) {
589
- return false;
590
- }
591
- return true;
592
- });
593
- }
594
- }
595
- },
596
- // Cookies API - Uses cookie jar for persistence across requests
597
- cookies: {
598
- get(name) {
599
- // Use cookie jar if available
600
- if (context.cookieJar !== null && context.cookieJar !== undefined) {
601
- return context.cookieJar.get(name);
602
- }
603
- return null;
604
- },
605
- set(name, value, options) {
606
- if (context.cookieJar !== null && context.cookieJar !== undefined) {
607
- context.cookieJar.set(name, value, options);
608
- }
609
- },
610
- has(name) {
611
- // Use cookie jar if available
612
- if (context.cookieJar !== null && context.cookieJar !== undefined) {
613
- return context.cookieJar.has(name);
614
- }
615
- return false;
616
- },
617
- remove(name) {
618
- // Use cookie jar if available
619
- if (context.cookieJar !== null && context.cookieJar !== undefined) {
620
- context.cookieJar.remove(name);
621
- }
622
- },
623
- clear() {
624
- // Use cookie jar if available
625
- if (context.cookieJar !== null && context.cookieJar !== undefined) {
626
- context.cookieJar.clear();
627
- }
628
- },
629
- toObject() {
630
- // Use cookie jar if available
631
- if (context.cookieJar !== null && context.cookieJar !== undefined) {
632
- return context.cookieJar.toObject();
633
- }
634
- return {};
635
- }
636
- },
637
- // Plugin event API (for PluginEvent script type)
638
- event: context.currentEvent !== null && context.currentEvent !== undefined ? {
639
- name: context.currentEvent.eventName,
640
- timestamp: context.currentEvent.timestamp,
641
- data: (() => {
642
- const rawData = context.currentEvent.data;
643
- // Add json() helper method
644
- const dataWithHelper = typeof rawData === 'string' ? { value: rawData } : rawData;
645
- dataWithHelper.json = function () {
646
- try {
647
- return (typeof rawData === 'string' ? JSON.parse(rawData) : rawData);
648
- }
649
- catch {
650
- return null;
651
- }
652
- };
653
- return dataWithHelper;
654
- })(),
655
- index: context.currentEvent.index
656
- } : null,
657
- // Hint expected message count (for streaming protocols)
658
- expectMessages(count, timeout) {
659
- // Only allowed in preRequestScript
660
- if (scriptType !== ScriptType.PreRequest) {
661
- throw new Error('quest.expectMessages() can only be called in preRequestScript');
662
- }
663
- // Validate count is positive integer
664
- if (!Number.isInteger(count) || count <= 0) {
665
- throw new Error('quest.expectMessages() requires a positive integer count');
666
- }
667
- // Validate protocol has plugin events with canHaveTests
668
- const protocolPlugin = context.protocolPlugin;
669
- const hasTestableEvents = protocolPlugin.events?.some((e) => e.canHaveTests === true) === true;
670
- if (hasTestableEvents === false) {
671
- throw new Error(`quest.expectMessages() is not supported for protocol '${context.protocol}' ` +
672
- `(no plugin events with canHaveTests)`);
673
- }
674
- // Store count in context for plugin optimization and test counting
675
- context.expectedMessages = count;
676
- }
677
- };
678
- }
679
- //# sourceMappingURL=QuestAPI.js.map