@flowscripter/dynamic-plugin-framework 1.3.18 → 1.4.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.
@@ -8,6 +8,7 @@ permissions:
8
8
  pull-requests: write
9
9
  id-token: write
10
10
  pages: write
11
+ packages: write
11
12
  jobs:
12
13
  call-release-bun-library:
13
14
  uses: flowscripter/.github/.github/workflows/release-bun-library.yml@v1
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  > Dynamic plugin framework for Bun based on Javascript Modules and import()
10
10
  > function
11
11
 
12
- [//]: # (TODO: Remove this when plugin indexing and discovery available.)
12
+ [//]: # "TODO: Remove this when plugin indexing and discovery available."
13
13
 
14
14
  **STILL IN DEVELOPMENT**
15
15
 
@@ -242,20 +242,17 @@ Test:
242
242
 
243
243
  `bun test`
244
244
 
245
- **NOTE**: The following tasks use Deno as it excels at these and Bun does not
246
- currently provide such functionality:
247
-
248
245
  Format:
249
246
 
250
- `deno fmt`
247
+ `bunx oxfmt`
251
248
 
252
249
  Lint:
253
250
 
254
- `deno lint index.ts src/ tests/`
251
+ `bunx oxlint index.ts src/ tests/`
255
252
 
256
253
  Generate HTML API Documentation:
257
254
 
258
- `deno doc --html --name=dynamic-plugin-framework index.ts`
255
+ `bunx typedoc --readme none index.ts`
259
256
 
260
257
  The following diagram provides an overview of the main internal classes:
261
258
 
package/bun.lock CHANGED
@@ -1,26 +1,25 @@
1
1
  {
2
2
  "lockfileVersion": 1,
3
+ "configVersion": 0,
3
4
  "workspaces": {
4
5
  "": {
5
6
  "name": "@flowscripter/dynamic-plugin-framework",
6
7
  "devDependencies": {
7
- "@types/bun": "^1.2.9",
8
+ "@types/bun": "^1.3.14",
8
9
  },
9
10
  "peerDependencies": {
10
- "typescript": "^5.8.3",
11
+ "typescript": "^6.0.3",
11
12
  },
12
13
  },
13
14
  },
14
15
  "packages": {
15
- "@types/bun": ["@types/bun@1.2.9", "", { "dependencies": { "bun-types": "1.2.9" } }, "sha512-epShhLGQYc4Bv/aceHbmBhOz1XgUnuTZgcxjxk+WXwNyDXavv5QHD1QEFV0FwbTSQtNq6g4ZcV6y0vZakTjswg=="],
16
+ "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
16
17
 
17
18
  "@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="],
18
19
 
19
- "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
20
+ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
20
21
 
21
- "bun-types": ["bun-types@1.2.9", "", { "dependencies": { "@types/node": "*", "@types/ws": "*" } }, "sha512-dk/kOEfQbajENN/D6FyiSgOKEuUi9PWfqKQJEgwKrCMWbjS/S6tEXp178mWvWAcUSYm9ArDlWHZKO3T/4cLXiw=="],
22
-
23
- "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
22
+ "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
24
23
 
25
24
  "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
26
25
  }
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
1
  {
2
2
  "name": "@flowscripter/dynamic-plugin-framework",
3
+ "version": "1.4.0",
3
4
  "description": "Dynamic plugin framework for Bun based on Javascript Modules and import() function",
5
+ "keywords": [
6
+ "bun",
7
+ "dynamic",
8
+ "framework",
9
+ "import",
10
+ "plugin"
11
+ ],
4
12
  "homepage": "https://github.com/flowscripter/dynamic-plugin-framework#readme",
13
+ "license": "MIT",
5
14
  "repository": {
6
15
  "type": "git",
7
16
  "url": "git+https://github.com/flowscripter/dynamic-plugin-framework.git"
8
17
  },
9
- "license": "MIT",
10
- "keywords": [
11
- "bun",
12
- "plugin",
13
- "framework",
14
- "dynamic",
15
- "import"
16
- ],
17
- "module": "index.ts",
18
18
  "type": "module",
19
- "version": "1.3.18",
19
+ "module": "index.ts",
20
20
  "publishConfig": {
21
21
  "access": "public"
22
22
  },
23
23
  "devDependencies": {
24
- "@types/bun": "^1.2.9"
24
+ "@types/bun": "^1.3.14"
25
25
  },
26
26
  "peerDependencies": {
27
- "typescript": "^5.8.3"
27
+ "typescript": "^6.0.3"
28
28
  }
29
29
  }
@@ -18,9 +18,7 @@ export default interface PluginManager {
18
18
  *
19
19
  * @return array of {@link ExtensionInfo}
20
20
  */
21
- getRegisteredExtensions(
22
- extensionPoint: string,
23
- ): Promise<ReadonlyArray<ExtensionInfo>>;
21
+ getRegisteredExtensions(extensionPoint: string): Promise<ReadonlyArray<ExtensionInfo>>;
24
22
 
25
23
  /**
26
24
  * Instantiate a specific Extension.
@@ -31,8 +29,5 @@ export default interface PluginManager {
31
29
  *
32
30
  * @return an Extension instance implementing an Extension Point.
33
31
  */
34
- instantiate(
35
- extensionHandle: string,
36
- hostData?: Map<string, string>,
37
- ): Promise<unknown>;
32
+ instantiate(extensionHandle: string, hostData?: Map<string, string>): Promise<unknown>;
38
33
  }
@@ -12,30 +12,25 @@ import type PluginRepository from "./plugin_repository/PluginRepository.ts";
12
12
  export default class DefaultPluginManager implements PluginManager {
13
13
  private readonly extensionPointRegistry: ExtensionPointRegistry;
14
14
  private readonly extensionRegistry: ExtensionRegistry;
15
- private readonly pluginRepositoriesByExtensionHandle = new Map<
16
- string,
17
- PluginRepository
18
- >();
15
+ private readonly pluginRepositoriesByExtensionHandle = new Map<string, PluginRepository>();
19
16
 
20
17
  /**
21
18
  * Constructor configures the instance using the optionally specified
22
19
  * {@link ExtensionPointRegistry} and {@link ExtensionRegistry}.
23
20
  *
24
21
  * @param pluginRepositories One or more {@link PluginRepository} instances to use for plugin discovery.
25
- * @param extensionPointRegistry optional {@link ExtensionPointRegistry]] implementation. Defaults to using
26
- * {@link InMemoryExtensionPointRegistry}.
22
+ * @param extensionPointRegistry optional {@link ExtensionPointRegistry} implementation. Defaults to using
23
+ * InMemoryExtensionPointRegistry.
27
24
  * @param extensionRegistry optional {@link ExtensionRegistry} implementation. Defaults to using
28
- * {@link InMemoryExtensionRegistry}
25
+ * InMemoryExtensionRegistry
29
26
  */
30
27
  public constructor(
31
28
  private readonly pluginRepositories: Array<PluginRepository>,
32
29
  extensionPointRegistry?: ExtensionPointRegistry,
33
30
  extensionRegistry?: ExtensionRegistry,
34
31
  ) {
35
- this.extensionPointRegistry = extensionPointRegistry ||
36
- new InMemoryExtensionPointRegistry();
37
- this.extensionRegistry = extensionRegistry ||
38
- new InMemoryExtensionRegistry();
32
+ this.extensionPointRegistry = extensionPointRegistry || new InMemoryExtensionPointRegistry();
33
+ this.extensionRegistry = extensionRegistry || new InMemoryExtensionRegistry();
39
34
  }
40
35
 
41
36
  public async registerExtensions(extensionPoint: string): Promise<void> {
@@ -47,18 +42,10 @@ export default class DefaultPluginManager implements PluginManager {
47
42
 
48
43
  for (let i = 0; i < this.pluginRepositories.length; i++) {
49
44
  const pluginRepository = this.pluginRepositories[i];
50
- for await (
51
- const extensionEntry of pluginRepository.scanForExtensions(
52
- extensionPoint,
53
- )
54
- ) {
55
- const extensionHandle =
56
- `${i}:${extensionEntry.pluginId}:${extensionEntry.extensionId}`;
45
+ for await (const extensionEntry of pluginRepository.scanForExtensions(extensionPoint)) {
46
+ const extensionHandle = `${i}:${extensionEntry.pluginId}:${extensionEntry.extensionId}`;
57
47
 
58
- this.pluginRepositoriesByExtensionHandle.set(
59
- extensionHandle,
60
- pluginRepository,
61
- );
48
+ this.pluginRepositoriesByExtensionHandle.set(extensionHandle, pluginRepository);
62
49
  await this.extensionRegistry.register(extensionHandle, extensionEntry);
63
50
  }
64
51
  }
@@ -67,9 +54,7 @@ export default class DefaultPluginManager implements PluginManager {
67
54
  public async getRegisteredExtensions(
68
55
  extensionPoint: string,
69
56
  ): Promise<ReadonlyArray<ExtensionInfo>> {
70
- const extensionMap = await this.extensionRegistry.getExtensions(
71
- extensionPoint,
72
- );
57
+ const extensionMap = await this.extensionRegistry.getExtensions(extensionPoint);
73
58
  const registeredExtensions = new Array<ExtensionInfo>();
74
59
 
75
60
  extensionMap.forEach((entry, handle) => {
@@ -96,16 +81,14 @@ export default class DefaultPluginManager implements PluginManager {
96
81
  const extensionEntry = await this.extensionRegistry.get(extensionHandle);
97
82
 
98
83
  // Get the Plugin Repository for the Extension Handle
99
- const pluginRepository = this.pluginRepositoriesByExtensionHandle.get(
100
- extensionHandle,
101
- );
84
+ const pluginRepository = this.pluginRepositoriesByExtensionHandle.get(extensionHandle);
102
85
 
103
86
  if (!pluginRepository) {
104
87
  return Promise.reject(`Extension handle ${extensionHandle} is unknown`);
105
88
  }
106
89
  // Get the Extension Descriptor from the Plugin Repository
107
- const extensionDescriptor = await pluginRepository
108
- .getExtensionDescriptorFromExtensionEntry(extensionEntry);
90
+ const extensionDescriptor =
91
+ await pluginRepository.getExtensionDescriptorFromExtensionEntry(extensionEntry);
109
92
 
110
93
  return extensionDescriptor.factory.create(hostData);
111
94
  }
@@ -13,14 +13,12 @@ export default interface PluginRepository {
13
13
  *
14
14
  * @return an async iterable of {@link ExtensionEntry} instances for all matching Extensions.
15
15
  */
16
- scanForExtensions(
17
- extensionPoint: string,
18
- ): AsyncIterable<Readonly<ExtensionEntry>>;
16
+ scanForExtensions(extensionPoint: string): AsyncIterable<Readonly<ExtensionEntry>>;
19
17
 
20
18
  /**
21
19
  * Return the {@link ExtensionDescriptor} for the Extension identified by the specified {@link ExtensionEntry}.
22
20
  *
23
- * @param extensionEntry the {@link extensionEntry} for the desired Extension.
21
+ * @param extensionEntry the {@link ExtensionEntry} for the desired Extension.
24
22
  *
25
23
  * @return an {@link ExtensionDescriptor} instance.
26
24
  */
@@ -18,28 +18,22 @@ export default class UrlListPluginRepository implements PluginRepository {
18
18
  * @throws *Error* if the URL set contains a non-valid URL.
19
19
  */
20
20
  public constructor(
21
- private readonly urlsAndExtensionPoints: Set<
22
- { url: string; extensionPoints: string[] }
23
- >,
21
+ private readonly urlsAndExtensionPoints: Set<{ url: string; extensionPoints: string[] }>,
24
22
  ) {
25
- if (!urlsAndExtensionPoints || (urlsAndExtensionPoints.size === 0)) {
26
- throw new Error(
27
- `Undefined or empty set of URL and extension points provided`,
28
- );
23
+ if (!urlsAndExtensionPoints || urlsAndExtensionPoints.size === 0) {
24
+ throw new Error(`Undefined or empty set of URL and extension points provided`);
29
25
  }
30
26
  this.urlsAndExtensionPoints.forEach((urlAndExtensionPoint) => {
31
27
  try {
32
28
  new URL(urlAndExtensionPoint.url);
33
29
  } catch (err) {
34
30
  throw new Error(
35
- `Cannot parse ${urlAndExtensionPoint.url} as a URL: ${
36
- (err as Error).message
37
- }`,
31
+ `Cannot parse ${urlAndExtensionPoint.url} as a URL: ${(err as Error).message}`,
38
32
  );
39
33
  }
40
34
  if (
41
35
  !urlAndExtensionPoint.extensionPoints ||
42
- (urlAndExtensionPoint.extensionPoints.length === 0)
36
+ urlAndExtensionPoint.extensionPoints.length === 0
43
37
  ) {
44
38
  throw new Error(`Undefined or empty set of extension points provided`);
45
39
  }
@@ -54,9 +48,7 @@ export default class UrlListPluginRepository implements PluginRepository {
54
48
  if (!urlAndExtensionPoints.extensionPoints.includes(extensionPoint)) {
55
49
  continue;
56
50
  }
57
- const plugin = await this.pluginSource.loadPlugin(
58
- new URL(urlAndExtensionPoints.url),
59
- );
51
+ const plugin = await this.pluginSource.loadPlugin(new URL(urlAndExtensionPoints.url));
60
52
 
61
53
  if (plugin) {
62
54
  // Once we have loaded the plugin, double check on the extension points in the plugin
@@ -79,9 +71,7 @@ export default class UrlListPluginRepository implements PluginRepository {
79
71
  }
80
72
  }
81
73
 
82
- public scanForExtensions(
83
- extensionPoint: string,
84
- ): AsyncIterable<ExtensionEntry> {
74
+ public scanForExtensions(extensionPoint: string): AsyncIterable<ExtensionEntry> {
85
75
  return this.getExtensionEntryAsyncIterable(extensionPoint);
86
76
  }
87
77
 
@@ -93,9 +83,7 @@ export default class UrlListPluginRepository implements PluginRepository {
93
83
  public async getExtensionDescriptorFromExtensionEntry(
94
84
  extensionEntry: ExtensionEntry,
95
85
  ): Promise<Readonly<ExtensionDescriptor>> {
96
- const plugin = await this.pluginSource.loadPlugin(
97
- new URL(extensionEntry.pluginId),
98
- );
86
+ const plugin = await this.pluginSource.loadPlugin(new URL(extensionEntry.pluginId));
99
87
 
100
88
  if (!plugin) {
101
89
  return Promise.reject(`Plugin ID ${extensionEntry.pluginId} is unknown`);
@@ -106,17 +94,11 @@ export default class UrlListPluginRepository implements PluginRepository {
106
94
  try {
107
95
  extensionId = parseInt(extensionEntry.extensionId);
108
96
  } catch (_e) {
109
- return Promise.reject(
110
- `Extension ID ${extensionEntry.extensionId} is unknown`,
111
- );
97
+ return Promise.reject(`Extension ID ${extensionEntry.extensionId} is unknown`);
112
98
  }
113
99
 
114
- if (
115
- (extensionId < 0) || (extensionId >= plugin.extensionDescriptors.length)
116
- ) {
117
- return Promise.reject(
118
- `Extension ID ${extensionEntry.extensionId} is unknown`,
119
- );
100
+ if (extensionId < 0 || extensionId >= plugin.extensionDescriptors.length) {
101
+ return Promise.reject(`Extension ID ${extensionEntry.extensionId} is unknown`);
120
102
  }
121
103
 
122
104
  return Promise.resolve(plugin.extensionDescriptors[extensionId]);
@@ -21,17 +21,12 @@ export default class UrlPluginSource {
21
21
 
22
22
  if (!plugin) {
23
23
  const pluginLoadResult = await loadPlugin(url.toString());
24
- if (
25
- pluginLoadResult.isValidPlugin &&
26
- (pluginLoadResult.plugin !== undefined)
27
- ) {
24
+ if (pluginLoadResult.isValidPlugin && pluginLoadResult.plugin !== undefined) {
28
25
  plugin = pluginLoadResult.plugin;
29
26
  this.pluginsByUrl.set(url, plugin);
30
27
  }
31
28
  if (pluginLoadResult.error) {
32
- throw new Error(
33
- `Failed to load plugin from ${url}: ${pluginLoadResult.error.message}`,
34
- );
29
+ throw new Error(`Failed to load plugin from ${url}: ${pluginLoadResult.error.message}`);
35
30
  }
36
31
  }
37
32
  return Promise.resolve(this.pluginsByUrl.get(url));
@@ -10,10 +10,7 @@ export default interface ExtensionRegistry {
10
10
  * @param extensionHandle a unique identifier under which to register the {@link ExtensionEntry}
11
11
  * @param extensionEntry the {@link ExtensionEntry} for the Extension to register
12
12
  */
13
- register(
14
- extensionHandle: string,
15
- extensionEntry: ExtensionEntry,
16
- ): Promise<void>;
13
+ register(extensionHandle: string, extensionEntry: ExtensionEntry): Promise<void>;
17
14
 
18
15
  /**
19
16
  * Return the specified registered {@link ExtensionEntry} instance.
@@ -32,7 +29,5 @@ export default interface ExtensionRegistry {
32
29
  * @return a map of extension handle to {@link ExtensionEntry} for all matching
33
30
  * registered {@link ExtensionEntry} instances
34
31
  */
35
- getExtensions(
36
- extensionPoint: string,
37
- ): Promise<ReadonlyMap<string, ExtensionEntry>>;
32
+ getExtensions(extensionPoint: string): Promise<ReadonlyMap<string, ExtensionEntry>>;
38
33
  }
@@ -3,8 +3,7 @@ import type ExtensionPointRegistry from "./ExtensionPointRegistry.ts";
3
3
  /**
4
4
  * Simple implementation of an {@link ExtensionPointRegistry} using an in-memory Set.
5
5
  */
6
- export default class InMemoryExtensionPointRegistry
7
- implements ExtensionPointRegistry {
6
+ export default class InMemoryExtensionPointRegistry implements ExtensionPointRegistry {
8
7
  private readonly extensionPoints: Set<string> = new Set();
9
8
 
10
9
  /**
@@ -5,40 +5,27 @@ import type ExtensionEntry from "../plugin_repository/ExtensionEntry.ts";
5
5
  * Simple implementation of an {@link ExtensionRegistry} using an in-memory map.
6
6
  */
7
7
  export default class InMemoryExtensionRegistry implements ExtensionRegistry {
8
- private readonly extensionEntriesByHandle: Map<string, ExtensionEntry> =
9
- new Map();
8
+ private readonly extensionEntriesByHandle: Map<string, ExtensionEntry> = new Map();
10
9
 
11
- private readonly extensionEntriesByExtensionPoint: Map<
12
- string,
13
- Map<string, ExtensionEntry>
14
- > = new Map();
10
+ private readonly extensionEntriesByExtensionPoint: Map<string, Map<string, ExtensionEntry>> =
11
+ new Map();
15
12
 
16
13
  /**
17
14
  * @inheritDoc
18
15
  *
19
16
  * @throws *Error* if the specified Extension handle has already been registered
20
17
  */
21
- public register(
22
- extensionHandle: string,
23
- extensionEntry: ExtensionEntry,
24
- ): Promise<void> {
18
+ public register(extensionHandle: string, extensionEntry: ExtensionEntry): Promise<void> {
25
19
  if (this.extensionEntriesByHandle.has(extensionHandle)) {
26
- return Promise.reject(
27
- `Extension handle ${extensionHandle} has already been registered`,
28
- );
20
+ return Promise.reject(`Extension handle ${extensionHandle} has already been registered`);
29
21
  }
30
22
  this.extensionEntriesByHandle.set(extensionHandle, extensionEntry);
31
23
 
32
- let extensionEntries = this.extensionEntriesByExtensionPoint.get(
33
- extensionEntry.extensionPoint,
34
- );
24
+ let extensionEntries = this.extensionEntriesByExtensionPoint.get(extensionEntry.extensionPoint);
35
25
 
36
26
  if (extensionEntries === undefined) {
37
27
  extensionEntries = new Map();
38
- this.extensionEntriesByExtensionPoint.set(
39
- extensionEntry.extensionPoint,
40
- extensionEntries,
41
- );
28
+ this.extensionEntriesByExtensionPoint.set(extensionEntry.extensionPoint, extensionEntries);
42
29
  }
43
30
  extensionEntries.set(extensionHandle, extensionEntry);
44
31
 
@@ -59,11 +46,7 @@ export default class InMemoryExtensionRegistry implements ExtensionRegistry {
59
46
  return Promise.resolve(Object.freeze(extensionEntry));
60
47
  }
61
48
 
62
- public getExtensions(
63
- extensionPoint: string,
64
- ): Promise<ReadonlyMap<string, ExtensionEntry>> {
65
- return Promise.resolve(
66
- this.extensionEntriesByExtensionPoint.get(extensionPoint) || new Map(),
67
- );
49
+ public getExtensions(extensionPoint: string): Promise<ReadonlyMap<string, ExtensionEntry>> {
50
+ return Promise.resolve(this.extensionEntriesByExtensionPoint.get(extensionPoint) || new Map());
68
51
  }
69
52
  }
@@ -56,9 +56,7 @@ async function getLocalUrl(remoteUrl: string) {
56
56
  *
57
57
  * @param url the URL of the module to import.
58
58
  */
59
- export default async function loadPlugin(
60
- url: string,
61
- ): Promise<Readonly<PluginLoadResult>> {
59
+ export default async function loadPlugin(url: string): Promise<Readonly<PluginLoadResult>> {
62
60
  const result: PluginLoadResult = {
63
61
  isValidPlugin: false,
64
62
  plugin: undefined,
@@ -71,10 +69,9 @@ export default async function loadPlugin(
71
69
  module = await import(url);
72
70
  } catch (err) {
73
71
  if (
74
- ((err as { message: string }).message === undefined) ||
75
- (!(err as { message: string }).message.startsWith("ENOENT")) ||
76
- (!url.toLowerCase().startsWith("http://") &&
77
- !url.toLowerCase().startsWith("https://"))
72
+ (err as { message: string }).message === undefined ||
73
+ !(err as { message: string }).message.startsWith("ENOENT") ||
74
+ (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
78
75
  ) {
79
76
  result.error = err as Error;
80
77
  return result;
@@ -93,25 +90,19 @@ export default async function loadPlugin(
93
90
 
94
91
  // check the assumed Plugin has an array of extension descriptors
95
92
  if (!Array.isArray(potentialPlugin.extensionDescriptors)) {
96
- result.error = new Error(
97
- `Plugin from ${url} does not provide an extensionDescriptors array`,
98
- );
93
+ result.error = new Error(`Plugin from ${url} does not provide an extensionDescriptors array`);
99
94
  return result;
100
95
  }
101
96
 
102
97
  // At this point assume it is a valid plugin and then disprove this
103
98
  result.isValidPlugin = true;
104
99
 
105
- for (
106
- const potentialExtensionDescriptor of potentialPlugin
107
- .extensionDescriptors
108
- ) {
100
+ for (const potentialExtensionDescriptor of potentialPlugin.extensionDescriptors) {
109
101
  // check for valid {@link ExtensionDescriptor.extensionPoint}
110
102
  if (
111
- (potentialExtensionDescriptor.extensionPoint === undefined) ||
112
- (!(potentialExtensionDescriptor.extensionPoint as unknown instanceof
113
- String) &&
114
- (typeof potentialExtensionDescriptor.extensionPoint !== "string"))
103
+ potentialExtensionDescriptor.extensionPoint === undefined ||
104
+ (!((potentialExtensionDescriptor.extensionPoint as unknown) instanceof String) &&
105
+ typeof potentialExtensionDescriptor.extensionPoint !== "string")
115
106
  ) {
116
107
  result.isValidPlugin = false;
117
108
  result.error = new Error(
@@ -121,10 +112,9 @@ export default async function loadPlugin(
121
112
  }
122
113
  // check for valid {@link ExtensionDescriptor.factory.create function}
123
114
  if (
124
- (potentialExtensionDescriptor.factory === undefined) ||
125
- (potentialExtensionDescriptor.factory.create === undefined) ||
126
- !(potentialExtensionDescriptor.factory.create as unknown instanceof
127
- Function)
115
+ potentialExtensionDescriptor.factory === undefined ||
116
+ potentialExtensionDescriptor.factory.create === undefined ||
117
+ !((potentialExtensionDescriptor.factory.create as unknown) instanceof Function)
128
118
  ) {
129
119
  result.isValidPlugin = false;
130
120
  result.error = new Error(
@@ -1,5 +1,7 @@
1
1
  export default {
2
- extensionDescriptors: [{
3
- extensionData: "foo",
4
- }],
2
+ extensionDescriptors: [
3
+ {
4
+ extensionData: "foo",
5
+ },
6
+ ],
5
7
  };
@@ -1,5 +1,7 @@
1
1
  export default {
2
- extensionDescriptors: [{
3
- extensionPoint: "foo",
4
- }],
2
+ extensionDescriptors: [
3
+ {
4
+ extensionPoint: "foo",
5
+ },
6
+ ],
5
7
  };
@@ -1,6 +1,8 @@
1
1
  export default {
2
- extensionDescriptors: [{
3
- extensionPoint: "foo",
4
- factory: "bar",
5
- }],
2
+ extensionDescriptors: [
3
+ {
4
+ extensionPoint: "foo",
5
+ factory: "bar",
6
+ },
7
+ ],
6
8
  };
@@ -1,8 +1,10 @@
1
1
  export default {
2
- extensionDescriptors: [{
3
- extensionPoint: "foo",
4
- factory: {
5
- bar: () => {},
2
+ extensionDescriptors: [
3
+ {
4
+ extensionPoint: "foo",
5
+ factory: {
6
+ bar: () => {},
7
+ },
6
8
  },
7
- }],
9
+ ],
8
10
  };
@@ -26,9 +26,7 @@ export const extensionDescriptor1: ExtensionDescriptor = {
26
26
  const plugin1: Plugin = {
27
27
  pluginData: new Map([["foo", "bar"]]),
28
28
 
29
- extensionDescriptors: [
30
- extensionDescriptor1,
31
- ],
29
+ extensionDescriptors: [extensionDescriptor1],
32
30
  };
33
31
 
34
32
  export default plugin1;
@@ -2,16 +2,11 @@ import { describe, expect, test } from "bun:test";
2
2
  import path from "node:path";
3
3
  import DefaultPluginManager from "../../src/plugin_manager/DefaultPluginManager.ts";
4
4
  import UrlListPluginRepository from "../../src/plugin_manager/plugin_repository/UrlListPluginRepository.ts";
5
- import {
6
- EXTENSION_POINT_1,
7
- type ExtensionPoint1,
8
- } from "../fixtures/Constants.ts";
5
+ import { EXTENSION_POINT_1, type ExtensionPoint1 } from "../fixtures/Constants.ts";
9
6
 
10
- const PLUGIN_1_URL = "file://" +
11
- path.join(
12
- path.dirname(Bun.fileURLToPath(import.meta.url)),
13
- "../fixtures/ValidPlugin1.ts",
14
- );
7
+ const PLUGIN_1_URL =
8
+ "file://" +
9
+ path.join(path.dirname(Bun.fileURLToPath(import.meta.url)), "../fixtures/ValidPlugin1.ts");
15
10
 
16
11
  describe("DefaultPluginManager Tests", () => {
17
12
  test("Register without a plugin repository does not fail", async () => {
@@ -24,21 +19,15 @@ describe("DefaultPluginManager Tests", () => {
24
19
  const urlListPluginRepository = new UrlListPluginRepository(
25
20
  new Set([{ url: PLUGIN_1_URL, extensionPoints: [EXTENSION_POINT_1] }]),
26
21
  );
27
- const defaultPluginManager = new DefaultPluginManager([
28
- urlListPluginRepository,
29
- ]);
22
+ const defaultPluginManager = new DefaultPluginManager([urlListPluginRepository]);
30
23
 
31
- let extensions = await defaultPluginManager.getRegisteredExtensions(
32
- EXTENSION_POINT_1,
33
- );
24
+ let extensions = await defaultPluginManager.getRegisteredExtensions(EXTENSION_POINT_1);
34
25
 
35
26
  expect(extensions.length).toEqual(0);
36
27
 
37
28
  await defaultPluginManager.registerExtensions(EXTENSION_POINT_1);
38
29
 
39
- extensions = await defaultPluginManager.getRegisteredExtensions(
40
- EXTENSION_POINT_1,
41
- );
30
+ extensions = await defaultPluginManager.getRegisteredExtensions(EXTENSION_POINT_1);
42
31
 
43
32
  expect(extensions.length).toEqual(1);
44
33
  });
@@ -47,23 +36,17 @@ describe("DefaultPluginManager Tests", () => {
47
36
  const urlListPluginRepository = new UrlListPluginRepository(
48
37
  new Set([{ url: PLUGIN_1_URL, extensionPoints: [EXTENSION_POINT_1] }]),
49
38
  );
50
- const defaultPluginManager = new DefaultPluginManager([
51
- urlListPluginRepository,
52
- ]);
39
+ const defaultPluginManager = new DefaultPluginManager([urlListPluginRepository]);
53
40
 
54
41
  await defaultPluginManager.registerExtensions(EXTENSION_POINT_1);
55
42
 
56
- let extensions = await defaultPluginManager.getRegisteredExtensions(
57
- EXTENSION_POINT_1,
58
- );
43
+ let extensions = await defaultPluginManager.getRegisteredExtensions(EXTENSION_POINT_1);
59
44
 
60
45
  expect(extensions.length).toEqual(1);
61
46
 
62
47
  await defaultPluginManager.registerExtensions(EXTENSION_POINT_1);
63
48
 
64
- extensions = await defaultPluginManager.getRegisteredExtensions(
65
- EXTENSION_POINT_1,
66
- );
49
+ extensions = await defaultPluginManager.getRegisteredExtensions(EXTENSION_POINT_1);
67
50
 
68
51
  expect(extensions.length).toEqual(1);
69
52
  });
@@ -72,24 +55,20 @@ describe("DefaultPluginManager Tests", () => {
72
55
  const urlListPluginRepository = new UrlListPluginRepository(
73
56
  new Set([{ url: PLUGIN_1_URL, extensionPoints: [EXTENSION_POINT_1] }]),
74
57
  );
75
- const defaultPluginManager = new DefaultPluginManager([
76
- urlListPluginRepository,
77
- ]);
58
+ const defaultPluginManager = new DefaultPluginManager([urlListPluginRepository]);
78
59
 
79
60
  await defaultPluginManager.registerExtensions(EXTENSION_POINT_1);
80
61
 
81
- const extensionInfos = await defaultPluginManager.getRegisteredExtensions(
82
- EXTENSION_POINT_1,
83
- );
62
+ const extensionInfos = await defaultPluginManager.getRegisteredExtensions(EXTENSION_POINT_1);
84
63
 
85
64
  const extensionInfo = extensionInfos[0];
86
65
 
87
66
  expect(extensionInfo.pluginData?.get("foo"), "bar");
88
67
  expect(extensionInfo.extensionData?.get("foo"), "bar");
89
68
 
90
- const instance = await defaultPluginManager.instantiate(
69
+ const instance = (await defaultPluginManager.instantiate(
91
70
  extensionInfo.extensionHandle,
92
- ) as ExtensionPoint1;
71
+ )) as ExtensionPoint1;
93
72
 
94
73
  expect(instance.sayHello()).toEqual("hello");
95
74
  });
@@ -1,16 +1,11 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import path from "node:path";
3
3
  import UrlListPluginRepository from "../../../src/plugin_manager/plugin_repository/UrlListPluginRepository.ts";
4
- import {
5
- EXTENSION_POINT_1,
6
- EXTENSION_POINT_2,
7
- } from "../../fixtures/Constants.ts";
4
+ import { EXTENSION_POINT_1, EXTENSION_POINT_2 } from "../../fixtures/Constants.ts";
8
5
 
9
- const PLUGIN_1_URL = "file://" +
10
- path.join(
11
- path.dirname(Bun.fileURLToPath(import.meta.url)),
12
- "../../fixtures/ValidPlugin1.ts",
13
- );
6
+ const PLUGIN_1_URL =
7
+ "file://" +
8
+ path.join(path.dirname(Bun.fileURLToPath(import.meta.url)), "../../fixtures/ValidPlugin1.ts");
14
9
 
15
10
  describe("UrlPluginSource Tests", () => {
16
11
  test("Throws on empty set of URLs", () => {
@@ -18,10 +13,8 @@ describe("UrlPluginSource Tests", () => {
18
13
  });
19
14
 
20
15
  test("Throws on invalid URL", () => {
21
- expect(() =>
22
- new UrlListPluginRepository(
23
- new Set([{ url: "foo", extensionPoints: ["bar"] }]),
24
- )
16
+ expect(
17
+ () => new UrlListPluginRepository(new Set([{ url: "foo", extensionPoints: ["bar"] }])),
25
18
  ).toThrow();
26
19
  });
27
20
 
@@ -32,11 +25,7 @@ describe("UrlPluginSource Tests", () => {
32
25
  const pluginRepository = new UrlListPluginRepository(urlSet);
33
26
 
34
27
  let count = 0;
35
- for await (
36
- const extensionEntry of pluginRepository.scanForExtensions(
37
- EXTENSION_POINT_1,
38
- )
39
- ) {
28
+ for await (const extensionEntry of pluginRepository.scanForExtensions(EXTENSION_POINT_1)) {
40
29
  expect(extensionEntry.pluginId).toEqual(PLUGIN_1_URL);
41
30
  expect(extensionEntry.extensionPoint).toEqual(EXTENSION_POINT_1);
42
31
  count += 1;
@@ -51,11 +40,7 @@ describe("UrlPluginSource Tests", () => {
51
40
  const pluginRepository = new UrlListPluginRepository(urlSet);
52
41
 
53
42
  let count = 0;
54
- for await (
55
- const extensionEntry of pluginRepository.scanForExtensions(
56
- EXTENSION_POINT_2,
57
- )
58
- ) {
43
+ for await (const extensionEntry of pluginRepository.scanForExtensions(EXTENSION_POINT_2)) {
59
44
  // this should not be called
60
45
  expect(extensionEntry.pluginId, "not good");
61
46
  count += 1;
@@ -2,27 +2,19 @@ import { describe, expect, test } from "bun:test";
2
2
  import path from "node:path";
3
3
  import UrlPluginSource from "../../../src/plugin_manager/plugin_repository/UrlPluginSource.ts";
4
4
 
5
- const PLUGIN_1_URL = "file://" +
6
- path.join(
7
- path.dirname(Bun.fileURLToPath(import.meta.url)),
8
- "../../fixtures/ValidPlugin1.ts",
9
- );
10
-
11
- const INVALID_PLUGIN_URL = "file://" +
12
- path.join(
13
- path.dirname(Bun.fileURLToPath(import.meta.url)),
14
- "../../fixtures/InvalidPlugin1.ts",
15
- );
5
+ const PLUGIN_1_URL =
6
+ "file://" +
7
+ path.join(path.dirname(Bun.fileURLToPath(import.meta.url)), "../../fixtures/ValidPlugin1.ts");
8
+
9
+ const INVALID_PLUGIN_URL =
10
+ "file://" +
11
+ path.join(path.dirname(Bun.fileURLToPath(import.meta.url)), "../../fixtures/InvalidPlugin1.ts");
16
12
 
17
13
  describe("UrlPluginSource Tests", () => {
18
14
  test("Throws on invalid plugin", () => {
19
15
  const urlPluginSource = new UrlPluginSource();
20
16
 
21
- expect(
22
- urlPluginSource.loadPlugin(
23
- new URL(INVALID_PLUGIN_URL),
24
- ),
25
- ).rejects.toThrow();
17
+ expect(urlPluginSource.loadPlugin(new URL(INVALID_PLUGIN_URL))).rejects.toThrow();
26
18
  });
27
19
 
28
20
  test("Loads a plugin", async () => {
@@ -1,21 +1,16 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import InMemoryExtensionPointRegistry from "../../../src/plugin_manager/registry/InMemoryExtensionPointRegistry.ts";
3
- import {
4
- EXTENSION_POINT_1,
5
- EXTENSION_POINT_2,
6
- } from "../../fixtures/Constants.ts";
3
+ import { EXTENSION_POINT_1, EXTENSION_POINT_2 } from "../../fixtures/Constants.ts";
7
4
 
8
5
  describe("InMemoryExtensionPointRegistry Tests", () => {
9
6
  test("Register extension point", async () => {
10
7
  const extensionPointRegistry = new InMemoryExtensionPointRegistry();
11
8
 
12
- expect(await extensionPointRegistry.isRegistered(EXTENSION_POINT_1))
13
- .toBeFalse();
9
+ expect(await extensionPointRegistry.isRegistered(EXTENSION_POINT_1)).toBeFalse();
14
10
 
15
11
  await extensionPointRegistry.register(EXTENSION_POINT_1);
16
12
 
17
- expect(await extensionPointRegistry.isRegistered(EXTENSION_POINT_1))
18
- .toBeTrue();
13
+ expect(await extensionPointRegistry.isRegistered(EXTENSION_POINT_1)).toBeTrue();
19
14
  });
20
15
 
21
16
  test("Cannot register extension point twice", async () => {
@@ -23,8 +18,7 @@ describe("InMemoryExtensionPointRegistry Tests", () => {
23
18
 
24
19
  await extensionPointRegistry.register(EXTENSION_POINT_1);
25
20
 
26
- expect(extensionPointRegistry.register(EXTENSION_POINT_1))
27
- .rejects.toThrow();
21
+ expect(extensionPointRegistry.register(EXTENSION_POINT_1)).rejects.toThrow();
28
22
  });
29
23
 
30
24
  test("Get registered extension points", async () => {
@@ -28,8 +28,7 @@ describe("InMemoryExtensionRegistry Tests", () => {
28
28
  test("Register extension", async () => {
29
29
  const extensionRegistry = new InMemoryExtensionRegistry();
30
30
 
31
- expect(extensionRegistry.get(EXTENSION_1_HANDLE)).rejects
32
- .toThrow();
31
+ expect(extensionRegistry.get(EXTENSION_1_HANDLE)).rejects.toThrow();
33
32
 
34
33
  await extensionRegistry.register(EXTENSION_1_HANDLE, extensionEntry1);
35
34
 
@@ -41,8 +40,7 @@ describe("InMemoryExtensionRegistry Tests", () => {
41
40
 
42
41
  await extensionRegistry.register(EXTENSION_1_HANDLE, extensionEntry1);
43
42
 
44
- expect(extensionRegistry.register(EXTENSION_1_HANDLE, extensionEntry1))
45
- .rejects.toThrow();
43
+ expect(extensionRegistry.register(EXTENSION_1_HANDLE, extensionEntry1)).rejects.toThrow();
46
44
  });
47
45
 
48
46
  test("Get registered extensions", async () => {
@@ -51,22 +49,14 @@ describe("InMemoryExtensionRegistry Tests", () => {
51
49
  await extensionRegistry.register(EXTENSION_1_HANDLE, extensionEntry1);
52
50
  await extensionRegistry.register(EXTENSION_2_HANDLE, extensionEntry2);
53
51
 
54
- const extensionEntries1 = await extensionRegistry.getExtensions(
55
- EXTENSION_POINT_1,
56
- );
52
+ const extensionEntries1 = await extensionRegistry.getExtensions(EXTENSION_POINT_1);
57
53
 
58
54
  expect(extensionEntries1.size).toEqual(1);
59
- expect(
60
- extensionEntries1.get(EXTENSION_1_HANDLE)?.extensionId,
61
- ).toEqual(EXTENSION_1_ID);
55
+ expect(extensionEntries1.get(EXTENSION_1_HANDLE)?.extensionId).toEqual(EXTENSION_1_ID);
62
56
 
63
- const extensionEntries2 = await extensionRegistry.getExtensions(
64
- EXTENSION_POINT_2,
65
- );
57
+ const extensionEntries2 = await extensionRegistry.getExtensions(EXTENSION_POINT_2);
66
58
 
67
59
  expect(extensionEntries2.size).toEqual(1);
68
- expect(
69
- extensionEntries2.get(EXTENSION_2_HANDLE)?.extensionId,
70
- ).toEqual(EXTENSION_2_ID);
60
+ expect(extensionEntries2.get(EXTENSION_2_HANDLE)?.extensionId).toEqual(EXTENSION_2_ID);
71
61
  });
72
62
  });
@@ -10,84 +10,58 @@ describe("PluginLoader Tests", () => {
10
10
  });
11
11
 
12
12
  test("Rejects invalid modules", async () => {
13
- const pluginLoadResult = await loadPlugin(
14
- "../plugin_repository/UrlListPluginRepository.ts",
15
- );
13
+ const pluginLoadResult = await loadPlugin("../plugin_repository/UrlListPluginRepository.ts");
16
14
 
17
15
  expect(pluginLoadResult.isValidPlugin).toBeFalse();
18
- expect(
19
- pluginLoadResult.error?.message,
20
- ).toEqual(
16
+ expect(pluginLoadResult.error?.message).toEqual(
21
17
  "Plugin from ../plugin_repository/UrlListPluginRepository.ts does not provide an extensionDescriptors array",
22
18
  );
23
19
  });
24
20
 
25
21
  test("Rejects invalid plugins", async () => {
26
- let pluginLoadResult = await loadPlugin(
27
- "../../../tests/fixtures/InvalidPlugin1.ts",
28
- );
22
+ let pluginLoadResult = await loadPlugin("../../../tests/fixtures/InvalidPlugin1.ts");
29
23
 
30
24
  expect(pluginLoadResult.isValidPlugin).toBeFalse();
31
25
  expect(pluginLoadResult.error?.name).toEqual("Error");
32
- expect(
33
- pluginLoadResult.error?.message,
34
- ).toEqual(
26
+ expect(pluginLoadResult.error?.message).toEqual(
35
27
  "Plugin from ../../../tests/fixtures/InvalidPlugin1.ts does not provide an extensionDescriptors array",
36
28
  );
37
29
 
38
- pluginLoadResult = await loadPlugin(
39
- "../../../tests/fixtures/InvalidPlugin2.ts",
40
- );
30
+ pluginLoadResult = await loadPlugin("../../../tests/fixtures/InvalidPlugin2.ts");
41
31
 
42
32
  expect(pluginLoadResult.isValidPlugin).toBeFalse();
43
33
  expect(pluginLoadResult.error?.name).toEqual("Error");
44
- expect(
45
- pluginLoadResult.error?.message,
46
- ).toEqual(
34
+ expect(pluginLoadResult.error?.message).toEqual(
47
35
  "Plugin from ../../../tests/fixtures/InvalidPlugin2.ts does not provide an extensionPoint string in one of the extensionDescriptors",
48
36
  );
49
37
 
50
- pluginLoadResult = await loadPlugin(
51
- "../../../tests/fixtures/InvalidPlugin3.ts",
52
- );
38
+ pluginLoadResult = await loadPlugin("../../../tests/fixtures/InvalidPlugin3.ts");
53
39
 
54
40
  expect(pluginLoadResult.isValidPlugin).toBeFalse();
55
41
  expect(pluginLoadResult.error?.name).toEqual("Error");
56
- expect(
57
- pluginLoadResult.error?.message,
58
- ).toEqual(
42
+ expect(pluginLoadResult.error?.message).toEqual(
59
43
  "Plugin from ../../../tests/fixtures/InvalidPlugin3.ts does not provide a factory with a create function in one of the extensionDescriptors",
60
44
  );
61
45
 
62
- pluginLoadResult = await loadPlugin(
63
- "../../../tests/fixtures/InvalidPlugin4.ts",
64
- );
46
+ pluginLoadResult = await loadPlugin("../../../tests/fixtures/InvalidPlugin4.ts");
65
47
 
66
48
  expect(pluginLoadResult.isValidPlugin).toBeFalse();
67
49
  expect(pluginLoadResult.error?.name).toEqual("Error");
68
- expect(
69
- pluginLoadResult.error?.message,
70
- ).toEqual(
50
+ expect(pluginLoadResult.error?.message).toEqual(
71
51
  "Plugin from ../../../tests/fixtures/InvalidPlugin4.ts does not provide a factory with a create function in one of the extensionDescriptors",
72
52
  );
73
53
 
74
- pluginLoadResult = await loadPlugin(
75
- "../../../tests/fixtures/InvalidPlugin5.ts",
76
- );
54
+ pluginLoadResult = await loadPlugin("../../../tests/fixtures/InvalidPlugin5.ts");
77
55
 
78
56
  expect(pluginLoadResult.isValidPlugin).toBeFalse();
79
57
  expect(pluginLoadResult.error?.name).toEqual("Error");
80
- expect(
81
- pluginLoadResult.error?.message,
82
- ).toEqual(
58
+ expect(pluginLoadResult.error?.message).toEqual(
83
59
  "Plugin from ../../../tests/fixtures/InvalidPlugin5.ts does not provide a factory with a create function in one of the extensionDescriptors",
84
60
  );
85
61
  });
86
62
 
87
63
  test("Returns valid plugin", async () => {
88
- const pluginLoadResult = await loadPlugin(
89
- "../../../tests/fixtures/ValidPlugin1.ts",
90
- );
64
+ const pluginLoadResult = await loadPlugin("../../../tests/fixtures/ValidPlugin1.ts");
91
65
 
92
66
  expect(pluginLoadResult.isValidPlugin).toBeTrue();
93
67
  expect(pluginLoadResult.error).toBeUndefined();
package/tsconfig.json CHANGED
@@ -8,6 +8,8 @@
8
8
  "jsx": "react-jsx",
9
9
  "allowJs": true,
10
10
 
11
+ "types": ["bun", "node"],
12
+
11
13
  // Bundler mode
12
14
  "moduleResolution": "bundler",
13
15
  "allowImportingTsExtensions": true,