customerio-node
Version:
A node client for the Customer.io event API. http://customer.io
126 lines (125 loc) • 4.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MissingParamError = exports.CustomerIORequestError = exports.buildQueryString = exports.isObjectIdType = exports.isIdentifierType = exports.isEmpty = void 0;
exports.pickDefined = pickDefined;
const types_1 = require("../lib/types");
/** Returns `true` for `null`, `undefined`, an empty/whitespace string, or a non-finite number. */
const isEmpty = (value) => {
if (value === null || value === undefined)
return true;
if (typeof value === 'string')
return value.trim() === '';
return !Number.isFinite(value);
};
exports.isEmpty = isEmpty;
/** Returns `true` if `value` is one of the {@link IdentifierType} enum values. */
const isIdentifierType = (value) => {
return Object.values(types_1.IdentifierType).includes(value);
};
exports.isIdentifierType = isIdentifierType;
/**
* Returns `true` if `value` is a valid object identifier kind (`object_id` or
* `cio_object_id`). Objects use a different id vocabulary than people, so this
* is intentionally separate from {@link isIdentifierType}.
*/
const isObjectIdType = (value) => {
return value === 'object_id' || value === 'cio_object_id';
};
exports.isObjectIdType = isObjectIdType;
/**
* Build a URL query string from a map of parameters.
*
* `null` and `undefined` values are omitted (so optional params disappear
* rather than serializing as empty). Keys and values are URL-encoded. Returns
* a leading-`?` string (e.g. `?a=1&b=2`) when at least one param is present,
* or an empty string when none are.
*/
const buildQueryString = (params) => {
const parts = [];
for (const [key, value] of Object.entries(params)) {
if (value === null || value === undefined || value === '') {
continue;
}
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
}
return parts.length > 0 ? `?${parts.join('&')}` : '';
};
exports.buildQueryString = buildQueryString;
/**
* Thrown when the Customer.io API responds with a non-2xx status.
*
* The error message is derived from the API response body when possible. The
* raw status code, response, and body are exposed for programmatic handling
* (e.g. retry on 5xx, ignore on 404).
*
* @remarks
* This is only thrown for HTTP responses with a non-2xx status. Transport-level
* failures (DNS, connection reset, refused) and timeouts are surfaced as the
* native `fetch` errors — `TypeError('fetch failed')` (with the underlying
* cause on `.cause`) and `DOMException('TimeoutError')` respectively — not as a
* `CustomerIORequestError`.
*
* @example
* ```ts
* try {
* await cio.identify('123', { email: 'a@example.com' });
* } catch (err) {
* if (err instanceof CustomerIORequestError && err.statusCode === 404) {
* // customer not found, fall through
* } else {
* throw err;
* }
* }
* ```
*/
class CustomerIORequestError extends Error {
/** HTTP status code returned by the API. */
statusCode;
/** Portable response metadata ({@link ResponseLike}: status, lowercased headers, `ok`). */
response;
/** The raw response body as a string. May be empty. */
body;
static composeMessage(json) {
if (!json) {
return 'Unknown error';
}
if (json.meta && json.meta.error) {
return json.meta.error;
}
else if (json.meta && json.meta.errors) {
const count = json.meta.errors.length;
return `${count} ${count === 1 ? 'error' : 'errors'}:
${json.meta.errors.map((error) => ` - ${error}`).join('\n')}`;
}
return 'Unknown error';
}
constructor(json, statusCode, response, body) {
super(CustomerIORequestError.composeMessage(json));
this.name = 'CustomerIORequestError';
this.statusCode = statusCode;
this.response = response;
this.body = body;
}
}
exports.CustomerIORequestError = CustomerIORequestError;
function pickDefined(source, keys) {
const result = {};
for (const key of keys) {
if (source[key] !== undefined) {
result[key] = source[key];
}
}
return result;
}
/**
* Thrown synchronously by SDK methods when a required parameter is missing.
*
* The `message` is always `"<paramName> is required"`.
*/
class MissingParamError extends Error {
constructor(param) {
super(`${param} is required`);
this.name = 'MissingParamError';
}
}
exports.MissingParamError = MissingParamError;