Skip to content

Commit

Permalink
feat: 🎸 add json encoding for snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 25, 2023
1 parent c1cd615 commit 41f9b8c
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/snapshot/__tests__/binary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ const data = {
},
};

test('sync and async snapshots are equivalent', async () => {
const { fs } = memfs(data);
fs.symlinkSync('/start/folder1/folder2/file6', '/start/folder1/symlink');
fs.writeFileSync('/start/binary', new Uint8Array([1, 2, 3]));
const snapshot1 = binary.toBinarySnapshotSync({ fs: fs, path: '/start' })!;
const snapshot2 = await binary.toBinarySnapshot({ fs: fs.promises, path: '/start' })!;
expect(snapshot1).toStrictEqual(snapshot2);
});

describe('synchronous', () => {
test('can create a binary snapshot and un-snapshot it back', () => {
const { fs } = memfs(data);
Expand Down
71 changes: 71 additions & 0 deletions src/snapshot/__tests__/json.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { memfs } from '../..';
import {SnapshotNodeType} from '../constants';
import * as json from '../json';

const data = {
'/start': {
file1: 'file1',
file2: 'file2',
'empty-folder': null,
'/folder1': {
file3: 'file3',
file4: 'file4',
'empty-folder': null,
'/folder2': {
file5: 'file5',
file6: 'file6',
'empty-folder': null,
'empty-folde2': null,
},
},
},
};

test('snapshot is a valid JSON', () => {
const { fs } = memfs(data);
fs.symlinkSync('/start/folder1/folder2/file6', '/start/folder1/symlink');
fs.writeFileSync('/start/binary', new Uint8Array([1, 2, 3]));
const snapshot = json.toJsonSnapshotSync({ fs, path: '/start' })!;
const pojo = JSON.parse(Buffer.from(snapshot).toString());
expect(Array.isArray(pojo)).toBe(true);
expect(pojo[0]).toBe(SnapshotNodeType.Folder);
});

test('sync and async snapshots are equivalent', async () => {
const { fs } = memfs(data);
fs.symlinkSync('/start/folder1/folder2/file6', '/start/folder1/symlink');
fs.writeFileSync('/start/binary', new Uint8Array([1, 2, 3]));
const snapshot1 = await json.toJsonSnapshotSync({ fs: fs, path: '/start' })!;
const snapshot2 = await json.toJsonSnapshot({ fs: fs.promises, path: '/start' })!;;
expect(snapshot1).toStrictEqual(snapshot2);
});

describe('synchronous', () => {
test('can create a binary snapshot and un-snapshot it back', () => {
const { fs } = memfs(data);
fs.symlinkSync('/start/folder1/folder2/file6', '/start/folder1/symlink');
fs.writeFileSync('/start/binary', new Uint8Array([1, 2, 3]));
const snapshot = json.toJsonSnapshotSync({ fs, path: '/start' })!;
const { fs: fs2, vol: vol2 } = memfs();
fs2.mkdirSync('/start', { recursive: true });
json.fromJsonSnapshotSync(snapshot, { fs: fs2, path: '/start' });
expect(fs2.readFileSync('/start/binary')).toStrictEqual(Buffer.from([1, 2, 3]));
const snapshot2 = json.toJsonSnapshotSync({ fs: fs2, path: '/start' })!;
expect(snapshot2).toStrictEqual(snapshot);
});
});

describe('asynchronous', () => {
test('can create a binary snapshot and un-snapshot it back', async () => {
const { fs } = memfs(data);
fs.symlinkSync('/start/folder1/folder2/file6', '/start/folder1/symlink');
fs.writeFileSync('/start/binary', new Uint8Array([1, 2, 3]));
const snapshot = await json.toJsonSnapshot({ fs: fs.promises, path: '/start' })!;
const { fs: fs2, vol: vol2 } = memfs();
fs2.mkdirSync('/start', { recursive: true });
await json.fromJsonSnapshot(snapshot, { fs: fs2.promises, path: '/start' });
expect(fs2.readFileSync('/start/binary')).toStrictEqual(Buffer.from([1, 2, 3]));
const snapshot2 = await json.toJsonSnapshot({ fs: fs2.promises, path: '/start' })!;
expect(snapshot2).toStrictEqual(snapshot);
});
});
3 changes: 2 additions & 1 deletion src/snapshot/binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import {CborEncoder} from 'json-joy/es6/json-pack/cbor/CborEncoder';
import {CborDecoder} from 'json-joy/es6/json-pack/cbor/CborDecoder';
import {fromSnapshotSync, toSnapshotSync} from './sync';
import {fromSnapshot, toSnapshot} from './async';
import {writer} from './shared';
import type {CborUint8Array} from 'json-joy/es6/json-pack/cbor/types';
import type {AsyncSnapshotOptions, SnapshotNode, SnapshotOptions} from './types';

const encoder = new CborEncoder();
const encoder = new CborEncoder(writer);
const decoder = new CborDecoder();

export const toBinarySnapshotSync = (options: SnapshotOptions): CborUint8Array<SnapshotNode> => {
Expand Down
1 change: 1 addition & 0 deletions src/snapshot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export type * from './types';
export * from './constants';
export * from './sync';
export * from './binary';
export * from './json';
32 changes: 32 additions & 0 deletions src/snapshot/json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {JsonEncoder} from 'json-joy/es6/json-pack/json/JsonEncoder';
import {JsonDecoder} from 'json-joy/es6/json-pack/json/JsonDecoder';
import {fromSnapshotSync, toSnapshotSync} from './sync';
import {fromSnapshot, toSnapshot} from './async';
import {writer} from './shared';
import type {AsyncSnapshotOptions, SnapshotNode, SnapshotOptions} from './types';

/** @todo Import this type from `json-joy` once it is available. */
export type JsonUint8Array<T> = Uint8Array & {__BRAND__: 'json'; __TYPE__: T};

const encoder = new JsonEncoder(writer);
const decoder = new JsonDecoder();

export const toJsonSnapshotSync = (options: SnapshotOptions): JsonUint8Array<SnapshotNode> => {
const snapshot = toSnapshotSync(options);
return encoder.encode(snapshot) as JsonUint8Array<SnapshotNode>;
};

export const fromJsonSnapshotSync = (uint8: JsonUint8Array<SnapshotNode>, options: SnapshotOptions): void => {
const snapshot = decoder.read(uint8) as SnapshotNode;
fromSnapshotSync(snapshot, options);
};

export const toJsonSnapshot = async (options: AsyncSnapshotOptions): Promise<JsonUint8Array<SnapshotNode>> => {
const snapshot = await toSnapshot(options);
return encoder.encode(snapshot) as JsonUint8Array<SnapshotNode>;
};

export const fromJsonSnapshot = async (uint8: JsonUint8Array<SnapshotNode>, options: AsyncSnapshotOptions): Promise<void> => {
const snapshot = decoder.read(uint8) as SnapshotNode;
await fromSnapshot(snapshot, options);
};
3 changes: 3 additions & 0 deletions src/snapshot/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {Writer} from 'json-joy/es6/util/buffers/Writer';

export const writer = new Writer(1024 * 32);

0 comments on commit 41f9b8c

Please sign in to comment.