@tdb/util
Version:
Shared helpers and utilities.
83 lines (75 loc) • 2.13 kB
text/typescript
import { value as valueUtil } from '../value';
/**
* Represents a query-string.
* NB: this record type is derived from NextJS's declaration
* for the [ctx.query] type.
*/
export type UrlQuery = Record<
string,
string | string[] | number | boolean | undefined
>;
/**
* Takes a query-string value and parses it into an object.
*/
export function toObject<T>(queryString?: string): T {
// Setup initial conditions.
const EMPTY = {};
if (!queryString) {
return EMPTY as T;
}
if (!queryString.trim()) {
return EMPTY as T;
}
// Remove "#" and "?" prefix.
let text = queryString.trim();
text = !text.startsWith('?') ? text : text.substring(1);
text = !text.startsWith('#') ? text : text.substring(1);
// Convert to object.
const pairs = text.split('&');
const obj = {};
let pair;
for (const i in pairs) {
if (pairs[i] === '') {
continue;
}
pair = pairs[i].split('=');
const key = decodeURIComponent(pair[0]);
const value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
obj[key] = value;
}
// Finish up.
return obj as T;
}
/**
* Converts a query-string [true/false/undefined] value to a boolean flag.
* Note:
* This is useful for when the presence of query-string key should
* indicate that the flag is present and true, eg.
*
* - "/foo?force" // <== true (implicit)
* - "/foo?force=true" // <== true (explicit)
* - "/foo?force=false" // <== false (explicit)
* - "/foo" // <== false, query flag undefined.
*
* Example:
*
* const force = valueAsFlag(req.query.force)
*
*/
export function valueAsFlag<T>(
value?: string | string[] | number | boolean,
): T {
return value === undefined
? false
: value === ''
? true
: valueUtil.toBool(value, false);
}
/**
* Checks a set of keys within a query-string to see if any of them
* are flags.
*/
export function isFlag(keys: string | string[] | undefined, query?: UrlQuery) {
keys = keys ? (Array.isArray(keys) ? keys : [keys]) : [];
return query ? keys.some(key => valueAsFlag(query[key])) : false;
}