diff --git a/.changeset/save-preferences-result.md b/.changeset/save-preferences-result.md new file mode 100644 index 00000000..43400ee6 --- /dev/null +++ b/.changeset/save-preferences-result.md @@ -0,0 +1,5 @@ +--- +'@gemstack/framework': patch +--- + +Fix `savePreferences` rejecting the RPC when the underlying write fails. The telefunction advertises a `{ ok: false, error }` result and already returns it for the not-enabled case, but a failed disk write threw straight through, so the client saw a rejected call instead of the typed error. The write is now wrapped, so both failure modes return `{ ok: false }` and the client handles them the same way. diff --git a/packages/framework/src/dashboard-rpc/preferences.telefunc.test.ts b/packages/framework/src/dashboard-rpc/preferences.telefunc.test.ts index f580400e..d9c7eaf3 100644 --- a/packages/framework/src/dashboard-rpc/preferences.telefunc.test.ts +++ b/packages/framework/src/dashboard-rpc/preferences.telefunc.test.ts @@ -1,6 +1,8 @@ import { strict as assert } from 'node:assert' import { test } from 'node:test' +import { provideTelefuncContext } from 'telefunc' import { onPreferences, savePreferences } from './preferences.telefunc.js' +import type { PreferencesStore } from '../registry.js' // Outside a Telefunc `serve({ context })` there is no preferences store on the context — the // same situation as the public relay, which never wires one. The RPCs must degrade safely: a @@ -15,3 +17,15 @@ test('savePreferences with no store is a not-enabled no-op', async () => { const result = await savePreferences({ autopilot: false }) assert.deepEqual(result, { ok: false, error: 'preferences are not enabled on this server' }) }) + +test('savePreferences returns the typed error when the store write fails, not a rejection', async () => { + const store: PreferencesStore = { + read: async () => ({}), + save: async () => { + throw new Error('disk full') + }, + } + provideTelefuncContext({ preferences: store }) + const result = await savePreferences({ autopilot: true }) + assert.deepEqual(result, { ok: false, error: 'failed to save preferences' }) +}) diff --git a/packages/framework/src/dashboard-rpc/preferences.telefunc.ts b/packages/framework/src/dashboard-rpc/preferences.telefunc.ts index a3f05acb..dca97dc9 100644 --- a/packages/framework/src/dashboard-rpc/preferences.telefunc.ts +++ b/packages/framework/src/dashboard-rpc/preferences.telefunc.ts @@ -22,6 +22,12 @@ export async function onPreferences(): Promise { export async function savePreferences(preferences: Preferences): Promise { const store = contextPreferences() if (!store) return { ok: false, error: 'preferences are not enabled on this server' } - await store.save(preferences) - return { ok: true } + // A failed write returns the advertised typed error rather than rejecting the RPC, + // so the client handles it the same as the not-enabled case (both `{ ok: false }`). + try { + await store.save(preferences) + return { ok: true } + } catch { + return { ok: false, error: 'failed to save preferences' } + } }