UNPKG

4.1 kBJavaScriptView Raw
1/**
2 * react-router v8.0.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { warnOnce } from "./warnings.js";
12import { sign, unsign } from "./crypto.js";
13import { parse, serialize } from "cookie-es";
14//#region lib/server-runtime/cookies.ts
15/**
16* Creates a logical container for managing a browser cookie from the server.
17*/
18const createCookie = (name, cookieOptions = {}) => {
19 let { secrets = [], ...options } = {
20 path: "/",
21 sameSite: "lax",
22 ...cookieOptions
23 };
24 warnOnceAboutExpiresCookie(name, options.expires);
25 return {
26 get name() {
27 return name;
28 },
29 get isSigned() {
30 return secrets.length > 0;
31 },
32 get expires() {
33 return typeof options.maxAge !== "undefined" ? new Date(Date.now() + options.maxAge * 1e3) : options.expires;
34 },
35 async parse(cookieHeader, parseOptions) {
36 if (!cookieHeader) return null;
37 let cookies = parse(cookieHeader, {
38 ...options,
39 ...parseOptions
40 });
41 if (name in cookies) {
42 let value = cookies[name];
43 if (typeof value === "string" && value !== "") return await decodeCookieValue(value, secrets);
44 else return "";
45 } else return null;
46 },
47 async serialize(value, serializeOptions) {
48 return serialize(name, value === "" ? "" : await encodeCookieValue(value, secrets), {
49 ...options,
50 ...serializeOptions
51 });
52 }
53 };
54};
55/**
56* Returns true if an object is a Remix cookie container.
57*
58* @see https://remix.run/utils/cookies#iscookie
59*/
60const isCookie = (object) => {
61 return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
62};
63async function encodeCookieValue(value, secrets) {
64 let encoded = encodeData(value);
65 if (secrets.length > 0) encoded = await sign(encoded, secrets[0]);
66 return encoded;
67}
68async function decodeCookieValue(value, secrets) {
69 if (secrets.length > 0) {
70 for (let secret of secrets) {
71 let unsignedValue = await unsign(value, secret);
72 if (unsignedValue !== false) return decodeData(unsignedValue);
73 }
74 return null;
75 }
76 return decodeData(value);
77}
78function encodeData(value) {
79 return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
80}
81function decodeData(value) {
82 try {
83 return JSON.parse(decodeURIComponent(myEscape(atob(value))));
84 } catch (e) {
85 return {};
86 }
87}
88function myEscape(value) {
89 let str = value.toString();
90 let result = "";
91 let index = 0;
92 let chr, code;
93 while (index < str.length) {
94 chr = str.charAt(index++);
95 if (/[\w*+\-./@]/.exec(chr)) result += chr;
96 else {
97 code = chr.charCodeAt(0);
98 if (code < 256) result += "%" + hex(code, 2);
99 else result += "%u" + hex(code, 4).toUpperCase();
100 }
101 }
102 return result;
103}
104function hex(code, length) {
105 let result = code.toString(16);
106 while (result.length < length) result = "0" + result;
107 return result;
108}
109function myUnescape(value) {
110 let str = value.toString();
111 let result = "";
112 let index = 0;
113 let chr, part;
114 while (index < str.length) {
115 chr = str.charAt(index++);
116 if (chr === "%") if (str.charAt(index) === "u") {
117 part = str.slice(index + 1, index + 5);
118 if (/^[\da-f]{4}$/i.exec(part)) {
119 result += String.fromCharCode(parseInt(part, 16));
120 index += 5;
121 continue;
122 }
123 } else {
124 part = str.slice(index, index + 2);
125 if (/^[\da-f]{2}$/i.exec(part)) {
126 result += String.fromCharCode(parseInt(part, 16));
127 index += 2;
128 continue;
129 }
130 }
131 result += chr;
132 }
133 return result;
134}
135function warnOnceAboutExpiresCookie(name, expires) {
136 warnOnce(!expires, `The "${name}" cookie has an "expires" property set. This will cause the expires value to not be updated when the session is committed. Instead, you should set the expires value when serializing the cookie. You can use \`commitSession(session, { expires })\` if using a session storage object, or \`cookie.serialize("value", { expires })\` if you're using the cookie directly.`);
137}
138//#endregion
139export { createCookie, isCookie };