@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,9 +0,0 @@
1
- import type { TestResult } from '@apiquest/types';
2
-
3
- export interface PluginEventTest extends TestResult {
4
- eventName?: string;
5
- }
6
-
7
- export interface ErrorWithPhase extends Error {
8
- phase?: string;
9
- }
@@ -1,289 +0,0 @@
1
- import type {
2
- Collection,
3
- CollectionItem,
4
- Request,
5
- Folder,
6
- RuntimeOptions,
7
- ValidationResult,
8
- ValidationError,
9
- } from '@apiquest/types';
10
- import { ScriptType } from '@apiquest/types';
11
- import { ScriptValidator } from './ScriptValidator.js';
12
- import type { PluginManager } from './PluginManager.js';
13
- import { Logger } from './Logger.js';
14
- import { isNullOrWhitespace } from './utils.js';
15
-
16
- /**
17
- * Validates collections and their items (folders/requests) for pre-run validation
18
- */
19
- export class CollectionValidator {
20
- private logger: Logger;
21
-
22
- constructor(
23
- private readonly pluginManager: PluginManager,
24
- baseLogger?: Logger
25
- ) {
26
- this.logger = baseLogger?.createLogger('CollectionValidator') ?? new Logger('CollectionValidator');
27
- }
28
-
29
- /**
30
- * Validate entire collection structure, scripts, and configurations
31
- */
32
- async validateCollection(
33
- collection: Collection,
34
- options: RuntimeOptions,
35
- strictMode: boolean = true
36
- ): Promise<ValidationResult> {
37
- const errors: ValidationError[] = [];
38
-
39
- this.logger.debug(`Validating collection: ${collection.info.name} (strict=${strictMode})`);
40
-
41
- // Get protocol plugin for validation
42
- const protocolPlugin = this.pluginManager.getPlugin(collection.protocol);
43
- if (protocolPlugin === undefined) {
44
- this.logger.warn(`Protocol plugin not loaded for validation: ${collection.protocol}`);
45
- }
46
-
47
- // Helper to recursively validate all items
48
- const validateItem = (item: CollectionItem, path: string): void => {
49
- if (item.type === 'folder') {
50
- const folder = item;
51
-
52
- // Validate folder scripts
53
- if (!isNullOrWhitespace(folder.folderPreScript)) {
54
- errors.push(
55
- ...ScriptValidator.validateScript(
56
- folder.folderPreScript!,
57
- ScriptType.FolderPre,
58
- path,
59
- undefined,
60
- protocolPlugin,
61
- strictMode
62
- )
63
- );
64
- }
65
- if (!isNullOrWhitespace(folder.folderPostScript)) {
66
- errors.push(
67
- ...ScriptValidator.validateScript(
68
- folder.folderPostScript!,
69
- ScriptType.FolderPost,
70
- path,
71
- undefined,
72
- protocolPlugin,
73
- strictMode
74
- )
75
- );
76
- }
77
- if (!isNullOrWhitespace(folder.preRequestScript)) {
78
- errors.push(
79
- ...ScriptValidator.validateScript(
80
- folder.preRequestScript!,
81
- ScriptType.PreRequest,
82
- path,
83
- undefined,
84
- protocolPlugin,
85
- strictMode
86
- )
87
- );
88
- }
89
- if (!isNullOrWhitespace(folder.postRequestScript)) {
90
- errors.push(
91
- ...ScriptValidator.validateScript(
92
- folder.postRequestScript!,
93
- ScriptType.PostRequest,
94
- path,
95
- undefined,
96
- protocolPlugin,
97
- strictMode
98
- )
99
- );
100
- }
101
-
102
- // Validate auth config if present
103
- if (folder.auth !== null && folder.auth !== undefined && folder.auth.type !== 'inherit' && folder.auth.type !== 'none') {
104
- const authPlugin = this.pluginManager.getAuthPlugin(folder.auth.type);
105
- if (authPlugin?.validate !== null && authPlugin?.validate !== undefined) {
106
- const authResult = authPlugin.validate(folder.auth, options);
107
- if (authResult.valid === false && authResult.errors !== null && authResult.errors !== undefined) {
108
- errors.push(...authResult.errors);
109
- }
110
- }
111
- }
112
-
113
- // Recursively validate folder items
114
- for (const child of folder.items) {
115
- const childPath = `${path}/${child.name}`;
116
- validateItem(child, childPath);
117
- }
118
- } else {
119
- // Request validation
120
- const request = item;
121
-
122
- // Validate request scripts
123
- if (!isNullOrWhitespace(request.preRequestScript)) {
124
- errors.push(
125
- ...ScriptValidator.validateScript(
126
- request.preRequestScript!,
127
- ScriptType.PreRequest,
128
- path,
129
- undefined,
130
- protocolPlugin,
131
- strictMode
132
- )
133
- );
134
- }
135
- if (!isNullOrWhitespace(request.postRequestScript)) {
136
- errors.push(
137
- ...ScriptValidator.validateScript(
138
- request.postRequestScript!,
139
- ScriptType.PostRequest,
140
- path,
141
- undefined,
142
- protocolPlugin,
143
- strictMode
144
- )
145
- );
146
- }
147
-
148
- // Validate plugin event scripts
149
- if (request.data.scripts !== null && request.data.scripts !== undefined && Array.isArray(request.data.scripts)) {
150
- // Check for duplicate event scripts (only one script per event type allowed)
151
- const eventCounts = new Map<string, number>();
152
- for (const script of request.data.scripts) {
153
- const count = eventCounts.get(script.event) ?? 0;
154
- eventCounts.set(script.event, count + 1);
155
-
156
- if (count >= 1) {
157
- errors.push({
158
- message: `Request has multiple scripts for event "${script.event}". Only one script per event type is allowed.`,
159
- location: path,
160
- source: 'script'
161
- });
162
- }
163
- }
164
-
165
- // Validate each script
166
- if (protocolPlugin?.events !== null && protocolPlugin?.events !== undefined) {
167
- for (const script of request.data.scripts) {
168
- const eventDef = protocolPlugin.events.find(e => e.name === script.event);
169
- if (eventDef !== null && eventDef !== undefined) {
170
- errors.push(
171
- ...ScriptValidator.validateScript(
172
- script.script,
173
- ScriptType.PluginEvent,
174
- path,
175
- eventDef,
176
- protocolPlugin,
177
- strictMode
178
- )
179
- );
180
- }
181
- }
182
- }
183
- }
184
-
185
- // Validate protocol request via plugin
186
- if (protocolPlugin?.validate !== null && protocolPlugin?.validate !== undefined) {
187
- const protocolResult = protocolPlugin.validate(request, options);
188
- if (protocolResult.valid === false && protocolResult.errors !== null && protocolResult.errors !== undefined) {
189
- errors.push(
190
- ...protocolResult.errors.map(err => ({
191
- ...err,
192
- location: path
193
- }))
194
- );
195
- }
196
- }
197
-
198
- // Validate auth config if present
199
- if (request.auth !== null && request.auth !== undefined && request.auth.type !== 'inherit' && request.auth.type !== 'none') {
200
- const authPlugin = this.pluginManager.getAuthPlugin(request.auth.type);
201
- if (authPlugin?.validate !== null && authPlugin?.validate !== undefined) {
202
- const authResult = authPlugin.validate(request.auth, options);
203
- if (authResult.valid === false && authResult.errors !== null && authResult.errors !== undefined) {
204
- errors.push(
205
- ...authResult.errors.map(err => ({
206
- ...err,
207
- location: path
208
- }))
209
- );
210
- }
211
- }
212
- }
213
- }
214
- };
215
-
216
- // Validate collection-level scripts
217
- if (!isNullOrWhitespace(collection.collectionPreScript)) {
218
- errors.push(
219
- ...ScriptValidator.validateScript(
220
- collection.collectionPreScript!,
221
- ScriptType.CollectionPre,
222
- '/',
223
- undefined,
224
- protocolPlugin,
225
- strictMode
226
- )
227
- );
228
- }
229
- if (!isNullOrWhitespace(collection.collectionPostScript)) {
230
- errors.push(
231
- ...ScriptValidator.validateScript(
232
- collection.collectionPostScript!,
233
- ScriptType.CollectionPost,
234
- '/',
235
- undefined,
236
- protocolPlugin,
237
- strictMode
238
- )
239
- );
240
- }
241
- if (!isNullOrWhitespace(collection.preRequestScript)) {
242
- errors.push(
243
- ...ScriptValidator.validateScript(
244
- collection.preRequestScript!,
245
- ScriptType.PreRequest,
246
- '/',
247
- undefined,
248
- protocolPlugin,
249
- strictMode
250
- )
251
- );
252
- }
253
- if (!isNullOrWhitespace(collection.postRequestScript)) {
254
- errors.push(
255
- ...ScriptValidator.validateScript(
256
- collection.postRequestScript!,
257
- ScriptType.PostRequest,
258
- '/',
259
- undefined,
260
- protocolPlugin,
261
- strictMode
262
- )
263
- );
264
- }
265
-
266
- // Validate collection-level auth
267
- if (collection.auth !== null && collection.auth !== undefined && collection.auth.type !== 'inherit' && collection.auth.type !== 'none') {
268
- const authPlugin = this.pluginManager.getAuthPlugin(collection.auth.type);
269
- if (authPlugin?.validate !== null && authPlugin?.validate !== undefined) {
270
- const authResult = authPlugin.validate(collection.auth, options);
271
- if (authResult.valid === false && authResult.errors !== null && authResult.errors !== undefined) {
272
- errors.push(...authResult.errors);
273
- }
274
- }
275
- }
276
-
277
- // Validate all items recursively
278
- for (const item of collection.items) {
279
- validateItem(item, `/${item.name}`);
280
- }
281
-
282
- this.logger.debug(`Validation completed with ${errors.length} error(s)`);
283
-
284
- return {
285
- valid: errors.length === 0,
286
- errors
287
- };
288
- }
289
- }
@@ -1,143 +0,0 @@
1
- import type {
2
- IReporter,
3
- Collection,
4
- RunOptions,
5
- Request,
6
- RequestResult,
7
- TestResult,
8
- RunResult,
9
- LogLevel,
10
- EventPayloads
11
- } from '@apiquest/types';
12
- import { LogLevel as LogLevelEnum } from '@apiquest/types';
13
- import type { EventEmitter } from 'events';
14
-
15
- export class ConsoleReporter implements IReporter {
16
- name = 'console';
17
- version: string = '1.0.0';
18
- description: string = 'Pretty CLI output for quest runs';
19
- reportTypes: string[] = ['console'];
20
-
21
- private logLevel: LogLevel;
22
- private color: boolean;
23
- private runner?: EventEmitter;
24
-
25
- constructor(options?: { logLevel?: LogLevel; color?: boolean; runner?: EventEmitter }) {
26
- this.logLevel = options?.logLevel ?? LogLevelEnum.INFO;
27
- this.color = options?.color ?? true; // Color enabled by default
28
- this.runner = options?.runner;
29
-
30
- // Subscribe to console events for logger output
31
- if (this.runner !== null && this.runner !== undefined) {
32
- this.setupConsoleLogging();
33
- }
34
- }
35
-
36
- private setupConsoleLogging(): void {
37
- if (this.runner === null || this.runner === undefined) return;
38
-
39
- // Log levels are hierarchical using LogLevel enum values
40
- this.runner.on('console', ({ level, levelName, message }: EventPayloads['console']) => {
41
- // Only show if message level is <= configured level (lower number = higher priority)
42
- if (level <= this.logLevel) {
43
- const tag = `[${(levelName !== null && levelName !== undefined && levelName.length > 0) ? levelName.toUpperCase() : 'LOG'}]`;
44
- if (level === LogLevelEnum.ERROR) {
45
- console.error(`${tag} ${message}`);
46
- } else if (level === LogLevelEnum.WARN) {
47
- console.warn(`${tag} ${message}`);
48
- } else {
49
- console.log(`${tag} ${message}`);
50
- }
51
- }
52
- });
53
- }
54
-
55
- private colorize(text: string, colorCode: string): string {
56
- if (!this.color) return text;
57
- return `${colorCode}${text}\x1b[0m`;
58
- }
59
-
60
- onRunStarted(collection: Collection, options: RunOptions): void {
61
- console.log('============================================================');
62
- console.log(` Quest v1.0.0`);
63
- console.log(` Collection: ${collection.info.name}`);
64
- console.log('============================================================');
65
- console.log('');
66
- }
67
-
68
- onBeforeRequest(payload: EventPayloads['beforeRequest']): void {
69
- console.log('');
70
- console.log(`${this.colorize('>', '\x1b[36m')} ${payload.request.name}`);
71
-
72
- const requestData = payload.request.data as Record<string, unknown> | null | undefined;
73
- if (requestData !== null && requestData !== undefined) {
74
- const method = (typeof requestData.method === 'string' && requestData.method.length > 0) ? requestData.method : 'GET';
75
- const url = (typeof requestData.url === 'string' && requestData.url.length > 0) ? requestData.url : '';
76
- console.log(` ${method} ${url}`);
77
- }
78
- }
79
-
80
- onAfterRequest(payload: EventPayloads['afterRequest']): void {
81
- if (payload.response.error !== null && payload.response.error !== undefined && payload.response.error.length > 0) {
82
- console.log(` ${this.colorize('[FAIL]', '\x1b[31m')} ERROR: ${payload.response.error}`);
83
- } else {
84
- const statusColor = payload.response.status >= 400 ? '\x1b[31m' : '\x1b[32m';
85
- console.log(` ${this.colorize('<', statusColor)} ${payload.response.status} ${payload.response.statusText} (${payload.duration}ms)`);
86
- }
87
- }
88
-
89
- onAssertion(payload: EventPayloads['assertion']): void {
90
- const test = payload.test;
91
- if (test.skipped) {
92
- console.log(` ${this.colorize('[SKIP]', '\x1b[90m')} ${test.name}`);
93
- } else if (test.passed) {
94
- console.log(` ${this.colorize('[PASS]', '\x1b[32m')} ${test.name}`);
95
- } else {
96
- console.log(` ${this.colorize('[FAIL]', '\x1b[31m')} ${test.name}`);
97
- if (test.error !== null && test.error !== undefined && test.error.length > 0) {
98
- console.log(` Error: ${test.error}`);
99
- }
100
- }
101
- }
102
-
103
- onRunCompleted(result: RunResult): void {
104
- console.log('');
105
- console.log('------------------------------------------------------------');
106
- console.log('');
107
- console.log('RESULTS:');
108
- console.log(` Collection: ${result.collectionName}`);
109
- console.log(` Duration: ${(result.duration / 1000).toFixed(2)}s`);
110
- console.log(` Requests: ${result.requestResults.length}`);
111
-
112
- const successful = result.requestResults.filter(r => r.success).length;
113
- const failed = result.requestResults.filter(r => !r.success).length;
114
-
115
- if (successful > 0) {
116
- console.log(` - Successful: ${successful}`);
117
- }
118
- if (failed > 0) {
119
- console.log(` - Failed: ${failed}`);
120
- }
121
-
122
- if (result.totalTests > 0) {
123
- console.log(` Tests: ${result.totalTests}`);
124
- console.log(` - Passed: ${result.passedTests}`);
125
- if (result.failedTests > 0) {
126
- console.log(` - Failed: ${result.failedTests}`);
127
- }
128
- if (result.skippedTests > 0) {
129
- console.log(` - Skipped: ${result.skippedTests}`);
130
- }
131
- }
132
-
133
- console.log('');
134
-
135
- // if any TEST results failed end with error exit code 1
136
- // Request failures are OK if tests expect and handle them
137
- if (result.failedTests > 0) {
138
- console.log(`${this.colorize('[FAIL]', '\x1b[31m')} Collection run completed with errors`); // Red
139
- } else {
140
- console.log(`${this.colorize('[PASS]', '\x1b[32m')} Collection run completed successfully`); // Green
141
- }
142
- }
143
- }
package/src/CookieJar.ts DELETED
@@ -1,258 +0,0 @@
1
- import { CookieJar as ToughCookieJar, Cookie } from 'tough-cookie';
2
- import type { ICookieJar, CookieJarOptions, Cookie as ICookie, CookieSetOptions } from '@apiquest/types';
3
-
4
- /**
5
- * CookieJar implementation using tough-cookie for production-quality cookie management.
6
- * Wraps tough-cookie's CookieJar to provide ICookieJar interface.
7
- */
8
- export class CookieJar implements ICookieJar {
9
- private jar: ToughCookieJar;
10
- private options: CookieJarOptions;
11
-
12
- constructor(options?: CookieJarOptions) {
13
- this.options = options ?? { persist: false };
14
- this.jar = new ToughCookieJar();
15
- }
16
-
17
- /**
18
- * Store cookies from Set-Cookie headers
19
- * @param setCookieHeaders - Single header string or array of header strings
20
- * @param requestUrl - URL the cookies came from (REQUIRED for domain/path matching)
21
- */
22
- store(setCookieHeaders: string | string[] | null | undefined, requestUrl: string): void {
23
- if (setCookieHeaders === null || setCookieHeaders === undefined) {
24
- return;
25
- }
26
-
27
- const headers = Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders];
28
-
29
- for (const header of headers) {
30
- try {
31
- this.jar.setCookieSync(header, requestUrl);
32
- } catch (error) {
33
- // Ignore invalid cookies (tough-cookie throws on malformed cookies)
34
- // Silent failure is fine here
35
- }
36
- }
37
- }
38
-
39
- /**
40
- * Get cookie value by name
41
- * When called without domain, searches ALL cookies (across all domains)
42
- *
43
- * @param name - Cookie name
44
- * @param domain - Optional domain to filter (not commonly used)
45
- * @param path - Optional path to filter (not commonly used)
46
- * @returns Cookie value or null if not found
47
- */
48
- get(name: string, domain?: string, path?: string): string | null {
49
- // Use toJSON to get ALL cookies, then manually filter for expiration only
50
- const allCookiesJson = this.jar.toJSON();
51
- if (allCookiesJson?.cookies === undefined) {
52
- return null;
53
- }
54
-
55
- // Search for cookie by name
56
- for (const cookieData of allCookiesJson.cookies) {
57
- // Use Cookie.fromJSON for proper typing
58
- const cookie = Cookie.fromJSON(cookieData);
59
- if (cookie === null || cookie === undefined) {
60
- continue;
61
- }
62
-
63
- if (cookie.key !== name) {
64
- continue;
65
- }
66
-
67
- // Check expiration
68
- const expiryTime = cookie.expiryTime();
69
- if (expiryTime !== null && expiryTime !== undefined && expiryTime < Date.now()) {
70
- continue; // Skip expired cookies
71
- }
72
-
73
- // If domain filter specified, check it
74
- if (domain !== null && domain !== undefined && domain !== '') {
75
- if (cookie.domain === null) {
76
- continue;
77
- }
78
- if (cookie.domain !== domain && !cookie.domain.endsWith(domain)) {
79
- continue;
80
- }
81
- }
82
-
83
- // If path filter specified, check it
84
- if (path !== null && path !== undefined && path !== '') {
85
- if (cookie.path === null || cookie.path !== path) {
86
- continue;
87
- }
88
- }
89
-
90
- return cookie.value;
91
- }
92
-
93
- return null;
94
- }
95
-
96
- /**
97
- * Check if cookie exists
98
- * @param name - Cookie name
99
- * @param domain - Optional domain filter
100
- * @param path - Optional path filter
101
- * @returns true if cookie exists
102
- */
103
- has(name: string, domain?: string, path?: string): boolean {
104
- return this.get(name, domain, path) !== null;
105
- }
106
-
107
- /**
108
- * Remove a cookie by name
109
- * @param name - Cookie name
110
- * @param domain - Optional domain
111
- * @param path - Optional path
112
- */
113
- remove(name: string, domain?: string, path?: string): void {
114
- const allCookiesJson = this.jar.toJSON();
115
- if (allCookiesJson?.cookies === undefined) {
116
- return;
117
- }
118
-
119
- // Find matching cookies using Cookie.fromJSON for proper typing
120
- const cookiesToRemove: Cookie[] = [];
121
- for (const cookieData of allCookiesJson.cookies) {
122
- const cookie = Cookie.fromJSON(cookieData);
123
- if (cookie === null || cookie === undefined) {
124
- continue;
125
- }
126
-
127
- if (cookie.key !== name) {
128
- continue;
129
- }
130
-
131
- // Check domain filter
132
- if (domain !== null && domain !== undefined && domain !== '') {
133
- if (cookie.domain === null) {
134
- continue;
135
- }
136
- if (cookie.domain !== domain && !cookie.domain.endsWith(domain)) {
137
- continue;
138
- }
139
- }
140
-
141
- // Check path filter
142
- if (path !== null && path !== undefined && path !== '') {
143
- if (cookie.path === null || cookie.path !== path) {
144
- continue;
145
- }
146
- }
147
-
148
- cookiesToRemove.push(cookie);
149
- }
150
-
151
- // Remove each matching cookie
152
- for (const cookie of cookiesToRemove) {
153
- // Domain should always be present in cookies from tough-cookie
154
- const cookieDomain = cookie.domain;
155
- if (cookieDomain === null || cookieDomain === undefined) {
156
- continue; // Skip cookies without domain
157
- }
158
- const cookiePath = cookie.path ?? '/';
159
- try {
160
- this.jar.store.removeCookie(cookieDomain, cookiePath, cookie.key, () => {
161
- // Callback required by tough-cookie API
162
- });
163
- } catch {
164
- // Ignore errors
165
- }
166
- }
167
- }
168
-
169
- /**
170
- * Set a cookie manually
171
- * Constructs URL from cookie's domain for RFC 6265 validation
172
- * @param name - Cookie name
173
- * @param value - Cookie value
174
- * @param options - Cookie options
175
- */
176
- set(name: string, value: string, options: CookieSetOptions): void {
177
- let cookieStr = `${name}=${value}; Domain=${options.domain}; Path=${options.path ?? '/'}`;
178
-
179
- if (options.expires !== null && options.expires !== undefined) {
180
- cookieStr += `; Expires=${options.expires}`;
181
- }
182
- if (options.httpOnly === true) {
183
- cookieStr += '; HttpOnly';
184
- }
185
- if (options.secure === true) {
186
- cookieStr += '; Secure';
187
- }
188
- if (options.sameSite !== null && options.sameSite !== undefined) {
189
- cookieStr += `; SameSite=${options.sameSite}`;
190
- }
191
-
192
- const protocol = options.secure === true ? 'https' : 'http';
193
- const url = `${protocol}://${options.domain}${options.path ?? '/'}`;
194
-
195
- this.jar.setCookieSync(cookieStr, url);
196
- }
197
-
198
- /**
199
- * Clear all cookies
200
- */
201
- clear(): void {
202
- this.jar.removeAllCookiesSync();
203
- }
204
-
205
- /**
206
- * Get all cookies as an object
207
- * Returns non-expired cookies from ALL domains
208
- * @returns Object with cookie names as keys and values
209
- */
210
- toObject(): Record<string, string> {
211
- const result: Record<string, string> = {};
212
-
213
- // Use toJSON to get all cookies, filter expired manually
214
- const allCookiesJson = this.jar.toJSON();
215
- if (allCookiesJson?.cookies === undefined) {
216
- return result;
217
- }
218
-
219
- // Add all non-expired cookies
220
- for (const cookieData of allCookiesJson.cookies) {
221
- try {
222
- const cookie = Cookie.fromJSON(cookieData);
223
- if (cookie === null || cookie === undefined) {
224
- continue;
225
- }
226
-
227
- const expiryTime = cookie.expiryTime();
228
- // Not expired if: no expiry time OR expiry time is in the future
229
- const isExpired = expiryTime !== null && expiryTime !== undefined && expiryTime < Date.now();
230
- if (!isExpired) {
231
- result[cookie.key] = cookie.value;
232
- }
233
- } catch {
234
- // Skip cookies that can't be parsed
235
- }
236
- }
237
-
238
- return result;
239
- }
240
-
241
- /**
242
- * Get Cookie header string for a URL
243
- * This is the main method used by HTTP plugin to send cookies with requests
244
- * @param url - URL to get cookies for
245
- * @returns Cookie header string in "name1=value1; name2=value2" format, or null if no cookies
246
- */
247
- getCookieHeader(url: string): string | null {
248
- try {
249
- const cookieString = this.jar.getCookieStringSync(url);
250
- if (cookieString === null || cookieString === undefined || cookieString === '') {
251
- return null;
252
- }
253
- return cookieString;
254
- } catch {
255
- return null;
256
- }
257
- }
258
- }