Skip to content

Commit

Permalink
Ad support for File
Browse files Browse the repository at this point in the history
  • Loading branch information
lxsmnsyc committed Apr 9, 2023
1 parent 385e77a commit d598849
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 7 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,11 @@ The following values are the only values accepted by `seroval`:
- `Promise` (with `serializeAsync`)
- [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol)
- [Well-known symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#static_properties)
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- Web API
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
- [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File)
- Cyclic references (both self and mutual)
- Isomorphic references (a reference that exist on both the serializer and deserializer side)

Expand Down Expand Up @@ -358,6 +361,8 @@ By default, all feature flags are enabled. The following are the feature flags a
- Throws and disables the following usage:
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
- [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File)

## Sponsors

Expand Down
9 changes: 7 additions & 2 deletions packages/seroval/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,11 @@ The following values are the only values accepted by `seroval`:
- `Promise` (with `serializeAsync`)
- [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol)
- [Well-known symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#static_properties)
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- Web API
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
- [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File)
- Cyclic references (both self and mutual)
- Isomorphic references (a reference that exist on both the serializer and deserializer side)

Expand Down Expand Up @@ -358,6 +361,8 @@ By default, all feature flags are enabled. The following are the feature flags a
- Throws and disables the following usage:
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
- [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File)

## Sponsors

Expand Down
9 changes: 8 additions & 1 deletion packages/seroval/src/tree/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ import {
SerovalPromiseNode,
SerovalSetNode,
} from './types';
import { createBlobNode, createURLNode, createURLSearchParamsNode } from './web-api';
import {
createBlobNode,
createFileNode,
createURLNode,
createURLSearchParamsNode,
} from './web-api';

type ObjectLikeNode =
| SerovalObjectNode
Expand Down Expand Up @@ -458,6 +463,8 @@ async function parse<T>(
return createURLSearchParamsNode(ctx, id, current as unknown as URLSearchParams);
case Blob:
return createBlobNode(ctx, id, current as unknown as Blob);
case File:
return createFileNode(ctx, id, current as unknown as File);
default:
break;
}
Expand Down
18 changes: 17 additions & 1 deletion packages/seroval/src/tree/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
SerovalBlobNode,
SerovalDataViewNode,
SerovalErrorNode,
SerovalFileNode,
SerovalIterableNode,
SerovalMapNode,
SerovalNode,
Expand Down Expand Up @@ -262,7 +263,20 @@ function deserializeBlob(
const source = deserializeTree(ctx, node.f) as ArrayBuffer;
const result = assignIndexedValue(ctx, node.i, new Blob(
[source],
{ type: node.c },
{ type: deserializeString(node.c) },
));
return result;
}

function deserializeFile(
ctx: SerializationContext,
node: SerovalFileNode,
) {
const source = deserializeTree(ctx, node.f) as ArrayBuffer;
const result = assignIndexedValue(ctx, node.i, new File(
[source],
deserializeString(node.m),
{ type: deserializeString(node.c), lastModified: node.b },
));
return result;
}
Expand Down Expand Up @@ -332,6 +346,8 @@ export default function deserializeTree(
return assignIndexedValue(ctx, node.i, getReference(deserializeString(node.s)));
case SerovalNodeType.Blob:
return deserializeBlob(ctx, node);
case SerovalNodeType.File:
return deserializeFile(ctx, node);
default:
throw new Error('Unsupported type');
}
Expand Down
12 changes: 12 additions & 0 deletions packages/seroval/src/tree/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
SerovalArrayBufferNode,
SerovalDataViewNode,
SerovalBlobNode,
SerovalFileNode,
} from './types';

function getAssignmentExpression(assignment: Assignment): string {
Expand Down Expand Up @@ -587,6 +588,15 @@ function serializeBlob(
return assignIndexedValue(ctx, node.i, 'new Blob(' + args + ')');
}

function serializeFile(
ctx: SerializationContext,
node: SerovalFileNode,
) {
const options = '{type:"' + node.c + '",lastModified:' + node.b + '}';
const args = '[' + serializeTree(ctx, node.f) + '],"' + node.m + '",' + options;
return assignIndexedValue(ctx, node.i, 'new File(' + args + ')');
}

export default function serializeTree(
ctx: SerializationContext,
node: SerovalNode,
Expand Down Expand Up @@ -653,6 +663,8 @@ export default function serializeTree(
return assignIndexedValue(ctx, node.i, GLOBAL_KEY + '.get("' + node.s + '")');
case SerovalNodeType.Blob:
return assignIndexedValue(ctx, node.i, serializeBlob(ctx, node));
case SerovalNodeType.File:
return assignIndexedValue(ctx, node.i, serializeFile(ctx, node));
default:
throw new Error('Unsupported type');
}
Expand Down
17 changes: 16 additions & 1 deletion packages/seroval/src/tree/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const enum SerovalNodeType {
ArrayBuffer,
DataView,
Blob,
File,
}

export interface SerovalBaseNode {
Expand Down Expand Up @@ -323,6 +324,19 @@ export interface SerovalBlobNode extends SerovalBaseNode {
f: SerovalNode;
}

export interface SerovalFileNode extends SerovalBaseNode {
t: SerovalNodeType.File;
i: number;
// file type
c: string;
// file name
m: string;
// reference to array buffer
f: SerovalNode;
// last modified
b: number;
}

export type SerovalNode =
| SerovalPrimitiveNode
| SerovalIndexedValueNode
Expand All @@ -342,4 +356,5 @@ export type SerovalNode =
| SerovalReferenceNode
| SerovalArrayBufferNode
| SerovalDataViewNode
| SerovalBlobNode;
| SerovalBlobNode
| SerovalFileNode;
21 changes: 21 additions & 0 deletions packages/seroval/src/tree/web-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { serializeString } from '../string';
import { serializeArrayBuffer } from './primitives';
import {
SerovalBlobNode,
SerovalFileNode,
SerovalNodeType,
SerovalURLNode,
SerovalURLSearchParamsNode,
Expand Down Expand Up @@ -69,3 +70,23 @@ export async function createBlobNode(
b: undefined,
};
}

export async function createFileNode(
ctx: ParserContext,
id: number,
current: File,
): Promise<SerovalFileNode> {
assert(ctx.features & Feature.WebAPI, 'Unsupported type "File"');
return {
t: SerovalNodeType.File,
i: id,
s: undefined,
l: undefined,
c: serializeString(current.type),
m: serializeString(current.name),
d: undefined,
f: serializeArrayBuffer(ctx, await current.arrayBuffer()),
a: undefined,
b: current.lastModified,
};
}
5 changes: 5 additions & 0 deletions packages/seroval/test/web-api/__snapshots__/file.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Vitest Snapshot v1

exports[`File > serializeAsync > supports File 1`] = `"Promise.resolve(new File([new Uint8Array([72,101,108,108,111,32,87,111,114,108,100]).buffer],\\"hello.txt\\",{type:\\"text/plain\\",lastModified:1681027542680}))"`;

exports[`File > toJSONAsync > supports File 1`] = `"{\\"t\\":{\\"t\\":18,\\"i\\":0,\\"f\\":{\\"t\\":31,\\"i\\":1,\\"c\\":\\"text/plain\\",\\"m\\":\\"hello.txt\\",\\"f\\":{\\"t\\":28,\\"i\\":2,\\"s\\":[72,101,108,108,111,32,87,111,114,108,100]},\\"b\\":1681027542680}},\\"r\\":0,\\"i\\":false,\\"f\\":16383,\\"m\\":[]}"`;
39 changes: 39 additions & 0 deletions packages/seroval/test/web-api/file.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, it, expect } from 'vitest';
import 'node-fetch-native/polyfill';
import {
deserialize,
fromJSON,
serializeAsync,
toJSONAsync,
} from '../../src';

describe('File', () => {
describe('serializeAsync', () => {
it('supports File', async () => {
const example = new File(['Hello World'], 'hello.txt', {
type: 'text/plain',
lastModified: 1681027542680,
});
const result = await serializeAsync(Promise.resolve(example));
expect(result).toMatchSnapshot();
const back = await deserialize<Promise<File>>(result);
expect(back).toBeInstanceOf(File);
expect(await back.text()).toBe(await example.text());
expect(back.type).toBe(example.type);
});
});
describe('toJSONAsync', () => {
it('supports File', async () => {
const example = new File(['Hello World'], 'hello.txt', {
type: 'text/plain',
lastModified: 1681027542680,
});
const result = await toJSONAsync(Promise.resolve(example));
expect(JSON.stringify(result)).toMatchSnapshot();
const back = await fromJSON<Promise<File>>(result);
expect(back).toBeInstanceOf(File);
expect(await back.text()).toBe(await example.text());
expect(back.type).toBe(example.type);
});
});
});

0 comments on commit d598849

Please sign in to comment.