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