@datastax/astra-db-ts
Version:
Data API TypeScript client
103 lines (102 loc) • 4.63 kB
JavaScript
// Copyright Datastax, Inc
// SPDX-License-Identifier: Apache-2.0
// Important to import from specific paths here to avoid circular dependencies
import { UUID } from '../../../documents/datatypes/uuid.js';
import { ObjectId } from '../../../documents/datatypes/object-id.js';
import { DataAPIVector, vector } from '../../../documents/datatypes/vector.js';
import { escapeFieldNames } from '../../../lib/index.js';
import { assertHasDeserializeFor, assertHasSerializeFor } from '../../../lib/api/ser-des/utils.js';
import { $DeserializeForCollection, $SerializeForCollection } from '../../../documents/collections/ser-des/constants.js';
import { SerDesTarget } from '../../../lib/api/ser-des/ctx.js';
import { betterTypeOf } from '../../../documents/utils.js';
export class CollectionCodecs {
static forId(clazz) {
assertIsCodecClass(clazz);
const { [$DeserializeForCollection]: deserialize } = clazz;
return [
CollectionCodecs.forName('', {
deserialize: (val, ctx) => ctx.target === SerDesTarget.InsertedId ? deserialize(val, ctx) : ctx.nevermind(),
}),
CollectionCodecs.forPath(['_id'], {
deserialize: (val, ctx) => ctx.target === SerDesTarget.Record ? deserialize(val, ctx) : ctx.nevermind(),
}),
].flat();
}
static forName(name, optsOrClass) {
validateIfCodecClass(optsOrClass);
return [{
tag: 'forName',
name: name,
opts: ($DeserializeForCollection in optsOrClass) ? { deserialize: optsOrClass[$DeserializeForCollection] } : optsOrClass,
}];
}
static forPath(path, optsOrClass) {
validateIfCodecClass(optsOrClass);
return [{
tag: 'forPath',
path: path,
opts: ($DeserializeForCollection in optsOrClass) ? { deserialize: optsOrClass[$DeserializeForCollection] } : optsOrClass,
}];
}
static forType(type, optsOrClass) {
validateIfCodecClass(optsOrClass);
return [{
tag: 'forType',
type: type,
opts: ($DeserializeForCollection in optsOrClass) ? { deserialize: optsOrClass[$DeserializeForCollection] } : optsOrClass,
}];
}
static custom(opts) {
return [{ tag: 'custom', opts: opts }];
}
static asCodecClass(clazz, fns) {
if (fns) {
if (!('prototype' in clazz)) {
throw new Error(`Cannot attach ser/des functions to non-class ${clazz}`);
}
clazz[$DeserializeForCollection] = fns.deserializeForCollection;
(clazz.prototype)[$SerializeForCollection] = fns.serializeForCollection;
}
assertIsCodecClass(clazz);
return clazz;
}
}
Object.defineProperty(CollectionCodecs, "Defaults", {
enumerable: true,
configurable: true,
writable: true,
value: {
$date: CollectionCodecs.forType('$date', {
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: date.valueOf() });
},
deserialize(value, ctx) {
return ctx.done(new Date(Number(value.$date)));
},
}),
$vector: CollectionCodecs.forName('$vector', {
serialize: (val, ctx) => (DataAPIVector.isVectorLike(val)) ? vector(val)[$SerializeForCollection](ctx) : ctx.nevermind(),
deserialize: DataAPIVector[$DeserializeForCollection],
}),
$uuid: CollectionCodecs.forType('$uuid', UUID),
$objectId: CollectionCodecs.forType('$objectId', ObjectId),
}
});
function assertIsCodecClass(clazz) {
if (typeof clazz !== 'function') {
throw new TypeError(`Invalid codec class: expected a constructor; got ${betterTypeOf(clazz)}`);
}
assertHasSerializeFor(clazz, $SerializeForCollection, '$SerializeForCollection');
assertHasDeserializeFor(clazz, $DeserializeForCollection, '$DeserializeForCollection');
}
function validateIfCodecClass(val) {
if (typeof val === 'function') {
// We can't check for $SerializeForCollection 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, $DeserializeForCollection, '$DeserializeForCollection');
}
}