@apiquest/types 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 (4) hide show
  1. package/README.md +54 -0
  2. package/package.json +27 -22
  3. package/src/index.ts +823 -823
  4. package/tsconfig.json +21 -21
package/src/index.ts CHANGED
@@ -1,823 +1,823 @@
1
- // Core Types and Interfaces for @apiquest/types
2
- // Schema v1.0 - Updated 2026-01-05
3
-
4
- // ============================================================================
5
- // Collection & Items
6
- // ============================================================================
7
-
8
- export interface Collection {
9
- $schema?: string;
10
- info: CollectionInfo;
11
-
12
- // Protocol (collection-level)
13
- protocol: string; // "http", "graphql", "grpc", "websocket", etc.
14
-
15
- auth?: Auth;
16
- variables?: Record<string, string | Variable>;
17
-
18
- // Collection lifecycle scripts
19
- collectionPreScript?: string;
20
- collectionPostScript?: string;
21
-
22
- // Request lifecycle scripts (run before/after EACH request)
23
- preRequestScript?: string;
24
- postRequestScript?: string;
25
-
26
- testData?: IterationData[];
27
- options?: RuntimeOptions;
28
- items: CollectionItem[];
29
- }
30
-
31
- export interface CollectionInfo {
32
- id: string;
33
- name: string;
34
- version?: string;
35
- description?: string;
36
- }
37
-
38
- export interface Folder {
39
- type: 'folder';
40
- id: string;
41
- name: string;
42
- description?: string;
43
- auth?: Auth;
44
-
45
- // Execution control
46
- dependsOn?: string[]; // Folder/Request IDs that must execute first
47
- condition?: string; // JavaScript expression to evaluate
48
-
49
- // Folder lifecycle scripts
50
- folderPreScript?: string;
51
- folderPostScript?: string;
52
-
53
- // Request lifecycle scripts
54
- preRequestScript?: string;
55
- postRequestScript?: string;
56
-
57
- options?: RuntimeOptions;
58
- items: CollectionItem[];
59
- }
60
-
61
- export interface Request {
62
- type: 'request';
63
- id: string;
64
- name: string;
65
- description?: string;
66
-
67
- // Execution control
68
- dependsOn?: string[]; // Request IDs that must execute first
69
- condition?: string; // JavaScript expression to evaluate
70
-
71
- // Protocol is inherited from collection.protocol
72
- auth?: Auth;
73
- data: {
74
- // Protocol-specific data
75
- [key: string]: unknown;
76
-
77
- // Plugin event scripts (e.g., for WebSocket: onMessage, onError, onComplete)
78
- scripts?: ProtocolScript[];
79
- };
80
-
81
- preRequestScript?: string;
82
- postRequestScript?: string; // Contains tests via quest.test()
83
-
84
- options?: RuntimeOptions;
85
- examples?: ResponseExample[];
86
- }
87
-
88
- export interface ProtocolScript {
89
- event: string; // "onMessage", "onError", "onComplete", etc.
90
- script: string;
91
- }
92
-
93
- export type CollectionItem = Request | Folder;
94
-
95
- export interface ResponseExample {
96
- name: string;
97
- description?: string;
98
- protocol: string;
99
- data: unknown;
100
- }
101
-
102
- // ============================================================================
103
- // Authentication
104
- // ============================================================================
105
-
106
- export interface Auth {
107
- type: string | 'inherit' | 'none';
108
- data?: Record<string, unknown>;
109
- }
110
-
111
- // ============================================================================
112
- // Variables
113
- // ============================================================================
114
-
115
- export interface Variable {
116
- value: string;
117
- enabled?: boolean;
118
- type?: "string" | "number" | "boolean";
119
- isSecret?: boolean;
120
- isRequired?: boolean; // enforce presence at runtime
121
- provider?: string; // "env", "vault:aws-secrets", etc., undefined for built-in
122
- description?: string;
123
- }
124
-
125
- export interface Environment {
126
- name: string;
127
- variables: Record<string, string | Variable>;
128
- }
129
-
130
- export interface IterationData {
131
- [key: string]: string | number | boolean;
132
- }
133
-
134
- // ============================================================================
135
- // Runtime Options
136
- // ============================================================================
137
-
138
- export interface CollectionRunnerOptions {
139
- pluginsDir?: string | string[]; // Optional path(s) to plugins folder(s) for dynamic loading
140
- logLevel?: LogLevel; // Optional log level (default: INFO)
141
- }
142
-
143
- export interface RuntimeOptions {
144
- // Validation
145
- strictMode?: boolean; // Enable/disable conditional test validation (default: true)
146
-
147
- // Execution control
148
- execution?: ExecutionOptions;
149
-
150
- // Filtering
151
- filter?: string; // Path-based regex filter
152
- excludeDeps?: boolean; // Exclude dependencies when filtering
153
-
154
- // External libraries
155
- libraries?: ExternalLibrary[];
156
-
157
- // Cookies
158
- cookies?: Cookie[];
159
- jar?: CookieJarOptions;
160
-
161
- // SSL/TLS
162
- ssl?: SSLOptions;
163
-
164
- // Proxy
165
- proxy?: ProxyOptions;
166
-
167
- // Timeouts
168
- timeout?: TimeoutOptions;
169
-
170
- // Redirects
171
- followRedirects?: boolean;
172
- maxRedirects?: number;
173
-
174
- // Logging
175
- logLevel?: LogLevel;
176
-
177
- // Plugin-specific options
178
- plugins?: Record<string, unknown>;
179
- }
180
-
181
- export interface ExecutionOptions {
182
- allowParallel?: boolean; // Enable parallel execution
183
- maxConcurrency?: number; // Max parallel requests
184
- bail?: boolean; // Stop on first failure
185
- delay?: number; // Delay between requests (ms)
186
- }
187
-
188
- export interface ExternalLibrary {
189
- name: string;
190
- source: LibrarySource;
191
- version?: string;
192
- }
193
-
194
- export type LibrarySource =
195
- | { type: 'npm'; package: string }
196
- | { type: 'cdn'; url: string }
197
- | { type: 'file'; path: string };
198
-
199
- export interface Cookie {
200
- name: string;
201
- value: string;
202
- domain: string;
203
- path?: string;
204
- expires?: string;
205
- httpOnly?: boolean;
206
- secure?: boolean;
207
- sameSite?: 'Strict' | 'Lax' | 'None';
208
- }
209
-
210
- export interface CookieSetOptions {
211
- domain: string;
212
- path?: string;
213
- expires?: string;
214
- httpOnly?: boolean;
215
- secure?: boolean;
216
- sameSite?: 'Strict' | 'Lax' | 'None';
217
- }
218
-
219
- export interface CookieJarOptions {
220
- persist: boolean; //default: false
221
- }
222
-
223
- export interface SSLOptions {
224
- validateCertificates?: boolean;
225
- clientCertificate?: {
226
- cert: string;
227
- key: string;
228
- passphrase?: string;
229
- };
230
- ca?: string;
231
- }
232
-
233
- export interface ProxyOptions {
234
- enabled: boolean;
235
- host: string;
236
- port: number;
237
- auth?: {
238
- username: string;
239
- password: string;
240
- };
241
- bypass?: string[];
242
- }
243
-
244
- export interface TimeoutOptions {
245
- request?: number;
246
- connection?: number;
247
- response?: number;
248
- }
249
-
250
- // ============================================================================
251
- // Execution History
252
- // ============================================================================
253
-
254
- export interface ExecutionRecord {
255
- // Identity
256
- id: string;
257
- name: string;
258
- path: string; // "/folder1/folder2/request"
259
-
260
- // Iteration metadata
261
- iteration: number;
262
-
263
- // Results
264
- response: ProtocolResponse;
265
- tests: TestResult[];
266
- timestamp: string;
267
- }
268
-
269
- export interface ExecutionHistoryEntry {
270
- requestId: string;
271
- requestName: string;
272
- response?: ProtocolResponse;
273
- timestamp: Date;
274
- collectionIteration: number;
275
- requestIteration: number;
276
- }
277
-
278
- // ============================================================================
279
- // Plugin Events
280
- // ============================================================================
281
-
282
- export interface PluginEvent {
283
- eventName: string; // "onMessage", "onError", etc.
284
- requestId: string; // Request that triggered the event
285
- timestamp: Date;
286
- data: unknown; // Plugin-specific event data
287
- index: number; // Event sequence number (0-based, per event type)
288
- }
289
-
290
- // ============================================================================
291
- // Execution Context
292
- // ============================================================================
293
-
294
- export interface ScopeFrame {
295
- level: 'collection' | 'folder' | 'request';
296
- id: string;
297
- vars: Record<string, string>;
298
- }
299
-
300
- export type IterationSource = 'collection' | 'cli' | 'none';
301
-
302
- export interface ExecutionContext {
303
- // Collection info
304
- collectionInfo: CollectionInfo;
305
- protocol: string; // Collection protocol (e.g., 'http', 'graphql')
306
-
307
- // Variable scopes
308
- collectionVariables: Record<string, string | Variable>;
309
- globalVariables: Record<string, string | Variable>;
310
- scopeStack: ScopeFrame[]; // Hierarchical scope stack
311
- environment?: Environment;
312
-
313
- // Current execution state
314
- currentRequest?: Request;
315
- currentResponse?: ProtocolResponse;
316
- currentPath?: string; // Current folder/request path
317
-
318
- // Plugin event tracking (for streaming protocols)
319
- expectedMessages?: number; // Hint from quest.expectMessages() for plugin optimization
320
- currentEvent?: PluginEvent; // Current plugin event (for eventScripts)
321
-
322
- // Iteration state
323
- iterationCurrent: number;
324
- iterationCount: number;
325
- iterationData?: IterationData[];
326
- iterationSource: IterationSource; // Where iteration data comes from
327
-
328
- // History
329
- executionHistory: ExecutionRecord[];
330
-
331
- // Runtime options
332
- options: RuntimeOptions;
333
-
334
- // Cookie jar - ICookieJar interface for type safety
335
- cookieJar: ICookieJar;
336
-
337
- // Event emitter for plugin callbacks
338
- eventEmitter?: unknown; // EventEmitter instance
339
-
340
- // Cached protocol plugin (loaded at collection initialization for fail-fast validation)
341
- protocolPlugin: IProtocolPlugin;
342
-
343
- // emitEvent - Allow plugins to emit custom events (websocket:message, sse:chunk, etc.)
344
- // These events are protocol-specific and streamed to the UI
345
- emitEvent?: (type: string, data: unknown) => void;
346
-
347
- // Abort signal for execution cancellation
348
- abortSignal: AbortSignal;
349
- }
350
-
351
- // ============================================================================
352
- // Cookie Jar Interface
353
- // ============================================================================
354
-
355
- export interface ICookieJar {
356
- get(name: string, domain?: string, path?: string): string | null;
357
- set(name: string, value: string, options: CookieSetOptions): void;
358
- has(name: string, domain?: string, path?: string): boolean;
359
- remove(name: string, domain?: string, path?: string): void;
360
- clear(): void;
361
- toObject(): Record<string, string>;
362
- getCookieHeader(url: string): string | null;
363
- store(setCookieHeaders: string | string[] | null | undefined, requestUrl: string): void;
364
- }
365
-
366
- // ============================================================================
367
- // Validation
368
- // ============================================================================
369
-
370
- export interface ValidationError {
371
- message: string;
372
- location: string; // Request path (e.g., "/Folder A/Request 1")
373
- source: 'script' | 'protocol' | 'auth' | 'vault' | 'schema';
374
- scriptType?: ScriptType;
375
- details?: {
376
- line?: number;
377
- column?: number;
378
- suggestion?: string;
379
- [key: string]: unknown;
380
- };
381
- }
382
-
383
- // ============================================================================
384
- // Logging
385
- // ============================================================================
386
-
387
- export enum LogLevel {
388
- ERROR = 0,
389
- WARN = 1,
390
- INFO = 2,
391
- DEBUG = 3,
392
- TRACE = 4
393
- }
394
-
395
- export interface ILogger {
396
- error(message: string, ...args: unknown[]): void;
397
- warn(message: string, ...args: unknown[]): void;
398
- info(message: string, ...args: unknown[]): void;
399
- debug(message: string, ...args: unknown[]): void;
400
- trace(message: string, ...args: unknown[]): void;
401
- setLevel(level: LogLevel): void;
402
- createLogger(component: string): ILogger;
403
- }
404
-
405
- // ============================================================================
406
- // Run Options & Results
407
- // ============================================================================
408
-
409
- export interface RunOptions extends Omit<RuntimeOptions, 'logLevel'> {
410
- // Additional CLI/API specific options not in RuntimeOptions
411
- environment?: Environment;
412
- globalVariables?: Record<string, string | Variable>;
413
- data?: IterationData[]; // CLI --data override
414
- iterations?: number; // Global iteration cap
415
- filter?: string; // Path-based regex filter
416
- excludeDeps?: boolean; // Exclude dependencies when filtering
417
- signal?: AbortSignal; // External abort signal
418
- }
419
-
420
- export interface RunResult {
421
- collectionId: string;
422
- collectionName: string;
423
- startTime: Date;
424
- endTime: Date;
425
- duration: number;
426
- requestResults: RequestResult[];
427
- totalTests: number;
428
- passedTests: number;
429
- failedTests: number;
430
- skippedTests: number;
431
- validationErrors?: ValidationError[]; // Pre-run validation errors
432
- aborted?: boolean;
433
- abortReason?: string;
434
- }
435
-
436
- export interface RequestResult {
437
- requestId: string;
438
- requestName: string;
439
- path: string;
440
- success: boolean;
441
- response?: ProtocolResponse;
442
- tests: TestResult[];
443
- duration: number;
444
- scriptError?: string;
445
- iteration: number;
446
- }
447
-
448
- export interface TestResult {
449
- name: string;
450
- passed: boolean;
451
- error?: string;
452
- skipped: boolean;
453
- }
454
-
455
- // ============================================================================
456
- // Plugin Package Metadata
457
- // ============================================================================
458
-
459
- export interface PluginPackageJson {
460
- name: string;
461
- version: string;
462
- main?: string;
463
- apiquest?: {
464
- runtime?: string[] | string;
465
- type: string;
466
- capabilities?: {
467
- provides?: {
468
- protocols?: string[];
469
- authTypes?: string[];
470
- provider?: string;
471
- };
472
- };
473
- };
474
- }
475
-
476
- // ============================================================================
477
- // Protocol Plugin Interface
478
- // ============================================================================
479
-
480
- export interface IProtocolPlugin {
481
- // Identity
482
- name: string;
483
- version: string;
484
- description: string;
485
-
486
- // What protocols does this plugin provide?
487
- // Single-protocol: ['http']
488
- // Multi-protocol bundle (rare): ['http', 'https', 'http2']
489
- protocols: string[];
490
-
491
- // Supported auth types for this protocol
492
- // Example: HTTP supports ['bearer', 'basic', 'oauth2', 'apikey']
493
- // SQL might support ['integrated', 'username-password']
494
- supportedAuthTypes: string[];
495
-
496
- // If true, ONLY supportedAuthTypes listed above are allowed
497
- // If false/undefined, accept additional auth plugins that declare this protocol
498
- strictAuthList?: boolean;
499
-
500
- // Schema for request.data (defines what fields the UI should show)
501
- // Example: HTTP has method, url, headers, body
502
- dataSchema: unknown; // JSON Schema or custom schema format
503
-
504
- // Schema for options.plugins[protocol] (runtime options)
505
- // Example: HTTP has keepAlive, compression, maxSockets
506
- optionsSchema?: PluginOptionsSchema;
507
-
508
- // Plugin event definitions (e.g., WebSocket: onMessage, onError, onComplete)
509
- events?: PluginEventDefinition[];
510
-
511
- execute(
512
- request: Request,
513
- context: ExecutionContext,
514
- options: RuntimeOptions, // Receives merged options
515
- emitEvent?: (eventName: string, eventData: unknown) => Promise<void>, // Optional callback for plugin events
516
- logger?: ILogger // Optional logger from fracture
517
- ): Promise<ProtocolResponse>;
518
-
519
- validate(request: Request, options: RuntimeOptions): ValidationResult;
520
- }
521
-
522
- export interface PluginOptionsSchema {
523
- // Defines options that can be set in options.plugins[protocol]
524
- // Example: options.plugins.http = { keepAlive: true, compression: "gzip" }
525
- [optionKey: string]: {
526
- type: 'boolean' | 'string' | 'number';
527
- default?: unknown;
528
- enum?: unknown[];
529
- description?: string;
530
- };
531
- }
532
-
533
- export interface PluginEventDefinition {
534
- name: string; // "onMessage", "onError", "onComplete"
535
- description: string;
536
- canHaveTests: boolean; // Can this event script contain quest.test()?
537
- required: boolean; // Is this event required or optional?
538
- }
539
-
540
- export interface ProtocolResponse {
541
- status: number;
542
- statusText: string;
543
- body: string;
544
- headers: Record<string, string | string[]>; // Headers can have multiple values (e.g., set-cookie)
545
- duration: number;
546
- error?: string;
547
- }
548
-
549
- export interface ValidationResult {
550
- valid: boolean;
551
- errors?: ValidationError[];
552
- }
553
-
554
- // ============================================================================
555
- // Auth Plugin Interface
556
- // ============================================================================
557
-
558
- export interface IAuthPlugin {
559
- // Identity
560
- name: string;
561
- version: string;
562
- description: string;
563
-
564
- // What auth types does this plugin provide?
565
- // Single auth: ['bearer']
566
- // Multi-auth bundle: ['bearer', 'basic', 'apikey', 'oauth2']
567
- authTypes: string[];
568
-
569
- // Which protocols does this auth work with? (REQUIRED)
570
- // Example: ['http', 'grpc', 'graphql']
571
- // No universal auth - must explicitly declare protocol support
572
- protocols: string[];
573
-
574
- // Schema for auth.data (defines what fields the UI should show)
575
- // Example: bearer has { token: string }, basic has { username: string, password: string }
576
- dataSchema: unknown; // JSON Schema or custom schema format
577
-
578
- apply(
579
- request: Request,
580
- auth: Auth,
581
- options: RuntimeOptions,
582
- logger?: ILogger // Optional logger from fracture
583
- ): Promise<Request>;
584
-
585
- validate(auth: Auth, options: RuntimeOptions): ValidationResult;
586
- }
587
-
588
- // ============================================================================
589
- // Value Provider Plugin Interface
590
- // ============================================================================
591
-
592
- export interface IValueProviderPlugin {
593
- provider: string; // "vault:aws-secrets", "vault:azure-keyvault", "vault:file"
594
- name: string;
595
- description: string;
596
-
597
- // Configuration schema (if provider needs setup)
598
- configSchema?: unknown;
599
-
600
- // Retrieve a secret value by key
601
- // Returns null if key doesn't exist
602
- // Throws error if provider fails (network, auth, etc.)
603
- getValue(
604
- key: string,
605
- config?: unknown,
606
- context?: ExecutionContext,
607
- logger?: ILogger // Optional logger from fracture
608
- ): Promise<string | null>;
609
-
610
- // Validate provider configuration
611
- validate(config?: unknown): ValidationResult;
612
- }
613
-
614
- // ============================================================================
615
- // Script Engine
616
- // ============================================================================
617
-
618
- export interface ScriptResult {
619
- success: boolean;
620
- tests: TestResult[];
621
- error?: string;
622
- consoleOutput: string[];
623
- }
624
-
625
- export enum ScriptType {
626
- CollectionPre = 'collection-pre',
627
- CollectionPost = 'collection-post',
628
- FolderPre = 'folder-pre',
629
- FolderPost = 'folder-post',
630
- PreRequest = 'request-pre',
631
- PostRequest = 'request-post',
632
- PluginEvent = 'plugin-event'
633
- }
634
-
635
- // ============================================================================
636
- // Reporter Interface
637
- // ============================================================================
638
-
639
- export interface IReporter {
640
- // Identity
641
- name: string;
642
- version: string;
643
- description: string;
644
-
645
- // What report types does this plugin provide?
646
- // Example: ['console'], ['json', 'json-summary'], ['html', 'junit']
647
- reportTypes: string[];
648
-
649
- // Reporter-specific options schema
650
- // Defines what options can be passed to getOptions()
651
- // Example: { outputFile: string, verbose: boolean, colors: boolean }
652
- optionsSchema?: unknown;
653
-
654
- // Get options for a specific report type
655
- // Called before run starts to configure the reporter
656
- getOptions?(reportType: string): unknown;
657
-
658
- // Lifecycle hooks
659
- onRunStarted(collection: Collection, options: RunOptions): void;
660
-
661
- // @deprecated Use onBeforeRequest instead
662
- onRequestStarted?(request: Request, path: string): void;
663
- // @deprecated Use onAfterRequest + onAssertion instead
664
- onRequestCompleted?(result: RequestResult): void;
665
- // @deprecated Use onAssertion instead
666
- onTestCompleted?(test: TestResult, request: string): void;
667
-
668
- // New event-based hooks using EventPayloads types
669
- onBeforeRequest?(payload: EventPayloads['beforeRequest']): void;
670
- onAfterRequest?(payload: EventPayloads['afterRequest']): void;
671
- onAssertion?(payload: EventPayloads['assertion']): void;
672
-
673
- onRunCompleted(result: RunResult): void;
674
- }
675
-
676
- // ============================================================================
677
- // Events
678
- // ============================================================================
679
-
680
- /**
681
- * Path type for EventEnvelope and ExecutionNode
682
- */
683
- export type NodeType = 'collection' | 'folder' | 'request';
684
- export type PathType = 'collection:/' | `folder:/${string}` | `request:/${string}`;
685
- /**
686
- * Event envelope that provides context for all events (except console)
687
- */
688
- export type EventEnvelope = {
689
- id: string; // Unique event ID
690
- path: PathType;
691
- pathType: NodeType;
692
- collectionInfo: CollectionInfo;
693
- iteration?: {
694
- current: number;
695
- total: number;
696
- source: IterationSource;
697
- rowIndex?: number;
698
- rowKeys?: string[];
699
- row?: Record<string, string | number | boolean>;
700
- };
701
- request?: Request;
702
- };
703
-
704
- export type RunnerEvent =
705
- // Collection lifecycle
706
- | 'beforeRun'
707
- | 'afterRun'
708
- | 'beforeCollectionPreScript'
709
- | 'afterCollectionPreScript'
710
- | 'beforeCollectionPostScript'
711
- | 'afterCollectionPostScript'
712
-
713
- // Folder lifecycle
714
- | 'beforeFolder'
715
- | 'afterFolder'
716
- | 'beforeFolderPreScript'
717
- | 'afterFolderPreScript'
718
- | 'beforeFolderPostScript'
719
- | 'afterFolderPostScript'
720
-
721
- // Iteration
722
- | 'beforeIteration'
723
- | 'afterIteration'
724
-
725
- // Request/Item
726
- | 'beforeItem'
727
- | 'afterItem'
728
- | 'beforePreScript'
729
- | 'afterPreScript'
730
- | 'beforeRequest'
731
- | 'afterRequest'
732
- | 'beforePostScript'
733
- | 'afterPostScript'
734
-
735
- // Tests (run within postRequestScript via quest.test())
736
- | 'assertion'
737
-
738
- // Utilities
739
- | 'console'
740
- | 'exception';
741
-
742
- export interface EventPayloads {
743
- // Run Lifecycle
744
- beforeRun: {
745
- options: RunOptions;
746
- validationResult?: ValidationResult;
747
- expectedTestCount?: number;
748
- } & Pick<EventEnvelope, 'collectionInfo'>;
749
-
750
- afterRun: {
751
- result: RunResult;
752
- } & Pick<EventEnvelope, 'collectionInfo'>;
753
-
754
- // Collection Scripts
755
- beforeCollectionPreScript: EventEnvelope & { path: 'collection:/' };
756
- afterCollectionPreScript: EventEnvelope & { path: 'collection:/'; result: ScriptResult };
757
- beforeCollectionPostScript: EventEnvelope & { path: 'collection:/' };
758
- afterCollectionPostScript: EventEnvelope & { path: 'collection:/'; result: ScriptResult };
759
-
760
- // Iteration Lifecycle
761
- beforeIteration: EventEnvelope & { iteration: Required<EventEnvelope['iteration']> };
762
- afterIteration: EventEnvelope & { iteration: Required<EventEnvelope['iteration']>; duration: number };
763
-
764
- // Folder Lifecycle
765
- beforeFolder: EventEnvelope;
766
- afterFolder: EventEnvelope & { duration: number };
767
-
768
- // Folder Scripts
769
- beforeFolderPreScript: EventEnvelope;
770
- afterFolderPreScript: EventEnvelope & { result: ScriptResult };
771
- beforeFolderPostScript: EventEnvelope;
772
- afterFolderPostScript: EventEnvelope & { result: ScriptResult };
773
-
774
- // Item Lifecycle
775
- beforeItem: EventEnvelope & { request: Request; path: string };
776
- afterItem: EventEnvelope & { request: Request; path: string; response?: ProtocolResponse; result: RequestResult };
777
-
778
- // Pre-Request Script
779
- beforePreScript: EventEnvelope & { request: Request; path: string };
780
- afterPreScript: EventEnvelope & { request: Request; path: string; result: ScriptResult };
781
-
782
- // Request
783
- beforeRequest: EventEnvelope & { request: Request; path: string };
784
- afterRequest: EventEnvelope & { request: Request; response: ProtocolResponse; duration: number };
785
-
786
- // Post-Request Script
787
- beforePostScript: EventEnvelope & { request: Request; path: string; response: ProtocolResponse };
788
- afterPostScript: EventEnvelope & { request: Request; path: string; response: ProtocolResponse; result: ScriptResult };
789
-
790
- // Test/Assertion
791
- assertion: EventEnvelope & {
792
- test: TestResult;
793
- request?: Request;
794
- path?: string;
795
- response?: ProtocolResponse;
796
- event?: {
797
- name: string;
798
- index: number;
799
- timestamp: string;
800
- data: unknown;
801
- };
802
- };
803
-
804
- // Utility Events
805
- console: {
806
- id: string; // Unique event ID
807
- message: string;
808
- level: LogLevel;
809
- levelName?: 'error' | 'warn' | 'info' | 'debug' | 'trace';
810
- component?: string;
811
- timestamp?: string;
812
- args?: unknown[];
813
- };
814
-
815
- exception: {
816
- id: string; // Unique event ID
817
- error: Error;
818
- phase: 'collection-pre' | 'collection-post' | 'folder-pre' | 'folder-post' | 'prerequest' | 'postrequest' | 'request';
819
- request?: Request;
820
- path?: string;
821
- response?: ProtocolResponse;
822
- };
823
- }
1
+ // Core Types and Interfaces for @apiquest/types
2
+ // Schema v1.0 - Updated 2026-01-05
3
+
4
+ // ============================================================================
5
+ // Collection & Items
6
+ // ============================================================================
7
+
8
+ export interface Collection {
9
+ $schema?: string;
10
+ info: CollectionInfo;
11
+
12
+ // Protocol (collection-level)
13
+ protocol: string; // "http", "graphql", "grpc", "websocket", etc.
14
+
15
+ auth?: Auth;
16
+ variables?: Record<string, string | Variable>;
17
+
18
+ // Collection lifecycle scripts
19
+ collectionPreScript?: string;
20
+ collectionPostScript?: string;
21
+
22
+ // Request lifecycle scripts (run before/after EACH request)
23
+ preRequestScript?: string;
24
+ postRequestScript?: string;
25
+
26
+ testData?: IterationData[];
27
+ options?: RuntimeOptions;
28
+ items: CollectionItem[];
29
+ }
30
+
31
+ export interface CollectionInfo {
32
+ id: string;
33
+ name: string;
34
+ version?: string;
35
+ description?: string;
36
+ }
37
+
38
+ export interface Folder {
39
+ type: 'folder';
40
+ id: string;
41
+ name: string;
42
+ description?: string;
43
+ auth?: Auth;
44
+
45
+ // Execution control
46
+ dependsOn?: string[]; // Folder/Request IDs that must execute first
47
+ condition?: string; // JavaScript expression to evaluate
48
+
49
+ // Folder lifecycle scripts
50
+ folderPreScript?: string;
51
+ folderPostScript?: string;
52
+
53
+ // Request lifecycle scripts
54
+ preRequestScript?: string;
55
+ postRequestScript?: string;
56
+
57
+ options?: RuntimeOptions;
58
+ items: CollectionItem[];
59
+ }
60
+
61
+ export interface Request {
62
+ type: 'request';
63
+ id: string;
64
+ name: string;
65
+ description?: string;
66
+
67
+ // Execution control
68
+ dependsOn?: string[]; // Request IDs that must execute first
69
+ condition?: string; // JavaScript expression to evaluate
70
+
71
+ // Protocol is inherited from collection.protocol
72
+ auth?: Auth;
73
+ data: {
74
+ // Protocol-specific data
75
+ [key: string]: unknown;
76
+
77
+ // Plugin event scripts (e.g., for WebSocket: onMessage, onError, onComplete)
78
+ scripts?: ProtocolScript[];
79
+ };
80
+
81
+ preRequestScript?: string;
82
+ postRequestScript?: string; // Contains tests via quest.test()
83
+
84
+ options?: RuntimeOptions;
85
+ examples?: ResponseExample[];
86
+ }
87
+
88
+ export interface ProtocolScript {
89
+ event: string; // "onMessage", "onError", "onComplete", etc.
90
+ script: string;
91
+ }
92
+
93
+ export type CollectionItem = Request | Folder;
94
+
95
+ export interface ResponseExample {
96
+ name: string;
97
+ description?: string;
98
+ protocol: string;
99
+ data: unknown;
100
+ }
101
+
102
+ // ============================================================================
103
+ // Authentication
104
+ // ============================================================================
105
+
106
+ export interface Auth {
107
+ type: string | 'inherit' | 'none';
108
+ data?: Record<string, unknown>;
109
+ }
110
+
111
+ // ============================================================================
112
+ // Variables
113
+ // ============================================================================
114
+
115
+ export interface Variable {
116
+ value: string;
117
+ enabled?: boolean;
118
+ type?: "string" | "number" | "boolean";
119
+ isSecret?: boolean;
120
+ isRequired?: boolean; // enforce presence at runtime
121
+ provider?: string; // "env", "vault:aws-secrets", etc., undefined for built-in
122
+ description?: string;
123
+ }
124
+
125
+ export interface Environment {
126
+ name: string;
127
+ variables: Record<string, string | Variable>;
128
+ }
129
+
130
+ export interface IterationData {
131
+ [key: string]: string | number | boolean;
132
+ }
133
+
134
+ // ============================================================================
135
+ // Runtime Options
136
+ // ============================================================================
137
+
138
+ export interface CollectionRunnerOptions {
139
+ pluginsDir?: string | string[]; // Optional path(s) to plugins folder(s) for dynamic loading
140
+ logLevel?: LogLevel; // Optional log level (default: INFO)
141
+ }
142
+
143
+ export interface RuntimeOptions {
144
+ // Validation
145
+ strictMode?: boolean; // Enable/disable conditional test validation (default: true)
146
+
147
+ // Execution control
148
+ execution?: ExecutionOptions;
149
+
150
+ // Filtering
151
+ filter?: string; // Path-based regex filter
152
+ excludeDeps?: boolean; // Exclude dependencies when filtering
153
+
154
+ // External libraries
155
+ libraries?: ExternalLibrary[];
156
+
157
+ // Cookies
158
+ cookies?: Cookie[];
159
+ jar?: CookieJarOptions;
160
+
161
+ // SSL/TLS
162
+ ssl?: SSLOptions;
163
+
164
+ // Proxy
165
+ proxy?: ProxyOptions;
166
+
167
+ // Timeouts
168
+ timeout?: TimeoutOptions;
169
+
170
+ // Redirects
171
+ followRedirects?: boolean;
172
+ maxRedirects?: number;
173
+
174
+ // Logging
175
+ logLevel?: LogLevel;
176
+
177
+ // Plugin-specific options
178
+ plugins?: Record<string, unknown>;
179
+ }
180
+
181
+ export interface ExecutionOptions {
182
+ allowParallel?: boolean; // Enable parallel execution
183
+ maxConcurrency?: number; // Max parallel requests
184
+ bail?: boolean; // Stop on first failure
185
+ delay?: number; // Delay between requests (ms)
186
+ }
187
+
188
+ export interface ExternalLibrary {
189
+ name: string;
190
+ source: LibrarySource;
191
+ version?: string;
192
+ }
193
+
194
+ export type LibrarySource =
195
+ | { type: 'npm'; package: string }
196
+ | { type: 'cdn'; url: string }
197
+ | { type: 'file'; path: string };
198
+
199
+ export interface Cookie {
200
+ name: string;
201
+ value: string;
202
+ domain: string;
203
+ path?: string;
204
+ expires?: string;
205
+ httpOnly?: boolean;
206
+ secure?: boolean;
207
+ sameSite?: 'Strict' | 'Lax' | 'None';
208
+ }
209
+
210
+ export interface CookieSetOptions {
211
+ domain: string;
212
+ path?: string;
213
+ expires?: string;
214
+ httpOnly?: boolean;
215
+ secure?: boolean;
216
+ sameSite?: 'Strict' | 'Lax' | 'None';
217
+ }
218
+
219
+ export interface CookieJarOptions {
220
+ persist: boolean; //default: false
221
+ }
222
+
223
+ export interface SSLOptions {
224
+ validateCertificates?: boolean;
225
+ clientCertificate?: {
226
+ cert: string;
227
+ key: string;
228
+ passphrase?: string;
229
+ };
230
+ ca?: string;
231
+ }
232
+
233
+ export interface ProxyOptions {
234
+ enabled: boolean;
235
+ host: string;
236
+ port: number;
237
+ auth?: {
238
+ username: string;
239
+ password: string;
240
+ };
241
+ bypass?: string[];
242
+ }
243
+
244
+ export interface TimeoutOptions {
245
+ request?: number;
246
+ connection?: number;
247
+ response?: number;
248
+ }
249
+
250
+ // ============================================================================
251
+ // Execution History
252
+ // ============================================================================
253
+
254
+ export interface ExecutionRecord {
255
+ // Identity
256
+ id: string;
257
+ name: string;
258
+ path: string; // "/folder1/folder2/request"
259
+
260
+ // Iteration metadata
261
+ iteration: number;
262
+
263
+ // Results
264
+ response: ProtocolResponse;
265
+ tests: TestResult[];
266
+ timestamp: string;
267
+ }
268
+
269
+ export interface ExecutionHistoryEntry {
270
+ requestId: string;
271
+ requestName: string;
272
+ response?: ProtocolResponse;
273
+ timestamp: Date;
274
+ collectionIteration: number;
275
+ requestIteration: number;
276
+ }
277
+
278
+ // ============================================================================
279
+ // Plugin Events
280
+ // ============================================================================
281
+
282
+ export interface PluginEvent {
283
+ eventName: string; // "onMessage", "onError", etc.
284
+ requestId: string; // Request that triggered the event
285
+ timestamp: Date;
286
+ data: unknown; // Plugin-specific event data
287
+ index: number; // Event sequence number (0-based, per event type)
288
+ }
289
+
290
+ // ============================================================================
291
+ // Execution Context
292
+ // ============================================================================
293
+
294
+ export interface ScopeFrame {
295
+ level: 'collection' | 'folder' | 'request';
296
+ id: string;
297
+ vars: Record<string, string>;
298
+ }
299
+
300
+ export type IterationSource = 'collection' | 'cli' | 'none';
301
+
302
+ export interface ExecutionContext {
303
+ // Collection info
304
+ collectionInfo: CollectionInfo;
305
+ protocol: string; // Collection protocol (e.g., 'http', 'graphql')
306
+
307
+ // Variable scopes
308
+ collectionVariables: Record<string, string | Variable>;
309
+ globalVariables: Record<string, string | Variable>;
310
+ scopeStack: ScopeFrame[]; // Hierarchical scope stack
311
+ environment?: Environment;
312
+
313
+ // Current execution state
314
+ currentRequest?: Request;
315
+ currentResponse?: ProtocolResponse;
316
+ currentPath?: string; // Current folder/request path
317
+
318
+ // Plugin event tracking (for streaming protocols)
319
+ expectedMessages?: number; // Hint from quest.expectMessages() for plugin optimization
320
+ currentEvent?: PluginEvent; // Current plugin event (for eventScripts)
321
+
322
+ // Iteration state
323
+ iterationCurrent: number;
324
+ iterationCount: number;
325
+ iterationData?: IterationData[];
326
+ iterationSource: IterationSource; // Where iteration data comes from
327
+
328
+ // History
329
+ executionHistory: ExecutionRecord[];
330
+
331
+ // Runtime options
332
+ options: RuntimeOptions;
333
+
334
+ // Cookie jar - ICookieJar interface for type safety
335
+ cookieJar: ICookieJar;
336
+
337
+ // Event emitter for plugin callbacks
338
+ eventEmitter?: unknown; // EventEmitter instance
339
+
340
+ // Cached protocol plugin (loaded at collection initialization for fail-fast validation)
341
+ protocolPlugin: IProtocolPlugin;
342
+
343
+ // emitEvent - Allow plugins to emit custom events (websocket:message, sse:chunk, etc.)
344
+ // These events are protocol-specific and streamed to the UI
345
+ emitEvent?: (type: string, data: unknown) => void;
346
+
347
+ // Abort signal for execution cancellation
348
+ abortSignal: AbortSignal;
349
+ }
350
+
351
+ // ============================================================================
352
+ // Cookie Jar Interface
353
+ // ============================================================================
354
+
355
+ export interface ICookieJar {
356
+ get(name: string, domain?: string, path?: string): string | null;
357
+ set(name: string, value: string, options: CookieSetOptions): void;
358
+ has(name: string, domain?: string, path?: string): boolean;
359
+ remove(name: string, domain?: string, path?: string): void;
360
+ clear(): void;
361
+ toObject(): Record<string, string>;
362
+ getCookieHeader(url: string): string | null;
363
+ store(setCookieHeaders: string | string[] | null | undefined, requestUrl: string): void;
364
+ }
365
+
366
+ // ============================================================================
367
+ // Validation
368
+ // ============================================================================
369
+
370
+ export interface ValidationError {
371
+ message: string;
372
+ location: string; // Request path (e.g., "/Folder A/Request 1")
373
+ source: 'script' | 'protocol' | 'auth' | 'vault' | 'schema';
374
+ scriptType?: ScriptType;
375
+ details?: {
376
+ line?: number;
377
+ column?: number;
378
+ suggestion?: string;
379
+ [key: string]: unknown;
380
+ };
381
+ }
382
+
383
+ // ============================================================================
384
+ // Logging
385
+ // ============================================================================
386
+
387
+ export enum LogLevel {
388
+ ERROR = 0,
389
+ WARN = 1,
390
+ INFO = 2,
391
+ DEBUG = 3,
392
+ TRACE = 4
393
+ }
394
+
395
+ export interface ILogger {
396
+ error(message: string, ...args: unknown[]): void;
397
+ warn(message: string, ...args: unknown[]): void;
398
+ info(message: string, ...args: unknown[]): void;
399
+ debug(message: string, ...args: unknown[]): void;
400
+ trace(message: string, ...args: unknown[]): void;
401
+ setLevel(level: LogLevel): void;
402
+ createLogger(component: string): ILogger;
403
+ }
404
+
405
+ // ============================================================================
406
+ // Run Options & Results
407
+ // ============================================================================
408
+
409
+ export interface RunOptions extends Omit<RuntimeOptions, 'logLevel'> {
410
+ // Additional CLI/API specific options not in RuntimeOptions
411
+ environment?: Environment;
412
+ globalVariables?: Record<string, string | Variable>;
413
+ data?: IterationData[]; // CLI --data override
414
+ iterations?: number; // Global iteration cap
415
+ filter?: string; // Path-based regex filter
416
+ excludeDeps?: boolean; // Exclude dependencies when filtering
417
+ signal?: AbortSignal; // External abort signal
418
+ }
419
+
420
+ export interface RunResult {
421
+ collectionId: string;
422
+ collectionName: string;
423
+ startTime: Date;
424
+ endTime: Date;
425
+ duration: number;
426
+ requestResults: RequestResult[];
427
+ totalTests: number;
428
+ passedTests: number;
429
+ failedTests: number;
430
+ skippedTests: number;
431
+ validationErrors?: ValidationError[]; // Pre-run validation errors
432
+ aborted?: boolean;
433
+ abortReason?: string;
434
+ }
435
+
436
+ export interface RequestResult {
437
+ requestId: string;
438
+ requestName: string;
439
+ path: string;
440
+ success: boolean;
441
+ response?: ProtocolResponse;
442
+ tests: TestResult[];
443
+ duration: number;
444
+ scriptError?: string;
445
+ iteration: number;
446
+ }
447
+
448
+ export interface TestResult {
449
+ name: string;
450
+ passed: boolean;
451
+ error?: string;
452
+ skipped: boolean;
453
+ }
454
+
455
+ // ============================================================================
456
+ // Plugin Package Metadata
457
+ // ============================================================================
458
+
459
+ export interface PluginPackageJson {
460
+ name: string;
461
+ version: string;
462
+ main?: string;
463
+ apiquest?: {
464
+ runtime?: string[] | string;
465
+ type: string;
466
+ capabilities?: {
467
+ provides?: {
468
+ protocols?: string[];
469
+ authTypes?: string[];
470
+ provider?: string;
471
+ };
472
+ };
473
+ };
474
+ }
475
+
476
+ // ============================================================================
477
+ // Protocol Plugin Interface
478
+ // ============================================================================
479
+
480
+ export interface IProtocolPlugin {
481
+ // Identity
482
+ name: string;
483
+ version: string;
484
+ description: string;
485
+
486
+ // What protocols does this plugin provide?
487
+ // Single-protocol: ['http']
488
+ // Multi-protocol bundle (rare): ['http', 'https', 'http2']
489
+ protocols: string[];
490
+
491
+ // Supported auth types for this protocol
492
+ // Example: HTTP supports ['bearer', 'basic', 'oauth2', 'apikey']
493
+ // SQL might support ['integrated', 'username-password']
494
+ supportedAuthTypes: string[];
495
+
496
+ // If true, ONLY supportedAuthTypes listed above are allowed
497
+ // If false/undefined, accept additional auth plugins that declare this protocol
498
+ strictAuthList?: boolean;
499
+
500
+ // Schema for request.data (defines what fields the UI should show)
501
+ // Example: HTTP has method, url, headers, body
502
+ dataSchema: unknown; // JSON Schema or custom schema format
503
+
504
+ // Schema for options.plugins[protocol] (runtime options)
505
+ // Example: HTTP has keepAlive, compression, maxSockets
506
+ optionsSchema?: PluginOptionsSchema;
507
+
508
+ // Plugin event definitions (e.g., WebSocket: onMessage, onError, onComplete)
509
+ events?: PluginEventDefinition[];
510
+
511
+ execute(
512
+ request: Request,
513
+ context: ExecutionContext,
514
+ options: RuntimeOptions, // Receives merged options
515
+ emitEvent?: (eventName: string, eventData: unknown) => Promise<void>, // Optional callback for plugin events
516
+ logger?: ILogger // Optional logger from fracture
517
+ ): Promise<ProtocolResponse>;
518
+
519
+ validate(request: Request, options: RuntimeOptions): ValidationResult;
520
+ }
521
+
522
+ export interface PluginOptionsSchema {
523
+ // Defines options that can be set in options.plugins[protocol]
524
+ // Example: options.plugins.http = { keepAlive: true, compression: "gzip" }
525
+ [optionKey: string]: {
526
+ type: 'boolean' | 'string' | 'number';
527
+ default?: unknown;
528
+ enum?: unknown[];
529
+ description?: string;
530
+ };
531
+ }
532
+
533
+ export interface PluginEventDefinition {
534
+ name: string; // "onMessage", "onError", "onComplete"
535
+ description: string;
536
+ canHaveTests: boolean; // Can this event script contain quest.test()?
537
+ required: boolean; // Is this event required or optional?
538
+ }
539
+
540
+ export interface ProtocolResponse {
541
+ status: number;
542
+ statusText: string;
543
+ body: string;
544
+ headers: Record<string, string | string[]>; // Headers can have multiple values (e.g., set-cookie)
545
+ duration: number;
546
+ error?: string;
547
+ }
548
+
549
+ export interface ValidationResult {
550
+ valid: boolean;
551
+ errors?: ValidationError[];
552
+ }
553
+
554
+ // ============================================================================
555
+ // Auth Plugin Interface
556
+ // ============================================================================
557
+
558
+ export interface IAuthPlugin {
559
+ // Identity
560
+ name: string;
561
+ version: string;
562
+ description: string;
563
+
564
+ // What auth types does this plugin provide?
565
+ // Single auth: ['bearer']
566
+ // Multi-auth bundle: ['bearer', 'basic', 'apikey', 'oauth2']
567
+ authTypes: string[];
568
+
569
+ // Which protocols does this auth work with? (REQUIRED)
570
+ // Example: ['http', 'grpc', 'graphql']
571
+ // No universal auth - must explicitly declare protocol support
572
+ protocols: string[];
573
+
574
+ // Schema for auth.data (defines what fields the UI should show)
575
+ // Example: bearer has { token: string }, basic has { username: string, password: string }
576
+ dataSchema: unknown; // JSON Schema or custom schema format
577
+
578
+ apply(
579
+ request: Request,
580
+ auth: Auth,
581
+ options: RuntimeOptions,
582
+ logger?: ILogger // Optional logger from fracture
583
+ ): Promise<Request>;
584
+
585
+ validate(auth: Auth, options: RuntimeOptions): ValidationResult;
586
+ }
587
+
588
+ // ============================================================================
589
+ // Value Provider Plugin Interface
590
+ // ============================================================================
591
+
592
+ export interface IValueProviderPlugin {
593
+ provider: string; // "vault:aws-secrets", "vault:azure-keyvault", "vault:file"
594
+ name: string;
595
+ description: string;
596
+
597
+ // Configuration schema (if provider needs setup)
598
+ configSchema?: unknown;
599
+
600
+ // Retrieve a secret value by key
601
+ // Returns null if key doesn't exist
602
+ // Throws error if provider fails (network, auth, etc.)
603
+ getValue(
604
+ key: string,
605
+ config?: unknown,
606
+ context?: ExecutionContext,
607
+ logger?: ILogger // Optional logger from fracture
608
+ ): Promise<string | null>;
609
+
610
+ // Validate provider configuration
611
+ validate(config?: unknown): ValidationResult;
612
+ }
613
+
614
+ // ============================================================================
615
+ // Script Engine
616
+ // ============================================================================
617
+
618
+ export interface ScriptResult {
619
+ success: boolean;
620
+ tests: TestResult[];
621
+ error?: string;
622
+ consoleOutput: string[];
623
+ }
624
+
625
+ export enum ScriptType {
626
+ CollectionPre = 'collection-pre',
627
+ CollectionPost = 'collection-post',
628
+ FolderPre = 'folder-pre',
629
+ FolderPost = 'folder-post',
630
+ PreRequest = 'request-pre',
631
+ PostRequest = 'request-post',
632
+ PluginEvent = 'plugin-event'
633
+ }
634
+
635
+ // ============================================================================
636
+ // Reporter Interface
637
+ // ============================================================================
638
+
639
+ export interface IReporter {
640
+ // Identity
641
+ name: string;
642
+ version: string;
643
+ description: string;
644
+
645
+ // What report types does this plugin provide?
646
+ // Example: ['console'], ['json', 'json-summary'], ['html', 'junit']
647
+ reportTypes: string[];
648
+
649
+ // Reporter-specific options schema
650
+ // Defines what options can be passed to getOptions()
651
+ // Example: { outputFile: string, verbose: boolean, colors: boolean }
652
+ optionsSchema?: unknown;
653
+
654
+ // Get options for a specific report type
655
+ // Called before run starts to configure the reporter
656
+ getOptions?(reportType: string): unknown;
657
+
658
+ // Lifecycle hooks
659
+ onRunStarted(collection: Collection, options: RunOptions): void;
660
+
661
+ // @deprecated Use onBeforeRequest instead
662
+ onRequestStarted?(request: Request, path: string): void;
663
+ // @deprecated Use onAfterRequest + onAssertion instead
664
+ onRequestCompleted?(result: RequestResult): void;
665
+ // @deprecated Use onAssertion instead
666
+ onTestCompleted?(test: TestResult, request: string): void;
667
+
668
+ // New event-based hooks using EventPayloads types
669
+ onBeforeRequest?(payload: EventPayloads['beforeRequest']): void;
670
+ onAfterRequest?(payload: EventPayloads['afterRequest']): void;
671
+ onAssertion?(payload: EventPayloads['assertion']): void;
672
+
673
+ onRunCompleted(result: RunResult): void;
674
+ }
675
+
676
+ // ============================================================================
677
+ // Events
678
+ // ============================================================================
679
+
680
+ /**
681
+ * Path type for EventEnvelope and ExecutionNode
682
+ */
683
+ export type NodeType = 'collection' | 'folder' | 'request';
684
+ export type PathType = 'collection:/' | `folder:/${string}` | `request:/${string}`;
685
+ /**
686
+ * Event envelope that provides context for all events (except console)
687
+ */
688
+ export type EventEnvelope = {
689
+ id: string; // Unique event ID
690
+ path: PathType;
691
+ pathType: NodeType;
692
+ collectionInfo: CollectionInfo;
693
+ iteration?: {
694
+ current: number;
695
+ total: number;
696
+ source: IterationSource;
697
+ rowIndex?: number;
698
+ rowKeys?: string[];
699
+ row?: Record<string, string | number | boolean>;
700
+ };
701
+ request?: Request;
702
+ };
703
+
704
+ export type RunnerEvent =
705
+ // Collection lifecycle
706
+ | 'beforeRun'
707
+ | 'afterRun'
708
+ | 'beforeCollectionPreScript'
709
+ | 'afterCollectionPreScript'
710
+ | 'beforeCollectionPostScript'
711
+ | 'afterCollectionPostScript'
712
+
713
+ // Folder lifecycle
714
+ | 'beforeFolder'
715
+ | 'afterFolder'
716
+ | 'beforeFolderPreScript'
717
+ | 'afterFolderPreScript'
718
+ | 'beforeFolderPostScript'
719
+ | 'afterFolderPostScript'
720
+
721
+ // Iteration
722
+ | 'beforeIteration'
723
+ | 'afterIteration'
724
+
725
+ // Request/Item
726
+ | 'beforeItem'
727
+ | 'afterItem'
728
+ | 'beforePreScript'
729
+ | 'afterPreScript'
730
+ | 'beforeRequest'
731
+ | 'afterRequest'
732
+ | 'beforePostScript'
733
+ | 'afterPostScript'
734
+
735
+ // Tests (run within postRequestScript via quest.test())
736
+ | 'assertion'
737
+
738
+ // Utilities
739
+ | 'console'
740
+ | 'exception';
741
+
742
+ export interface EventPayloads {
743
+ // Run Lifecycle
744
+ beforeRun: {
745
+ options: RunOptions;
746
+ validationResult?: ValidationResult;
747
+ expectedTestCount?: number;
748
+ } & Pick<EventEnvelope, 'collectionInfo'>;
749
+
750
+ afterRun: {
751
+ result: RunResult;
752
+ } & Pick<EventEnvelope, 'collectionInfo'>;
753
+
754
+ // Collection Scripts
755
+ beforeCollectionPreScript: EventEnvelope & { path: 'collection:/' };
756
+ afterCollectionPreScript: EventEnvelope & { path: 'collection:/'; result: ScriptResult };
757
+ beforeCollectionPostScript: EventEnvelope & { path: 'collection:/' };
758
+ afterCollectionPostScript: EventEnvelope & { path: 'collection:/'; result: ScriptResult };
759
+
760
+ // Iteration Lifecycle
761
+ beforeIteration: EventEnvelope & { iteration: Required<EventEnvelope['iteration']> };
762
+ afterIteration: EventEnvelope & { iteration: Required<EventEnvelope['iteration']>; duration: number };
763
+
764
+ // Folder Lifecycle
765
+ beforeFolder: EventEnvelope;
766
+ afterFolder: EventEnvelope & { duration: number };
767
+
768
+ // Folder Scripts
769
+ beforeFolderPreScript: EventEnvelope;
770
+ afterFolderPreScript: EventEnvelope & { result: ScriptResult };
771
+ beforeFolderPostScript: EventEnvelope;
772
+ afterFolderPostScript: EventEnvelope & { result: ScriptResult };
773
+
774
+ // Item Lifecycle
775
+ beforeItem: EventEnvelope & { request: Request; path: string };
776
+ afterItem: EventEnvelope & { request: Request; path: string; response?: ProtocolResponse; result: RequestResult };
777
+
778
+ // Pre-Request Script
779
+ beforePreScript: EventEnvelope & { request: Request; path: string };
780
+ afterPreScript: EventEnvelope & { request: Request; path: string; result: ScriptResult };
781
+
782
+ // Request
783
+ beforeRequest: EventEnvelope & { request: Request; path: string };
784
+ afterRequest: EventEnvelope & { request: Request; response: ProtocolResponse; duration: number };
785
+
786
+ // Post-Request Script
787
+ beforePostScript: EventEnvelope & { request: Request; path: string; response: ProtocolResponse };
788
+ afterPostScript: EventEnvelope & { request: Request; path: string; response: ProtocolResponse; result: ScriptResult };
789
+
790
+ // Test/Assertion
791
+ assertion: EventEnvelope & {
792
+ test: TestResult;
793
+ request?: Request;
794
+ path?: string;
795
+ response?: ProtocolResponse;
796
+ event?: {
797
+ name: string;
798
+ index: number;
799
+ timestamp: string;
800
+ data: unknown;
801
+ };
802
+ };
803
+
804
+ // Utility Events
805
+ console: {
806
+ id: string; // Unique event ID
807
+ message: string;
808
+ level: LogLevel;
809
+ levelName?: 'error' | 'warn' | 'info' | 'debug' | 'trace';
810
+ component?: string;
811
+ timestamp?: string;
812
+ args?: unknown[];
813
+ };
814
+
815
+ exception: {
816
+ id: string; // Unique event ID
817
+ error: Error;
818
+ phase: 'collection-pre' | 'collection-post' | 'folder-pre' | 'folder-post' | 'prerequest' | 'postrequest' | 'request';
819
+ request?: Request;
820
+ path?: string;
821
+ response?: ProtocolResponse;
822
+ };
823
+ }