import * as Cookie from 'js-cookie'; import { isJson } from '../value/value'; import { ICookieOptions, CookieValue, CookieProperty, ICookieChangedEvent, } from './types'; import { subject$ } from './events.rx'; /** * Creates a wrapper for a cookie property at a specific key. */ export function prop( key: string, defaultOptions: ICookieOptions = {}, ): CookieProperty { const func = ( value?: T | null, options: ICookieOptions = {}, ) => { const args = { ...defaultOptions, ...options }; return value === undefined ? getValue(key, args) : setValue(key, value, args); }; const result = func as CookieProperty; result.key = key; result.isProp = true; result.options = defaultOptions; return result; } /** * INTERNAL */ function getValue(key: string, options: ICookieOptions) { // Retrieve the value from the client-side object, // or the given server-side cookies. const { ctx } = options; const cookies = (ctx && ctx.req && ctx.req.cookies) || Cookie.getJSON() || {}; let value = cookies[key]; // TODO:BUG // If the key contains a `/` characther this will fail because the paths is encoded. // Account for URL encoding in the cookie keys. if (key.includes('/')) { throw new Error( `Cookie keys cannot contain "/" character. Given key: "${key}".`, ); } // Optionally parse as JSON. if (isJson(value)) { try { value = JSON.parse(value as string); } catch (error) { // Ignore parse error - just return the raw value. } } // Update with default value if no value was found. if (value === undefined && options.default !== undefined) { value = options.default; } // Finish up. return value as T; } function setValue( key: string, value: T | null, options: ICookieOptions, ) { const fireEvent = ( action: ICookieChangedEvent['action'], value?: CookieValue, ) => { subject$.next({ key, value, action }); return value; }; if (value === null) { Cookie.remove(key); return fireEvent('DELETE'); } else { const { expires, path, domain, isSecure: secure } = options; Cookie.set(key, value, { expires, path, domain, secure }); return fireEvent('UPDATE', getValue(key, options)); } }