Dynamic plugin framework for Bun based on Javascript Modules and import() function
npm install @flowscripter/dynamic-plugin-framework




> Dynamic plugin framework for Bun based on Javascript Modules and import()
> function
[//]: # (TODO: Remove this when plugin indexing and discovery available.)
STILL IN DEVELOPMENT
This project provides a framework for defining plugins which can be dynamically
discovered and imported into a running process.
#### Key Features
- Universal support for both Bun and browser runtimes
- Dynamic plugin import using
Javascript dynamic import
- ES2015 module based
- Written in Typescript
#### Key Concepts
The framework's key concepts are borrowed from the Eclipse Project's extension
framework. The key concepts are:
- A Plugin provides one or more Extension implementations.
- Each Extension implementation declares an ExtensionPoint identifier and an
ExtensionFactory via an ExtensionDescriptor.
- A HostApplication instantiates a PluginManager.
- The HostApplication can register one or more ExtensionPoint identifiers
that the PluginManager should be aware of.
- The PluginManager scans one or more PluginRepository implementations to
find and register Plugin objects for any ExtensionPoint identifiers it is
aware of.
- The HostApplication uses the PluginManager to query for and select an
Extension for a desired ExtensionPoint identifier.
- The PluginManager uses the associated ExtensionFactory to instantiate the
selected Extension.
The following high level class diagram illustrates these relationships:
``mermaid
classDiagram
direction LR
class HostApplication {
}
class ExtensionPoint1 {
<
EXTENSION_POINT_1
}
class Plugin {
<
}
class PluginManager {
<
}
class PluginRepository {
<
}
class ExtensionDescriptor {
<
}
class ExtensionFactory {
<
}
class Plugin1 {
}
class Extension1Descriptor {
}
class Extension1Factory {
}
class Extension1 {
}
PluginRepository --> "0..*" Plugin
PluginManager --> "1..*" PluginRepository: scans
Plugin --> "1..*" ExtensionDescriptor
ExtensionDescriptor --> ExtensionFactory
PluginManager --> "0..*" Plugin : registers
PluginManager ..> ExtensionFactory : invokes
Extension1Factory ..|> ExtensionFactory
Extension1Descriptor ..|> ExtensionDescriptor
Plugin1 ..|> Plugin
Extension1 ..|> ExtensionPoint1
Plugin1 --> Extension1Descriptor
Extension1Descriptor --> Extension1Factory
Extension1Factory ..> Extension1 : creates
Extension1Descriptor ..> ExtensionPoint1: declares
HostApplication --> PluginManager
HostApplication ..> ExtensionPoint1: defines
`
The following sequence diagram illustrates the key steps for a HostApplicationPluginManager
to use a for discovery and registration of Plugin instances:
`mermaid`
%%{init: { "sequence": { "mirrorActors":false }}}%%
sequenceDiagram
HostApplication->>PluginManager:<
Note over PluginRepository1,PluginRepository2: May use an index or scan plugins directly
HostApplication->>PluginManager:registerExtensions(EXTENSION_POINT_1)
activate PluginManager
PluginManager->>PluginRepository1:scanForExtensions(EXTENSION_POINT_1)
activate PluginRepository1
PluginRepository1-->>PluginManager:ExtensionEntry[]
deactivate PluginRepository1
PluginManager->>PluginRepository2:scanForExtensions(EXTENSION_POINT_1)
activate PluginRepository2
PluginRepository2-->>PluginManager:ExtensionEntry[]
deactivate PluginRepository2
PluginManager-->>HostApplication:ExtensionInfo[]
deactivate PluginManager
Once registration has been performed, the HostApplication may query thePluginManager for Extensions of known ExtensionPoints and then instantiate
them:
`mermaid`
%%{init: { "sequence": { "mirrorActors":false, diagramMarginY: 0 }}}%%
sequenceDiagram
HostApplication->>PluginManager:getRegisteredExtensions(EXTENSION_POINT_1)
activate PluginManager
PluginManager->>PluginManager:filterExtensions
PluginManager->>HostApplication:ExtensionInfo[]
deactivate PluginManager
HostApplication->>HostApplication:select Extension1
HostApplication->>PluginManager:instantiate(extension1Handle)
activate PluginManager
PluginManager->>PluginRepository1:getExtensionDescriptorFromExtensionEntry(extensionEntry)
PluginRepository1->>Plugin1:<
Plugin1-->>PluginManager:ExtensionDescriptor
PluginManager->>Extension1Factory:create()
activate Extension1Factory
Extension1Factory->>Extension1:<
Extension1Factory-->>PluginManager:Extension1
deactivate Extension1Factory
PluginManager-->>HostApplication:Extension1
deactivate PluginManager
HostApplication->>Extension1:extensionPoint1Method
activate Extension1
deactivate Extension1
As ExtensionPoints are simply Typescript objects, for the purposes of testingExtension
or validation, it is possible to bypass the framework altogether and import an and use it directly:
`mermaid`
%%{init: { "sequence": { "mirrorActors":false }}}%%
sequenceDiagram
HostApplication->>Extension1:<
HostApplication->>Extension1:extensionPoint1Method
activate Extension1
deactivate Extension1
The following example projects are available which support execution in both a
terminal and a browser:
- Plugin
- Host Application
- Host Webapp
The following diagram provides an overview of the Plugin API:
`mermaid
classDiagram
class Plugin {
<
pluginData: any
}
class ExtensionDescriptor {
<
extensionPoint
extensionData: any
}
class ExtensionFactory {
<
create(hostData: any) Extension
}
Plugin --> "1..*" ExtensionDescriptor: extensionDescriptors
ExtensionDescriptor --> ExtensionFactory: factory
`
The following diagram provides an overview of the PluginManager API:
`mermaid
classDiagram
class PluginManager {
<
registerExtensions(extensionPoint)
getRegisteredExtensions(extensionPoint) Set
instantiate(extensionHandle, hostData: Map
}
class ExtensionInfo {
<
extensionHandle
extensionData: Map
pluginData: Map
}
`
API docs for the library:
Install dependencies:
bun install
Test:
bun test
NOTE: The following tasks use Deno as it excels at these and Bun does not
currently provide such functionality:
Format:
deno fmt
Lint:
deno lint index.ts src/ tests/
Generate HTML API Documentation:
deno doc --html --name=dynamic-plugin-framework index.ts
The following diagram provides an overview of the main internal classes:
`mermaid
classDiagram
class PluginManager {
<
}
class DefaultPluginManager {
}
class ExtensionPointRegistry {
<
}
class ExtensionRegistry {
<
}
class PluginRepository {
<
}
class UrlListPluginRepository {
}
class InMemoryExtensionRegistry {
}
class InMemoryExtensionPointRegistry {
}
PluginManager <|.. DefaultPluginManager
ExtensionPointRegistry <|.. InMemoryExtensionPointRegistry
ExtensionRegistry <|.. InMemoryExtensionRegistry
DefaultPluginManager --> ExtensionPointRegistry
DefaultPluginManager --> ExtensionRegistry
DefaultPluginManager --> "1..*" PluginRepository
PluginRepository <|.. UrlListPluginRepository
``
MIT © Flowscripter