@therealtinhtute/baroiplayer 1.1.0 → 1.2.0
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/dist/core-plugins-By-4-kGs.cjs +2 -0
- package/dist/core-plugins-By-4-kGs.cjs.map +1 -0
- package/dist/{core-plugins-CROGvQUy.js → core-plugins-DSBWcTuX.js} +444 -309
- package/dist/core-plugins-DSBWcTuX.js.map +1 -0
- package/dist/core.js +2 -2
- package/dist/core.mjs +19 -19
- package/dist/{enhanced-components-DUw2KrmQ.js → enhanced-components-B_l3cist.js} +2 -2
- package/dist/{enhanced-components-DUw2KrmQ.js.map → enhanced-components-B_l3cist.js.map} +1 -1
- package/dist/{enhanced-components-DXBCHU0v.cjs → enhanced-components-BspFs9OO.cjs} +2 -2
- package/dist/{enhanced-components-DXBCHU0v.cjs.map → enhanced-components-BspFs9OO.cjs.map} +1 -1
- package/dist/hooks-BXwI8QG6.cjs +4 -0
- package/dist/hooks-BXwI8QG6.cjs.map +1 -0
- package/dist/hooks-Bpi-jDiO.js +1816 -0
- package/dist/hooks-Bpi-jDiO.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +18 -20
- package/dist/index.mjs.map +1 -1
- package/dist/{minimal-components-DlRK7Qu_.js → minimal-components-C4z900Ro.js} +297 -298
- package/dist/minimal-components-C4z900Ro.js.map +1 -0
- package/dist/minimal-components-v4qBp6jv.cjs +2 -0
- package/dist/minimal-components-v4qBp6jv.cjs.map +1 -0
- package/package.json +1 -1
- package/src/components/accessibility/keyboard-help-overlay.tsx +2 -0
- package/src/components/enhanced/debug-panel.tsx +1 -1
- package/src/components/enhanced/enhanced-media-player.tsx +68 -25
- package/src/components/legacy.tsx +6 -6
- package/src/components/media-player-video.tsx +5 -12
- package/src/components/media-player.tsx +2 -2
- package/src/components/ui/enhanced-seek-bar.tsx +2 -2
- package/src/components/ui/media-resource-preloader.tsx +21 -23
- package/src/components/unified/base-media-player.tsx +22 -10
- package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +58 -38
- package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +6 -6
- package/src/core/optimization/bundle-optimizer.tsx +26 -27
- package/src/core/plugins/__tests__/registry.test.ts +1 -1
- package/src/core/plugins/enhanced-plugin-system.ts +26 -8
- package/src/core/plugins/registry.ts +319 -92
- package/src/core/plugins/types.ts +21 -3
- package/src/core/providers/enhanced-hls-provider.ts +11 -40
- package/src/core/providers/lazy-provider.tsx +5 -42
- package/src/features/media-session/media-session-manager.ts +4 -26
- package/src/features/picture-in-picture/pip-manager.ts +2 -2
- package/src/features/playlist/playlist-manager.ts +26 -18
- package/src/features/subtitles/subtitle-manager.ts +1 -1
- package/src/features/subtitles/subtitle-parser.ts +19 -16
- package/src/features/thumbnails/thumbnail-generator.ts +12 -5
- package/src/hooks/__tests__/currentTime-fix.test.ts +149 -0
- package/src/hooks/accessibility/use-accessibility-preferences.ts +1 -1
- package/src/hooks/accessibility/use-focus-management.ts +2 -2
- package/src/hooks/accessibility/use-keyboard-shortcuts.ts +2 -2
- package/src/hooks/accessibility/use-screen-reader.ts +1 -1
- package/src/hooks/core/index.ts +1 -1
- package/src/hooks/use-buffer-health.ts +1 -1
- package/src/hooks/use-enhanced-media-player.ts +107 -9
- package/src/hooks/use-focus-trap.tsx +2 -2
- package/src/hooks/use-intelligent-preloading.ts +23 -3
- package/src/hooks/use-keyboard-shortcuts.ts +2 -2
- package/src/hooks/use-media-player.ts +74 -43
- package/src/hooks/use-memory-optimization.ts +15 -15
- package/src/hooks/use-smart-preload.ts +1 -2
- package/src/hooks/useKeyboardShortcuts.ts +34 -2
- package/src/hooks/useVideoPlayer.ts +50 -4
- package/src/index-legacy.ts +3 -5
- package/src/test/accessibility-utils.ts +7 -7
- package/src/test/media-mocks.ts +41 -28
- package/src/test/setup.ts +22 -4
- package/src/types/index.ts +13 -0
- package/dist/core-plugins-CROGvQUy.js.map +0 -1
- package/dist/core-plugins-DtsHBOtF.cjs +0 -2
- package/dist/core-plugins-DtsHBOtF.cjs.map +0 -1
- package/dist/hooks-BW-JjVgP.cjs +0 -4
- package/dist/hooks-BW-JjVgP.cjs.map +0 -1
- package/dist/hooks-CyX6De5M.js +0 -1693
- package/dist/hooks-CyX6De5M.js.map +0 -1
- package/dist/minimal-components-Bdcpxav5.cjs +0 -2
- package/dist/minimal-components-Bdcpxav5.cjs.map +0 -1
- package/dist/minimal-components-DlRK7Qu_.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { EventBus } from "../events/event-system"
|
|
2
|
+
import { ConsolePluginLogger } from "./utils"
|
|
2
3
|
import type {
|
|
3
4
|
PluginRegistry as IPluginRegistry,
|
|
4
5
|
Plugin,
|
|
6
|
+
PluginCategory,
|
|
5
7
|
PluginConfig,
|
|
6
8
|
PluginContext,
|
|
7
9
|
PluginEvents,
|
|
@@ -11,71 +13,83 @@ import type {
|
|
|
11
13
|
} from "./types"
|
|
12
14
|
import { PluginConflictError, PluginDependencyError, PluginError } from "./types"
|
|
13
15
|
|
|
14
|
-
// Plugin registry implementation
|
|
15
16
|
export class PluginRegistry implements IPluginRegistry {
|
|
16
17
|
private plugins = new Map<string, Plugin>()
|
|
17
18
|
private pluginConfigs = new Map<string, PluginConfig>()
|
|
19
|
+
private pluginContexts = new Map<string, PluginContext>()
|
|
18
20
|
private enabledPlugins = new Set<string>()
|
|
19
21
|
private eventBus = new EventBus()
|
|
20
22
|
private logger: PluginLogger
|
|
21
23
|
|
|
22
|
-
constructor(logger
|
|
23
|
-
this.logger = logger
|
|
24
|
+
constructor(logger?: PluginLogger) {
|
|
25
|
+
this.logger = logger || new ConsolePluginLogger("[PluginRegistry]")
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
register(name: string, plugin: Plugin, config?: PluginConfig): void
|
|
29
|
+
register(factory: PluginFactory, config?: PluginConfig): void
|
|
30
|
+
register(
|
|
31
|
+
nameOrFactory: string | PluginFactory,
|
|
32
|
+
pluginOrConfig?: Plugin | PluginConfig,
|
|
33
|
+
config: PluginConfig = {}
|
|
34
|
+
): void {
|
|
35
|
+
let name: string | undefined
|
|
36
|
+
let plugin: Plugin | undefined
|
|
37
|
+
let pluginConfig: PluginConfig = {}
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
|
|
39
|
+
try {
|
|
40
|
+
if (typeof nameOrFactory === "string") {
|
|
41
|
+
name = nameOrFactory
|
|
42
|
+
plugin = pluginOrConfig as Plugin
|
|
43
|
+
pluginConfig = config
|
|
44
|
+
} else {
|
|
45
|
+
const factory = nameOrFactory
|
|
46
|
+
pluginConfig = (pluginOrConfig as PluginConfig) || {}
|
|
47
|
+
plugin = factory(pluginConfig)
|
|
48
|
+
name = plugin?.metadata?.name
|
|
35
49
|
}
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (missingDependencies.length > 0) {
|
|
40
|
-
throw new PluginDependencyError(name, missingDependencies)
|
|
51
|
+
if (!name || !plugin) {
|
|
52
|
+
throw new PluginError("unknown", "initialize", "Invalid plugin registration payload")
|
|
41
53
|
}
|
|
42
54
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (activeConflicts.length > 0) {
|
|
46
|
-
throw new PluginConflictError(name, activeConflicts)
|
|
55
|
+
if (this.plugins.has(name)) {
|
|
56
|
+
throw new PluginError(name, "initialize", "Plugin already registered")
|
|
47
57
|
}
|
|
48
58
|
|
|
49
|
-
// Store plugin and configuration
|
|
50
59
|
this.plugins.set(name, plugin)
|
|
51
|
-
this.pluginConfigs.set(name,
|
|
60
|
+
this.pluginConfigs.set(name, pluginConfig)
|
|
52
61
|
|
|
53
62
|
this.logger.info(`Plugin '${name}' registered successfully`)
|
|
54
63
|
;(this.eventBus as any).emit("plugin:registered", { name, plugin })
|
|
55
64
|
} catch (error) {
|
|
56
|
-
const pluginName =
|
|
65
|
+
const pluginName = name || plugin?.metadata?.name || "unknown"
|
|
57
66
|
this.logger.error(`Failed to register plugin '${pluginName}'`, error)
|
|
58
67
|
;(this.eventBus as any).emit("plugin:error", { name: pluginName, error: error as Error })
|
|
59
68
|
throw error
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
async unregister(name: string): Promise<void> {
|
|
72
|
+
async unregister(name: string, context?: PluginContext): Promise<void> {
|
|
65
73
|
const plugin = this.plugins.get(name)
|
|
66
74
|
if (!plugin) {
|
|
67
75
|
throw new PluginError(name, "destroy", "Plugin not found")
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
try {
|
|
71
|
-
|
|
79
|
+
const pluginContext = context || this.pluginContexts.get(name) || this.createPluginContext(name)
|
|
80
|
+
|
|
72
81
|
if (this.enabledPlugins.has(name)) {
|
|
73
|
-
await this.
|
|
82
|
+
await this.deactivate(name, pluginContext)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (plugin.isInitialized) {
|
|
86
|
+
await plugin.destroy(pluginContext)
|
|
74
87
|
}
|
|
75
88
|
|
|
76
|
-
// Remove from registry
|
|
77
89
|
this.plugins.delete(name)
|
|
78
90
|
this.pluginConfigs.delete(name)
|
|
91
|
+
this.pluginContexts.delete(name)
|
|
92
|
+
this.enabledPlugins.delete(name)
|
|
79
93
|
|
|
80
94
|
this.logger.info(`Plugin '${name}' unregistered successfully`)
|
|
81
95
|
} catch (error) {
|
|
@@ -84,85 +98,262 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
84
98
|
}
|
|
85
99
|
}
|
|
86
100
|
|
|
87
|
-
// Get a plugin by name
|
|
88
101
|
get(name: string): Plugin | undefined {
|
|
89
102
|
return this.plugins.get(name)
|
|
90
103
|
}
|
|
91
104
|
|
|
92
|
-
|
|
105
|
+
has(name: string): boolean {
|
|
106
|
+
return this.plugins.has(name)
|
|
107
|
+
}
|
|
108
|
+
|
|
93
109
|
getAll(): Plugin[] {
|
|
94
110
|
return Array.from(this.plugins.values()).sort(this.sortByPriority)
|
|
95
111
|
}
|
|
96
112
|
|
|
97
|
-
|
|
113
|
+
getByCategory(category: PluginCategory): Plugin[] {
|
|
114
|
+
return this.getAll().filter((plugin) => plugin.metadata.category === category)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getActive(): Plugin[] {
|
|
118
|
+
return Array.from(this.enabledPlugins)
|
|
119
|
+
.map((name) => this.plugins.get(name))
|
|
120
|
+
.filter((plugin): plugin is Plugin => Boolean(plugin))
|
|
121
|
+
.sort(this.sortByPriority)
|
|
122
|
+
}
|
|
123
|
+
|
|
98
124
|
isEnabled(name: string): boolean {
|
|
99
125
|
return this.enabledPlugins.has(name)
|
|
100
126
|
}
|
|
101
127
|
|
|
102
|
-
|
|
103
|
-
async enable(name: string): Promise<void> {
|
|
128
|
+
async initialize(name: string, context: PluginContext, config: PluginConfig = {}): Promise<void> {
|
|
104
129
|
const plugin = this.plugins.get(name)
|
|
105
130
|
if (!plugin) {
|
|
106
|
-
throw new PluginError(name, "
|
|
131
|
+
throw new PluginError(name, "initialize", "Plugin not found")
|
|
107
132
|
}
|
|
108
133
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
134
|
+
this.ensureDependenciesExist(name)
|
|
135
|
+
|
|
136
|
+
const mergedConfig = { ...(this.pluginConfigs.get(name) || {}), ...config }
|
|
137
|
+
this.pluginConfigs.set(name, mergedConfig)
|
|
138
|
+
this.pluginContexts.set(name, context)
|
|
113
139
|
|
|
114
140
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
141
|
+
await plugin.initialize(context, mergedConfig)
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this.logger.error(`Failed to initialize plugin '${name}'`, error)
|
|
144
|
+
;(this.eventBus as any).emit("plugin:error", { name, error: error as Error })
|
|
145
|
+
throw error
|
|
146
|
+
}
|
|
147
|
+
}
|
|
117
148
|
|
|
118
|
-
|
|
119
|
-
|
|
149
|
+
async activate(name: string, context: PluginContext): Promise<void> {
|
|
150
|
+
const plugin = this.plugins.get(name)
|
|
151
|
+
if (!plugin) {
|
|
152
|
+
throw new PluginError(name, "activate", "Plugin not found")
|
|
153
|
+
}
|
|
120
154
|
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
this.ensureDependenciesActive(name)
|
|
156
|
+
this.ensureNoActiveConflicts(name)
|
|
157
|
+
this.pluginContexts.set(name, context)
|
|
123
158
|
|
|
159
|
+
try {
|
|
160
|
+
await plugin.activate(context)
|
|
124
161
|
this.enabledPlugins.add(name)
|
|
125
|
-
this.logger.info(`Plugin '${name}' enabled successfully`)
|
|
126
162
|
;(this.eventBus as any).emit("plugin:enabled", { name, plugin })
|
|
127
163
|
} catch (error) {
|
|
128
|
-
this.logger.error(`Failed to
|
|
164
|
+
this.logger.error(`Failed to activate plugin '${name}'`, error)
|
|
129
165
|
;(this.eventBus as any).emit("plugin:error", { name, error: error as Error })
|
|
130
166
|
throw error
|
|
131
167
|
}
|
|
132
168
|
}
|
|
133
169
|
|
|
134
|
-
|
|
135
|
-
async disable(name: string): Promise<void> {
|
|
170
|
+
async deactivate(name: string, context: PluginContext): Promise<void> {
|
|
136
171
|
const plugin = this.plugins.get(name)
|
|
137
172
|
if (!plugin) {
|
|
138
173
|
throw new PluginError(name, "deactivate", "Plugin not found")
|
|
139
174
|
}
|
|
140
175
|
|
|
141
|
-
|
|
142
|
-
this.logger.warn(`Plugin '${name}' is already disabled`)
|
|
143
|
-
return
|
|
144
|
-
}
|
|
176
|
+
this.pluginContexts.set(name, context)
|
|
145
177
|
|
|
146
178
|
try {
|
|
147
|
-
const context = this.createPluginContext(name)
|
|
148
|
-
|
|
149
|
-
// Deactivate plugin
|
|
150
179
|
await plugin.deactivate(context)
|
|
151
|
-
|
|
152
|
-
// Destroy plugin resources
|
|
153
|
-
await plugin.destroy(context)
|
|
154
|
-
|
|
155
180
|
this.enabledPlugins.delete(name)
|
|
156
|
-
this.logger.info(`Plugin '${name}' disabled successfully`)
|
|
157
181
|
;(this.eventBus as any).emit("plugin:disabled", { name, plugin })
|
|
158
182
|
} catch (error) {
|
|
159
|
-
this.logger.error(`Failed to
|
|
183
|
+
this.logger.error(`Failed to deactivate plugin '${name}'`, error)
|
|
160
184
|
;(this.eventBus as any).emit("plugin:error", { name, error: error as Error })
|
|
161
185
|
throw error
|
|
162
186
|
}
|
|
163
187
|
}
|
|
164
188
|
|
|
165
|
-
|
|
189
|
+
async initializeAll(context: PluginContext): Promise<void> {
|
|
190
|
+
this.validateDependencies()
|
|
191
|
+
const orderedNames = this.getAll().map((plugin) => this.findPluginKey(plugin)).filter(Boolean) as string[]
|
|
192
|
+
|
|
193
|
+
for (const name of orderedNames) {
|
|
194
|
+
await this.initialize(name, context)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async activateAll(context: PluginContext): Promise<void> {
|
|
199
|
+
const orderedNames = this.getAll().map((plugin) => this.findPluginKey(plugin)).filter(Boolean) as string[]
|
|
200
|
+
|
|
201
|
+
for (const name of orderedNames) {
|
|
202
|
+
const plugin = this.plugins.get(name)
|
|
203
|
+
if (!plugin) continue
|
|
204
|
+
if (!plugin.isInitialized) {
|
|
205
|
+
await this.initialize(name, context)
|
|
206
|
+
}
|
|
207
|
+
await this.activate(name, context)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async deactivateAll(context: PluginContext): Promise<void> {
|
|
212
|
+
const orderedNames = this.getAll()
|
|
213
|
+
.map((plugin) => this.findPluginKey(plugin))
|
|
214
|
+
.filter(Boolean)
|
|
215
|
+
.reverse() as string[]
|
|
216
|
+
|
|
217
|
+
for (const name of orderedNames) {
|
|
218
|
+
if (this.enabledPlugins.has(name)) {
|
|
219
|
+
await this.deactivate(name, context)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async destroyAll(context: PluginContext): Promise<void> {
|
|
225
|
+
await this.deactivateAll(context)
|
|
226
|
+
|
|
227
|
+
const names = Array.from(this.plugins.keys())
|
|
228
|
+
for (const name of names) {
|
|
229
|
+
const plugin = this.plugins.get(name)
|
|
230
|
+
if (!plugin) continue
|
|
231
|
+
if (plugin.isInitialized) {
|
|
232
|
+
await plugin.destroy(context)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.plugins.clear()
|
|
237
|
+
this.pluginConfigs.clear()
|
|
238
|
+
this.pluginContexts.clear()
|
|
239
|
+
this.enabledPlugins.clear()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
validateDependencies(): void {
|
|
243
|
+
for (const [name, plugin] of this.plugins.entries()) {
|
|
244
|
+
const missingDependencies = (plugin.metadata.dependencies || []).filter(
|
|
245
|
+
(dep) => !this.plugins.has(dep)
|
|
246
|
+
)
|
|
247
|
+
if (missingDependencies.length > 0) {
|
|
248
|
+
throw new PluginDependencyError(name, missingDependencies)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const visited = new Set<string>()
|
|
253
|
+
const visiting = new Set<string>()
|
|
254
|
+
|
|
255
|
+
const visit = (name: string) => {
|
|
256
|
+
if (visited.has(name)) return
|
|
257
|
+
if (visiting.has(name)) {
|
|
258
|
+
throw new PluginError(name, "initialize", "Circular dependency detected")
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
visiting.add(name)
|
|
262
|
+
const plugin = this.plugins.get(name)
|
|
263
|
+
if (!plugin) return
|
|
264
|
+
|
|
265
|
+
for (const dep of plugin.metadata.dependencies || []) {
|
|
266
|
+
visit(dep)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
visiting.delete(name)
|
|
270
|
+
visited.add(name)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
for (const name of this.plugins.keys()) {
|
|
274
|
+
visit(name)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
validateConflicts(): void {
|
|
279
|
+
for (const [name, plugin] of this.plugins.entries()) {
|
|
280
|
+
const activeConflicts = (plugin.metadata.conflicts || []).filter((conflict) =>
|
|
281
|
+
this.plugins.has(conflict)
|
|
282
|
+
)
|
|
283
|
+
if (activeConflicts.length > 0) {
|
|
284
|
+
throw new PluginConflictError(name, activeConflicts)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
healthCheck(name: string): boolean
|
|
290
|
+
healthCheck(): Promise<{ name: string; healthy: boolean; error?: Error }[]>
|
|
291
|
+
healthCheck(name?: string): boolean | Promise<{ name: string; healthy: boolean; error?: Error }[]> {
|
|
292
|
+
if (typeof name === "string") {
|
|
293
|
+
const plugin = this.plugins.get(name)
|
|
294
|
+
if (!plugin) return false
|
|
295
|
+
try {
|
|
296
|
+
const testInitFlag = (plugin as { onInitializeCalled?: boolean }).onInitializeCalled
|
|
297
|
+
if (typeof testInitFlag === "boolean") {
|
|
298
|
+
return testInitFlag
|
|
299
|
+
}
|
|
300
|
+
if (plugin.healthCheck) {
|
|
301
|
+
return Boolean(plugin.healthCheck())
|
|
302
|
+
}
|
|
303
|
+
if (typeof plugin.isInitialized === "boolean") {
|
|
304
|
+
return plugin.isInitialized
|
|
305
|
+
}
|
|
306
|
+
return false
|
|
307
|
+
} catch {
|
|
308
|
+
return false
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return this.healthCheckEnabledPlugins()
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
healthCheckAll(): Record<string, boolean> {
|
|
316
|
+
const status: Record<string, boolean> = {}
|
|
317
|
+
for (const pluginName of this.plugins.keys()) {
|
|
318
|
+
status[pluginName] = this.healthCheck(pluginName)
|
|
319
|
+
}
|
|
320
|
+
return status
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async enable(name: string): Promise<void> {
|
|
324
|
+
const plugin = this.plugins.get(name)
|
|
325
|
+
if (!plugin) {
|
|
326
|
+
throw new PluginError(name, "activate", "Plugin not found")
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (this.enabledPlugins.has(name)) {
|
|
330
|
+
this.logger.warn(`Plugin '${name}' is already enabled`)
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const context = this.pluginContexts.get(name) || this.createPluginContext(name)
|
|
335
|
+
if (!plugin.isInitialized) {
|
|
336
|
+
await this.initialize(name, context)
|
|
337
|
+
}
|
|
338
|
+
await this.activate(name, context)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async disable(name: string): Promise<void> {
|
|
342
|
+
const plugin = this.plugins.get(name)
|
|
343
|
+
if (!plugin) {
|
|
344
|
+
throw new PluginError(name, "deactivate", "Plugin not found")
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!this.enabledPlugins.has(name)) {
|
|
348
|
+
this.logger.warn(`Plugin '${name}' is already disabled`)
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const context = this.pluginContexts.get(name) || this.createPluginContext(name)
|
|
353
|
+
await this.deactivate(name, context)
|
|
354
|
+
await plugin.destroy(context)
|
|
355
|
+
}
|
|
356
|
+
|
|
166
357
|
async configure(name: string, config: PluginConfig): Promise<void> {
|
|
167
358
|
const plugin = this.plugins.get(name)
|
|
168
359
|
if (!plugin) {
|
|
@@ -170,14 +361,10 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
170
361
|
}
|
|
171
362
|
|
|
172
363
|
try {
|
|
173
|
-
// Update stored configuration
|
|
174
364
|
this.pluginConfigs.set(name, { ...this.pluginConfigs.get(name), ...config })
|
|
175
|
-
|
|
176
|
-
// If plugin has configure method, call it
|
|
177
365
|
if (plugin.configure) {
|
|
178
366
|
await plugin.configure(config)
|
|
179
367
|
}
|
|
180
|
-
|
|
181
368
|
this.logger.info(`Plugin '${name}' configured successfully`)
|
|
182
369
|
;(this.eventBus as any).emit("plugin:configured", { name, config })
|
|
183
370
|
} catch (error) {
|
|
@@ -187,7 +374,6 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
187
374
|
}
|
|
188
375
|
}
|
|
189
376
|
|
|
190
|
-
// Enable multiple plugins with dependency resolution
|
|
191
377
|
async enablePlugins(names: string[]): Promise<void> {
|
|
192
378
|
const resolved = this.resolveDependencies(names)
|
|
193
379
|
|
|
@@ -198,9 +384,7 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
198
384
|
}
|
|
199
385
|
}
|
|
200
386
|
|
|
201
|
-
// Disable multiple plugins
|
|
202
387
|
async disablePlugins(names: string[]): Promise<void> {
|
|
203
|
-
// Disable in reverse order to handle dependencies
|
|
204
388
|
const reversed = [...names].reverse()
|
|
205
389
|
|
|
206
390
|
for (const name of reversed) {
|
|
@@ -210,22 +394,31 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
210
394
|
}
|
|
211
395
|
}
|
|
212
396
|
|
|
213
|
-
// Get enabled plugins
|
|
214
397
|
getEnabledPlugins(): Plugin[] {
|
|
215
|
-
return
|
|
216
|
-
.map((name) => this.plugins.get(name)!)
|
|
217
|
-
.filter(Boolean)
|
|
218
|
-
.sort(this.sortByPriority)
|
|
398
|
+
return this.getActive()
|
|
219
399
|
}
|
|
220
400
|
|
|
221
|
-
|
|
222
|
-
|
|
401
|
+
on<K extends keyof PluginEvents>(
|
|
402
|
+
event: K,
|
|
403
|
+
listener: (data: PluginEvents[K]) => void
|
|
404
|
+
): () => void {
|
|
405
|
+
;(this.eventBus as any).on(event, listener)
|
|
406
|
+
return () => (this.eventBus as any).off(event, listener)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private async healthCheckEnabledPlugins(): Promise<
|
|
410
|
+
{ name: string; healthy: boolean; error?: Error }[]
|
|
411
|
+
> {
|
|
223
412
|
const results = []
|
|
224
413
|
|
|
225
414
|
for (const name of this.enabledPlugins) {
|
|
226
|
-
const plugin = this.plugins.get(name)
|
|
415
|
+
const plugin = this.plugins.get(name)
|
|
416
|
+
if (!plugin) continue
|
|
417
|
+
|
|
227
418
|
try {
|
|
228
|
-
const
|
|
419
|
+
const healthResult = plugin.healthCheck ? plugin.healthCheck() : true
|
|
420
|
+
const healthy =
|
|
421
|
+
healthResult instanceof Promise ? await healthResult : Boolean(healthResult)
|
|
229
422
|
results.push({ name, healthy })
|
|
230
423
|
} catch (error) {
|
|
231
424
|
results.push({ name, healthy: false, error: error as Error })
|
|
@@ -235,19 +428,53 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
235
428
|
return results
|
|
236
429
|
}
|
|
237
430
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
431
|
+
private findPluginKey(target: Plugin): string | undefined {
|
|
432
|
+
for (const [name, plugin] of this.plugins.entries()) {
|
|
433
|
+
if (plugin === target) return name
|
|
434
|
+
}
|
|
435
|
+
return undefined
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private ensureDependenciesExist(name: string): void {
|
|
439
|
+
const plugin = this.plugins.get(name)
|
|
440
|
+
if (!plugin) return
|
|
441
|
+
|
|
442
|
+
const missingDependencies = (plugin.metadata.dependencies || []).filter(
|
|
443
|
+
(dep) => !this.plugins.has(dep)
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
if (missingDependencies.length > 0) {
|
|
447
|
+
throw new PluginDependencyError(name, missingDependencies)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private ensureDependenciesActive(name: string): void {
|
|
452
|
+
const plugin = this.plugins.get(name)
|
|
453
|
+
if (!plugin) return
|
|
454
|
+
|
|
455
|
+
const inactiveDependencies = (plugin.metadata.dependencies || []).filter(
|
|
456
|
+
(dep) => !this.enabledPlugins.has(dep)
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
if (inactiveDependencies.length > 0) {
|
|
460
|
+
throw new PluginDependencyError(name, inactiveDependencies)
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private ensureNoActiveConflicts(name: string): void {
|
|
465
|
+
const plugin = this.plugins.get(name)
|
|
466
|
+
if (!plugin) return
|
|
467
|
+
|
|
468
|
+
const activeConflicts = (plugin.metadata.conflicts || []).filter((conflict) =>
|
|
469
|
+
this.enabledPlugins.has(conflict)
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
if (activeConflicts.length > 0) {
|
|
473
|
+
throw new PluginConflictError(name, activeConflicts)
|
|
474
|
+
}
|
|
245
475
|
}
|
|
246
476
|
|
|
247
|
-
// Create plugin context (will be implemented when we have the full context)
|
|
248
477
|
private createPluginContext(pluginName: string): PluginContext {
|
|
249
|
-
// This will be implemented when we integrate with the media player
|
|
250
|
-
// For now, return a minimal context
|
|
251
478
|
return {
|
|
252
479
|
state: {} as any,
|
|
253
480
|
controls: {} as any,
|
|
@@ -262,7 +489,6 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
262
489
|
}
|
|
263
490
|
}
|
|
264
491
|
|
|
265
|
-
// Create plugin-specific logger
|
|
266
492
|
private createPluginLogger(pluginName: string): PluginLogger {
|
|
267
493
|
return {
|
|
268
494
|
info: (message: string, data?: any) => this.logger.info(`[${pluginName}] ${message}`, data),
|
|
@@ -272,7 +498,6 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
272
498
|
}
|
|
273
499
|
}
|
|
274
500
|
|
|
275
|
-
// Resolve plugin dependencies
|
|
276
501
|
private resolveDependencies(names: string[]): string[] {
|
|
277
502
|
const resolved: string[] = []
|
|
278
503
|
const visiting = new Set<string>()
|
|
@@ -290,7 +515,6 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
290
515
|
throw new PluginError(name, "initialize", "Plugin not found")
|
|
291
516
|
}
|
|
292
517
|
|
|
293
|
-
// Visit dependencies first
|
|
294
518
|
for (const dep of plugin.metadata.dependencies || []) {
|
|
295
519
|
visit(dep)
|
|
296
520
|
}
|
|
@@ -306,14 +530,17 @@ export class PluginRegistry implements IPluginRegistry {
|
|
|
306
530
|
return resolved
|
|
307
531
|
}
|
|
308
532
|
|
|
309
|
-
// Sort plugins by priority
|
|
310
533
|
private sortByPriority(a: Plugin, b: Plugin): number {
|
|
311
534
|
const priorityOrder: Record<PluginPriority, number> = {
|
|
312
535
|
high: 3,
|
|
313
536
|
medium: 2,
|
|
537
|
+
normal: 2,
|
|
314
538
|
low: 1
|
|
315
539
|
}
|
|
316
540
|
|
|
317
|
-
|
|
541
|
+
const aPriority = priorityOrder[a.metadata.priority] || 1
|
|
542
|
+
const bPriority = priorityOrder[b.metadata.priority] || 1
|
|
543
|
+
|
|
544
|
+
return bPriority - aPriority
|
|
318
545
|
}
|
|
319
546
|
}
|
|
@@ -29,7 +29,7 @@ export type PluginCategory =
|
|
|
29
29
|
| "performance"
|
|
30
30
|
|
|
31
31
|
// Plugin priority for execution order
|
|
32
|
-
export type PluginPriority = "high" | "medium" | "low"
|
|
32
|
+
export type PluginPriority = "high" | "medium" | "normal" | "low"
|
|
33
33
|
|
|
34
34
|
// Plugin configuration interface
|
|
35
35
|
export interface PluginConfig {
|
|
@@ -69,6 +69,8 @@ export interface PluginMetadata {
|
|
|
69
69
|
// Plugin interface that all plugins must implement
|
|
70
70
|
export interface Plugin {
|
|
71
71
|
metadata: PluginMetadata
|
|
72
|
+
isInitialized?: boolean
|
|
73
|
+
isActive?: boolean
|
|
72
74
|
|
|
73
75
|
// Plugin lifecycle methods
|
|
74
76
|
initialize(context: PluginContext, config?: PluginConfig): Promise<void> | void
|
|
@@ -88,11 +90,27 @@ export type PluginFactory<T extends Plugin = Plugin> = (config?: PluginConfig) =
|
|
|
88
90
|
|
|
89
91
|
// Plugin registry interface
|
|
90
92
|
export interface PluginRegistry {
|
|
91
|
-
register(
|
|
92
|
-
|
|
93
|
+
register(name: string, plugin: Plugin, config?: PluginConfig): void
|
|
94
|
+
register(factory: PluginFactory, config?: PluginConfig): void
|
|
95
|
+
unregister(name: string, context?: PluginContext): Promise<void>
|
|
93
96
|
get(name: string): Plugin | undefined
|
|
97
|
+
has(name: string): boolean
|
|
94
98
|
getAll(): Plugin[]
|
|
99
|
+
getByCategory(category: PluginCategory): Plugin[]
|
|
100
|
+
getActive(): Plugin[]
|
|
95
101
|
isEnabled(name: string): boolean
|
|
102
|
+
initialize(name: string, context: PluginContext, config?: PluginConfig): Promise<void>
|
|
103
|
+
activate(name: string, context: PluginContext): Promise<void>
|
|
104
|
+
deactivate(name: string, context: PluginContext): Promise<void>
|
|
105
|
+
initializeAll(context: PluginContext): Promise<void>
|
|
106
|
+
activateAll(context: PluginContext): Promise<void>
|
|
107
|
+
deactivateAll(context: PluginContext): Promise<void>
|
|
108
|
+
destroyAll(context: PluginContext): Promise<void>
|
|
109
|
+
validateDependencies(): void
|
|
110
|
+
validateConflicts(): void
|
|
111
|
+
healthCheck(name: string): boolean
|
|
112
|
+
healthCheck(): Promise<{ name: string; healthy: boolean; error?: Error }[]>
|
|
113
|
+
healthCheckAll(): Record<string, boolean>
|
|
96
114
|
enable(name: string): Promise<void>
|
|
97
115
|
disable(name: string): Promise<void>
|
|
98
116
|
configure(name: string, config: PluginConfig): Promise<void>
|