diff --git a/netlify/functions/api.mjs b/netlify/functions/api.mjs index 184d194..d0025f0 100644 --- a/netlify/functions/api.mjs +++ b/netlify/functions/api.mjs @@ -1,9 +1,7 @@ import { getStore } from '@netlify/blobs'; import { createApi } from '../../src/api.js'; -let api; -// Netlify v2 request handler; state is persisted in strongly consistent Blobs. +// Netlify v2 request handler; refresh request-scoped Blob credentials on every invocation. export default async function handler(request) { - api ||= createApi({ store: getStore('releasecue', { consistency: 'strong' }) }); - return api(request); + return createApi({ store: getStore('releasecue', { consistency: 'strong' }) })(request); } diff --git a/test/build.test.js b/test/build.test.js index 6441558..a3e02cb 100644 --- a/test/build.test.js +++ b/test/build.test.js @@ -5,6 +5,12 @@ import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { build } from '../scripts/build.mjs'; +test('function refreshes the request-scoped Blob client instead of caching expiring credentials', async () => { + const wrapper = await readFile(new URL('../netlify/functions/api.mjs', import.meta.url), 'utf8'); + assert.doesNotMatch(wrapper, /api\s*\|\|=/); + assert.match(wrapper, /createApi\(\{ store: getStore\('releasecue', \{ consistency: 'strong' \}\) \}\)\(request\)/); +}); + test('client ships onboarding, persistent dashboard, and release action surfaces with recovery states', async () => { const [html, app, config] = await Promise.all([ readFile(new URL('../public/index.html', import.meta.url), 'utf8'),