Skip to content

Commit

Permalink
Merge pull request #324 from ulixee/extend-Typeserialiser
Browse files Browse the repository at this point in the history
feat(commons): extend typeserializer with array types
  • Loading branch information
blakebyrnes authored Oct 22, 2024
2 parents 0bbd60e + 6e23a45 commit c37f177
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 73 deletions.
21 changes: 3 additions & 18 deletions agent/main/test/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BrowserUtils, Helpers, TestLogger } from '@ulixee/unblocked-agent-testing/index';
import TypeSerializer, { stringifiedTypeSerializerClass } from '@ulixee/commons/lib/TypeSerializer';
import getTestObject from '@ulixee/commons/test/helpers/getTestObject';

import { CanceledPromiseError } from '@ulixee/commons/interfaces/IPendingWaitEvent';
import { Browser, BrowserContext } from '../index';

Expand All @@ -13,24 +15,7 @@ describe('basic tests', () => {
});

beforeAll(async () => {
testObject = {
name: 'original',
map: new Map<string, number>([
['1', 1],
['2', 2],
]),
set: new Set([1, 2, 3, 4]),
regex: /test13234/gi,
date: new Date('2021-03-17T15:41:06.513Z'),
buffer: Buffer.from('This is a test buffer'),
error: new CanceledPromiseError('This is canceled'),
};

testObject.nestedObject = { ...testObject, name: 'nested' };
testObject.nestedArray = [
{ ...testObject, name: 'item1' },
{ ...testObject, name: 'item2' },
];
testObject = getTestObject();

browser = new Browser(BrowserUtils.browserEngineOptions);
Helpers.onClose(() => browser.close(), true);
Expand Down
86 changes: 50 additions & 36 deletions commons/lib/TypeSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,30 +171,23 @@ export default class TypeSerializer {
}
return result;
}
}

if (ArrayBuffer.isView(value)) {
const binary = Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
.map(byte => String.fromCharCode(byte))
.join('');
return {
__type: Types.ArrayBuffer64,
value: globalThis.btoa(binary),
args: {
arrayType: value[Symbol.toStringTag],
byteOffset: value.byteOffset,
byteLength: value.byteLength,
},
};
}
if (value instanceof ArrayBuffer) {
const binary = Array.from(new Uint8Array(value))
.map(byte => String.fromCharCode(byte))
.join('');
return {
__type: Types.ArrayBuffer64,
value: globalThis.btoa(binary),
};
}
if (ArrayBuffer.isView(value)) {
const buffer = new Uint8Array(value.buffer);
return {
__type: Types.ArrayBuffer64,
value: this.Uint8ArrayToBase64String(buffer),
arrayType: value[Symbol.toStringTag],
};
}

if (value instanceof ArrayBuffer) {
const buffer = new Uint8Array(value);
return {
__type: Types.ArrayBuffer64,
value: this.Uint8ArrayToBase64String(buffer),
};
}

if (type === 'object' && 'toJSON' in value) {
Expand All @@ -204,6 +197,32 @@ export default class TypeSerializer {
return value;
}

private static Uint8ArrayToBase64String(value: Uint8Array): string {
const str = Array.from(value)
.map(byte => String.fromCharCode(byte))
.join('');

// base64 is only here for backwards compatability, this migth be removed
// in the future. Also 'binary' is needed to support communication between
// chrome and nodejs: https://stackoverflow.com/questions/23097928/node-js-throws-btoa-is-not-defined-error
if (this.isNodejs) {
return Buffer.from(str, 'binary').toString('base64');
}
return globalThis.btoa(str);
}

private static base64StringToUint8Array(value: string): Uint8Array {
const str = this.isNodejs
? Buffer.from(value, 'base64').toString('binary')
: globalThis.atob(value);

const uint8Array = new Uint8Array(new ArrayBuffer(str.length));
for (let i = 0; i < str.length; i++) {
uint8Array[i] = str.charCodeAt(i);
}
return uint8Array;
}

private static reviver(stackMarker: string, key: string, entry: any): any {
if (!entry || !entry.__type) return entry;

Expand All @@ -216,20 +235,15 @@ export default class TypeSerializer {
if (type === Types.NegativeInfinity) return Number.NEGATIVE_INFINITY;
if (type === Types.DateIso) return new Date(value);
if (type === Types.Buffer64 || type === Types.ArrayBuffer64) {
if (this.isNodejs) {
return Buffer.from(value, 'base64');
}

const decoded = globalThis.atob(value);
const uint8Array = new Uint8Array(new ArrayBuffer(decoded.length));
for (let i = 0; i < decoded.length; i++) {
uint8Array[i] = decoded.charCodeAt(i);
const buffer = this.base64StringToUint8Array(value);
if (!entry.arrayType) {
if (this.isNodejs) return Buffer.from(buffer);
// Chrome doesnt have buffer type in this case just dont parse it
// and leave as is. If needed you can still manually handle this
// case but its impossible for use here to know exactly what the goal is.
return entry;
}
if (!entry.args) return uint8Array;

const { arrayType, byteOffset, byteLength } = entry.args;

return new globalThis[arrayType](uint8Array.buffer, byteOffset, byteLength);
return new globalThis[entry.arrayType](buffer.buffer);
}
if (type === Types.RegExp) return new RegExp(value[0], value[1]);
if (type === Types.Map) {
Expand Down
21 changes: 2 additions & 19 deletions commons/test/TypeSerializer.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
import TypeSerializer from '../lib/TypeSerializer';
import { CanceledPromiseError } from '../interfaces/IPendingWaitEvent';
import getTestObject from './helpers/getTestObject';

let testObject: any;
beforeAll(() => {
testObject = {
name: 'original',
map: new Map<string, number>([
['1', 1],
['2', 2],
]),
set: new Set([1, 2, 3, 4]),
regex: /test13234/gi,
date: new Date('2021-03-17T15:41:06.513Z'),
buffer: Buffer.from('This is a test buffer'),
error: new CanceledPromiseError('This is canceled'),
};

testObject.nestedObject = { ...testObject, name: 'nested' };
testObject.nestedArray = [
{ ...testObject, name: 'item1' },
{ ...testObject, name: 'item2' },
];
testObject = getTestObject();
});

test('it should be able to serialize a complex object in nodejs', () => {
Expand Down
28 changes: 28 additions & 0 deletions commons/test/helpers/getTestObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CanceledPromiseError } from '../../interfaces/IPendingWaitEvent';

export default function getTestObject() {
const testObject: any = {
name: 'original',
map: new Map<string, number>([
['1', 1],
['2', 2],
]),
set: new Set([1, 2, 3, 4]),
regex: /test13234/gi,
date: new Date('2021-03-17T15:41:06.513Z'),
buffer: Buffer.from('This is a test buffer'),
error: new CanceledPromiseError('This is canceled'),
uintArray: Uint32Array.from([4, 5]),
intArray: Int32Array.from([4, 7]),
floatArray: Float32Array.from([4.1]),
float64Array: Float64Array.from([4.1, 5.3]),
};

testObject.nestedObject = { ...testObject, name: 'nested' };
testObject.nestedArray = [
{ ...testObject, name: 'item1' },
{ ...testObject, name: 'item2' },
];

return testObject;
}

0 comments on commit c37f177

Please sign in to comment.