UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

186 lines (161 loc) 4.81 kB
import { AlephaError } from "../errors/AlephaError.ts"; import { $inject } from "../primitives/$inject.ts"; import { JsonSchemaCodec } from "./JsonSchemaCodec.ts"; import { KeylessJsonSchemaCodec } from "./KeylessJsonSchemaCodec.ts"; import type { SchemaCodec } from "./SchemaCodec.ts"; import { SchemaValidator, type ValidateOptions } from "./SchemaValidator.ts"; import type { Static, StaticEncode, TSchema } from "./TypeProvider.ts"; export type Encoding = "object" | "string" | "binary"; export interface EncodeOptions<T extends Encoding = Encoding> { /** * The output encoding format: * - 'string': Returns JSON string * - 'binary': Returns Uint8Array (for protobuf, msgpack, etc.) * * @default "string" */ as?: T; /** * The encoder to use (e.g., 'json', 'protobuf', 'msgpack') * * @default "json" */ encoder?: string; /** * Validation options to apply before encoding. */ validation?: ValidateOptions | false; } export type EncodeResult< T extends TSchema, E extends Encoding, > = E extends "string" ? string : E extends "binary" ? Uint8Array : StaticEncode<T>; export interface DecodeOptions { /** * The encoder to use (e.g., 'json', 'protobuf', 'msgpack') * * @default "json" */ encoder?: string; /** * Validation options to apply before encoding. */ validation?: ValidateOptions | false; } /** * CodecManager manages multiple codec formats and provides a unified interface * for encoding and decoding data with different formats. */ export class CodecManager { protected readonly codecs: Map<string, SchemaCodec> = new Map(); protected readonly jsonCodec = $inject(JsonSchemaCodec); protected readonly keylessCodec = $inject(KeylessJsonSchemaCodec); protected readonly schemaValidator = $inject(SchemaValidator); public default = "json"; constructor() { // Register default JSON codec this.register({ name: "json", codec: this.jsonCodec, default: true, }); // Register keyless JSON codec (smaller, faster decoding) this.register({ name: "keyless", codec: this.keylessCodec, }); } /** * Register a new codec format. */ public register(opts: CodecRegisterOptions): void { this.codecs.set(opts.name, opts.codec); if (opts.default) { this.default = opts.name; } } /** * Get a specific codec by name. * * @param name - The name of the codec * @returns The codec instance * @throws {AlephaError} If the codec is not found */ public getCodec(name: string): SchemaCodec { const codec = this.codecs.get(name); if (!codec) { throw new AlephaError( `Codec "${name}" not found. Available codecs: ${Array.from(this.codecs.keys()).join(", ")}`, ); } return codec; } /** * Encode data using the specified codec and output format. */ public encode<T extends TSchema, E extends Encoding = "object">( schema: T, value: unknown, options?: EncodeOptions<E>, ): EncodeResult<T, E> { const codec = this.getCodec(options?.encoder ?? this.default); const as = options?.as ?? "object"; if (options?.validation !== false) { value = this.schemaValidator.validate(schema, value, options?.validation); } if (as === "object") { // Return the validated object as-is return value as EncodeResult<T, E>; } if (as === "binary") { // not used by JSON, but for other codecs like Protobuf, MsgPack, etc. return codec.encodeToBinary(schema, value as Static<T>) as EncodeResult< T, E >; } // encode directly to string return codec.encodeToString(schema, value as Static<T>) as EncodeResult< T, E >; } /** * Decode data using the specified codec. */ public decode<T extends TSchema>( schema: T, data: any, options?: DecodeOptions, ): Static<T> { const encoderName = options?.encoder ?? this.default; const codec = this.getCodec(encoderName); let value = codec.decode(schema, data); if (options?.validation !== false) { value = this.schemaValidator.validate(schema, value, options?.validation); } return value as Static<T>; } /** * Validate decoded data against the schema. * * This is automatically called before encoding or after decoding. */ public validate<T extends TSchema>( schema: T, value: unknown, options?: ValidateOptions, ): Static<T> { return this.schemaValidator.validate(schema, value, options); } } // --------------------------------------------------------------------------------------------------------------------- export interface CodecRegisterOptions { name: string; codec: SchemaCodec; default?: boolean; }