+/**
+ * Adds a new set-cookie header value to response, with supplied data.
+ * @param {http.ServerResponse} res response
+ * @param {(string, string) => void} res.appendHeader sets header values
+ * @param {string} name cookie name
+ * @param {string} value cookie value
+ * @param {object=} opt cookie options
+ * @param {string=} opt.domain cookie domain
+ * @param {Date=} opt.expires cookie expiration
+ * @param {boolean=} opt.httpOnly cookie client visibility
+ * @param {number=} opt.maxAge cookie lifetime
+ * @param {string=} opt.path cookie path
+ * @param {string=} opt.sameSite cookie sharing
+ * @param {boolean=} opt.secure cookie security
+ */
+function addCookie(res, name, value, opt = {}) {
+ const options = {
+ domain: undefined,
+ expires: undefined,
+ httpOnly: false,
+ maxAge: undefined,
+ path: undefined,
+ sameSite: undefined,
+ secure: false,
+ ...opt,
+ };
+ // TODO: validate name, value
+ const cookieParts = [
+ `${name}=${value}`,
+ ];
+ if (options.domain) {
+ cookieParts.push(`Domain=${options.domain}`);
+ }
+ if (options.expires) {
+ if (!(options.expires instanceof Date)) {
+ throw new TypeError('cookie expires must be Date');
+ }
+ cookieParts.push(`Expires=${options.expires.toUTCString()}`);
+ }
+ if (options.httpOnly) {
+ cookieParts.push('HttpOnly');
+ }
+ if (options.maxAge) {
+ cookieParts.push(`Max-Age=${options.maxAge}`);
+ }
+ if (options.path) {
+ cookieParts.push(`Path=${options.path}`);
+ }
+ if (options.sameSite) {
+ if (!(['Strict', 'Lax', 'None'].includes(options.sameSite))) {
+ throw new RangeError('cookie sameSite value not valid');
+ }
+ cookieParts.push(`SameSite=${options.sameSite}`);
+ }
+ if (options.secure) {
+ cookieParts.push('Secure');
+ }
+ res.appendHeader(Enum.Header.SetCookie, cookieParts.join('; '));
+}
+
+