@apiquest/fracture 1.0.2 → 1.0.5

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 (195) hide show
  1. package/README.md +207 -0
  2. package/bin/cli.js +2 -2
  3. package/dist/CollectionRunner.d.ts +2 -0
  4. package/dist/CollectionRunner.d.ts.map +1 -1
  5. package/dist/CollectionRunner.js +23 -5
  6. package/dist/CollectionRunner.js.map +1 -1
  7. package/dist/LibraryLoader.d.ts +49 -0
  8. package/dist/LibraryLoader.d.ts.map +1 -0
  9. package/dist/LibraryLoader.js +198 -0
  10. package/dist/LibraryLoader.js.map +1 -0
  11. package/dist/PluginLoader.d.ts.map +1 -1
  12. package/dist/PluginLoader.js +9 -6
  13. package/dist/PluginLoader.js.map +1 -1
  14. package/dist/PluginResolver.d.ts +1 -1
  15. package/dist/PluginResolver.d.ts.map +1 -1
  16. package/dist/PluginResolver.js +1 -1
  17. package/dist/PluginResolver.js.map +1 -1
  18. package/dist/ScriptEngine.d.ts +2 -1
  19. package/dist/ScriptEngine.d.ts.map +1 -1
  20. package/dist/ScriptEngine.js +19 -12
  21. package/dist/ScriptEngine.js.map +1 -1
  22. package/dist/cli/index.js +35 -3
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/cli/plugin-commands.d.ts.map +1 -1
  25. package/dist/cli/plugin-commands.js +48 -81
  26. package/dist/cli/plugin-commands.js.map +1 -1
  27. package/dist/cli/plugin-installer.d.ts +48 -0
  28. package/dist/cli/plugin-installer.d.ts.map +1 -0
  29. package/dist/cli/plugin-installer.js +136 -0
  30. package/dist/cli/plugin-installer.js.map +1 -0
  31. package/dist/cli/plugin-registry.d.ts +17 -0
  32. package/dist/cli/plugin-registry.d.ts.map +1 -0
  33. package/dist/cli/plugin-registry.js +77 -0
  34. package/dist/cli/plugin-registry.js.map +1 -0
  35. package/package.json +55 -50
  36. package/tsconfig.json +20 -20
  37. package/tsconfig.test.json +5 -5
  38. package/vitest.config.ts +22 -22
  39. package/dist/ExecutionTree.d.ts +0 -77
  40. package/dist/ExecutionTree.d.ts.map +0 -1
  41. package/dist/ExecutionTree.js +0 -265
  42. package/dist/ExecutionTree.js.map +0 -1
  43. package/dist/fracture/src/CollectionAnalyzer.d.ts +0 -17
  44. package/dist/fracture/src/CollectionAnalyzer.d.ts.map +0 -1
  45. package/dist/fracture/src/CollectionAnalyzer.js +0 -70
  46. package/dist/fracture/src/CollectionAnalyzer.js.map +0 -1
  47. package/dist/fracture/src/CollectionRunner.d.ts +0 -39
  48. package/dist/fracture/src/CollectionRunner.d.ts.map +0 -1
  49. package/dist/fracture/src/CollectionRunner.js +0 -802
  50. package/dist/fracture/src/CollectionRunner.js.map +0 -1
  51. package/dist/fracture/src/CollectionRunner.types.d.ts +0 -8
  52. package/dist/fracture/src/CollectionRunner.types.d.ts.map +0 -1
  53. package/dist/fracture/src/CollectionRunner.types.js +0 -2
  54. package/dist/fracture/src/CollectionRunner.types.js.map +0 -1
  55. package/dist/fracture/src/CollectionValidator.d.ts +0 -14
  56. package/dist/fracture/src/CollectionValidator.d.ts.map +0 -1
  57. package/dist/fracture/src/CollectionValidator.js +0 -145
  58. package/dist/fracture/src/CollectionValidator.js.map +0 -1
  59. package/dist/fracture/src/ConsoleReporter.d.ts +0 -24
  60. package/dist/fracture/src/ConsoleReporter.d.ts.map +0 -1
  61. package/dist/fracture/src/ConsoleReporter.js +0 -123
  62. package/dist/fracture/src/ConsoleReporter.js.map +0 -1
  63. package/dist/fracture/src/CookieJar.d.ts +0 -70
  64. package/dist/fracture/src/CookieJar.d.ts.map +0 -1
  65. package/dist/fracture/src/CookieJar.js +0 -233
  66. package/dist/fracture/src/CookieJar.js.map +0 -1
  67. package/dist/fracture/src/ExecutionTree.d.ts +0 -77
  68. package/dist/fracture/src/ExecutionTree.d.ts.map +0 -1
  69. package/dist/fracture/src/ExecutionTree.js +0 -258
  70. package/dist/fracture/src/ExecutionTree.js.map +0 -1
  71. package/dist/fracture/src/Logger.d.ts +0 -25
  72. package/dist/fracture/src/Logger.d.ts.map +0 -1
  73. package/dist/fracture/src/Logger.js +0 -78
  74. package/dist/fracture/src/Logger.js.map +0 -1
  75. package/dist/fracture/src/PluginLoader.d.ts +0 -23
  76. package/dist/fracture/src/PluginLoader.d.ts.map +0 -1
  77. package/dist/fracture/src/PluginLoader.js +0 -102
  78. package/dist/fracture/src/PluginLoader.js.map +0 -1
  79. package/dist/fracture/src/PluginManager.d.ts +0 -64
  80. package/dist/fracture/src/PluginManager.d.ts.map +0 -1
  81. package/dist/fracture/src/PluginManager.js +0 -162
  82. package/dist/fracture/src/PluginManager.js.map +0 -1
  83. package/dist/fracture/src/PluginResolver.d.ts +0 -35
  84. package/dist/fracture/src/PluginResolver.d.ts.map +0 -1
  85. package/dist/fracture/src/PluginResolver.js +0 -128
  86. package/dist/fracture/src/PluginResolver.js.map +0 -1
  87. package/dist/fracture/src/QuestAPI.d.ts +0 -9
  88. package/dist/fracture/src/QuestAPI.d.ts.map +0 -1
  89. package/dist/fracture/src/QuestAPI.js +0 -679
  90. package/dist/fracture/src/QuestAPI.js.map +0 -1
  91. package/dist/fracture/src/QuestAPI.types.d.ts +0 -35
  92. package/dist/fracture/src/QuestAPI.types.d.ts.map +0 -1
  93. package/dist/fracture/src/QuestAPI.types.js +0 -3
  94. package/dist/fracture/src/QuestAPI.types.js.map +0 -1
  95. package/dist/fracture/src/QuestTestAPI.d.ts +0 -12
  96. package/dist/fracture/src/QuestTestAPI.d.ts.map +0 -1
  97. package/dist/fracture/src/QuestTestAPI.js +0 -133
  98. package/dist/fracture/src/QuestTestAPI.js.map +0 -1
  99. package/dist/fracture/src/ScriptEngine.d.ts +0 -21
  100. package/dist/fracture/src/ScriptEngine.d.ts.map +0 -1
  101. package/dist/fracture/src/ScriptEngine.js +0 -183
  102. package/dist/fracture/src/ScriptEngine.js.map +0 -1
  103. package/dist/fracture/src/ScriptValidator.d.ts +0 -68
  104. package/dist/fracture/src/ScriptValidator.d.ts.map +0 -1
  105. package/dist/fracture/src/ScriptValidator.js +0 -351
  106. package/dist/fracture/src/ScriptValidator.js.map +0 -1
  107. package/dist/fracture/src/TestCounter.d.ts +0 -18
  108. package/dist/fracture/src/TestCounter.d.ts.map +0 -1
  109. package/dist/fracture/src/TestCounter.js +0 -82
  110. package/dist/fracture/src/TestCounter.js.map +0 -1
  111. package/dist/fracture/src/VariableResolver.d.ts +0 -20
  112. package/dist/fracture/src/VariableResolver.d.ts.map +0 -1
  113. package/dist/fracture/src/VariableResolver.js +0 -100
  114. package/dist/fracture/src/VariableResolver.js.map +0 -1
  115. package/dist/fracture/src/cli/index.d.ts +0 -3
  116. package/dist/fracture/src/cli/index.d.ts.map +0 -1
  117. package/dist/fracture/src/cli/index.js +0 -347
  118. package/dist/fracture/src/cli/index.js.map +0 -1
  119. package/dist/fracture/src/cli/plugin-commands.d.ts +0 -6
  120. package/dist/fracture/src/cli/plugin-commands.d.ts.map +0 -1
  121. package/dist/fracture/src/cli/plugin-commands.js +0 -263
  122. package/dist/fracture/src/cli/plugin-commands.js.map +0 -1
  123. package/dist/fracture/src/cli/plugin-discovery.d.ts +0 -11
  124. package/dist/fracture/src/cli/plugin-discovery.d.ts.map +0 -1
  125. package/dist/fracture/src/cli/plugin-discovery.js +0 -64
  126. package/dist/fracture/src/cli/plugin-discovery.js.map +0 -1
  127. package/dist/fracture/src/index.d.ts +0 -13
  128. package/dist/fracture/src/index.d.ts.map +0 -1
  129. package/dist/fracture/src/index.js +0 -17
  130. package/dist/fracture/src/index.js.map +0 -1
  131. package/dist/fracture/src/utils.d.ts +0 -28
  132. package/dist/fracture/src/utils.d.ts.map +0 -1
  133. package/dist/fracture/src/utils.js +0 -48
  134. package/dist/fracture/src/utils.js.map +0 -1
  135. package/dist/plugin-auth/src/apikey-auth.d.ts +0 -3
  136. package/dist/plugin-auth/src/apikey-auth.d.ts.map +0 -1
  137. package/dist/plugin-auth/src/apikey-auth.js +0 -73
  138. package/dist/plugin-auth/src/apikey-auth.js.map +0 -1
  139. package/dist/plugin-auth/src/basic-auth.d.ts +0 -3
  140. package/dist/plugin-auth/src/basic-auth.d.ts.map +0 -1
  141. package/dist/plugin-auth/src/basic-auth.js +0 -61
  142. package/dist/plugin-auth/src/basic-auth.js.map +0 -1
  143. package/dist/plugin-auth/src/bearer-auth.d.ts +0 -3
  144. package/dist/plugin-auth/src/bearer-auth.d.ts.map +0 -1
  145. package/dist/plugin-auth/src/bearer-auth.js +0 -49
  146. package/dist/plugin-auth/src/bearer-auth.js.map +0 -1
  147. package/dist/plugin-auth/src/helpers.d.ts +0 -3
  148. package/dist/plugin-auth/src/helpers.d.ts.map +0 -1
  149. package/dist/plugin-auth/src/helpers.js +0 -8
  150. package/dist/plugin-auth/src/helpers.js.map +0 -1
  151. package/dist/plugin-auth/src/index.d.ts +0 -10
  152. package/dist/plugin-auth/src/index.d.ts.map +0 -1
  153. package/dist/plugin-auth/src/index.js +0 -25
  154. package/dist/plugin-auth/src/index.js.map +0 -1
  155. package/dist/plugin-auth/src/oauth2-auth.d.ts +0 -35
  156. package/dist/plugin-auth/src/oauth2-auth.d.ts.map +0 -1
  157. package/dist/plugin-auth/src/oauth2-auth.js +0 -266
  158. package/dist/plugin-auth/src/oauth2-auth.js.map +0 -1
  159. package/dist/plugin-http/src/index.d.ts +0 -4
  160. package/dist/plugin-http/src/index.d.ts.map +0 -1
  161. package/dist/plugin-http/src/index.js +0 -266
  162. package/dist/plugin-http/src/index.js.map +0 -1
  163. package/dist/plugin-vault-file/src/index.d.ts +0 -67
  164. package/dist/plugin-vault-file/src/index.d.ts.map +0 -1
  165. package/dist/plugin-vault-file/src/index.js +0 -171
  166. package/dist/plugin-vault-file/src/index.js.map +0 -1
  167. package/dist/types.d.ts +0 -374
  168. package/dist/types.d.ts.map +0 -1
  169. package/dist/types.js +0 -13
  170. package/dist/types.js.map +0 -1
  171. package/src/CollectionAnalyzer.ts +0 -102
  172. package/src/CollectionRunner.ts +0 -1423
  173. package/src/CollectionRunner.types.ts +0 -9
  174. package/src/CollectionValidator.ts +0 -289
  175. package/src/ConsoleReporter.ts +0 -143
  176. package/src/CookieJar.ts +0 -258
  177. package/src/DagScheduler.ts +0 -439
  178. package/src/Logger.ts +0 -85
  179. package/src/PluginLoader.ts +0 -126
  180. package/src/PluginManager.ts +0 -208
  181. package/src/PluginResolver.ts +0 -154
  182. package/src/QuestAPI.ts +0 -764
  183. package/src/QuestAPI.types.ts +0 -33
  184. package/src/QuestTestAPI.ts +0 -164
  185. package/src/RequestFilter.ts +0 -224
  186. package/src/ScriptEngine.ts +0 -219
  187. package/src/ScriptValidator.ts +0 -428
  188. package/src/TaskGraph.ts +0 -598
  189. package/src/TestCounter.ts +0 -109
  190. package/src/VariableResolver.ts +0 -114
  191. package/src/cli/index.ts +0 -480
  192. package/src/cli/plugin-commands.ts +0 -341
  193. package/src/cli/plugin-discovery.ts +0 -44
  194. package/src/index.ts +0 -24
  195. package/src/utils.ts +0 -52
@@ -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