diff options
| author | Adam Malczewski <[email protected]> | 2026-06-10 09:51:00 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-10 09:51:00 +0900 |
| commit | a980aba97aed34692dd655e716747c4ff53fb186 (patch) | |
| tree | 56b32ed9db8f66f9f6141eda923f5d1f7c1ba6d9 /packages/host-bin/src/load-external.test.ts | |
| parent | 52ce1bb8b25cc6ba4ba7d2734c35c95e0a08d723 (diff) | |
| download | dispatch-a980aba97aed34692dd655e716747c4ff53fb186.tar.gz dispatch-a980aba97aed34692dd655e716747c4ff53fb186.zip | |
host-bin: external-extension loader + claude credential wiring
Add loadExternalExtensions(): fault-isolated dynamic import of out-of-repo
extensions declared via DISPATCH_EXTERNAL_EXTENSIONS. main.ts assembles the
credential-store in boot() so a 'claude' credential is registered when an
external anthropic provider is loaded; config.ts surfaces the anthropic model /
credential-key settings those extensions read.
Diffstat (limited to 'packages/host-bin/src/load-external.test.ts')
| -rw-r--r-- | packages/host-bin/src/load-external.test.ts | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/packages/host-bin/src/load-external.test.ts b/packages/host-bin/src/load-external.test.ts new file mode 100644 index 0000000..38e3622 --- /dev/null +++ b/packages/host-bin/src/load-external.test.ts @@ -0,0 +1,47 @@ +import type { Logger } from "@dispatch/kernel"; +import { describe, expect, it } from "vitest"; +import { loadExternalExtensions } from "./load-external.js"; + +function makeLogger(): Logger { + const noop = () => {}; + const logger = { + debug: noop, + info: noop, + warn: noop, + error: noop, + child: () => logger, + span: () => { + throw new Error("not used"); + }, + } as unknown as Logger; + return logger; +} + +describe("loadExternalExtensions", () => { + it("returns an empty array for no specifiers", async () => { + expect(await loadExternalExtensions([], makeLogger())).toEqual([]); + }); + + it("loads a real extension module by package name", async () => { + // auth-apikey is a bundled extension that exports `extension`; we use it as + // a stand-in for an external module to exercise the dynamic-import path. + const loaded = await loadExternalExtensions(["@dispatch/auth-apikey"], makeLogger()); + expect(loaded).toHaveLength(1); + expect(loaded[0]?.manifest.id).toBe("auth-apikey"); + }); + + it("skips a specifier that cannot be imported without throwing", async () => { + const loaded = await loadExternalExtensions( + ["./does-not-exist-xyz.js", "@dispatch/auth-apikey"], + makeLogger(), + ); + // The bad one is skipped; the good one still loads. + expect(loaded.map((e) => e.manifest.id)).toEqual(["auth-apikey"]); + }); + + it("skips a module that exports no valid extension", async () => { + // `@dispatch/journal-sink` exports factories but no `extension`. + const loaded = await loadExternalExtensions(["@dispatch/journal-sink"], makeLogger()); + expect(loaded).toEqual([]); + }); +}); |
