@datastax/astra-db-ts
Version:
Data API TypeScript client
186 lines (185 loc) • 6.39 kB
JavaScript
;
// Copyright Datastax, Inc
// SPDX-License-Identifier: Apache-2.0
Object.defineProperty(exports, "__esModule", { value: true });
exports.coerceNums = exports.coerceNumber = exports.coerceBigNumber = exports.NumCoercionError = exports.buildGetNumCoercionForPathFn = void 0;
const bignumber_js_1 = require("bignumber.js");
const utils_js_1 = require("../../../lib/utils.js");
const utils_js_2 = require("../../../lib/api/ser-des/utils.js");
const index_js_1 = require("../../../lib/index.js");
const $NumCoercion = Symbol('NumCoercion');
const buildGetNumCoercionForPathFn = (cfg) => {
return (typeof cfg?.enableBigNumbers === 'object')
? collNumCoercionFnFromCfg(cfg.enableBigNumbers)
: cfg?.enableBigNumbers;
};
exports.buildGetNumCoercionForPathFn = buildGetNumCoercionForPathFn;
const collNumCoercionFnFromCfg = (cfg) => {
const defaultCoercion = cfg['*'];
if (!defaultCoercion) {
throw new Error('The configuration must contain a "*" key');
}
// Minor optimization to make `{ '*': 'xyz' }` equal in performance to `() => 'xyz'`
if (Object.keys(cfg).length === 1) {
return () => defaultCoercion;
}
const tree = buildNumCoercionTree(cfg);
return (path) => {
return findMatchingPath(path, tree) ?? defaultCoercion;
};
};
const buildNumCoercionTree = (cfg) => {
const result = Object.create(null);
Object.entries(cfg).forEach(([path, coercion]) => {
const keys = (0, index_js_1.unescapeFieldPath)(path);
let current = result;
keys.forEach((key, index) => {
current[key] ?? (current[key] = Object.create(null));
if (index === keys.length - 1) {
current[key][$NumCoercion] = coercion;
}
current = current[key];
});
});
return result;
};
const findMatchingPath = (path, tree) => {
let coercion = undefined;
for (let i = 0; tree && i <= path.length; i++) {
if (i === path.length) {
return tree[$NumCoercion];
}
const exactMatch = tree[path[i]];
if (exactMatch) {
tree = exactMatch;
}
else {
tree = tree['*'];
coercion = tree?.[$NumCoercion] ?? coercion;
}
}
return coercion;
};
class NumCoercionError extends Error {
constructor(path, value, from, to) {
super(`Failed to coerce value from ${from} to ${to} at path: ${path.join('.')}`);
Object.defineProperty(this, "path", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "from", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "to", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.path = path;
this.value = value;
this.from = from;
this.to = to;
}
}
exports.NumCoercionError = NumCoercionError;
const coerceBigNumber = (value, path, getNumCoercionForPath, pathMatches) => {
const coercer = getNumCoercionForPath(path, pathMatches);
if (typeof coercer === 'function') {
return coercer(value, path);
}
switch (coercer) {
case 'number': {
return value.toNumber();
}
case 'strict_number': {
const asNum = value.toNumber();
if (!value.isEqualTo(asNum)) {
throw new NumCoercionError(path, value, 'bignumber', 'number');
}
return asNum;
}
case 'bigint': {
if (!value.isInteger()) {
throw new NumCoercionError(path, value, 'bignumber', 'bigint');
}
return BigInt(value.toFixed(0));
}
case 'bignumber':
return value;
case 'string':
return value.toString();
case 'number_or_string': {
const asNum = value.toNumber();
if (!value.isEqualTo(asNum)) {
return value.toString();
}
return asNum;
}
}
};
exports.coerceBigNumber = coerceBigNumber;
const coerceNumber = (value, path, getNumCoercionForPath, pathMatches) => {
const coercer = getNumCoercionForPath(path, pathMatches);
if (typeof coercer === 'function') {
return coercer(value, path);
}
switch (coercer) {
case 'bigint': {
if (!Number.isInteger(value)) {
throw new NumCoercionError(path, value, 'number', 'bigint');
}
return BigInt(value);
}
case 'bignumber':
return (0, bignumber_js_1.BigNumber)(value);
case 'string':
return String(value);
case 'number':
case 'strict_number':
case 'number_or_string':
return value;
}
};
exports.coerceNumber = coerceNumber;
const coerceNums = (val, getNumCoercionForPath) => {
return coerceNumsImpl(val, [], getNumCoercionForPath, (p) => (0, utils_js_2.pathMatches)([], p));
};
exports.coerceNums = coerceNums;
const coerceNumsImpl = (val, path, getNumCoercionForPath, pathMatchesFn) => {
if (typeof val === 'number') {
return (0, exports.coerceNumber)(val, path, getNumCoercionForPath, pathMatchesFn);
}
if (!val || typeof val !== 'object') {
return val;
}
if ((0, utils_js_1.isBigNumber)(val)) {
return (0, exports.coerceBigNumber)(val, path, getNumCoercionForPath, pathMatchesFn);
}
path.push('<temp>');
if (Array.isArray(val)) {
for (let i = 0; i < val.length; i++) {
path[path.length - 1] = i;
val[i] = coerceNumsImpl(val[i], path, getNumCoercionForPath, (p) => (0, utils_js_2.pathMatches)(path, p));
}
}
else {
for (const key of Object.keys(val)) {
path[path.length - 1] = key;
val[key] = coerceNumsImpl(val[key], path, getNumCoercionForPath, (p) => (0, utils_js_2.pathMatches)(path, p));
}
}
path.pop();
return val;
};