@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
123 lines (122 loc) • 4.34 kB
JavaScript
import { _deepEquals } from '../object/deepEquals.js';
import { _stringify } from '../string/stringify.js';
import { _isBackendErrorResponseObject, _isErrorObject, AssertionError } from './error.util.js';
/**
* Evaluates the `condition` (casts it to Boolean).
* Expects it to be truthy, otherwise throws AppError.
*
* Should be used NOT for "expected" / user-facing errors, but
* vice-versa - for completely unexpected and 100% buggy "should never happen" cases.
*
* It'll result in http 500 on the server (cause that's the right code for "unexpected" errors).
* Pass { backendResponseStatusCode: x } at errorData argument to override the http code (will be picked up by backend-lib).
*
* API is similar to Node's assert(), except:
* 1. Throws js-lib's AppError
* 2. Has a default message, if not provided
*
* Since 2024-07-10 it no longer sets `userFriendly: true` by default.
*/
export function _assert(condition, // will be evaluated as Boolean
message, errorData) {
if (!condition) {
throw new AssertionError(message || 'condition failed', {
...errorData,
});
}
}
/**
* Like _assert(), but prints more helpful error message.
* API is similar to Node's assert.equals().
*
* Does SHALLOW, but strict equality (===), use _assertDeepEquals() for deep equality.
*/
export function _assertEquals(actual, expected, message, errorData) {
if (actual !== expected) {
const msg = message ||
['not equal', `expected: ${_stringify(expected)}`, `got : ${_stringify(actual)}`]
.filter(Boolean)
.join('\n');
throw new AssertionError(msg, {
...errorData,
});
}
}
/**
* Like _assert(), but prints more helpful error message.
* API is similar to Node's assert.deepEquals().
*
* Does DEEP equality via _deepEquals()
*/
export function _assertDeepEquals(actual, expected, message, errorData) {
if (!_deepEquals(actual, expected)) {
const msg = message ||
['not deeply equal', `expected: ${_stringify(expected)}`, `got : ${_stringify(actual)}`]
.filter(Boolean)
.join('\n');
throw new AssertionError(msg, {
...errorData,
});
}
}
export function _assertIsError(err, errorClass = Error) {
if (!(err instanceof errorClass)) {
throw new AssertionError(`Expected to be instanceof ${errorClass.name}, actual typeof: ${typeof err}`);
}
}
/**
* Asserts that passed object is indeed an Error of defined ErrorClass.
* If yes - returns peacefully (with TypeScript assertion).
* In not - throws (re-throws) that error up.
*/
export function _assertErrorClassOrRethrow(err, errorClass) {
if (!(err instanceof errorClass)) {
// re-throw
throw err;
}
}
export function _assertIsErrorObject(obj) {
if (!_isErrorObject(obj)) {
throw new AssertionError(`Expected to be ErrorObject, actual typeof: ${typeof obj}`);
}
}
export function _assertIsBackendErrorResponseObject(obj) {
if (!_isBackendErrorResponseObject(obj)) {
throw new AssertionError(`Expected to be BackendErrorResponseObject, actual typeof: ${typeof obj}`);
}
}
export function _assertIsString(v, message) {
_assertTypeOf(v, 'string', message);
}
export function _assertIsNumber(v, message) {
_assertTypeOf(v, 'number', message);
}
export function _assertTypeOf(v, expectedType, message) {
if (typeof v !== expectedType) {
const msg = message || `Expected typeof ${expectedType}, actual typeof: ${typeof v}`;
throw new AssertionError(msg);
}
}
/**
* Casts an arbitrary number as UnixTimestamp.
* Right now does not perform any validation (unlike `asUnixTimestamp2000`),
* but only type casting.
*/
export function asUnixTimestamp(n) {
return n;
}
const TS_2500 = 16725225600; // 2500-01-01
const TS_2000 = 946684800; // 2000-01-01
/**
* Casts an arbitrary number as UnixTimestamp2000.
* Throws if the number is not inside 2000-01-01 and 2500-01-01 time interval,
* which would indicate a bug.
*/
export function asUnixTimestamp2000(n) {
if (!n || n < TS_2000 || n > TS_2500) {
throw new AssertionError(`Number is not a valid UnixTimestamp2000: ${n}`, {
fingerprint: 'asUnixTimestamp2000',
});
}
return n;
}