summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Matt Kane <m@mk.gg> 2025-04-02 11:30:54 +0100
committerGravatar GitHub <noreply@github.com> 2025-04-02 11:30:54 +0100
commitc43bf8cd0513c2260d4ba32b5beffe97306e2e09 (patch)
tree15de99ad7cd7f91f9db558a569e4ece40621c7e8
parent0edd79fb54906e40dfc423534e387d1fe1855f7e (diff)
downloadastro-c43bf8cd0513c2260d4ba32b5beffe97306e2e09.tar.gz
astro-c43bf8cd0513c2260d4ba32b5beffe97306e2e09.tar.zst
astro-c43bf8cd0513c2260d4ba32b5beffe97306e2e09.zip
feat: add `session.load(id)` method (#13539)
-rw-r--r--.changeset/famous-areas-peel.md7
-rw-r--r--packages/astro/src/core/session.ts14
-rw-r--r--packages/astro/test/fixtures/sessions/src/actions/index.ts8
-rw-r--r--packages/astro/test/sessions.test.js28
4 files changed, 57 insertions, 0 deletions
diff --git a/.changeset/famous-areas-peel.md b/.changeset/famous-areas-peel.md
new file mode 100644
index 000000000..958f1c0be
--- /dev/null
+++ b/.changeset/famous-areas-peel.md
@@ -0,0 +1,7 @@
+---
+'astro': patch
+---
+
+Adds support for loading a session by ID
+
+Adds a new `session.load()` method to the experimental session API that allows you to load a session by ID. In normal use a session is loaded automatically from the session cookie. This method allows a session to be loaded manually instead. This is useful for cases where the session ID has been persisted somewhere other than the browser cookie. For example, a session ID might be stored in a user database. This would allow that user's session to be loaded when logging-in on another device or in a different browser. It would also allow a session to be loaded in an API when cookies can't be set, such as when loading across domains.
diff --git a/packages/astro/src/core/session.ts b/packages/astro/src/core/session.ts
index 89bffd348..f86fa536b 100644
--- a/packages/astro/src/core/session.ts
+++ b/packages/astro/src/core/session.ts
@@ -293,6 +293,20 @@ export class AstroSession<TDriver extends SessionDriverName = any> {
}
/**
+ * Loads a session from storage with the given ID, and replaces the current session.
+ * Any changes made to the current session will be lost.
+ * This is not normally needed, as the session is automatically loaded using the cookie.
+ * However it can be used to restore a session where the ID has been recorded somewhere
+ * else (e.g. in a database).
+ */
+ async load(sessionID: string) {
+ this.#sessionID = sessionID;
+ this.#data = undefined;
+ await this.#setCookie();
+ await this.#ensureData();
+ }
+
+ /**
* Sets the session cookie.
*/
async #setCookie() {
diff --git a/packages/astro/test/fixtures/sessions/src/actions/index.ts b/packages/astro/test/fixtures/sessions/src/actions/index.ts
index 856f68ba8..5935e9ea0 100644
--- a/packages/astro/test/fixtures/sessions/src/actions/index.ts
+++ b/packages/astro/test/fixtures/sessions/src/actions/index.ts
@@ -17,6 +17,14 @@ export const server = {
return await context.session.get('cart');
},
}),
+ loadCart: defineAction({
+ input: z.object({ id: z.string() }),
+ handler: async (input, context) => {
+ context.session.load(input.id);
+ const cart = await context.session.get('cart');
+ return { cart };
+ }
+ }),
clearCart: defineAction({
accept: 'json',
handler: async (input, context) => {
diff --git a/packages/astro/test/sessions.test.js b/packages/astro/test/sessions.test.js
index 738a0c0a3..212ed4657 100644
--- a/packages/astro/test/sessions.test.js
+++ b/packages/astro/test/sessions.test.js
@@ -99,6 +99,34 @@ describe('Astro.session', () => {
'Favorite URL set to https://example.com/ from https://domain.invalid/',
);
});
+
+ it('can load a session by ID', async () => {
+ const firstResponse = await fetchResponse('/_actions/addToCart', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Origin': 'http://example.com',
+ },
+ body: new URLSearchParams({ productId: 'item1' }),
+ });
+ const firstResponseData = devalue.parse(await firstResponse.text());
+ assert.equal(firstResponseData.cart.includes('item1'), true);
+
+ const firstHeaders = Array.from(app.setCookieHeaders(firstResponse));
+ const firstSessionId = firstHeaders[0].split(';')[0].split('=')[1];
+
+ // Load without a cookie, but with the session ID for the action to load
+ const secondResponse = await fetchResponse('/_actions/loadCart', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ id: firstSessionId }),
+ });
+ const cartData = devalue.parse(await secondResponse.text());
+ assert.deepEqual(cartData.cart, firstResponseData.cart);
+ });
+
});
describe('Development', () => {