iron-session 8.0.3 → 9.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,271 +1,375 @@
1
- import { serialize, parse } from 'cookie';
2
- import { seal, defaults, unseal } from 'iron-webcrypto';
3
- import * as crypto from 'uncrypto';
4
-
5
- // src/core.ts
6
- var timestampSkewSec = 60;
7
- var fourteenDaysInSeconds = 14 * 24 * 3600;
8
- var currentMajorVersion = 2;
9
- var versionDelimiter = "~";
10
- var defaultOptions = {
11
- ttl: fourteenDaysInSeconds,
12
- cookieOptions: { httpOnly: true, secure: true, sameSite: "lax", path: "/" }
1
+ import { parseCookie, stringifySetCookie } from "cookie";
2
+ import { defaults, seal, unseal } from "iron-webcrypto";
3
+ //#region src/core.ts
4
+ const timestampSkewSec = 60;
5
+ const fourteenDaysInSeconds = 1209600;
6
+ const currentMajorVersion = 2;
7
+ const versionDelimiter = "~";
8
+ const defaultOptions = {
9
+ ttl: fourteenDaysInSeconds,
10
+ cookieOptions: {
11
+ httpOnly: true,
12
+ secure: true,
13
+ sameSite: "lax",
14
+ path: "/"
15
+ },
16
+ chunk: false
13
17
  };
14
18
  function normalizeStringPasswordToMap(password) {
15
- return typeof password === "string" ? { 1: password } : password;
19
+ return typeof password === "string" ? { 1: password } : password;
16
20
  }
17
- function parseSeal(seal) {
18
- const [sealWithoutVersion, tokenVersionAsString] = seal.split(versionDelimiter);
19
- const tokenVersion = tokenVersionAsString == null ? null : parseInt(tokenVersionAsString, 10);
20
- return { sealWithoutVersion, tokenVersion };
21
+ /**
22
+ * Removes the trailing `~<version>` marker from a seal.
23
+ *
24
+ * The marker sits outside the seal's HMAC, so it is attacker-controlled and its
25
+ * value must never select a code path. iron-session v8 used it to unwrap a
26
+ * `persistent` key from v6-era cookies; v9 drops that format, so the marker is
27
+ * now inert metadata that we strip and ignore.
28
+ */
29
+ function stripVersion(seal) {
30
+ const delimiterIndex = seal.indexOf(versionDelimiter);
31
+ return delimiterIndex === -1 ? seal : seal.slice(0, delimiterIndex);
21
32
  }
22
33
  function computeCookieMaxAge(ttl) {
23
- if (ttl === 0) {
24
- return 2147483647;
25
- }
26
- return ttl - timestampSkewSec;
34
+ if (ttl === 0) return 2147483647;
35
+ return Math.max(1, ttl - timestampSkewSec);
36
+ }
37
+ async function sealData(data, { password, ttl = fourteenDaysInSeconds }) {
38
+ const passwordsMap = normalizeStringPasswordToMap(password);
39
+ const mostRecentPasswordId = Math.max(...Object.keys(passwordsMap).map(Number));
40
+ const secret = passwordsMap[mostRecentPasswordId];
41
+ if (secret === void 0) throw new Error("iron-session: Bad usage. The password map has no usable entry, it must be a non-empty object keyed by numbers, for example { 1: 'your-password' }.");
42
+ const passwordForSeal = {
43
+ id: mostRecentPasswordId.toString(),
44
+ secret
45
+ };
46
+ let seal$1;
47
+ try {
48
+ seal$1 = await seal(data !== null && typeof data === "object" ? { ...data } : data, passwordForSeal, {
49
+ ...defaults,
50
+ ttl: ttl * 1e3
51
+ });
52
+ } catch (error) {
53
+ if (error instanceof Error && error.message === "Data is not JSON serializable") throw new Error("iron-session: The session data is not JSON serializable. Store plain JSON values only: a Date must be stored as a timestamp (Date.now()) or an ISO string, and Map/Set/BigInt/undefined/functions are not supported.", { cause: error });
54
+ throw error;
55
+ }
56
+ return `${seal$1}${versionDelimiter}${currentMajorVersion}`;
57
+ }
58
+ function classifyUnsealError(error) {
59
+ if (!(error instanceof Error)) return "invalid";
60
+ if (error.message.startsWith("Expired seal")) return "expired";
61
+ if (error.message.startsWith("Cannot find password")) return "unknown-password";
62
+ return "invalid";
63
+ }
64
+ async function unsealData(seal, { password, ttl = fourteenDaysInSeconds, onUnsealError }) {
65
+ const passwordsMap = normalizeStringPasswordToMap(password);
66
+ const sealWithoutVersion = stripVersion(seal);
67
+ try {
68
+ return await unseal(sealWithoutVersion, passwordsMap, {
69
+ ...defaults,
70
+ ttl: ttl * 1e3
71
+ }) ?? {};
72
+ } catch (error) {
73
+ onUnsealError?.(classifyUnsealError(error), error);
74
+ return {};
75
+ }
76
+ }
77
+ function isWebRequest(req) {
78
+ return "headers" in req && typeof req.headers.get === "function";
79
+ }
80
+ function readCookieHeader(req, name) {
81
+ const header = isWebRequest(req) ? req.headers.get("cookie") : req.headers.cookie;
82
+ return parseCookie(header ?? "")[name];
83
+ }
84
+ function cookieHeaderNames(req) {
85
+ const header = isWebRequest(req) ? req.headers.get("cookie") : req.headers.cookie;
86
+ return Object.keys(parseCookie(header ?? ""));
27
87
  }
28
- function getCookie(req, cookieName) {
29
- return parse(
30
- ("headers" in req && typeof req.headers.get === "function" ? req.headers.get("cookie") : req.headers.cookie) ?? ""
31
- )[cookieName] ?? "";
88
+ /**
89
+ * `cookie@2` rejects an explicit `maxAge: undefined` under
90
+ * `exactOptionalPropertyTypes`, and an absent `Max-Age` is exactly what
91
+ * `maxAge: undefined` is meant to produce, so the key is dropped instead of
92
+ * forwarded.
93
+ */
94
+ function serializeCookie(name, value, { maxAge, ...cookieOptions }) {
95
+ return stringifySetCookie({
96
+ ...cookieOptions,
97
+ ...maxAge === void 0 ? {} : { maxAge },
98
+ name,
99
+ value
100
+ });
32
101
  }
33
- function getServerActionCookie(cookieName, cookieHandler) {
34
- const cookieObject = cookieHandler.get(cookieName);
35
- const cookie = cookieObject?.value;
36
- if (typeof cookie === "string") {
37
- return cookie;
38
- }
39
- return "";
102
+ /**
103
+ * Node's `http` server, which is also Express, Connect and Next.js API routes.
104
+ *
105
+ * @example
106
+ * const session = await getIronSession(nodeCookies(req, res), options);
107
+ */
108
+ function nodeCookies(req, res) {
109
+ return {
110
+ read: (name) => readCookieHeader(req, name),
111
+ names: () => cookieHeaderNames(req),
112
+ assertWritable: () => {
113
+ if (res.headersSent) throw new Error("iron-session: Cannot set session cookie: session.save() was called after headers were sent. Make sure to call it before any res.send() or res.end()");
114
+ },
115
+ write: (name, value, options) => {
116
+ const existing = res.getHeader("set-cookie") ?? [];
117
+ const previous = Array.isArray(existing) ? existing : [existing.toString()];
118
+ res.setHeader("set-cookie", [...previous, serializeCookie(name, value, options)]);
119
+ }
120
+ };
40
121
  }
41
- function setCookie(res, cookieValue) {
42
- if ("headers" in res && typeof res.headers.append === "function") {
43
- res.headers.append("set-cookie", cookieValue);
44
- return;
45
- }
46
- let existingSetCookie = res.getHeader("set-cookie") ?? [];
47
- if (!Array.isArray(existingSetCookie)) {
48
- existingSetCookie = [existingSetCookie.toString()];
49
- }
50
- res.setHeader("set-cookie", [
51
- ...existingSetCookie,
52
- cookieValue
53
- ]);
122
+ /**
123
+ * Web-standard `Request` plus anything with appendable headers: a `Response`,
124
+ * a bare `Headers`, or Next's `NextResponse`.
125
+ *
126
+ * @example
127
+ * const headers = new Headers();
128
+ * const session = await getIronSession(webCookies(request, headers), options);
129
+ * return new Response(body, { headers });
130
+ */
131
+ function webCookies(request, response) {
132
+ const headers = response instanceof Headers ? response : response.headers;
133
+ return {
134
+ read: (name) => readCookieHeader(request, name),
135
+ names: () => cookieHeaderNames(request),
136
+ assertWritable: () => {
137
+ if (response instanceof Response && response.bodyUsed) throw new Error("iron-session: Cannot set session cookie: the response body has already been consumed, so the Set-Cookie header would be dropped. Call session.save() before returning or streaming the response.");
138
+ },
139
+ write: (name, value, options) => {
140
+ headers.append("set-cookie", serializeCookie(name, value, options));
141
+ }
142
+ };
54
143
  }
55
- function createSealData(_crypto) {
56
- return async function sealData2(data, {
57
- password,
58
- ttl = fourteenDaysInSeconds
59
- }) {
60
- const passwordsMap = normalizeStringPasswordToMap(password);
61
- const mostRecentPasswordId = Math.max(
62
- ...Object.keys(passwordsMap).map(Number)
63
- );
64
- const passwordForSeal = {
65
- id: mostRecentPasswordId.toString(),
66
- secret: passwordsMap[mostRecentPasswordId]
67
- };
68
- const seal$1 = await seal(_crypto, data, passwordForSeal, {
69
- ...defaults,
70
- ttl: ttl * 1e3
71
- });
72
- return `${seal$1}${versionDelimiter}${currentMajorVersion}`;
73
- };
144
+ /**
145
+ * Next.js `proxy.ts` (called `middleware.ts` before Next 16).
146
+ *
147
+ * Writing a raw `set-cookie` header here does not work the way you would
148
+ * expect: Next only merges a cookie into the current render when it goes
149
+ * through `response.cookies.set()`, so `session.save()` in middleware appeared
150
+ * to succeed and then vanished. Writing to `request.cookies` as well makes the
151
+ * new value visible to code that reads the session later in the same request,
152
+ * which is what makes rotation work.
153
+ *
154
+ * @example
155
+ * export async function proxy(request: NextRequest) {
156
+ * const response = NextResponse.next();
157
+ * const session = await getIronSession(nextProxyCookies(request, response), options);
158
+ * session.lastSeen = Date.now();
159
+ * await session.save();
160
+ * return response;
161
+ * }
162
+ */
163
+ function nextProxyCookies(request, response) {
164
+ return {
165
+ read: (name) => request.cookies.get(name)?.value,
166
+ names: () => request.cookies.getAll?.().map((cookie) => cookie.name) ?? [],
167
+ write: (name, value, options) => {
168
+ response.cookies.set(name, value, options);
169
+ request.cookies.set(name, value);
170
+ }
171
+ };
74
172
  }
75
- function createUnsealData(_crypto) {
76
- return async function unsealData2(seal, {
77
- password,
78
- ttl = fourteenDaysInSeconds
79
- }) {
80
- const passwordsMap = normalizeStringPasswordToMap(password);
81
- const { sealWithoutVersion, tokenVersion } = parseSeal(seal);
82
- try {
83
- const data = await unseal(_crypto, sealWithoutVersion, passwordsMap, {
84
- ...defaults,
85
- ttl: ttl * 1e3
86
- }) ?? {};
87
- if (tokenVersion === 2) {
88
- return data;
89
- }
90
- return { ...data.persistent };
91
- } catch (error) {
92
- if (error instanceof Error && /^(Expired seal|Bad hmac value|Cannot find password|Incorrect number of sealed components)/.test(
93
- error.message
94
- )) {
95
- return {};
96
- }
97
- throw error;
98
- }
99
- };
173
+ /** Wraps a `cookies()`-style store (Next's App Router) in a jar. */
174
+ function cookieStoreJar(cookieStore) {
175
+ return {
176
+ read: (name) => cookieStore.get(name)?.value,
177
+ names: () => cookieStore.getAll?.().map((cookie) => cookie.name) ?? [],
178
+ write: (name, value, options) => {
179
+ cookieStore.set(name, value, options);
180
+ }
181
+ };
182
+ }
183
+ function isCookieJar(value) {
184
+ return typeof value === "object" && value !== null && typeof value.read === "function" && typeof value.write === "function";
185
+ }
186
+ function isCookieStore(value) {
187
+ return typeof value === "object" && value !== null && typeof value.get === "function" && typeof value.set === "function";
100
188
  }
101
189
  function getSessionConfig(sessionOptions) {
102
- const options = {
103
- ...defaultOptions,
104
- ...sessionOptions,
105
- cookieOptions: {
106
- ...defaultOptions.cookieOptions,
107
- ...sessionOptions.cookieOptions || {}
108
- }
109
- };
110
- if (sessionOptions.cookieOptions && "maxAge" in sessionOptions.cookieOptions) {
111
- if (sessionOptions.cookieOptions.maxAge === void 0) {
112
- options.ttl = 0;
113
- }
114
- } else {
115
- options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);
116
- }
117
- return options;
190
+ if (!sessionOptions.cookieName) throw new Error("iron-session: Bad usage. Missing cookie name.");
191
+ if (!sessionOptions.password) throw new Error("iron-session: Bad usage. Missing password.");
192
+ const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);
193
+ if (Object.keys(passwordsMap).length === 0) throw new Error("iron-session: Bad usage. The password map is empty, it must be keyed by numbers, for example { 1: 'your-password' }.");
194
+ for (const [id, password] of Object.entries(passwordsMap)) {
195
+ if (!Number.isInteger(Number(id))) throw new Error(`iron-session: Bad usage. Password ids must be integers, got ${JSON.stringify(id)}. Use { 1: '...', 2: '...' }.`);
196
+ if (typeof password !== "string" || password.length < 32) throw new Error("iron-session: Bad usage. Password must be at least 32 characters long.");
197
+ }
198
+ const options = {
199
+ ...defaultOptions,
200
+ ...sessionOptions,
201
+ passwordsMap,
202
+ cookieOptions: {
203
+ ...defaultOptions.cookieOptions,
204
+ ...sessionOptions.cookieOptions
205
+ }
206
+ };
207
+ if (sessionOptions.cookieOptions && "maxAge" in sessionOptions.cookieOptions) {
208
+ if (sessionOptions.cookieOptions.maxAge === void 0) options.ttl = 0;
209
+ } else options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);
210
+ const { expires } = options.cookieOptions;
211
+ if (expires instanceof Date && expires.getTime() < Date.now()) throw new Error(`iron-session: Bad usage. cookieOptions.expires is in the past (${expires.toISOString()}), so the browser will discard the cookie and the session will never persist. This usually means the Date was created once at module scope. Use \`ttl\` instead.`);
212
+ return options;
213
+ }
214
+ /**
215
+ * The single session implementation. Reads the cookie through the jar, and
216
+ * defines save/destroy/updateConfig against that same jar.
217
+ */
218
+ async function createSession(jar, sessionOptions) {
219
+ let config = getSessionConfig(sessionOptions);
220
+ let onUnsealError = sessionOptions.onUnsealError;
221
+ const sealFromCookies = readSeal(jar, config.cookieName);
222
+ const session = sealFromCookies ? await unsealData(sealFromCookies, {
223
+ password: config.passwordsMap,
224
+ ttl: config.ttl,
225
+ ...onUnsealError ? { onUnsealError } : {}
226
+ }) : {};
227
+ let destroyed = false;
228
+ Object.defineProperties(session, {
229
+ updateConfig: { value: function updateConfig(newSessionOptions) {
230
+ config = getSessionConfig(newSessionOptions);
231
+ onUnsealError = newSessionOptions.onUnsealError;
232
+ } },
233
+ save: { value: async function save() {
234
+ if (destroyed) throw new Error("iron-session: Cannot save a destroyed session. session.destroy() signs the user out, and saving afterwards would restore the cookie you just cleared. Get a fresh session if you need to write one.");
235
+ jar.assertWritable?.();
236
+ const seal = await sealData(session, {
237
+ password: config.passwordsMap,
238
+ ttl: config.ttl
239
+ });
240
+ writeSeal(jar, config, seal);
241
+ } },
242
+ destroy: { value: function destroy() {
243
+ destroyed = true;
244
+ for (const key of Object.keys(session)) delete session[key];
245
+ jar.write(config.cookieName, "", {
246
+ ...config.cookieOptions,
247
+ maxAge: 0
248
+ });
249
+ clearStaleCookies(jar, config.cookieName, config.cookieOptions, {
250
+ whole: true,
251
+ chunksUpTo: 0
252
+ });
253
+ } }
254
+ });
255
+ return session;
256
+ }
257
+ /** Browsers cap one cookie at 4096 bytes over the whole `Set-Cookie` value. */
258
+ const MAX_COOKIE_BYTES = 4096;
259
+ /**
260
+ * Hard cap on chunks, not configurable on purpose.
261
+ *
262
+ * Four chunks is already ~16 KB of `Cookie` header on every single request, and
263
+ * nginx's default `large_client_header_buffers` is 8 KB. Letting people raise
264
+ * this just moves the failure from our error message to a 400 at their CDN.
265
+ */
266
+ const MAX_CHUNKS = 4;
267
+ const chunkName = (cookieName, index) => `${cookieName}.${index}`;
268
+ function cookieBytes(name, value, cookieOptions) {
269
+ return new TextEncoder().encode(serializeCookie(name, value, cookieOptions)).length;
270
+ }
271
+ /**
272
+ * Reads the seal, whether it was stored in one cookie or split across several.
273
+ *
274
+ * Both shapes are always accepted regardless of the `chunk` setting, so turning
275
+ * chunking on or off does not invalidate cookies that are already out there.
276
+ *
277
+ * Chunk discovery probes `name.0`, `name.1`, ... and stops at the first gap,
278
+ * bounded by {@link MAX_CHUNKS}. There is deliberately no cookie holding the
279
+ * chunk count: that value would be attacker-controlled, and a `name.count` of
280
+ * 99999999 is free CPU amplification before anyone is authenticated.
281
+ *
282
+ * The chunks are concatenated and handed to `unseal` whole. Nothing here
283
+ * inspects, validates or trusts an individual chunk, and that is what makes
284
+ * reassembly safe: the HMAC already covers the entire seal string, so a deleted
285
+ * chunk, reordered chunks, or a chunk swapped in from a different session all
286
+ * fail integrity and land in the unreadable-cookie path.
287
+ */
288
+ function readSeal(jar, cookieName) {
289
+ const whole = jar.read(cookieName);
290
+ if (whole) return whole;
291
+ let seal = "";
292
+ for (let index = 0; index < MAX_CHUNKS; index += 1) {
293
+ const part = jar.read(chunkName(cookieName, index));
294
+ if (!part) break;
295
+ seal += part;
296
+ }
297
+ return seal;
298
+ }
299
+ /**
300
+ * Expires cookies that are no longer part of the session.
301
+ *
302
+ * This is the bug chunking would otherwise ship with. A session that shrinks
303
+ * from three chunks to one leaves `name.1` and `name.2` in the browser, the next
304
+ * read concatenates the new chunk 0 with the two stale ones, the HMAC fails, and
305
+ * the user is signed out on every request from then on while `save()` keeps
306
+ * reporting success. There is no error anywhere in that loop.
307
+ *
308
+ * Only cookies actually present on the request are expired, so a normal
309
+ * unchunked save does not emit four pointless `Set-Cookie` headers. The expiry
310
+ * reuses the same `path` and `domain` as the write, otherwise the browser treats
311
+ * it as a different cookie and the delete does nothing.
312
+ */
313
+ function clearStaleCookies(jar, cookieName, cookieOptions, keep) {
314
+ const expire = (name) => {
315
+ if (jar.read(name)) jar.write(name, "", {
316
+ ...cookieOptions,
317
+ maxAge: 0
318
+ });
319
+ };
320
+ if (!keep.whole) expire(cookieName);
321
+ for (let index = keep.chunksUpTo; index < MAX_CHUNKS; index += 1) expire(chunkName(cookieName, index));
118
322
  }
119
- var badUsageMessage = "iron-session: Bad usage: use getIronSession(req, res, options) or getIronSession(cookieStore, options).";
120
- function createGetIronSession(sealData2, unsealData2) {
121
- return getIronSession2;
122
- async function getIronSession2(reqOrCookieStore, resOrsessionOptions, sessionOptions) {
123
- if (!reqOrCookieStore) {
124
- throw new Error(badUsageMessage);
125
- }
126
- if (!resOrsessionOptions) {
127
- throw new Error(badUsageMessage);
128
- }
129
- if (!sessionOptions) {
130
- return getIronSessionFromCookieStore(
131
- reqOrCookieStore,
132
- resOrsessionOptions,
133
- sealData2,
134
- unsealData2
135
- );
136
- }
137
- const req = reqOrCookieStore;
138
- const res = resOrsessionOptions;
139
- if (!sessionOptions) {
140
- throw new Error(badUsageMessage);
141
- }
142
- if (!sessionOptions.cookieName) {
143
- throw new Error("iron-session: Bad usage. Missing cookie name.");
144
- }
145
- if (!sessionOptions.password) {
146
- throw new Error("iron-session: Bad usage. Missing password.");
147
- }
148
- const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);
149
- if (Object.values(passwordsMap).some((password) => password.length < 32)) {
150
- throw new Error(
151
- "iron-session: Bad usage. Password must be at least 32 characters long."
152
- );
153
- }
154
- let sessionConfig = getSessionConfig(sessionOptions);
155
- const sealFromCookies = getCookie(req, sessionConfig.cookieName);
156
- const session = sealFromCookies ? await unsealData2(sealFromCookies, {
157
- password: passwordsMap,
158
- ttl: sessionConfig.ttl
159
- }) : {};
160
- Object.defineProperties(session, {
161
- updateConfig: {
162
- value: function updateConfig(newSessionOptions) {
163
- sessionConfig = getSessionConfig(newSessionOptions);
164
- }
165
- },
166
- save: {
167
- value: async function save() {
168
- if ("headersSent" in res && res.headersSent) {
169
- throw new Error(
170
- "iron-session: Cannot set session cookie: session.save() was called after headers were sent. Make sure to call it before any res.send() or res.end()"
171
- );
172
- }
173
- const seal = await sealData2(session, {
174
- password: passwordsMap,
175
- ttl: sessionConfig.ttl
176
- });
177
- const cookieValue = serialize(
178
- sessionConfig.cookieName,
179
- seal,
180
- sessionConfig.cookieOptions
181
- );
182
- if (cookieValue.length > 4096) {
183
- throw new Error(
184
- `iron-session: Cookie length is too big (${cookieValue.length} bytes), browsers will refuse it. Try to remove some data.`
185
- );
186
- }
187
- setCookie(res, cookieValue);
188
- }
189
- },
190
- destroy: {
191
- value: function destroy() {
192
- Object.keys(session).forEach((key) => {
193
- delete session[key];
194
- });
195
- const cookieValue = serialize(sessionConfig.cookieName, "", {
196
- ...sessionConfig.cookieOptions,
197
- maxAge: 0
198
- });
199
- setCookie(res, cookieValue);
200
- }
201
- }
202
- });
203
- return session;
204
- }
323
+ /**
324
+ * Writes the seal, splitting it across cookies when it does not fit and
325
+ * chunking is enabled.
326
+ *
327
+ * The seal is split as an opaque string, after the version suffix is applied to
328
+ * the whole thing. Rejoining is a plain concatenation with no separator.
329
+ */
330
+ function writeSeal(jar, config, seal) {
331
+ const { cookieName, cookieOptions } = config;
332
+ const wholeBytes = cookieBytes(cookieName, seal, cookieOptions);
333
+ if (wholeBytes <= MAX_COOKIE_BYTES) {
334
+ jar.write(cookieName, seal, cookieOptions);
335
+ clearStaleCookies(jar, cookieName, cookieOptions, {
336
+ whole: true,
337
+ chunksUpTo: 0
338
+ });
339
+ return;
340
+ }
341
+ if (!config.chunk) {
342
+ const wasChunked = Boolean(jar.read(chunkName(cookieName, 0)));
343
+ throw new Error(wasChunked ? `iron-session: Cookie length is too big (${wholeBytes} bytes) and \`chunk\` is not enabled here, but this session is already stored across several cookies. You have more than one options object and only some of them set \`chunk: true\`. Use the same options everywhere you call getIronSession, including middleware.` : `iron-session: Cookie length is too big (${wholeBytes} bytes), browsers will refuse it. Remove some data from the session, or set \`chunk: true\` to split it across several cookies.`);
344
+ }
345
+ const perChunkOverhead = cookieBytes(chunkName(cookieName, 0), "", cookieOptions);
346
+ const budget = MAX_COOKIE_BYTES - perChunkOverhead;
347
+ if (budget <= 0) throw new Error(`iron-session: The cookie name and options alone take ${perChunkOverhead} bytes, which leaves no room for session data. Use a shorter cookie name.`);
348
+ const chunks = [];
349
+ for (let offset = 0; offset < seal.length; offset += budget) chunks.push(seal.slice(offset, offset + budget));
350
+ if (chunks.length > MAX_CHUNKS) throw new Error(`iron-session: The session needs ${chunks.length} cookies and the maximum is ${MAX_CHUNKS}. Even at ${MAX_CHUNKS} the whole Cookie header is sent on every request and proxies commonly cap it at 8 KB, so raising this would fail at your CDN instead. Store an id in the session and keep the data in your database.`);
351
+ chunks.forEach((value, index) => {
352
+ jar.write(chunkName(cookieName, index), value, cookieOptions);
353
+ });
354
+ clearStaleCookies(jar, cookieName, cookieOptions, {
355
+ whole: false,
356
+ chunksUpTo: chunks.length
357
+ });
205
358
  }
206
- async function getIronSessionFromCookieStore(cookieStore, sessionOptions, sealData2, unsealData2) {
207
- if (!sessionOptions.cookieName) {
208
- throw new Error("iron-session: Bad usage. Missing cookie name.");
209
- }
210
- if (!sessionOptions.password) {
211
- throw new Error("iron-session: Bad usage. Missing password.");
212
- }
213
- const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);
214
- if (Object.values(passwordsMap).some((password) => password.length < 32)) {
215
- throw new Error(
216
- "iron-session: Bad usage. Password must be at least 32 characters long."
217
- );
218
- }
219
- let sessionConfig = getSessionConfig(sessionOptions);
220
- const sealFromCookies = getServerActionCookie(
221
- sessionConfig.cookieName,
222
- cookieStore
223
- );
224
- const session = sealFromCookies ? await unsealData2(sealFromCookies, {
225
- password: passwordsMap,
226
- ttl: sessionConfig.ttl
227
- }) : {};
228
- Object.defineProperties(session, {
229
- updateConfig: {
230
- value: function updateConfig(newSessionOptions) {
231
- sessionConfig = getSessionConfig(newSessionOptions);
232
- }
233
- },
234
- save: {
235
- value: async function save() {
236
- const seal = await sealData2(session, {
237
- password: passwordsMap,
238
- ttl: sessionConfig.ttl
239
- });
240
- const cookieLength = sessionConfig.cookieName.length + seal.length + JSON.stringify(sessionConfig.cookieOptions).length;
241
- if (cookieLength > 4096) {
242
- throw new Error(
243
- `iron-session: Cookie length is too big (${cookieLength} bytes), browsers will refuse it. Try to remove some data.`
244
- );
245
- }
246
- cookieStore.set(
247
- sessionConfig.cookieName,
248
- seal,
249
- sessionConfig.cookieOptions
250
- );
251
- }
252
- },
253
- destroy: {
254
- value: function destroy() {
255
- Object.keys(session).forEach((key) => {
256
- delete session[key];
257
- });
258
- const cookieOptions = { ...sessionConfig.cookieOptions, maxAge: 0 };
259
- cookieStore.set(sessionConfig.cookieName, "", cookieOptions);
260
- }
261
- }
262
- });
263
- return session;
359
+ const badUsageMessage = "iron-session: Bad usage: use getIronSession(req, res, options) or getIronSession(cookieStore, options).";
360
+ async function getIronSession(first, second, third) {
361
+ if (!first || !second) throw new Error(badUsageMessage);
362
+ if (!third) {
363
+ const options = second;
364
+ if (isCookieJar(first)) return createSession(first, options);
365
+ if (isCookieStore(first)) return createSession(cookieStoreJar(first), options);
366
+ throw new Error(badUsageMessage);
367
+ }
368
+ const req = first;
369
+ const res = second;
370
+ return createSession(isWebRequest(req) ? webCookies(req, res) : nodeCookies(req, res), third);
264
371
  }
265
- var sealData = createSealData(crypto);
266
- var unsealData = createUnsealData(crypto);
267
- var getIronSession = createGetIronSession(sealData, unsealData);
372
+ //#endregion
373
+ export { getIronSession, nextProxyCookies, nodeCookies, sealData, unsealData, webCookies };
268
374
 
269
- export { getIronSession, sealData, unsealData };
270
- //# sourceMappingURL=index.js.map
271
375
  //# sourceMappingURL=index.js.map