Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: buffer serialization #615

Merged
merged 1 commit into from
Feb 7, 2025
Merged
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
2 changes: 2 additions & 0 deletions packages/serialization/src/SerializerBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Serializer from './Serializer';
import ClassResolver from './interfaces/ClassResolver';
import ArraySerializer from './serializers/ArraySerializer';
import BigIntSerializer from './serializers/BigIntSerializer';
import BufferSerializer from './serializers/BufferSerializer';
import ClassSerializer from './serializers/ClassSerializer';
import DateSerializer from './serializers/DateSerializer';
import ErrorSerializer from './serializers/ErrorSerializer';
Expand Down Expand Up @@ -37,6 +38,7 @@ export default class SerializerBuilder
serializer.addSerializer(new MapSerializer());
serializer.addSerializer(new ArraySerializer());
serializer.addSerializer(new TypedArraySerializer());
serializer.addSerializer(new BufferSerializer());

return serializer;
}
Expand Down
39 changes: 39 additions & 0 deletions packages/serialization/src/serializers/BufferSerializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

import ValueSerializer from '../ValueSerializer';
import SerializedBuffer from '../types/serialized/SerializedBuffer';
import InvalidBufferString from './errors/InvalidBufferString';

export default class BufferSerializer extends ValueSerializer
{
canSerialize(value: unknown): boolean
{
return value instanceof Buffer;
}

canDeserialize(value: unknown): boolean
{
const buffer = value as SerializedBuffer;

return buffer instanceof Object
&& buffer.serialized === true
&& buffer.name === 'Buffer'
&& typeof buffer.base64 === 'string';
}

async serialize(buffer: Buffer): Promise<SerializedBuffer>
{
return { serialized: true, name: 'Buffer', base64: buffer.toString('base64') };
}

async deserialize(object: SerializedBuffer): Promise<Buffer>
{
try
{
return Buffer.from(object.base64, 'base64');
}
catch
{
throw new InvalidBufferString(object.base64);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

export default class InvalidBufferString extends Error
{
constructor(bufferString: string)
{
super(`Invalid Buffer string '${bufferString}'`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

import Serialized from '../Serialized';

type SerializedBuffer = Serialized &
{
base64: string
};

export default SerializedBuffer;
80 changes: 80 additions & 0 deletions packages/serialization/test/serializers/BufferSerializer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

import { describe, expect, it } from 'vitest';

import BufferSerializer from '../../src/serializers/BufferSerializer';

import { BUFFERS } from './fixtures';

const serializer = new BufferSerializer();

describe('serializers/BufferSerializer', () =>
{
describe('.canSerialize(value)', () =>
{
it('should tell it can serialize a buffer', () =>
{
const supportsBuffer = serializer.canSerialize(BUFFERS.VALID);

expect(supportsBuffer).toBeTruthy();
});

it('should tell it cannot serialize others', () =>
{
const supportsNonObject = serializer.canSerialize(BUFFERS.NON_OBJECT);
const supportsNonBuffer = serializer.canSerialize(BUFFERS.NON_BUFFER);

expect(supportsNonObject).toBeFalsy();
expect(supportsNonBuffer).toBeFalsy();
});
});

describe('.canDeserialize(value)', () =>
{
it('should tell it can deserialize a buffer', () =>
{
const supportsBuffer = serializer.canDeserialize(BUFFERS.VALID_SERIALIZED);

expect(supportsBuffer).toBeTruthy();
});

it('should tell it cannot deserialize others', () =>
{
const supportsNonObject = serializer.canDeserialize(BUFFERS.NON_OBJECT);
const supportsNotSerialized = serializer.canDeserialize(BUFFERS.NOT_SERIALIZED);
const supportsInvalidName = serializer.canDeserialize(BUFFERS.INVALID_NAME);
const supportsInvalidBufferValue = serializer.canDeserialize(BUFFERS.INVALID_BUFFER_VALUE);

expect(supportsNonObject).toBeFalsy();
expect(supportsNotSerialized).toBeFalsy();
expect(supportsInvalidName).toBeFalsy();
expect(supportsInvalidBufferValue).toBeFalsy();
});
});

describe('.serialize(buffer)', () =>
{
it('should serialize a buffer', async () =>
{
const resultValidBuffer = await serializer.serialize(BUFFERS.VALID);

expect(resultValidBuffer).toStrictEqual(BUFFERS.VALID_SERIALIZED);
});
});

describe('.deserialize(object)', () =>
{
it('should deserialize a buffer', async () =>
{
const resultValidBuffer = await serializer.deserialize(BUFFERS.VALID_SERIALIZED);

expect(resultValidBuffer).toStrictEqual(BUFFERS.VALID);
});

it('should ignore invalid characters in buffer string', async () =>
{
const resultInvalidBuffer = await serializer.deserialize(BUFFERS.INVALID_BUFFER_STRING);

expect(resultInvalidBuffer).toStrictEqual(BUFFERS.VALID);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

const VALID_BASE64_DATA = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABEElEQVR42mL8//8/AyUYIIgB';
const INVALID_BASE64_DATA = `##${VALID_BASE64_DATA}??`;

const validBuffer = Buffer.from(VALID_BASE64_DATA, 'base64');

const serializedValidBuffer = { serialized: true, name: 'Buffer', base64: VALID_BASE64_DATA };

const nonObject = 42;
const nonBuffer = new Map();
const notSerialized = { name: 'Buffer', base64: '' };
const invalidName = { serialized: true, name: 'Map', base64: VALID_BASE64_DATA };
const invalidBufferValue = { serialized: true, name: 'Buffer', base64: true };
const invalidBufferString = { serialized: true, name: 'Buffer', base64: INVALID_BASE64_DATA };

export const BUFFERS =
{
VALID: validBuffer,
VALID_SERIALIZED: serializedValidBuffer,
NON_OBJECT: nonObject,
NON_BUFFER: nonBuffer,
NOT_SERIALIZED: notSerialized,
INVALID_NAME: invalidName,
INVALID_BUFFER_VALUE: invalidBufferValue,
INVALID_BUFFER_STRING: invalidBufferString
};
1 change: 1 addition & 0 deletions packages/serialization/test/serializers/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

export * from './arrays.fixture';
export * from './bigIntegers.fixture';
export * from './buffers.fixture';
export * from './classes.fixture';
export * from './classResolver.fixture';
export * from './dates.fixture';
Expand Down