@nerdware/ddb-single-table
Version:
A schema-based DynamoDB modeling tool, high-level API, and type-generator built to supercharge single-table designs!⚡
122 lines (121 loc) • 4.88 kB
JavaScript
import { CONNREFUSED as ECONNREFUSED } from "node:dns";
import { safeJsonStringify, isPlainObject, isString } from "@nerdware/ts-type-safety-utils";
/** Internal `errors` util */
const isNonEmptyString = (value) => isString(value) && !!value;
/**
* This is the base `error` class for custom errors defined in this package. If the
* `message` arg is anything other than a truthy string, then the resultant Error's
* `message` property will be set to a default value. A second `fallbackMsg` arg can
* be provided to override the default message.
*/
export class DdbSingleTableError extends Error {
static DEFAULT_MSG = "An unknown error occurred";
name;
constructor(message, fallbackMsg = DdbSingleTableError.DEFAULT_MSG) {
// This ctor allows `message` to be any type, but it's only used if it's a truthy string.
super(isNonEmptyString(message) ? message : fallbackMsg);
this.name = this.constructor.name;
Error.captureStackTrace(this, DdbSingleTableError);
}
}
/**
* This error is used for network/connection errors.
*/
export class DdbConnectionError extends DdbSingleTableError {
static DEFAULT_MSG = "Failed to connect to the provided DynamoDB endpoint";
/** Dictionary of relevant, connection-related NodeJS `err.code` values. */
static NODE_ERROR_CODES = {
ECONNREFUSED,
};
/** The {@link NodeJS.ErrnoException|NodeJS error code}. */
code;
constructor(arg) {
// Set defaults:
let message = DdbConnectionError.DEFAULT_MSG, code = DdbConnectionError.NODE_ERROR_CODES.ECONNREFUSED;
if (isNonEmptyString(arg)) {
message = arg;
}
else if (isPlainObject(arg)) {
if (isNonEmptyString(arg.message))
message += ` (${arg.message})`;
if (isNonEmptyString(arg.code))
code = arg.code;
}
super(message);
this.code = code;
Error.captureStackTrace(this, DdbConnectionError);
}
}
/**
* This error is thrown by schema-validation functions when a `TableKeysSchema`
* or `ModelSchema` is invalid.
*
* The provided {@link SchemaValidationErrorPayload} is used to create an error
* message formatted as follows:
*
* > `{schemaName} is invalid: {problem}.`
*/
export class SchemaValidationError extends DdbSingleTableError {
static DEFAULT_MSG = "Invalid schema";
constructor(message) {
const msgOrFormattedString = isPlainObject(message) && isString(message.schemaName) && isString(message.problem)
? `${message.schemaName} is invalid: ${message.problem}.`
: message;
super(msgOrFormattedString, SchemaValidationError.DEFAULT_MSG);
Error.captureStackTrace(this, SchemaValidationError);
}
}
/**
* This error is thrown by Model `IOAction` functions when run-time input
* data is invalid.
*/
export class ItemInputError extends DdbSingleTableError {
static DEFAULT_MSG = "Invalid item input";
constructor(message) {
super(message, ItemInputError.DEFAULT_MSG);
Error.captureStackTrace(this, ItemInputError);
}
}
/**
* This error is thrown by expression-generator utils when a run-time arg is invalid
* (e.g., more than two K-V pairs for a `KeyConditionExpression`).
*
* To provide a consistent error message format, provide an [`InvalidExpressionErrorPayload`][err]
* to the constructor.
*
* [err]: {@link InvalidExpressionErrorPayload}
*/
export class InvalidExpressionError extends DdbSingleTableError {
static DEFAULT_MSG = "Invalid expression";
constructor(arg) {
const message = isNonEmptyString(arg)
? arg
: isPlainObject(arg)
&& isString(arg.expressionName)
&& isString(arg.invalidValueDescription)
&& isString(arg.problem)
? `Invalid ${arg.invalidValueDescription} (generating ${arg.expressionName}): \n`
+ `${arg.problem}: ${safeJsonStringify(arg.invalidValue, null, 2)}`
: InvalidExpressionError.DEFAULT_MSG;
super(message);
Error.captureStackTrace(this, InvalidExpressionError);
}
}
/**
* Helper function which provides a consistent attribute identifier for error messages.
*/
export const getAttrErrID = (modelName, attrName, { alias }) => {
return `${modelName} property "${alias || attrName}"`;
};
/**
* Helper function which stringifies a nested schema for error messages.
*/
export const stringifyNestedSchema = (nestedSchema, spaces = 2) => {
// prettier-ignore
const strippedKeys = [
"isHashKey", "isRangeKey", "index", "required", "alias", "default", "validate", "transformValue",
];
return safeJsonStringify(nestedSchema, (key, value) => {
return typeof key === "string" && strippedKeys.includes(key) ? undefined : value;
}, spaces);
};