@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
package/src/Logger.ts
CHANGED
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
import { randomUUID } from 'crypto';
|
|
3
|
-
import { ILogger, LogLevel } from '@apiquest/types';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Logger for fracture runtime and host integrations.
|
|
7
|
-
*/
|
|
8
|
-
export class Logger implements ILogger {
|
|
9
|
-
private level: LogLevel;
|
|
10
|
-
private component: string;
|
|
11
|
-
private emitter?: EventEmitter;
|
|
12
|
-
|
|
13
|
-
constructor(component: string, level: LogLevel = LogLevel.INFO, emitter?: EventEmitter) {
|
|
14
|
-
this.component = component;
|
|
15
|
-
this.level = level;
|
|
16
|
-
this.emitter = emitter;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
createLogger(component: string): Logger {
|
|
20
|
-
return new Logger(component, this.level, this.emitter);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
setLevel(level: LogLevel): void {
|
|
24
|
-
this.level = level;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
error(message: string, ...args: unknown[]): void {
|
|
28
|
-
this.log(LogLevel.ERROR, message, ...args);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
warn(message: string, ...args: unknown[]): void {
|
|
32
|
-
this.log(LogLevel.WARN, message, ...args);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
info(message: string, ...args: unknown[]): void {
|
|
36
|
-
this.log(LogLevel.INFO, message, ...args);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
debug(message: string, ...args: unknown[]): void {
|
|
40
|
-
this.log(LogLevel.DEBUG, message, ...args);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
trace(message: string, ...args: unknown[]): void {
|
|
44
|
-
this.log(LogLevel.TRACE, message, ...args);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private log(level: LogLevel, message: string, ...args: unknown[]): void {
|
|
48
|
-
if (level > this.level) return;
|
|
49
|
-
|
|
50
|
-
const levelNames = ['error', 'warn', 'info', 'debug', 'trace'];
|
|
51
|
-
const levelName = levelNames[level];
|
|
52
|
-
const prefix = `[${this.component}]`;
|
|
53
|
-
const fullMessage = `${prefix} ${message}`;
|
|
54
|
-
|
|
55
|
-
const formattedArgs = args.length > 0
|
|
56
|
-
? ' ' + args.map(a => {
|
|
57
|
-
if (a instanceof Error) {
|
|
58
|
-
return a.message;
|
|
59
|
-
}
|
|
60
|
-
if (typeof a === 'object' && a !== null) {
|
|
61
|
-
try {
|
|
62
|
-
return JSON.stringify(a);
|
|
63
|
-
} catch {
|
|
64
|
-
return String(a);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return String(a);
|
|
68
|
-
}).join(' ')
|
|
69
|
-
: '';
|
|
70
|
-
|
|
71
|
-
const finalMessage = fullMessage + formattedArgs;
|
|
72
|
-
|
|
73
|
-
if (this.emitter !== null && this.emitter !== undefined) {
|
|
74
|
-
this.emitter.emit('console', {
|
|
75
|
-
id: randomUUID(),
|
|
76
|
-
level,
|
|
77
|
-
levelName,
|
|
78
|
-
component: this.component,
|
|
79
|
-
message: finalMessage,
|
|
80
|
-
args,
|
|
81
|
-
timestamp: new Date().toISOString()
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { ILogger, LogLevel } from '@apiquest/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Logger for fracture runtime and host integrations.
|
|
7
|
+
*/
|
|
8
|
+
export class Logger implements ILogger {
|
|
9
|
+
private level: LogLevel;
|
|
10
|
+
private component: string;
|
|
11
|
+
private emitter?: EventEmitter;
|
|
12
|
+
|
|
13
|
+
constructor(component: string, level: LogLevel = LogLevel.INFO, emitter?: EventEmitter) {
|
|
14
|
+
this.component = component;
|
|
15
|
+
this.level = level;
|
|
16
|
+
this.emitter = emitter;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
createLogger(component: string): Logger {
|
|
20
|
+
return new Logger(component, this.level, this.emitter);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setLevel(level: LogLevel): void {
|
|
24
|
+
this.level = level;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
error(message: string, ...args: unknown[]): void {
|
|
28
|
+
this.log(LogLevel.ERROR, message, ...args);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
warn(message: string, ...args: unknown[]): void {
|
|
32
|
+
this.log(LogLevel.WARN, message, ...args);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
info(message: string, ...args: unknown[]): void {
|
|
36
|
+
this.log(LogLevel.INFO, message, ...args);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
debug(message: string, ...args: unknown[]): void {
|
|
40
|
+
this.log(LogLevel.DEBUG, message, ...args);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
trace(message: string, ...args: unknown[]): void {
|
|
44
|
+
this.log(LogLevel.TRACE, message, ...args);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private log(level: LogLevel, message: string, ...args: unknown[]): void {
|
|
48
|
+
if (level > this.level) return;
|
|
49
|
+
|
|
50
|
+
const levelNames = ['error', 'warn', 'info', 'debug', 'trace'];
|
|
51
|
+
const levelName = levelNames[level];
|
|
52
|
+
const prefix = `[${this.component}]`;
|
|
53
|
+
const fullMessage = `${prefix} ${message}`;
|
|
54
|
+
|
|
55
|
+
const formattedArgs = args.length > 0
|
|
56
|
+
? ' ' + args.map(a => {
|
|
57
|
+
if (a instanceof Error) {
|
|
58
|
+
return a.message;
|
|
59
|
+
}
|
|
60
|
+
if (typeof a === 'object' && a !== null) {
|
|
61
|
+
try {
|
|
62
|
+
return JSON.stringify(a);
|
|
63
|
+
} catch {
|
|
64
|
+
return String(a);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return String(a);
|
|
68
|
+
}).join(' ')
|
|
69
|
+
: '';
|
|
70
|
+
|
|
71
|
+
const finalMessage = fullMessage + formattedArgs;
|
|
72
|
+
|
|
73
|
+
if (this.emitter !== null && this.emitter !== undefined) {
|
|
74
|
+
this.emitter.emit('console', {
|
|
75
|
+
id: randomUUID(),
|
|
76
|
+
level,
|
|
77
|
+
levelName,
|
|
78
|
+
component: this.component,
|
|
79
|
+
message: finalMessage,
|
|
80
|
+
args,
|
|
81
|
+
timestamp: new Date().toISOString()
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/PluginLoader.ts
CHANGED
|
@@ -1,126 +1,126 @@
|
|
|
1
|
-
import type { IProtocolPlugin, IAuthPlugin, IValueProviderPlugin } from '@apiquest/types';
|
|
2
|
-
import { Logger } from './Logger.js';
|
|
3
|
-
import { PluginManager } from './PluginManager.js';
|
|
4
|
-
import type { ResolvedPlugin } from './PluginResolver.js';
|
|
5
|
-
import type { PluginRequirements } from './CollectionAnalyzer.js';
|
|
6
|
-
import { isNullOrEmpty } from './utils.js';
|
|
7
|
-
|
|
8
|
-
export class PluginLoader {
|
|
9
|
-
private logger: Logger;
|
|
10
|
-
private pluginManager: PluginManager;
|
|
11
|
-
private loadedPlugins: Set<string> = new Set();
|
|
12
|
-
|
|
13
|
-
constructor(pluginManager: PluginManager, baseLogger?: Logger) {
|
|
14
|
-
this.pluginManager = pluginManager;
|
|
15
|
-
this.logger = baseLogger?.createLogger('PluginLoader') ?? new Logger('PluginLoader');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Load only plugins needed by the collection
|
|
20
|
-
*/
|
|
21
|
-
async loadRequiredPlugins(
|
|
22
|
-
resolved: ResolvedPlugin[],
|
|
23
|
-
requirements: PluginRequirements
|
|
24
|
-
): Promise<void> {
|
|
25
|
-
const needed = this.filterNeededPlugins(resolved, requirements);
|
|
26
|
-
|
|
27
|
-
this.logger.info(`Loading ${needed.length} required plugins (${resolved.length} available)`);
|
|
28
|
-
|
|
29
|
-
const loadPromises = needed.map(plugin =>
|
|
30
|
-
this.loadPlugin(plugin).catch(err => {
|
|
31
|
-
this.logger.error(`Failed to load ${plugin.name}:`, err);
|
|
32
|
-
throw err;
|
|
33
|
-
})
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
await Promise.all(loadPromises);
|
|
37
|
-
this.logger.debug('Required plugins loaded');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Filter resolved plugins to only those needed by collection
|
|
42
|
-
*/
|
|
43
|
-
private filterNeededPlugins(
|
|
44
|
-
resolved: ResolvedPlugin[],
|
|
45
|
-
requirements: PluginRequirements
|
|
46
|
-
): ResolvedPlugin[] {
|
|
47
|
-
const needed: ResolvedPlugin[] = [];
|
|
48
|
-
|
|
49
|
-
for (const plugin of resolved) {
|
|
50
|
-
let isNeeded = false;
|
|
51
|
-
|
|
52
|
-
if (plugin.type === 'protocol') {
|
|
53
|
-
// Check if collection uses this protocol
|
|
54
|
-
if (plugin.protocols?.some(p => requirements.protocols.has(p)) === true) {
|
|
55
|
-
isNeeded = true;
|
|
56
|
-
}
|
|
57
|
-
} else if (plugin.type === 'auth') {
|
|
58
|
-
// Check if collection uses any of these auth types
|
|
59
|
-
if (plugin.authTypes?.some(a => requirements.authTypes.has(a)) === true) {
|
|
60
|
-
isNeeded = true;
|
|
61
|
-
}
|
|
62
|
-
} else if (plugin.type === 'value') {
|
|
63
|
-
// Check if collection uses this value provider
|
|
64
|
-
const provider = plugin.provider;
|
|
65
|
-
if (provider !== null && provider !== undefined && provider !== '' && requirements.valueProviders.has(provider)) {
|
|
66
|
-
isNeeded = true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (isNeeded) {
|
|
71
|
-
this.logger.debug(`Plugin needed: ${plugin.name} v${plugin.version} (${plugin.type})`);
|
|
72
|
-
needed.push(plugin);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return needed;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Dynamically import and register a single plugin
|
|
81
|
-
*/
|
|
82
|
-
private async loadPlugin(plugin: ResolvedPlugin): Promise<void> {
|
|
83
|
-
const { pathToFileURL } = await import('url');
|
|
84
|
-
|
|
85
|
-
// Skip if already loaded
|
|
86
|
-
if (this.loadedPlugins.has(plugin.name)) {
|
|
87
|
-
this.logger.debug(`Already loaded: ${plugin.name}`);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
this.logger.debug(`Loading ${plugin.name} v${plugin.version} from ${plugin.path}`);
|
|
92
|
-
|
|
93
|
-
// Mark as loaded
|
|
94
|
-
this.loadedPlugins.add(plugin.name);
|
|
95
|
-
|
|
96
|
-
// Convert to file:// URL for Windows compatibility
|
|
97
|
-
const moduleUrl = pathToFileURL(plugin.entryPoint).href;
|
|
98
|
-
const pluginModule = await import(moduleUrl) as Record<string, unknown>;
|
|
99
|
-
|
|
100
|
-
// Handle different export patterns
|
|
101
|
-
const defaultExport = pluginModule.default;
|
|
102
|
-
const namedExport = pluginModule[Object.keys(pluginModule)[0]];
|
|
103
|
-
const exported = defaultExport ?? namedExport;
|
|
104
|
-
|
|
105
|
-
if (exported === null || exported === undefined) {
|
|
106
|
-
throw new Error(`Plugin ${plugin.name} has no exports`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Register based on plugin type
|
|
110
|
-
if (plugin.type === 'protocol') {
|
|
111
|
-
this.pluginManager.registerPlugin(exported as IProtocolPlugin);
|
|
112
|
-
this.logger.debug(`Registered protocol plugin: ${plugin.protocols?.join(', ') ?? ''}`);
|
|
113
|
-
} else if (plugin.type === 'auth') {
|
|
114
|
-
// Auth plugins might export array or single
|
|
115
|
-
const authArray = Array.isArray(exported) ? (exported as IAuthPlugin[]) : [exported as IAuthPlugin];
|
|
116
|
-
|
|
117
|
-
for (const authPlugin of authArray) {
|
|
118
|
-
this.pluginManager.registerAuthPlugin(authPlugin);
|
|
119
|
-
this.logger.debug(`Registered auth plugin: ${authPlugin.authTypes.join(', ')}`);
|
|
120
|
-
}
|
|
121
|
-
} else if (plugin.type === 'value') {
|
|
122
|
-
this.pluginManager.registerVariableProvider(exported as IValueProviderPlugin);
|
|
123
|
-
this.logger.debug(`Registered value provider: ${plugin.provider ?? ''}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
1
|
+
import type { IProtocolPlugin, IAuthPlugin, IValueProviderPlugin } from '@apiquest/types';
|
|
2
|
+
import { Logger } from './Logger.js';
|
|
3
|
+
import { PluginManager } from './PluginManager.js';
|
|
4
|
+
import type { ResolvedPlugin } from './PluginResolver.js';
|
|
5
|
+
import type { PluginRequirements } from './CollectionAnalyzer.js';
|
|
6
|
+
import { isNullOrEmpty } from './utils.js';
|
|
7
|
+
|
|
8
|
+
export class PluginLoader {
|
|
9
|
+
private logger: Logger;
|
|
10
|
+
private pluginManager: PluginManager;
|
|
11
|
+
private loadedPlugins: Set<string> = new Set();
|
|
12
|
+
|
|
13
|
+
constructor(pluginManager: PluginManager, baseLogger?: Logger) {
|
|
14
|
+
this.pluginManager = pluginManager;
|
|
15
|
+
this.logger = baseLogger?.createLogger('PluginLoader') ?? new Logger('PluginLoader');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Load only plugins needed by the collection
|
|
20
|
+
*/
|
|
21
|
+
async loadRequiredPlugins(
|
|
22
|
+
resolved: ResolvedPlugin[],
|
|
23
|
+
requirements: PluginRequirements
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
const needed = this.filterNeededPlugins(resolved, requirements);
|
|
26
|
+
|
|
27
|
+
this.logger.info(`Loading ${needed.length} required plugins (${resolved.length} available)`);
|
|
28
|
+
|
|
29
|
+
const loadPromises = needed.map(plugin =>
|
|
30
|
+
this.loadPlugin(plugin).catch(err => {
|
|
31
|
+
this.logger.error(`Failed to load ${plugin.name}:`, err);
|
|
32
|
+
throw err;
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
await Promise.all(loadPromises);
|
|
37
|
+
this.logger.debug('Required plugins loaded');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Filter resolved plugins to only those needed by collection
|
|
42
|
+
*/
|
|
43
|
+
private filterNeededPlugins(
|
|
44
|
+
resolved: ResolvedPlugin[],
|
|
45
|
+
requirements: PluginRequirements
|
|
46
|
+
): ResolvedPlugin[] {
|
|
47
|
+
const needed: ResolvedPlugin[] = [];
|
|
48
|
+
|
|
49
|
+
for (const plugin of resolved) {
|
|
50
|
+
let isNeeded = false;
|
|
51
|
+
|
|
52
|
+
if (plugin.type === 'protocol') {
|
|
53
|
+
// Check if collection uses this protocol
|
|
54
|
+
if (plugin.protocols?.some(p => requirements.protocols.has(p)) === true) {
|
|
55
|
+
isNeeded = true;
|
|
56
|
+
}
|
|
57
|
+
} else if (plugin.type === 'auth') {
|
|
58
|
+
// Check if collection uses any of these auth types
|
|
59
|
+
if (plugin.authTypes?.some(a => requirements.authTypes.has(a)) === true) {
|
|
60
|
+
isNeeded = true;
|
|
61
|
+
}
|
|
62
|
+
} else if (plugin.type === 'value') {
|
|
63
|
+
// Check if collection uses this value provider
|
|
64
|
+
const provider = plugin.provider;
|
|
65
|
+
if (provider !== null && provider !== undefined && provider !== '' && requirements.valueProviders.has(provider)) {
|
|
66
|
+
isNeeded = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isNeeded) {
|
|
71
|
+
this.logger.debug(`Plugin needed: ${plugin.name} v${plugin.version} (${plugin.type})`);
|
|
72
|
+
needed.push(plugin);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return needed;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Dynamically import and register a single plugin
|
|
81
|
+
*/
|
|
82
|
+
private async loadPlugin(plugin: ResolvedPlugin): Promise<void> {
|
|
83
|
+
const { pathToFileURL } = await import('url');
|
|
84
|
+
|
|
85
|
+
// Skip if already loaded
|
|
86
|
+
if (this.loadedPlugins.has(plugin.name)) {
|
|
87
|
+
this.logger.debug(`Already loaded: ${plugin.name}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.logger.debug(`Loading ${plugin.name} v${plugin.version} from ${plugin.path}`);
|
|
92
|
+
|
|
93
|
+
// Mark as loaded
|
|
94
|
+
this.loadedPlugins.add(plugin.name);
|
|
95
|
+
|
|
96
|
+
// Convert to file:// URL for Windows compatibility
|
|
97
|
+
const moduleUrl = pathToFileURL(plugin.entryPoint).href;
|
|
98
|
+
const pluginModule = await import(moduleUrl) as Record<string, unknown>;
|
|
99
|
+
|
|
100
|
+
// Handle different export patterns
|
|
101
|
+
const defaultExport = pluginModule.default;
|
|
102
|
+
const namedExport = pluginModule[Object.keys(pluginModule)[0]];
|
|
103
|
+
const exported = defaultExport ?? namedExport;
|
|
104
|
+
|
|
105
|
+
if (exported === null || exported === undefined) {
|
|
106
|
+
throw new Error(`Plugin ${plugin.name} has no exports`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Register based on plugin type
|
|
110
|
+
if (plugin.type === 'protocol') {
|
|
111
|
+
this.pluginManager.registerPlugin(exported as IProtocolPlugin);
|
|
112
|
+
this.logger.debug(`Registered protocol plugin: ${plugin.protocols?.join(', ') ?? ''}`);
|
|
113
|
+
} else if (plugin.type === 'auth') {
|
|
114
|
+
// Auth plugins might export array or single
|
|
115
|
+
const authArray = Array.isArray(exported) ? (exported as IAuthPlugin[]) : [exported as IAuthPlugin];
|
|
116
|
+
|
|
117
|
+
for (const authPlugin of authArray) {
|
|
118
|
+
this.pluginManager.registerAuthPlugin(authPlugin);
|
|
119
|
+
this.logger.debug(`Registered auth plugin: ${authPlugin.authTypes.join(', ')}`);
|
|
120
|
+
}
|
|
121
|
+
} else if (plugin.type === 'value') {
|
|
122
|
+
this.pluginManager.registerVariableProvider(exported as IValueProviderPlugin);
|
|
123
|
+
this.logger.debug(`Registered value provider: ${plugin.provider ?? ''}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|