Skip to content

Commit

Permalink
fix): allow special characters in Action names
Browse files Browse the repository at this point in the history
  • Loading branch information
ascorbic committed Oct 4, 2024
1 parent 8309d7f commit 88d1143
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-snakes-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Allows special characters in Action names
5 changes: 4 additions & 1 deletion packages/astro/src/actions/runtime/virtual/get-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import type { ActionAccept, ActionClient } from './server.js';
export async function getAction(
path: string,
): Promise<ActionClient<unknown, ActionAccept, ZodType>> {
const pathKeys = path.replace('/_actions/', '').split('.');
const pathKeys = path
.replace('/_actions/', '')
.split('.')
.map((key) => decodeURIComponent(key));
// @ts-expect-error virtual module
let { server: actionLookup } = await import('astro:internal-actions');

Expand Down
5 changes: 4 additions & 1 deletion packages/astro/templates/actions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {
getActionQueryString,
} from 'astro:actions';

const ENCODED_DOT = "%2E";

function toActionProxy(actionCallback = {}, aggregatedPath = '') {
return new Proxy(actionCallback, {
get(target, objKey) {
if (objKey in target || typeof objKey === 'symbol') {
return target[objKey];
}
const path = aggregatedPath + objKey.toString();
// Add the key, encoding dots so they're not interpreted as nested properties.
const path = aggregatedPath + encodeURIComponent(objKey.toString()).replaceAll('.', ENCODED_DOT);
function action(param) {
return handleAction(param, path, this);
}
Expand Down
35 changes: 35 additions & 0 deletions packages/astro/test/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,23 @@ describe('Astro Actions', () => {
assert.equal(data.success, true);
assert.equal(data.isFormData, true, 'Should receive plain FormData');
});

it('Handles special characters in action names', async () => {
for (const name of ['with%2Fslash', 'with%20space', 'with%2Edot']) {
const res = await fixture.fetch(`/_actions/${name}`, {
method: 'POST',
body: JSON.stringify({ name: 'ben' }),
headers: {
'Content-Type': 'application/json',
},
});
assert.equal(res.ok, true);
const text = await res.text();
assert.equal(res.headers.get('Content-Type'), 'application/json+devalue');
const data = devalue.parse(text);
assert.equal(data, 'Hello, ben!');
}
})
});

describe('build', () => {
Expand Down Expand Up @@ -428,6 +445,24 @@ describe('Astro Actions', () => {
const dataRest = devalue.parse(await resRest.text());
assert.equal('fake', dataRest?.uploadId);
});

it('Handles special characters in action names', async () => {
for (const name of ['with%2Fslash', 'with%20space', 'with%2Edot']) {
const req = new Request(`http://example.com/_actions/${name}`, {
method: 'POST',
body: JSON.stringify({ name: 'ben' }),
headers: {
'Content-Type': 'application/json',
},
});
const res = await app.render(req);
assert.equal(res.ok, true);
const text = await res.text();
assert.equal(res.headers.get('Content-Type'), 'application/json+devalue');
const data = devalue.parse(text);
assert.equal(data, 'Hello, ben!');
}
});
});
});

Expand Down
24 changes: 24 additions & 0 deletions packages/astro/test/fixtures/actions/src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,28 @@ export const server = {
};
},
}),
"with.dot": defineAction({
input: z.object({
name: z.string(),
}),
handler: async (input) => {
return `Hello, ${input.name}!`
}
}),
"with space": defineAction({
input: z.object({
name: z.string(),
}),
handler: async (input) => {
return `Hello, ${input.name}!`
}
}),
"with/slash": defineAction({
input: z.object({
name: z.string(),
}),
handler: async (input) => {
return `Hello, ${input.name}!`
}
}),
};

0 comments on commit 88d1143

Please sign in to comment.