@apiquest/fracture 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -0
- package/bin/cli.js +2 -2
- package/dist/CollectionRunner.js +3 -3
- package/dist/ScriptEngine.js +4 -4
- package/dist/cli/plugin-commands.d.ts.map +1 -1
- package/dist/cli/plugin-commands.js +2 -1
- package/dist/cli/plugin-commands.js.map +1 -1
- package/package.json +55 -50
- package/src/CollectionAnalyzer.ts +102 -102
- package/src/CollectionRunner.ts +1423 -1423
- package/src/CollectionRunner.types.ts +9 -9
- package/src/CollectionValidator.ts +289 -289
- package/src/ConsoleReporter.ts +143 -143
- package/src/CookieJar.ts +258 -258
- package/src/DagScheduler.ts +439 -439
- package/src/Logger.ts +85 -85
- package/src/PluginLoader.ts +126 -126
- package/src/PluginManager.ts +208 -208
- package/src/PluginResolver.ts +154 -154
- package/src/QuestAPI.ts +764 -764
- package/src/QuestAPI.types.ts +33 -33
- package/src/QuestTestAPI.ts +164 -164
- package/src/RequestFilter.ts +224 -224
- package/src/ScriptEngine.ts +219 -219
- package/src/ScriptValidator.ts +428 -428
- package/src/TaskGraph.ts +598 -598
- package/src/TestCounter.ts +109 -109
- package/src/VariableResolver.ts +114 -114
- package/src/cli/index.ts +480 -480
- package/src/cli/plugin-commands.ts +342 -341
- package/src/cli/plugin-discovery.ts +44 -44
- package/src/index.ts +24 -24
- package/src/utils.ts +52 -52
- package/tsconfig.json +20 -20
- package/tsconfig.test.json +5 -5
- package/vitest.config.ts +22 -22
- package/dist/ExecutionTree.d.ts +0 -77
- package/dist/ExecutionTree.d.ts.map +0 -1
- package/dist/ExecutionTree.js +0 -265
- package/dist/ExecutionTree.js.map +0 -1
- package/dist/fracture/src/CollectionAnalyzer.d.ts +0 -17
- package/dist/fracture/src/CollectionAnalyzer.d.ts.map +0 -1
- package/dist/fracture/src/CollectionAnalyzer.js +0 -70
- package/dist/fracture/src/CollectionAnalyzer.js.map +0 -1
- package/dist/fracture/src/CollectionRunner.d.ts +0 -39
- package/dist/fracture/src/CollectionRunner.d.ts.map +0 -1
- package/dist/fracture/src/CollectionRunner.js +0 -802
- package/dist/fracture/src/CollectionRunner.js.map +0 -1
- package/dist/fracture/src/CollectionRunner.types.d.ts +0 -8
- package/dist/fracture/src/CollectionRunner.types.d.ts.map +0 -1
- package/dist/fracture/src/CollectionRunner.types.js +0 -2
- package/dist/fracture/src/CollectionRunner.types.js.map +0 -1
- package/dist/fracture/src/CollectionValidator.d.ts +0 -14
- package/dist/fracture/src/CollectionValidator.d.ts.map +0 -1
- package/dist/fracture/src/CollectionValidator.js +0 -145
- package/dist/fracture/src/CollectionValidator.js.map +0 -1
- package/dist/fracture/src/ConsoleReporter.d.ts +0 -24
- package/dist/fracture/src/ConsoleReporter.d.ts.map +0 -1
- package/dist/fracture/src/ConsoleReporter.js +0 -123
- package/dist/fracture/src/ConsoleReporter.js.map +0 -1
- package/dist/fracture/src/CookieJar.d.ts +0 -70
- package/dist/fracture/src/CookieJar.d.ts.map +0 -1
- package/dist/fracture/src/CookieJar.js +0 -233
- package/dist/fracture/src/CookieJar.js.map +0 -1
- package/dist/fracture/src/ExecutionTree.d.ts +0 -77
- package/dist/fracture/src/ExecutionTree.d.ts.map +0 -1
- package/dist/fracture/src/ExecutionTree.js +0 -258
- package/dist/fracture/src/ExecutionTree.js.map +0 -1
- package/dist/fracture/src/Logger.d.ts +0 -25
- package/dist/fracture/src/Logger.d.ts.map +0 -1
- package/dist/fracture/src/Logger.js +0 -78
- package/dist/fracture/src/Logger.js.map +0 -1
- package/dist/fracture/src/PluginLoader.d.ts +0 -23
- package/dist/fracture/src/PluginLoader.d.ts.map +0 -1
- package/dist/fracture/src/PluginLoader.js +0 -102
- package/dist/fracture/src/PluginLoader.js.map +0 -1
- package/dist/fracture/src/PluginManager.d.ts +0 -64
- package/dist/fracture/src/PluginManager.d.ts.map +0 -1
- package/dist/fracture/src/PluginManager.js +0 -162
- package/dist/fracture/src/PluginManager.js.map +0 -1
- package/dist/fracture/src/PluginResolver.d.ts +0 -35
- package/dist/fracture/src/PluginResolver.d.ts.map +0 -1
- package/dist/fracture/src/PluginResolver.js +0 -128
- package/dist/fracture/src/PluginResolver.js.map +0 -1
- package/dist/fracture/src/QuestAPI.d.ts +0 -9
- package/dist/fracture/src/QuestAPI.d.ts.map +0 -1
- package/dist/fracture/src/QuestAPI.js +0 -679
- package/dist/fracture/src/QuestAPI.js.map +0 -1
- package/dist/fracture/src/QuestAPI.types.d.ts +0 -35
- package/dist/fracture/src/QuestAPI.types.d.ts.map +0 -1
- package/dist/fracture/src/QuestAPI.types.js +0 -3
- package/dist/fracture/src/QuestAPI.types.js.map +0 -1
- package/dist/fracture/src/QuestTestAPI.d.ts +0 -12
- package/dist/fracture/src/QuestTestAPI.d.ts.map +0 -1
- package/dist/fracture/src/QuestTestAPI.js +0 -133
- package/dist/fracture/src/QuestTestAPI.js.map +0 -1
- package/dist/fracture/src/ScriptEngine.d.ts +0 -21
- package/dist/fracture/src/ScriptEngine.d.ts.map +0 -1
- package/dist/fracture/src/ScriptEngine.js +0 -183
- package/dist/fracture/src/ScriptEngine.js.map +0 -1
- package/dist/fracture/src/ScriptValidator.d.ts +0 -68
- package/dist/fracture/src/ScriptValidator.d.ts.map +0 -1
- package/dist/fracture/src/ScriptValidator.js +0 -351
- package/dist/fracture/src/ScriptValidator.js.map +0 -1
- package/dist/fracture/src/TestCounter.d.ts +0 -18
- package/dist/fracture/src/TestCounter.d.ts.map +0 -1
- package/dist/fracture/src/TestCounter.js +0 -82
- package/dist/fracture/src/TestCounter.js.map +0 -1
- package/dist/fracture/src/VariableResolver.d.ts +0 -20
- package/dist/fracture/src/VariableResolver.d.ts.map +0 -1
- package/dist/fracture/src/VariableResolver.js +0 -100
- package/dist/fracture/src/VariableResolver.js.map +0 -1
- package/dist/fracture/src/cli/index.d.ts +0 -3
- package/dist/fracture/src/cli/index.d.ts.map +0 -1
- package/dist/fracture/src/cli/index.js +0 -347
- package/dist/fracture/src/cli/index.js.map +0 -1
- package/dist/fracture/src/cli/plugin-commands.d.ts +0 -6
- package/dist/fracture/src/cli/plugin-commands.d.ts.map +0 -1
- package/dist/fracture/src/cli/plugin-commands.js +0 -263
- package/dist/fracture/src/cli/plugin-commands.js.map +0 -1
- package/dist/fracture/src/cli/plugin-discovery.d.ts +0 -11
- package/dist/fracture/src/cli/plugin-discovery.d.ts.map +0 -1
- package/dist/fracture/src/cli/plugin-discovery.js +0 -64
- package/dist/fracture/src/cli/plugin-discovery.js.map +0 -1
- package/dist/fracture/src/index.d.ts +0 -13
- package/dist/fracture/src/index.d.ts.map +0 -1
- package/dist/fracture/src/index.js +0 -17
- package/dist/fracture/src/index.js.map +0 -1
- package/dist/fracture/src/utils.d.ts +0 -28
- package/dist/fracture/src/utils.d.ts.map +0 -1
- package/dist/fracture/src/utils.js +0 -48
- package/dist/fracture/src/utils.js.map +0 -1
- package/dist/plugin-auth/src/apikey-auth.d.ts +0 -3
- package/dist/plugin-auth/src/apikey-auth.d.ts.map +0 -1
- package/dist/plugin-auth/src/apikey-auth.js +0 -73
- package/dist/plugin-auth/src/apikey-auth.js.map +0 -1
- package/dist/plugin-auth/src/basic-auth.d.ts +0 -3
- package/dist/plugin-auth/src/basic-auth.d.ts.map +0 -1
- package/dist/plugin-auth/src/basic-auth.js +0 -61
- package/dist/plugin-auth/src/basic-auth.js.map +0 -1
- package/dist/plugin-auth/src/bearer-auth.d.ts +0 -3
- package/dist/plugin-auth/src/bearer-auth.d.ts.map +0 -1
- package/dist/plugin-auth/src/bearer-auth.js +0 -49
- package/dist/plugin-auth/src/bearer-auth.js.map +0 -1
- package/dist/plugin-auth/src/helpers.d.ts +0 -3
- package/dist/plugin-auth/src/helpers.d.ts.map +0 -1
- package/dist/plugin-auth/src/helpers.js +0 -8
- package/dist/plugin-auth/src/helpers.js.map +0 -1
- package/dist/plugin-auth/src/index.d.ts +0 -10
- package/dist/plugin-auth/src/index.d.ts.map +0 -1
- package/dist/plugin-auth/src/index.js +0 -25
- package/dist/plugin-auth/src/index.js.map +0 -1
- package/dist/plugin-auth/src/oauth2-auth.d.ts +0 -35
- package/dist/plugin-auth/src/oauth2-auth.d.ts.map +0 -1
- package/dist/plugin-auth/src/oauth2-auth.js +0 -266
- package/dist/plugin-auth/src/oauth2-auth.js.map +0 -1
- package/dist/plugin-http/src/index.d.ts +0 -4
- package/dist/plugin-http/src/index.d.ts.map +0 -1
- package/dist/plugin-http/src/index.js +0 -266
- package/dist/plugin-http/src/index.js.map +0 -1
- package/dist/plugin-vault-file/src/index.d.ts +0 -67
- package/dist/plugin-vault-file/src/index.d.ts.map +0 -1
- package/dist/plugin-vault-file/src/index.js +0 -171
- package/dist/plugin-vault-file/src/index.js.map +0 -1
- package/dist/types.d.ts +0 -374
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -13
- package/dist/types.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
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
|
+
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 +1,289 @@
|
|
|
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
|
+
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
|
+
}
|