JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 56
  • Score
    100M100P100Q82806F

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@flowscripter/dynamic-plugin-framework) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    dynamic-plugin-framework

    version build coverage docs license: MIT

    Dynamic plugin framework for Bun based on Javascript Modules and import() function

    STILL IN DEVELOPMENT

    Overview

    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:

    classDiagram
        direction LR
    
        class HostApplication {
        }
    
        class ExtensionPoint1 {
            <<interface>>
            EXTENSION_POINT_1
        }
    
        class Plugin {
            <<interface>>
        }
    
        class PluginManager {
            <<interface>>
        }
    
        class PluginRepository {
            <<interface>>
        }
    
        class ExtensionDescriptor {
            <<interface>>
        }
    
        class ExtensionFactory {
            <<interface>>
        }
    
        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 HostApplication to use a PluginManager for discovery and registration of Plugin instances:

    %%{init: { "sequence": { "mirrorActors":false }}}%%
    sequenceDiagram
        HostApplication->>PluginManager:<<static import>>
        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 the PluginManager for Extensions of known ExtensionPoints and then instantiate them:

    %%{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:<<dynamic import>>
        Plugin1-->>PluginManager:ExtensionDescriptor
        PluginManager->>Extension1Factory:create()
        activate Extension1Factory
        Extension1Factory->>Extension1:<<new>>
        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 testing or validation, it is possible to bypass the framework altogether and import an Extension and use it directly:

    %%{init: { "sequence": { "mirrorActors":false }}}%%
    sequenceDiagram
        HostApplication->>Extension1:<<static import>>
        HostApplication->>Extension1:extensionPoint1Method
        activate Extension1
        deactivate Extension1

    Examples

    The following example projects are available which support execution in both a terminal and a browser:

    API

    The following diagram provides an overview of the Plugin API:

    classDiagram
        class Plugin {
            <<interface>>
            pluginData: any
        }
    
        class ExtensionDescriptor {
            <<interface>>
            extensionPoint
            extensionData: any
        }
    
        class ExtensionFactory {
            <<interface>>
            create(hostData: any) Extension
        }
    
        Plugin --> "1..*" ExtensionDescriptor: extensionDescriptors
        ExtensionDescriptor --> ExtensionFactory: factory

    The following diagram provides an overview of the PluginManager API:

    classDiagram
        class PluginManager {
            <<interface>>
            registerExtensions(extensionPoint)
            getRegisteredExtensions(extensionPoint) Set<ExtensionInfo>
            instantiate(extensionHandle, hostData: Map<string, string>) Extension
        }
    
        class ExtensionInfo {
            <<interface>>
            extensionHandle
            extensionData: Map<string, string>
            pluginData: Map<string, string>
        }

    API docs for the library:

    API Documentation

    Development

    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:

    classDiagram
        class PluginManager {
            <<interface>>
        }
    
        class DefaultPluginManager {
        }
    
        class ExtensionPointRegistry {
            <<interface>>
        }
    
        class ExtensionRegistry {
            <<interface>>
        }
    
        class PluginRepository {
            <<interface>>
        }
    
        class UrlListPluginRepository {
        }
    
        class InMemoryExtensionRegistry {
        }
    
        class InMemoryExtensionPointRegistry {
        }
    
        PluginManager <|.. DefaultPluginManager
        ExtensionPointRegistry <|.. InMemoryExtensionPointRegistry
        ExtensionRegistry <|.. InMemoryExtensionRegistry
        DefaultPluginManager --> ExtensionPointRegistry
        DefaultPluginManager --> ExtensionRegistry
        DefaultPluginManager --> "1..*" PluginRepository
        PluginRepository <|.. UrlListPluginRepository

    License

    MIT © Flowscripter