@coinbase/cdp-sdk
Version:
SDK for interacting with the Coinbase Developer Platform Wallet API
116 lines • 4.81 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpendControlError = exports.SpendControlErrorCodes = void 0;
exports.parseDuration = parseDuration;
exports.parseAmount = parseAmount;
/**
* Typed string constants for every {@link SpendControlErrorCode} value.
*/
exports.SpendControlErrorCodes = {
PER_PAYMENT_CAP: "per_payment_cap",
CUMULATIVE_CAP: "cumulative_cap",
ALREADY_APPLIED: "already_applied",
CONFIGURATION_INVALID: "configuration_invalid",
LEDGER_CAPACITY_EXCEEDED: "ledger_capacity_exceeded",
NETWORK_NOT_ALLOWED: "network_not_allowed",
ASSET_NOT_ALLOWED: "asset_not_allowed",
PAYEE_NOT_ALLOWED: "payee_not_allowed",
AMOUNT_UNPARSEABLE: "amount_unparseable",
MALFORMED_REQUIREMENT: "malformed_requirement",
MAX_TIMEOUT_EXCEEDED: "max_timeout_exceeded",
};
/**
* Thrown when a payment is blocked by the configured spend controls.
*/
class SpendControlError extends Error {
/** - Identifies the type of violation. */
code;
/** - Structured context for the error (always defined, may be empty). */
details;
/**
* Creates a SpendControlError with the given code and message.
*
* @param code - The error code identifying the violation type.
* @param message - Human-readable error message.
* @param details - Optional structured context for the error.
*/
constructor(code, message, details = {}) {
super(message);
this.name = "SpendControlError";
this.code = code;
this.details = details;
Object.setPrototypeOf(this, SpendControlError.prototype);
}
}
exports.SpendControlError = SpendControlError;
const MS_UNIT_FACTORS = {
ms: 1,
s: 1_000,
m: 60 * 1_000,
h: 60 * 60 * 1_000,
d: 24 * 60 * 60 * 1_000,
};
const DURATION_PATTERN = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)\s*$/i;
/**
* Converts a {@link Duration} to milliseconds.
*
* Accepts a non-negative number (already in milliseconds) or a shorthand
* string: `"500ms"`, `"30s"`, `"5m"`, `"1h"`, `"24h"`, `"7d"`.
*
* @param input - The duration value to parse.
* @returns The duration in milliseconds.
* @throws {SpendControlError} `"amount_unparseable"` if the input can't be parsed.
*/
function parseDuration(input) {
if (typeof input === "number") {
if (!Number.isFinite(input) || input < 0) {
throw new SpendControlError("amount_unparseable", `Duration must be a non-negative finite number, received ${input}`, { input });
}
return Math.floor(input);
}
const match = DURATION_PATTERN.exec(input);
if (!match) {
throw new SpendControlError("amount_unparseable", `Cannot parse duration ${JSON.stringify(input)}; expected e.g. "500ms", "30s", "5m", "1h", "24h", "7d"`, { input });
}
const [, numericPart, unit] = match;
const factor = MS_UNIT_FACTORS[unit.toLowerCase()];
return Math.floor(Number(numericPart) * factor);
}
const AMOUNT_SHORTHAND_PATTERN = /^([^:]+):(\d+)$/;
/**
* Parses an amount into `{ atomic: bigint; asset?: string }`.
*
* @param value - The amount value to parse (bigint, string, or Amount object).
* @param asset - Optional asset identifier to scope the amount.
* @returns Parsed amount with atomic bigint value and optional asset.
* @throws {SpendControlError} `"amount_unparseable"` on bad input.
*/
function parseAmount(value, asset) {
if (typeof value === "bigint") {
if (value < 0n) {
throw new SpendControlError("amount_unparseable", `Amount must be non-negative, got ${value}`, { input: value.toString() });
}
return { atomic: value, asset };
}
if (typeof value === "string") {
const trimmed = value.trim();
if (trimmed === "") {
throw new SpendControlError("amount_unparseable", "Amount string is empty", { input: value });
}
const shorthand = AMOUNT_SHORTHAND_PATTERN.exec(trimmed);
if (shorthand) {
const [, parsedAsset, parsedAtomic] = shorthand;
return parseAmount({ atomic: parsedAtomic, asset: parsedAsset }, asset ?? parsedAsset);
}
if (!/^\d+$/.test(trimmed)) {
throw new SpendControlError("amount_unparseable", `Cannot parse amount ${JSON.stringify(value)}; expected a non-negative integer or an "<asset>:<atomic>" shorthand`, { input: value });
}
return { atomic: BigInt(trimmed), asset };
}
if (value && typeof value === "object" && "atomic" in value) {
const innerAsset = asset ?? value.asset;
return parseAmount(value.atomic, innerAsset);
}
throw new SpendControlError("amount_unparseable", `Cannot parse amount: unsupported value of type ${typeof value}`, { input: value });
}
//# sourceMappingURL=types.js.map