| |
| |
| |
| |
| |
| |
| |
| 'use strict'; |
|
|
| |
| |
| |
| |
|
|
| exports.parse = parse; |
| exports.serialize = serialize; |
|
|
| |
| |
| |
| |
|
|
| var decode = decodeURIComponent; |
| var encode = encodeURIComponent; |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function parse(str, options) { |
| if (typeof str !== 'string') { |
| throw new TypeError('argument str must be a string'); |
| } |
|
|
| var obj = {} |
| var opt = options || {}; |
| var pairs = str.split(';') |
| var dec = opt.decode || decode; |
|
|
| for (var i = 0; i < pairs.length; i++) { |
| var pair = pairs[i]; |
| var index = pair.indexOf('=') |
|
|
| |
| if (index < 0) { |
| continue; |
| } |
|
|
| var key = pair.substring(0, index).trim() |
|
|
| |
| if (undefined == obj[key]) { |
| var val = pair.substring(index + 1, pair.length).trim() |
|
|
| |
| if (val[0] === '"') { |
| val = val.slice(1, -1) |
| } |
|
|
| obj[key] = tryDecode(val, dec); |
| } |
| } |
|
|
| return obj; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function serialize(name, val, options) { |
| var opt = options || {}; |
| var enc = opt.encode || encode; |
|
|
| if (typeof enc !== 'function') { |
| throw new TypeError('option encode is invalid'); |
| } |
|
|
| if (!fieldContentRegExp.test(name)) { |
| throw new TypeError('argument name is invalid'); |
| } |
|
|
| var value = enc(val); |
|
|
| if (value && !fieldContentRegExp.test(value)) { |
| throw new TypeError('argument val is invalid'); |
| } |
|
|
| var str = name + '=' + value; |
|
|
| if (null != opt.maxAge) { |
| var maxAge = opt.maxAge - 0; |
|
|
| if (isNaN(maxAge) || !isFinite(maxAge)) { |
| throw new TypeError('option maxAge is invalid') |
| } |
|
|
| str += '; Max-Age=' + Math.floor(maxAge); |
| } |
|
|
| if (opt.domain) { |
| if (!fieldContentRegExp.test(opt.domain)) { |
| throw new TypeError('option domain is invalid'); |
| } |
|
|
| str += '; Domain=' + opt.domain; |
| } |
|
|
| if (opt.path) { |
| if (!fieldContentRegExp.test(opt.path)) { |
| throw new TypeError('option path is invalid'); |
| } |
|
|
| str += '; Path=' + opt.path; |
| } |
|
|
| if (opt.expires) { |
| if (typeof opt.expires.toUTCString !== 'function') { |
| throw new TypeError('option expires is invalid'); |
| } |
|
|
| str += '; Expires=' + opt.expires.toUTCString(); |
| } |
|
|
| if (opt.httpOnly) { |
| str += '; HttpOnly'; |
| } |
|
|
| if (opt.secure) { |
| str += '; Secure'; |
| } |
|
|
| if (opt.sameSite) { |
| var sameSite = typeof opt.sameSite === 'string' |
| ? opt.sameSite.toLowerCase() : opt.sameSite; |
|
|
| switch (sameSite) { |
| case true: |
| str += '; SameSite=Strict'; |
| break; |
| case 'lax': |
| str += '; SameSite=Lax'; |
| break; |
| case 'strict': |
| str += '; SameSite=Strict'; |
| break; |
| case 'none': |
| str += '; SameSite=None'; |
| break; |
| default: |
| throw new TypeError('option sameSite is invalid'); |
| } |
| } |
|
|
| return str; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| function tryDecode(str, decode) { |
| try { |
| return decode(str); |
| } catch (e) { |
| return str; |
| } |
| } |
|
|