diff --git a/.changeset/large-colts-jump.md b/.changeset/large-colts-jump.md new file mode 100644 index 000000000000..faa265319c75 --- /dev/null +++ b/.changeset/large-colts-jump.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixed an issue where attempting to assign a variable onto locals threw an error. diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts index b62ba8bedf88..7e6128c373b2 100644 --- a/packages/astro/src/core/endpoint/index.ts +++ b/packages/astro/src/core/endpoint/index.ts @@ -57,7 +57,9 @@ export function createAPIContext({ ResponseWithEncoding, url: new URL(request.url), get clientAddress() { - if (!(clientAddressSymbol in request)) { + if (clientAddressSymbol in request) { + return Reflect.get(request, clientAddressSymbol) as string; + } if (adapterName) { throw new AstroError({ ...AstroErrorData.ClientAddressNotAvailable, @@ -66,26 +68,31 @@ export function createAPIContext({ } else { throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable); } - } - - return Reflect.get(request, clientAddressSymbol); }, - } as APIContext; - - // We define a custom property, so we can check the value passed to locals - Object.defineProperty(context, 'locals', { - enumerable: true, - get() { - return Reflect.get(request, clientLocalsSymbol); + get locals() { + let locals = Reflect.get(request, clientLocalsSymbol) + + if (locals === undefined) { + locals = {} + Reflect.set(request, clientLocalsSymbol, locals) + } + + if (typeof locals !== 'object') { + throw new AstroError(AstroErrorData.LocalsNotAnObject); + } + + return locals; }, - set(val) { + // We define a custom property, so we can check the value passed to locals + set locals(val) { if (typeof val !== 'object') { throw new AstroError(AstroErrorData.LocalsNotAnObject); } else { Reflect.set(request, clientLocalsSymbol, val); } }, - }); + } satisfies APIContext; + return context; } diff --git a/packages/integrations/node/test/fixtures/locals/src/middleware.ts b/packages/integrations/node/test/fixtures/locals/src/middleware.ts new file mode 100644 index 000000000000..e349ca41d827 --- /dev/null +++ b/packages/integrations/node/test/fixtures/locals/src/middleware.ts @@ -0,0 +1,6 @@ +import { defineMiddleware } from 'astro:middleware'; + +export const onRequest = defineMiddleware(({ url, locals }, next) => { + if (url.pathname === "/from-astro-middleware") locals.foo = "baz"; + return next(); +}) diff --git a/packages/integrations/node/test/fixtures/locals/src/pages/api.js b/packages/integrations/node/test/fixtures/locals/src/pages/api.js index 8b209c5826c3..3c279e37bdba 100644 --- a/packages/integrations/node/test/fixtures/locals/src/pages/api.js +++ b/packages/integrations/node/test/fixtures/locals/src/pages/api.js @@ -1,6 +1,6 @@ -export async function post({ locals }) { - let out = { ...locals }; +export async function POST({ locals }) { + const out = { ...locals }; return new Response(JSON.stringify(out), { headers: { diff --git a/packages/integrations/node/test/fixtures/locals/src/pages/foo.astro b/packages/integrations/node/test/fixtures/locals/src/pages/from-astro-middleware.astro similarity index 100% rename from packages/integrations/node/test/fixtures/locals/src/pages/foo.astro rename to packages/integrations/node/test/fixtures/locals/src/pages/from-astro-middleware.astro diff --git a/packages/integrations/node/test/fixtures/locals/src/pages/from-node-middleware.astro b/packages/integrations/node/test/fixtures/locals/src/pages/from-node-middleware.astro new file mode 100644 index 000000000000..224a875ecc8f --- /dev/null +++ b/packages/integrations/node/test/fixtures/locals/src/pages/from-node-middleware.astro @@ -0,0 +1,4 @@ +--- +const { foo } = Astro.locals; +--- +

{foo}

diff --git a/packages/integrations/node/test/locals.test.js b/packages/integrations/node/test/locals.test.js index f7fc6b73f320..b593f3eb96b1 100644 --- a/packages/integrations/node/test/locals.test.js +++ b/packages/integrations/node/test/locals.test.js @@ -15,11 +15,10 @@ describe('API routes', () => { await fixture.build(); }); - it('Can render locals in page', async () => { + it('Can use locals added by node middleware', async () => { const { handler } = await import('./fixtures/locals/dist/server/entry.mjs'); let { req, res, text } = createRequestAndResponse({ - method: 'POST', - url: '/foo', + url: '/from-node-middleware', }); let locals = { foo: 'bar' }; @@ -32,6 +31,34 @@ describe('API routes', () => { expect(html).to.contain('

bar

'); }); + it('Throws an error when provided non-objects as locals', async () => { + const { handler } = await import('./fixtures/locals/dist/server/entry.mjs'); + let { req, res, done } = createRequestAndResponse({ + url: '/from-node-middleware', + }); + + handler(req, res, undefined, "locals"); + req.send(); + + await done; + expect(res).to.deep.include({ statusCode: 500 }); + }); + + it('Can use locals added by astro middleware', async () => { + const { handler } = await import('./fixtures/locals/dist/server/entry.mjs'); + + const { req, res, text } = createRequestAndResponse({ + url: '/from-astro-middleware', + }); + + handler(req, res, () => {}); + req.send(); + + const html = await text(); + + expect(html).to.contain('

baz

'); + }); + it('Can access locals in API', async () => { const { handler } = await import('./fixtures/locals/dist/server/entry.mjs'); let { req, res, done } = createRequestAndResponse({