UNPKG

@datastax/astra-db-ts

Version:
103 lines (102 loc) 4.63 kB
// 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'); } }