UNPKG

3.57 kBJavaScriptView Raw
1import { WebUUID } from 'web-uuid';
2import { Base64Decoder, Base64Encoder } from 'base64-encoding';
3import { createDraft, finishDraft, enableMapSet } from 'immer';
4import { Encoder as BinaryEncoder, Decoder as BinaryDecoder } from 'msgpackr';
5enableMapSet();
6const stringifySessionCookie = (value) => new Base64Encoder({ url: true }).encode(new BinaryEncoder({ structuredClone: true }).encode(value));
7const parseSessionCookie = (value) => new BinaryDecoder({ structuredClone: true }).decode(new Base64Decoder().decode(value));
8/**
9 * Cookie session middleware for worker runtimes.
10 *
11 * Requires a cookie store, preferably encrypted or signed.
12 *
13 * Important: This will serialize the entire session data and store it in a cookie. It is sent with every request!
14 * Only applicable for small session objects. Use `storageSession` for a traditional, KV store-backed session.
15 */
16export function cookieSession(options = {}) {
17 return async (ax) => {
18 const ctx = await ax;
19 const { cookieStore, cookies } = ctx;
20 const { defaultSession, cookieName = 'obj', expirationTtl = 5 * 60 } = options;
21 const cookieVal = cookies[cookieName];
22 const original = cookieVal ? parseSessionCookie(cookieVal) : defaultSession !== null && defaultSession !== void 0 ? defaultSession : {};
23 const session = createDraft(original);
24 const newContext = Object.assign(ctx, { session, cookieSession: session });
25 ctx.effects.push(() => {
26 const next = finishDraft(session);
27 if (next !== original) {
28 cookieStore.set({
29 name: cookieName,
30 value: stringifySessionCookie(next),
31 expires: new Date(Date.now() + expirationTtl * 1000),
32 sameSite: 'lax',
33 httpOnly: true,
34 });
35 }
36 });
37 return newContext;
38 };
39}
40/**
41 * Session middleware for worker runtimes.
42 *
43 * Need to provide a `StorageArea` to persist the session between requests.
44 * See `@worker-tools/kv-storage`.
45 *
46 */
47// FIXME: Will "block" until session object is retrieved from KV => provide "unyielding" version that returns a promise?
48export function storageSession(options) {
49 return async (ax) => {
50 var _a, _b;
51 const ctx = await ax;
52 const { cookies, cookieStore } = ctx;
53 const { storage, defaultSession, cookieName = 'sid', expirationTtl = 5 * 60 } = options;
54 const cookieVal = cookies[cookieName];
55 const sid = cookieVal ? new WebUUID(cookieVal) : WebUUID.v4();
56 const original = (_b = (_a = (await storage.get(sid))) !== null && _a !== void 0 ? _a : defaultSession) !== null && _b !== void 0 ? _b : {};
57 const session = createDraft(original);
58 const newContext = Object.assign(ctx, { session, storageSession: session });
59 ctx.waitUntil((async () => {
60 await ctx.handled;
61 await ctx.flushed;
62 const next = finishDraft(session);
63 if (next !== original) {
64 await storage.set(sid, next, { expirationTtl });
65 }
66 })());
67 if (!cookieVal) {
68 ctx.effects.push(() => {
69 cookieStore.set({
70 name: cookieName,
71 value: sid.id,
72 expires: new Date(Date.now() + expirationTtl * 1000),
73 sameSite: 'lax',
74 httpOnly: true,
75 });
76 });
77 }
78 return newContext;
79 };
80}
81//# sourceMappingURL=session.js.map
\No newline at end of file