@tdb/util
Version:
Shared helpers and utilities.
94 lines (84 loc) • 2.38 kB
text/typescript
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<T extends CookieValue>(
key: string,
defaultOptions: ICookieOptions = {},
): CookieProperty<T> {
const func = <T extends CookieValue>(
value?: T | null,
options: ICookieOptions = {},
) => {
const args = { ...defaultOptions, ...options };
return value === undefined
? getValue<T>(key, args)
: setValue<T>(key, value, args);
};
const result = func as CookieProperty<T>;
result.key = key;
result.isProp = true;
result.options = defaultOptions;
return result;
}
/**
* INTERNAL
*/
function getValue<T extends CookieValue>(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<T extends CookieValue>(
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<T>(key, options));
}
}