/** * Guarded plugin auto-detection (8.0.9) — the "install it and it's on" contract. * * With `plugins` unset, brainy probes for the first-party accelerator * (@soulcraft/cor). The probe applies the same loud posture as the explicit * list to everything except "not installed": * - not installed → plain brainy, silently (the free path) * - installed + healthy → registered and activated * - installed + broken → init() THROWS (import failure, invalid shape, * or failed activation — an installed * accelerator never silently vanishes) * - plugins: [] / false → no probe at all (explicit opt-out) * - explicit list → unchanged: required, loud on any failure * * The import is isolated behind the private `importPluginPackage` seam so the * three outcomes are simulated without the package being present. */ import { describe, it, expect, afterEach } from 'vitest' import { Brainy, NounType } from '../../src/index.js' const proto = Brainy.prototype as any const origImport = proto.importPluginPackage afterEach(() => { proto.importPluginPackage = origImport }) /** Simulate the probe outcome per package name. */ function stubImport(fn: (pkg: string) => Promise): string[] { const probed: string[] = [] proto.importPluginPackage = async function (pkg: string) { probed.push(pkg) return fn(pkg) } return probed } function notInstalledError(pkg: string): Error { // Node's exact shape for a missing package in a dynamic ESM import. const e = new Error(`Cannot find package '${pkg}' imported from /app/index.js`) ;(e as any).code = 'ERR_MODULE_NOT_FOUND' return e } /** A minimal valid plugin that registers one well-known provider. */ function validPlugin(name: string) { return { name, activate: async (context: any) => { context.registerProvider('distance', { euclidean: (a: number[], b: number[]) => 0 }) return true } } } describe('Guarded plugin auto-detection (plugins: undefined)', () => { it('not installed → plain brainy, no plugins, init succeeds silently', async () => { const probed = stubImport(async (pkg) => { throw notInstalledError(pkg) }) const brain: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() expect(probed).toContain('@soulcraft/cor') // the probe ran… expect(brain.pluginRegistry.hasActivePlugins()).toBe(false) // …and found nothing, quietly const id = await brain.add({ data: 'works without any plugin', type: NounType.Concept }) expect(id).toBeTruthy() await brain.close() }) it('installed + healthy → auto-registered and activated', async () => { stubImport(async () => ({ default: validPlugin('@soulcraft/cor') })) const brain: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await brain.init() expect(brain.pluginRegistry.getActivePlugins()).toContain('@soulcraft/cor') expect(brain.pluginRegistry.hasProvider('distance')).toBe(true) await brain.close() }) it('installed but import fails (broken install) → init() throws, never a silent JS fallback', async () => { stubImport(async () => { // A resolution failure INSIDE the package (names a sub-path, not the // package) — present but broken, must be loud. const e = new Error( "Cannot find module '/app/node_modules/@soulcraft/cor/dist/native.js' imported from " + '/app/node_modules/@soulcraft/cor/dist/index.js' ) ;(e as any).code = 'ERR_MODULE_NOT_FOUND' throw e }) const brain: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await expect(brain.init()).rejects.toThrow(/installed but failed to load/) }) it('installed but not a valid plugin (missing activate) → init() throws', async () => { stubImport(async () => ({ default: { name: '@soulcraft/cor' } })) // no activate() const brain: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await expect(brain.init()).rejects.toThrow(/not a valid Brainy plugin/) }) it('installed but activation fails → init() throws (activateAll posture applies)', async () => { stubImport(async () => ({ default: { name: '@soulcraft/cor', activate: async () => { throw new Error('native binary missing for platform') } } })) const brain: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true }) await expect(brain.init()).rejects.toThrow(/failed to activate/) }) it('plugins: [] and plugins: false → no probe at all (explicit opt-out)', async () => { const probed = stubImport(async () => ({ default: validPlugin('@soulcraft/cor') })) for (const plugins of [[], false] as const) { const brain: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, plugins: plugins as any, silent: true }) await brain.init() expect(brain.pluginRegistry.hasActivePlugins()).toBe(false) await brain.close() } expect(probed).toEqual([]) // the seam was never touched }) it('explicit list is unchanged: a listed-but-missing plugin throws with the config-pointing message', async () => { stubImport(async (pkg) => { throw notInstalledError(pkg) }) const brain: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, plugins: ['@soulcraft/cor'], silent: true }) await expect(brain.init()).rejects.toThrow(/listed in config\.plugins but could not be loaded/) }) })