diff --git a/src/Encoder.ts b/src/Encoder.ts index cda0822..73fccc5 100644 --- a/src/Encoder.ts +++ b/src/Encoder.ts @@ -60,6 +60,14 @@ export type EncoderOptions = Partial< */ ignoreUndefined: boolean; + /** + * If `true`, undefineds are not handled by the library and are instead + * made available to extension codecs + * + * Defaults to `false` + */ + allowUndefinedCustomEncoding: boolean; + /** * If `true`, integer numbers are encoded as floating point numbers, * with the `forceFloat32` option taken into account. @@ -80,6 +88,7 @@ export class Encoder { private readonly sortKeys: boolean; private readonly forceFloat32: boolean; private readonly ignoreUndefined: boolean; + private readonly allowUndefinedCustomEncoding: boolean; private readonly forceIntegerToFloat: boolean; private pos: number; @@ -96,6 +105,7 @@ export class Encoder { this.sortKeys = options?.sortKeys ?? false; this.forceFloat32 = options?.forceFloat32 ?? false; this.ignoreUndefined = options?.ignoreUndefined ?? false; + this.allowUndefinedCustomEncoding = options?.allowUndefinedCustomEncoding ?? false; this.forceIntegerToFloat = options?.forceIntegerToFloat ?? false; this.pos = 0; @@ -132,7 +142,8 @@ export class Encoder { throw new Error(`Too deep objects in depth ${depth}`); } - if (object == null) { + const objectIsNil = this.allowUndefinedCustomEncoding ? object === null : object == null + if (objectIsNil) { this.encodeNil(); } else if (typeof object === "boolean") { this.encodeBoolean(object); diff --git a/test/ExtensionCodec.test.ts b/test/ExtensionCodec.test.ts index 21ae8db..e825eca 100644 --- a/test/ExtensionCodec.test.ts +++ b/test/ExtensionCodec.test.ts @@ -96,6 +96,32 @@ describe("ExtensionCodec", () => { }); }); + context("undefined", () => { + const extensionCodec = new ExtensionCodec(); + + // undefined + extensionCodec.register({ + type: 0x1, + encode: (object: unknown): Uint8Array | null => { + if (object === undefined) { + return new Uint8Array(0); + } + return null; + }, + decode: (data: Uint8Array) => { + if (data.length === 0) { + return undefined; + } + throw new Error("invalid data"); + }, + }); + + it("encodes and decodes undefined (synchronously)", () => { + const encoded = encode([undefined], { extensionCodec, allowUndefinedCustomEncoding: true }); + assert.deepStrictEqual(decode(encoded, { extensionCodec }), [undefined]); + }); + }); + context("custom extensions with custom context", () => { class Context { public expectations: Array = [];