/** * Test fixture: a hook that exercises the hook-owned-state accessor across * the process boundary — set/get round trip on both stores, delete, a * committed transaction, a discarded transaction, and the undeclared-name * throw the whole design exists to produce. * * Reports through a JSON file in its state directory: hook return values * are no longer carried anywhere (hook-owned-state D5), and the state * directory is the sanctioned place a hook hands structured data to its * caller. */ import { writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { defineHook } from '@celilo/capabilities'; export default defineHook({ hook: 'on_install', requires: [], handler: async (ctx) => { const report: Record = {}; // Round trip on each store. await ctx.secrets.set('bot_token', 'token-value'); report.secretRoundTrip = await ctx.secrets.get('bot_token'); await ctx.config.set('public_ip', '203.0.113.7'); report.configRoundTrip = await ctx.config.get('public_ip'); // The map surface still answers from the values the context carried. report.mapRead = ctx.config.mapOnlyValue; // Delete removes; deleting a declared-but-never-written name is a no-op. // (An UNDECLARED name on delete throws — that is 3.4, tested below. // Declared-and-absent is the no-op case 3.7 pins.) await ctx.secrets.delete('bot_token'); await ctx.secrets.set('api_key', 'to-delete'); await ctx.secrets.delete('api_key'); report.deletedIsGone = await ctx.secrets.get('api_key'); // Committed transaction: both writes land. await ctx.secrets.transaction((s) => { s.set('bot_token', 'committed-a'); s.set('api_key', 'committed-b'); }); report.transactionA = await ctx.secrets.get('bot_token'); report.transactionB = await ctx.secrets.get('api_key'); // Discarded transaction: nothing lands, including the overwrite of the // committed value above. try { await ctx.secrets.transaction(async (s) => { s.set('bot_token', 'discarded'); throw new Error('hook failed midway'); }); } catch (error) { report.discarded = (error as Error).message; } report.afterDiscard = await ctx.secrets.get('bot_token'); // An undeclared name throws, naming the module and the declared set. try { await ctx.secrets.set('not_declared', 'x'); } catch (error) { report.undeclaredSecret = (error as Error).message; } // Hand the report to the caller through the state directory, the one // channel a hook still has. writeFileSync(join(ctx.stateDir as string, 'report.json'), JSON.stringify(report)); }, });