@datastax/astra-db-ts
Version:
Data API TypeScript client
156 lines (155 loc) • 6.54 kB
JavaScript
// Copyright Datastax, Inc
// SPDX-License-Identifier: Apache-2.0
// Important to import from specific paths here to avoid circular dependencies
import { DataAPIBlob } from '../../../documents/datatypes/blob.js';
import { DataAPIDate } from '../../../documents/datatypes/date.js';
import { DataAPIDuration } from '../../../documents/datatypes/duration.js';
import { DataAPITime } from '../../../documents/datatypes/time.js';
import { UUID } from '../../../documents/datatypes/uuid.js';
import { DataAPIVector } from '../../../documents/datatypes/vector.js';
import { escapeFieldNames } from '../../../lib/index.js';
import { BigNumber } from 'bignumber.js';
import { $DeserializeForTable, $SerializeForTable } from '../../../documents/tables/ser-des/constants.js';
import { DataAPIInet } from '../../../documents/datatypes/inet.js';
import { betterTypeOf } from '../../../documents/utils.js';
import { assertHasDeserializeFor, assertHasSerializeFor } from '../../../lib/api/ser-des/utils.js';
export class TableCodecs {
static forName(name, optsOrClass) {
validateIfCodecClass(optsOrClass);
return [{
tag: 'forName',
name: name,
opts: ($DeserializeForTable in optsOrClass) ? { deserialize: optsOrClass[$DeserializeForTable] } : optsOrClass,
}];
}
static forPath(path, optsOrClass) {
validateIfCodecClass(optsOrClass);
return [{
tag: 'forPath',
path: path,
opts: ($DeserializeForTable in optsOrClass) ? { deserialize: optsOrClass[$DeserializeForTable] } : optsOrClass,
}];
}
static forType(type, optsOrClass) {
validateIfCodecClass(optsOrClass);
return [{
tag: 'forType',
type: type,
opts: ($DeserializeForTable in optsOrClass) ? { deserialize: optsOrClass[$DeserializeForTable] } : optsOrClass,
}];
}
static custom(opts) {
return [{ tag: 'custom', opts: opts }];
}
static asCodecClass(clazz, fns) {
if (fns) {
if (!('prototype' in clazz)) {
throw new TypeError(`Cannot attach ser/des functions to non-class ${clazz}`);
}
clazz[$DeserializeForTable] = fns.deserializeForTable;
(clazz.prototype)[$SerializeForTable] = fns.serializeForTable;
}
assertIsCodecClass(clazz);
return clazz;
}
}
Object.defineProperty(TableCodecs, "Defaults", {
enumerable: true,
configurable: true,
writable: true,
value: {
bigint: TableCodecs.forType('bigint', {
deserialize: (value, ctx) => ctx.done(BigInt(value)),
}),
blob: TableCodecs.forType('blob', DataAPIBlob),
counter: TableCodecs.forType('counter', {
deserialize: (value, ctx) => ctx.done(BigInt(value)),
}),
date: TableCodecs.forType('date', DataAPIDate),
decimal: TableCodecs.forType('decimal', {
deserialize: (value, ctx) => ctx.done(BigNumber(value)),
}),
double: TableCodecs.forType('double', {
deserialize: (value, ctx) => ctx.done(parseFloat(value)),
}),
duration: TableCodecs.forType('duration', DataAPIDuration),
float: TableCodecs.forType('float', {
deserialize: (value, ctx) => ctx.done(parseFloat(value)),
}),
int: TableCodecs.forType('int', {
deserialize: (value, ctx) => ctx.done(value),
}),
inet: TableCodecs.forType('inet', DataAPIInet),
smallint: TableCodecs.forType('smallint', {
deserialize: (value, ctx) => ctx.done(value),
}),
time: TableCodecs.forType('time', DataAPITime),
timestamp: TableCodecs.forType('timestamp', {
serializeClass: Date,
serialize(date, ctx) {
if (isNaN(date.valueOf())) {
throw new Error(`Can not serialize an invalid date (at '${escapeFieldNames(ctx.path)}')`);
}
return ctx.done(date.toISOString());
},
deserialize(value, ctx) {
return ctx.done(new Date(value));
},
}),
timeuuid: TableCodecs.forType('timeuuid', UUID),
tinyint: TableCodecs.forType('tinyint', {
deserialize: (value, ctx) => ctx.done(value),
}),
uuid: TableCodecs.forType('uuid', UUID),
vector: TableCodecs.forType('vector', DataAPIVector),
varint: TableCodecs.forType('varint', {
deserialize: (value, ctx) => {
return ctx.done(BigInt(value));
},
}),
map: TableCodecs.forType('map', {
serializeClass: Map,
serialize: (value, ctx) => {
if (value.size) {
return ctx.recurse([...value.entries()]);
}
else {
return ctx.done({}); // BUG https://github.com/stargate/data-api/issues/2005 - can not pass an empty array for a map
}
},
deserialize(_, ctx) {
ctx.mapAfter((es) => new Map(Array.isArray(es) ? es : Object.entries(es)));
return ctx.recurse();
},
}),
list: TableCodecs.forType('list', {
deserialize(_, ctx) {
return ctx.recurse();
},
}),
set: TableCodecs.forType('set', {
serializeClass: Set,
serialize: (value, ctx) => {
return ctx.recurse([...value]);
},
deserialize(_, ctx) {
ctx.mapAfter((es) => new Set(es));
return ctx.recurse();
},
}),
}
});
function assertIsCodecClass(clazz) {
if (typeof clazz !== 'function') {
throw new TypeError(`Invalid codec class: expected a constructor; got ${betterTypeOf(clazz)}`);
}
assertHasSerializeFor(clazz, $SerializeForTable, '$SerializeForTable');
assertHasDeserializeFor(clazz, $DeserializeForTable, '$DeserializeForTable');
}
function validateIfCodecClass(val) {
if (typeof val === 'function') {
// We can't check for $SerializeForTable here because it may not be on the prototype, depending on how it's
// implemented in the class. This at least helps catch cases when a completely wrong class is passed.
assertHasDeserializeFor(val, $DeserializeForTable, '$DeserializeForTable');
}
}