Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 72 additions & 25 deletions packages/dds/tree/src/codec/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,10 @@ export interface IJsonCodec<
TDecoded,
TEncoded = JsonCompatibleReadOnly,
TValidate = TEncoded,
TContext = void,
> extends IEncoder<TDecoded, TEncoded, TContext>,
IDecoder<TDecoded, TValidate, TContext> {
TEncodeContext = void,
TDecodeContext = TEncodeContext,
> extends IEncoder<TDecoded, TEncoded, TEncodeContext>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update TContext (line 175) in the doc comments now that it's TEncodeContext and TDecodeContext?

IDecoder<TDecoded, TValidate, TDecodeContext> {
encodedSchema?: TAnySchema;
}

Expand All @@ -211,9 +212,13 @@ export interface IJsonCodec<
*
* This portion of a codec is not responsible for managing versioning or validation of the data against the schema.
*/
export interface JsonCodecPart<TDecoded, TEncodedSchema extends TAnySchema, TContext = void>
extends IEncoder<TDecoded, Static<TEncodedSchema>, TContext>,
IDecoder<TDecoded, Static<TEncodedSchema>, TContext> {
export interface JsonCodecPart<
TDecoded,
TEncodedSchema extends TAnySchema,
TEncodeContext = void,
TDecodeContext = TEncodeContext,
> extends IEncoder<TDecoded, Static<TEncodedSchema>, TEncodeContext>,
IDecoder<TDecoded, Static<TEncodedSchema>, TDecodeContext> {
/**
* TypeBox schema which describes the encoded format for this chunk of data.
* @remarks
Expand All @@ -230,11 +235,18 @@ export function eraseEncodedType<
TDecoded,
TEncoded = JsonCompatibleReadOnly,
TValidate = TEncoded,
TContext = void,
TEncodeContext = void,
TDecodeContext = TEncodeContext,
>(
codec: IJsonCodec<TDecoded, TEncoded, TValidate, TContext>,
): IJsonCodec<TDecoded, TValidate, TValidate, TContext> {
return codec as unknown as IJsonCodec<TDecoded, TValidate, TValidate, TContext>;
codec: IJsonCodec<TDecoded, TEncoded, TValidate, TEncodeContext, TDecodeContext>,
): IJsonCodec<TDecoded, TValidate, TValidate, TEncodeContext, TDecodeContext> {
return codec as unknown as IJsonCodec<
TDecoded,
TValidate,
TValidate,
TEncodeContext,
TDecodeContext
>;
}

/**
Expand All @@ -246,11 +258,15 @@ export function eraseEncodedType<
* allows avoiding some duplicate work at encode/decode time, since the vast majority of document usage will not
* involve mixed format versions.
*
* @privateRemarks This interface currently assumes all codecs in a family require the same encode/decode context,
* which isn't necessarily true.
* This may need to be relaxed in the future.
* @privateRemarks
* Encode and decode can be parameterized independently — the encode-side context type need not equal the decode-side context type.
* `TDecodeContext` defaults to `TEncodeContext` so callers that share a single context shape (the common case) don't need to specify it.
*/
export interface ICodecFamily<TDecoded, TContext = void> {
export interface ICodecFamily<
TDecoded,
TEncodeContext = void,
TDecodeContext = TEncodeContext,
> {
/**
* @returns a codec that can be used to encode and decode data in the specified format.
* @throws if the format version is not supported by this family.
Expand All @@ -260,7 +276,13 @@ export interface ICodecFamily<TDecoded, TContext = void> {
*/
resolve(
formatVersion: FormatVersion,
): IJsonCodec<TDecoded, JsonCompatibleReadOnly, JsonCompatibleReadOnly, TContext>;
): IJsonCodec<
TDecoded,
JsonCompatibleReadOnly,
JsonCompatibleReadOnly,
TEncodeContext,
TDecodeContext
>;

/**
* @returns an iterable of all format versions supported by this family.
Expand Down Expand Up @@ -338,17 +360,29 @@ export const DependentFormatVersion = {
/**
* Creates a codec family from a registry of codecs.
*/
export function makeCodecFamily<TDecoded, TContext>(
export function makeCodecFamily<TDecoded, TEncodeContext, TDecodeContext = TEncodeContext>(
Comment thread
CraigMacomber marked this conversation as resolved.
registry: Iterable<
[
formatVersion: FormatVersion,
codec: IJsonCodec<TDecoded, JsonCompatibleReadOnly, JsonCompatibleReadOnly, TContext>,
codec: IJsonCodec<
TDecoded,
JsonCompatibleReadOnly,
JsonCompatibleReadOnly,
TEncodeContext,
TDecodeContext
>,
]
>,
): ICodecFamily<TDecoded, TContext> {
): ICodecFamily<TDecoded, TEncodeContext, TDecodeContext> {
const codecs: Map<
FormatVersion,
IJsonCodec<TDecoded, JsonCompatibleReadOnly, JsonCompatibleReadOnly, TContext>
IJsonCodec<
TDecoded,
JsonCompatibleReadOnly,
JsonCompatibleReadOnly,
TEncodeContext,
TDecodeContext
>
> = new Map();
for (const [formatVersion, codec] of registry) {
if (codecs.has(formatVersion)) {
Expand All @@ -360,7 +394,13 @@ export function makeCodecFamily<TDecoded, TContext>(
return {
resolve(
formatVersion: FormatVersion,
): IJsonCodec<TDecoded, JsonCompatibleReadOnly, JsonCompatibleReadOnly, TContext> {
): IJsonCodec<
TDecoded,
JsonCompatibleReadOnly,
JsonCompatibleReadOnly,
TEncodeContext,
TDecodeContext
> {
const codec = codecs.get(formatVersion);
assert(codec !== undefined, 0x5e6 /* Requested codec for unsupported format. */);
return codec;
Expand Down Expand Up @@ -395,25 +435,32 @@ export function withSchemaValidation<
EncodedSchema extends TSchema,
TEncodedFormat,
TValidate,
TContext,
TEncodeContext,
TDecodeContext = TEncodeContext,
>(
schema: EncodedSchema,
codec: IJsonCodec<TInMemoryFormat, TEncodedFormat, TValidate, TContext>,
codec: IJsonCodec<
TInMemoryFormat,
TEncodedFormat,
TValidate,
TEncodeContext,
TDecodeContext
>,
validator?: JsonValidator | FormatValidator,
): IJsonCodec<TInMemoryFormat, TEncodedFormat, TValidate, TContext> {
): IJsonCodec<TInMemoryFormat, TEncodedFormat, TValidate, TEncodeContext, TDecodeContext> {
if (!validator) {
return codec;
}
const compiledFormat = extractJsonValidator(validator).compile(schema);
return {
encode: (obj: TInMemoryFormat, context: TContext): TEncodedFormat => {
encode: (obj: TInMemoryFormat, context: TEncodeContext): TEncodedFormat => {
const encoded = codec.encode(obj, context);
if (!compiledFormat.check(encoded)) {
fail(0xac0 /* Encoded data should validate */);
}
return encoded;
},
decode: (encoded: TValidate, context: TContext): TInMemoryFormat => {
decode: (encoded: TValidate, context: TDecodeContext): TInMemoryFormat => {
if (!compiledFormat.check(encoded)) {
fail(0xac1 /* Data being decoded should validate */);
}
Expand Down
Loading
Loading